Merge "Splitting latency tracker test into two test classes" into main
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index af8ac24..25d487a 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -24,7 +24,6 @@
"aconfig_settingslib_flags_java_lib",
"aconfig_trade_in_mode_flags_java_lib",
"adpf_flags_java_lib",
- "android.adaptiveauth.flags-aconfig-java",
"android.app.appfunctions.flags-aconfig-java",
"android.app.assist.flags-aconfig-java",
"android.app.contextualsearch.flags-aconfig-java",
@@ -112,8 +111,8 @@
"com.android.window.flags.window-aconfig-java",
"configinfra_framework_flags_java_exported_lib",
"conscrypt_exported_aconfig_flags_lib",
- "sdk_sandbox_exported_flags_lib",
"device_policy_aconfig_flags_lib",
+ "devicelock-exported-aconfig-flags-lib",
"display_flags_lib",
"dropbox_flags_lib",
"framework-jobscheduler-job.flags-aconfig-java",
@@ -126,6 +125,8 @@
"libcore_readonly_aconfig_flags_lib",
"libgui_flags_java_lib",
"power_flags_lib",
+ "networksecurity_exported_aconfig_flags_lib",
+ "sdk_sandbox_exported_flags_lib",
"surfaceflinger_flags_java_lib",
"telecom_flags_core_java_lib",
"telephony_flags_core_java_lib",
@@ -218,6 +219,14 @@
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
+// Conscrypt - Networksecurity
+java_aconfig_library {
+ name: "networksecurity_exported_aconfig_flags_lib",
+ aconfig_declarations: "networksecurity-aconfig-flags",
+ mode: "exported",
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
+
// Telecom
java_aconfig_library {
name: "telecom_flags_core_java_lib",
@@ -874,9 +883,9 @@
min_sdk_version: "30",
apex_available: [
"//apex_available:platform",
+ "com.android.extservices",
"com.android.nfcservices",
"com.android.permission",
- "com.android.extservices",
],
}
@@ -1444,6 +1453,13 @@
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
+java_aconfig_library {
+ name: "android.app.supervision.flags-aconfig-java-host",
+ aconfig_declarations: "android.app.supervision.flags-aconfig",
+ host_supported: true,
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
+
// SurfaceFlinger
java_aconfig_library {
name: "surfaceflinger_flags_java_lib",
@@ -1665,20 +1681,6 @@
min_sdk_version: "30",
}
-// Adaptive Auth
-aconfig_declarations {
- name: "android.adaptiveauth.flags-aconfig",
- package: "android.adaptiveauth",
- container: "system",
- srcs: ["core/java/android/adaptiveauth/*.aconfig"],
-}
-
-java_aconfig_library {
- name: "android.adaptiveauth.flags-aconfig-java",
- aconfig_declarations: "android.adaptiveauth.flags-aconfig",
- defaults: ["framework-minus-apex-aconfig-java-defaults"],
-}
-
// CrashRecovery Module
aconfig_declarations {
name: "android.crashrecovery.flags-aconfig",
diff --git a/Android.bp b/Android.bp
index b2102f8..01e882b 100644
--- a/Android.bp
+++ b/Android.bp
@@ -251,10 +251,6 @@
"android.hardware.usb.gadget-V1.0-java",
"android.hardware.usb.gadget-V1.1-java",
"android.hardware.usb.gadget-V1.2-java",
- "android.hardware.vibrator-V1.0-java",
- "android.hardware.vibrator-V1.1-java",
- "android.hardware.vibrator-V1.2-java",
- "android.hardware.vibrator-V1.3-java",
"android.hardware.vibrator-V3-java",
"android.se.omapi-V1-java",
"android.system.suspend.control.internal-java",
diff --git a/apct-tests/perftests/aconfig/src/android/os/flagging/AconfigPackagePerfTest.java b/apct-tests/perftests/aconfig/src/android/os/flagging/AconfigPackagePerfTest.java
index e790874..c87ad98 100644
--- a/apct-tests/perftests/aconfig/src/android/os/flagging/AconfigPackagePerfTest.java
+++ b/apct-tests/perftests/aconfig/src/android/os/flagging/AconfigPackagePerfTest.java
@@ -48,7 +48,8 @@
return Arrays.asList(new Object[][] {{false}, {true}});
}
- private static final Set<String> PLATFORM_CONTAINERS = Set.of("system", "vendor", "product");
+ private static final Set<String> PLATFORM_CONTAINERS =
+ Set.of("system", "system_ext", "vendor", "product");
private static List<parsed_flag> sFlags;
@BeforeClass
diff --git a/apct-tests/perftests/core/src/android/graphics/perftests/BitmapPerfTest.java b/apct-tests/perftests/core/src/android/graphics/perftests/BitmapPerfTest.java
index abdf82f..8aff8b1 100644
--- a/apct-tests/perftests/core/src/android/graphics/perftests/BitmapPerfTest.java
+++ b/apct-tests/perftests/core/src/android/graphics/perftests/BitmapPerfTest.java
@@ -36,13 +36,7 @@
@Test
public void testParcelBitmap() {
BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
-
- // Make a large enough bitmap to be a good benchmark.
- Bitmap bitmap = Bitmap.createBitmap(1000, 1000, Bitmap.Config.ARGB_8888);
- Canvas canvas = new Canvas(bitmap);
- // Paint the canvas purple.
- // Purple is a good color for a benchmark. Purple benchmarks are the best.
- canvas.drawColor(Color.parseColor("purple"));
+ final Bitmap bitmap = makeBitmap();
while (state.keepRunning()) {
Parcel parcel = Parcel.obtain();
@@ -52,4 +46,25 @@
bitmap.recycle();
}
+
+ @Test
+ public void testBitmapAsShared() {
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final Bitmap bitmap = makeBitmap();
+
+ while (state.keepRunning()) {
+ Bitmap unused = bitmap.asShared();
+ }
+
+ bitmap.recycle();
+ }
+
+ private Bitmap makeBitmap() {
+ Bitmap bitmap = Bitmap.createBitmap(1000, 1000, Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(bitmap);
+ // Paint the canvas purple.
+ // Purple is a good color for a benchmark. Purple benchmarks are the best.
+ canvas.drawColor(Color.parseColor("purple"));
+ return bitmap;
+ }
}
diff --git a/apex/jobscheduler/service/aconfig/alarm.aconfig b/apex/jobscheduler/service/aconfig/alarm.aconfig
index 9181ef0..758621d 100644
--- a/apex/jobscheduler/service/aconfig/alarm.aconfig
+++ b/apex/jobscheduler/service/aconfig/alarm.aconfig
@@ -2,13 +2,6 @@
container: "system"
flag {
- name: "start_user_before_scheduled_alarms"
- namespace: "multiuser"
- description: "Persist list of users with alarms scheduled and wakeup stopped users before alarms are due"
- bug: "314907186"
-}
-
-flag {
name: "acquire_wakelock_before_send"
namespace: "backstage_power"
description: "Acquire the userspace alarm wakelock before sending the alarm"
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
index 44e4999..39d8bb7 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -1784,9 +1784,9 @@
mMetricsHelper = new MetricsHelper(getContext(), mLock);
mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
- mStartUserBeforeScheduledAlarms = Flags.startUserBeforeScheduledAlarms()
- && UserManager.supportsMultipleUsers() && Resources.getSystem().getBoolean(
- com.android.internal.R.bool.config_allowAlarmsOnStoppedUsers);
+ mStartUserBeforeScheduledAlarms = UserManager.supportsMultipleUsers()
+ && Resources.getSystem().getBoolean(
+ com.android.internal.R.bool.config_allowAlarmsOnStoppedUsers);
if (mStartUserBeforeScheduledAlarms) {
mUserWakeupStore = new UserWakeupStore();
mUserWakeupStore.init();
@@ -2992,9 +2992,6 @@
pw.println("Feature Flags:");
pw.increaseIndent();
- pw.print(Flags.FLAG_START_USER_BEFORE_SCHEDULED_ALARMS,
- Flags.startUserBeforeScheduledAlarms());
- pw.println();
pw.print(Flags.FLAG_ACQUIRE_WAKELOCK_BEFORE_SEND, Flags.acquireWakelockBeforeSend());
pw.println();
pw.decreaseIndent();
diff --git a/api/Android.bp b/api/Android.bp
index 14c2766..204d5d6 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -351,7 +351,6 @@
"android.hardware.usb-V1.0-java-constants",
"android.hardware.usb-V1.1-java-constants",
"android.hardware.usb.gadget-V1.0-java",
- "android.hardware.vibrator-V1.3-java",
"framework-protos",
]
diff --git a/core/api/Android.bp b/core/api/Android.bp
index 06eea52..359ce12 100644
--- a/core/api/Android.bp
+++ b/core/api/Android.bp
@@ -16,6 +16,7 @@
default_visibility: [
"//frameworks/base",
"//frameworks/base/api",
+ "//frameworks/base/tools/systemfeatures",
],
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
@@ -63,6 +64,7 @@
visibility: [
"//frameworks/base",
"//frameworks/base/api",
+ "//frameworks/base/tools/systemfeatures",
"//cts/tests/signature/api",
],
}
diff --git a/core/api/current.txt b/core/api/current.txt
index c28a4c9..5b1b1f7 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -6494,6 +6494,7 @@
method public long getTimeoutAfter();
method public boolean hasImage();
method @FlaggedApi("android.app.api_rich_ongoing") public boolean hasPromotableCharacteristics();
+ method @FlaggedApi("android.app.nm_summarization") public boolean hasSummarizedContent();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.media.AudioAttributes AUDIO_ATTRIBUTES_DEFAULT;
field public static final int BADGE_ICON_LARGE = 2; // 0x2
@@ -6800,6 +6801,7 @@
method @NonNull public android.app.Notification.Builder setGroup(String);
method @NonNull public android.app.Notification.Builder setGroupAlertBehavior(int);
method @NonNull public android.app.Notification.Builder setGroupSummary(boolean);
+ method @FlaggedApi("android.app.nm_summarization") @NonNull public android.app.Notification.Builder setHasSummarizedContent(boolean);
method @NonNull public android.app.Notification.Builder setLargeIcon(android.graphics.Bitmap);
method @NonNull public android.app.Notification.Builder setLargeIcon(android.graphics.drawable.Icon);
method @Deprecated public android.app.Notification.Builder setLights(@ColorInt int, int, int);
@@ -15138,14 +15140,14 @@
public final class SQLiteDatabase extends android.database.sqlite.SQLiteClosable {
method public void beginTransaction();
method public void beginTransactionNonExclusive();
- method @FlaggedApi("android.database.sqlite.sqlite_apis_35") public void beginTransactionReadOnly();
+ method public void beginTransactionReadOnly();
method public void beginTransactionWithListener(@Nullable android.database.sqlite.SQLiteTransactionListener);
method public void beginTransactionWithListenerNonExclusive(@Nullable android.database.sqlite.SQLiteTransactionListener);
- method @FlaggedApi("android.database.sqlite.sqlite_apis_35") public void beginTransactionWithListenerReadOnly(@Nullable android.database.sqlite.SQLiteTransactionListener);
+ method public void beginTransactionWithListenerReadOnly(@Nullable android.database.sqlite.SQLiteTransactionListener);
method public android.database.sqlite.SQLiteStatement compileStatement(String) throws android.database.SQLException;
method @NonNull public static android.database.sqlite.SQLiteDatabase create(@Nullable android.database.sqlite.SQLiteDatabase.CursorFactory);
method @NonNull public static android.database.sqlite.SQLiteDatabase createInMemory(@NonNull android.database.sqlite.SQLiteDatabase.OpenParams);
- method @FlaggedApi("android.database.sqlite.sqlite_apis_35") @NonNull public android.database.sqlite.SQLiteRawStatement createRawStatement(@NonNull String);
+ method @NonNull public android.database.sqlite.SQLiteRawStatement createRawStatement(@NonNull String);
method public int delete(@NonNull String, @Nullable String, @Nullable String[]);
method public static boolean deleteDatabase(@NonNull java.io.File);
method public void disableWriteAheadLogging();
@@ -15156,13 +15158,13 @@
method public void execSQL(@NonNull String, @NonNull Object[]) throws android.database.SQLException;
method public static String findEditTable(String);
method public java.util.List<android.util.Pair<java.lang.String,java.lang.String>> getAttachedDbs();
- method @FlaggedApi("android.database.sqlite.sqlite_apis_35") public long getLastChangedRowCount();
- method @FlaggedApi("android.database.sqlite.sqlite_apis_35") public long getLastInsertRowId();
+ method public long getLastChangedRowCount();
+ method public long getLastInsertRowId();
method public long getMaximumSize();
method public long getPageSize();
method public String getPath();
method @Deprecated public java.util.Map<java.lang.String,java.lang.String> getSyncedTables();
- method @FlaggedApi("android.database.sqlite.sqlite_apis_35") public long getTotalChangedRowCount();
+ method public long getTotalChangedRowCount();
method public int getVersion();
method public boolean inTransaction();
method public long insert(@NonNull String, @Nullable String, @Nullable android.content.ContentValues);
@@ -15384,7 +15386,7 @@
method public int update(@NonNull android.database.sqlite.SQLiteDatabase, @NonNull android.content.ContentValues, @Nullable String, @Nullable String[]);
}
- @FlaggedApi("android.database.sqlite.sqlite_apis_35") public final class SQLiteRawStatement implements java.io.Closeable {
+ public final class SQLiteRawStatement implements java.io.Closeable {
method public void bindBlob(int, @NonNull byte[]);
method public void bindBlob(int, @NonNull byte[], int, int);
method public void bindDouble(int, double);
@@ -44101,6 +44103,7 @@
field public static final String EVENT_CALL_REMOTELY_UNHELD = "android.telecom.event.CALL_REMOTELY_UNHELD";
field @FlaggedApi("com.android.server.telecom.flags.call_sequencing_call_resume_failed") public static final String EVENT_CALL_RESUME_FAILED = "android.telecom.event.CALL_RESUME_FAILED";
field public static final String EVENT_CALL_SWITCH_FAILED = "android.telecom.event.CALL_SWITCH_FAILED";
+ field @FlaggedApi("com.android.server.telecom.flags.revert_disconnecting_during_merge") public static final String EVENT_DISCONNECT_FAILED = "android.telecom.event.DISCONNECT_FAILED";
field public static final String EVENT_MERGE_COMPLETE = "android.telecom.event.MERGE_COMPLETE";
field public static final String EVENT_MERGE_START = "android.telecom.event.MERGE_START";
field public static final String EVENT_ON_HOLD_TONE_END = "android.telecom.event.ON_HOLD_TONE_END";
@@ -54437,6 +54440,7 @@
method public void requestPointerCapture();
method public boolean requestRectangleOnScreen(android.graphics.Rect);
method public boolean requestRectangleOnScreen(android.graphics.Rect, boolean);
+ method @FlaggedApi("android.view.accessibility.request_rectangle_with_source") public boolean requestRectangleOnScreen(@NonNull android.graphics.Rect, boolean, int);
method public final void requestUnbufferedDispatch(android.view.MotionEvent);
method public final void requestUnbufferedDispatch(int);
method @NonNull public final <T extends android.view.View> T requireViewById(@IdRes int);
@@ -54761,6 +54765,10 @@
field protected static final int[] PRESSED_SELECTED_WINDOW_FOCUSED_STATE_SET;
field protected static final int[] PRESSED_STATE_SET;
field protected static final int[] PRESSED_WINDOW_FOCUSED_STATE_SET;
+ field @FlaggedApi("android.view.accessibility.request_rectangle_with_source") public static final int RECTANGLE_ON_SCREEN_REQUEST_SOURCE_INPUT_FOCUS = 3; // 0x3
+ field @FlaggedApi("android.view.accessibility.request_rectangle_with_source") public static final int RECTANGLE_ON_SCREEN_REQUEST_SOURCE_SCROLL_ONLY = 1; // 0x1
+ field @FlaggedApi("android.view.accessibility.request_rectangle_with_source") public static final int RECTANGLE_ON_SCREEN_REQUEST_SOURCE_TEXT_CURSOR = 2; // 0x2
+ field @FlaggedApi("android.view.accessibility.request_rectangle_with_source") public static final int RECTANGLE_ON_SCREEN_REQUEST_SOURCE_UNDEFINED = 0; // 0x0
field @FlaggedApi("android.view.flags.toolkit_set_frame_rate_read_only") public static final float REQUESTED_FRAME_RATE_CATEGORY_DEFAULT = (0.0f/0.0f);
field @FlaggedApi("android.view.flags.toolkit_set_frame_rate_read_only") public static final float REQUESTED_FRAME_RATE_CATEGORY_HIGH = -4.0f;
field @FlaggedApi("android.view.flags.toolkit_set_frame_rate_read_only") public static final float REQUESTED_FRAME_RATE_CATEGORY_LOW = -2.0f;
@@ -54869,7 +54877,7 @@
ctor public View.MeasureSpec();
method public static int getMode(int);
method public static int getSize(int);
- method public static int makeMeasureSpec(@IntRange(from=0, to=0x40000000 - 1) int, int);
+ method public static int makeMeasureSpec(@IntRange(from=0, to=0x3fffffff) int, int);
method public static String toString(int);
field public static final int AT_MOST = -2147483648; // 0x80000000
field public static final int EXACTLY = 1073741824; // 0x40000000
@@ -55314,6 +55322,7 @@
method public void recomputeViewAttributes(android.view.View);
method public void requestChildFocus(android.view.View, android.view.View);
method public boolean requestChildRectangleOnScreen(@NonNull android.view.View, android.graphics.Rect, boolean);
+ method @FlaggedApi("android.view.accessibility.request_rectangle_with_source") public default boolean requestChildRectangleOnScreen(@NonNull android.view.View, @NonNull android.graphics.Rect, boolean, int);
method public void requestDisallowInterceptTouchEvent(boolean);
method public void requestFitSystemWindows();
method public void requestLayout();
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 4855352..1cc1d82 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -159,6 +159,7 @@
field public static final String GET_APP_OPS_STATS = "android.permission.GET_APP_OPS_STATS";
field @FlaggedApi("android.app.bic_client") public static final String GET_BACKGROUND_INSTALLED_PACKAGES = "android.permission.GET_BACKGROUND_INSTALLED_PACKAGES";
field @FlaggedApi("android.app.get_binding_uid_importance") public static final String GET_BINDING_UID_IMPORTANCE = "android.permission.GET_BINDING_UID_IMPORTANCE";
+ field @FlaggedApi("com.android.devicelock.flags.get_enrollment_type") public static final String GET_DEVICE_LOCK_ENROLLMENT_TYPE = "android.permission.GET_DEVICE_LOCK_ENROLLMENT_TYPE";
field public static final String GET_HISTORICAL_APP_OPS_STATS = "android.permission.GET_HISTORICAL_APP_OPS_STATS";
field public static final String GET_PROCESS_STATE_AND_OOM_SCORE = "android.permission.GET_PROCESS_STATE_AND_OOM_SCORE";
field public static final String GET_RUNTIME_PERMISSIONS = "android.permission.GET_RUNTIME_PERMISSIONS";
@@ -3417,6 +3418,31 @@
method @NonNull public android.companion.virtual.ActivityPolicyExemption.Builder setPackageName(@NonNull String);
}
+ @FlaggedApi("android.companion.virtualdevice.flags.viewconfiguration_apis") public final class ViewConfigurationParams implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public java.time.Duration getDoubleTapMinTimeDuration();
+ method @NonNull public java.time.Duration getDoubleTapTimeoutDuration();
+ method @FloatRange(from=0) public float getMaximumFlingVelocityDpPerSecond();
+ method @FloatRange(from=0) public float getMinimumFlingVelocityDpPerSecond();
+ method @FloatRange(from=0) public float getScrollFriction();
+ method @NonNull public java.time.Duration getTapTimeoutDuration();
+ method @FloatRange(from=0) public float getTouchSlopDp();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.companion.virtual.ViewConfigurationParams> CREATOR;
+ }
+
+ @FlaggedApi("android.companion.virtualdevice.flags.viewconfiguration_apis") public static final class ViewConfigurationParams.Builder {
+ ctor public ViewConfigurationParams.Builder();
+ method @NonNull public android.companion.virtual.ViewConfigurationParams build();
+ method @NonNull public android.companion.virtual.ViewConfigurationParams.Builder setDoubleTapMinTimeDuration(@NonNull java.time.Duration);
+ method @NonNull public android.companion.virtual.ViewConfigurationParams.Builder setDoubleTapTimeoutDuration(@NonNull java.time.Duration);
+ method @NonNull public android.companion.virtual.ViewConfigurationParams.Builder setMaximumFlingVelocityDpPerSecond(@FloatRange(from=0) float);
+ method @NonNull public android.companion.virtual.ViewConfigurationParams.Builder setMinimumFlingVelocityDpPerSecond(@FloatRange(from=0) float);
+ method @NonNull public android.companion.virtual.ViewConfigurationParams.Builder setScrollFriction(@FloatRange(from=0) float);
+ method @NonNull public android.companion.virtual.ViewConfigurationParams.Builder setTapTimeoutDuration(@NonNull java.time.Duration);
+ method @NonNull public android.companion.virtual.ViewConfigurationParams.Builder setTouchSlopDp(@FloatRange(from=0) float);
+ }
+
public final class VirtualDevice implements android.os.Parcelable {
method public boolean hasCustomAudioInputSupport();
method public boolean hasCustomCameraSupport();
@@ -3506,6 +3532,7 @@
method @Nullable public String getName();
method @FlaggedApi("android.companion.virtualdevice.flags.device_aware_display_power") @NonNull public java.time.Duration getScreenOffTimeout();
method @NonNull public java.util.Set<android.os.UserHandle> getUsersWithMatchingAccounts();
+ method @FlaggedApi("android.companion.virtualdevice.flags.viewconfiguration_apis") @Nullable public android.companion.virtual.ViewConfigurationParams getViewConfigurationParams();
method @NonNull public java.util.List<android.companion.virtual.sensor.VirtualSensorConfig> getVirtualSensorConfigs();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @Deprecated public static final int ACTIVITY_POLICY_DEFAULT_ALLOWED = 0; // 0x0
@@ -3545,6 +3572,7 @@
method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setName(@NonNull String);
method @FlaggedApi("android.companion.virtualdevice.flags.device_aware_display_power") @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setScreenOffTimeout(@NonNull java.time.Duration);
method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setUsersWithMatchingAccounts(@NonNull java.util.Set<android.os.UserHandle>);
+ method @FlaggedApi("android.companion.virtualdevice.flags.viewconfiguration_apis") @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setViewConfigurationParams(@Nullable android.companion.virtual.ViewConfigurationParams);
method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setVirtualSensorCallback(@NonNull java.util.concurrent.Executor, @NonNull android.companion.virtual.sensor.VirtualSensorCallback);
method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setVirtualSensorDirectChannelCallback(@NonNull java.util.concurrent.Executor, @NonNull android.companion.virtual.sensor.VirtualSensorDirectChannelCallback);
}
@@ -6114,7 +6142,7 @@
method @NonNull public android.hardware.input.VirtualTouchEvent.Builder setAction(int);
method @NonNull public android.hardware.input.VirtualTouchEvent.Builder setEventTimeNanos(long);
method @NonNull public android.hardware.input.VirtualTouchEvent.Builder setMajorAxisSize(@FloatRange(from=0.0f) float);
- method @NonNull public android.hardware.input.VirtualTouchEvent.Builder setPointerId(@IntRange(from=0, to=0x10 - 1) int);
+ method @NonNull public android.hardware.input.VirtualTouchEvent.Builder setPointerId(@IntRange(from=0, to=0xf) int);
method @NonNull public android.hardware.input.VirtualTouchEvent.Builder setPressure(@FloatRange(from=0.0f) float);
method @NonNull public android.hardware.input.VirtualTouchEvent.Builder setToolType(int);
method @NonNull public android.hardware.input.VirtualTouchEvent.Builder setX(float);
@@ -7373,6 +7401,7 @@
public class AudioDeviceVolumeManager {
method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_AUDIO_ROUTING, android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED}) public android.media.VolumeInfo getDeviceVolume(@NonNull android.media.VolumeInfo, @NonNull android.media.AudioDeviceAttributes);
method @FlaggedApi("android.media.audio.unify_absolute_volume_management") @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_AUDIO_ROUTING, "android.permission.QUERY_AUDIO_STATE", android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED}) public int getDeviceVolumeBehavior(@NonNull android.media.AudioDeviceAttributes);
+ method @FlaggedApi("android.media.audio.unify_absolute_volume_management") @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public void notifyAbsoluteVolumeChanged(@NonNull android.media.VolumeInfo, @NonNull android.media.AudioDeviceAttributes);
method @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_AUDIO_ROUTING, android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED}) public void setDeviceVolume(@NonNull android.media.VolumeInfo, @NonNull android.media.AudioDeviceAttributes);
method @FlaggedApi("android.media.audio.unify_absolute_volume_management") @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_AUDIO_ROUTING, android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED}) public void setDeviceVolumeBehavior(@NonNull android.media.AudioDeviceAttributes, int);
field @FlaggedApi("android.media.audio.unify_absolute_volume_management") public static final int DEVICE_VOLUME_BEHAVIOR_ABSOLUTE = 3; // 0x3
@@ -11615,6 +11644,8 @@
}
public final class Trace {
+ method @FlaggedApi("android.os.async_trace_for_track") public static void asyncTraceForTrackBegin(@NonNull String, @NonNull String, int);
+ method @FlaggedApi("android.os.async_trace_for_track") public static void asyncTraceForTrackEnd(@NonNull String, int);
field public static final long TRACE_TAG_AIDL = 16777216L; // 0x1000000L
}
@@ -11922,7 +11953,7 @@
method @WorkerThread public void allocateBytes(java.io.FileDescriptor, long, @RequiresPermission int) throws java.io.IOException;
method @WorkerThread public long getAllocatableBytes(@NonNull java.util.UUID, @RequiresPermission int) throws java.io.IOException;
method @RequiresPermission(android.Manifest.permission.WRITE_MEDIA_STORAGE) public int getExternalStorageMountMode(int, @NonNull String);
- method @FlaggedApi("android.os.storage_lifetime_api") @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getInternalStorageRemainingLifetime();
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getInternalStorageRemainingLifetime();
method public static boolean hasIsolatedStorage();
method public void updateExternalStorageFileQuotaType(@NonNull java.io.File, int) throws java.io.IOException;
field @RequiresPermission(android.Manifest.permission.ALLOCATE_AGGRESSIVE) public static final int FLAG_ALLOCATE_AGGRESSIVE = 1; // 0x1
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index a8fcecb..65db882 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -1813,6 +1813,7 @@
method @FlaggedApi("com.android.input.flags.device_associations") @RequiresPermission("android.permission.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY") public void removeUniqueIdAssociationByDescriptor(@NonNull String);
method @RequiresPermission("android.permission.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY") public void removeUniqueIdAssociationByPort(@NonNull String);
method public void resetLockedModifierState();
+ method @RequiresPermission(android.Manifest.permission.SET_POINTER_SPEED) public void setMouseScalingEnabled(boolean, int);
field public static final long BLOCK_UNTRUSTED_TOUCHES = 158002302L; // 0x96aec7eL
}
@@ -1958,6 +1959,7 @@
public class AudioDeviceVolumeManager {
method @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_AUDIO_ROUTING, android.Manifest.permission.QUERY_AUDIO_STATE, android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED}) public boolean isFullVolumeDevice();
+ method @FlaggedApi("android.media.audio.unify_absolute_volume_management") @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_AUDIO_ROUTING, android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED, android.Manifest.permission.BLUETOOTH_PRIVILEGED, "android.permission.BLUETOOTH_STACK"}) public void resetDeviceAbsoluteVolumeBehavior(@NonNull android.media.AudioDeviceAttributes);
}
public final class AudioFocusRequest {
diff --git a/core/java/android/adaptiveauth/flags.aconfig b/core/java/android/adaptiveauth/flags.aconfig
deleted file mode 100644
index d88d01a..0000000
--- a/core/java/android/adaptiveauth/flags.aconfig
+++ /dev/null
@@ -1,9 +0,0 @@
-package: "android.adaptiveauth"
-container: "system"
-
-flag {
- name: "report_biometric_auth_attempts"
- namespace: "biometrics"
- description: "Control the usage of the biometric auth signal in adaptive auth"
- bug: "285053096"
-}
\ No newline at end of file
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index f9f8a31..9191642 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -7372,11 +7372,16 @@
/**
* Check to see whether this activity is in the process of finishing,
* either because you called {@link #finish} on it or someone else
- * has requested that it finished. This is often used in
- * {@link #onPause} to determine whether the activity is simply pausing or
- * completely finishing.
+ * has requested that it finished.
*
- * @return If the activity is finishing, returns true; else returns false.
+ * <p>This is often used in {@link #onPause} to determine whether the activity is simply pausing
+ * or completely finishing. However, if the finish request is made after the activity has
+ * already been paused/stopped, there won't be another {@link #onPause} or {@link #onStop} with
+ * this API returning {@code true}.</p>
+ *
+ * <p>For example, if an activity is first minimized, and then gets killed in background,
+ * it should first receive {@link #onPause} and {@link #onStop} with this API returning
+ * {@code false}, and then receive {@link #onDestroy} with this API returning {@code true}.</p>
*
* @see #finish
*/
@@ -7394,9 +7399,18 @@
/**
* Check to see whether this activity is in the process of being destroyed in order to be
- * recreated with a new configuration. This is often used in
- * {@link #onStop} to determine whether the state needs to be cleaned up or will be passed
- * on to the next instance of the activity via {@link #onRetainNonConfigurationInstance()}.
+ * recreated with a new configuration.
+ *
+ * <p>This is often used in {@link #onStop} to determine whether the state needs to be cleaned
+ * up or will be passed on to the next instance of the activity via
+ * {@link #onRetainNonConfigurationInstance()}. However, if the activity has already been in the
+ * background as stopped, and then gets recreated with different configuration, there won't be
+ * another {@link #onPause} or {@link #onStop} with this API returning {@code true}.</p>
+ *
+ * <p>For example, if an activity that is not handling size configuration change is first
+ * minimized, and then get rotated with the display, it should first receive {@link #onPause}
+ * and {@link #onStop} with this API returning {@code false}, and then receive
+ * {@link #onDestroy} with this API returning {@code true}.</p>
*
* @return If the activity is being torn down in order to be recreated with a new configuration,
* returns true; else returns false.
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 40c5373..e589e39 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -238,6 +238,7 @@
import com.android.internal.app.IVoiceInteractor;
import com.android.internal.content.ReferrerIntent;
import com.android.internal.os.ApplicationSharedMemory;
+import com.android.internal.os.BackgroundThread;
import com.android.internal.os.BinderCallsStats;
import com.android.internal.os.BinderInternal;
import com.android.internal.os.DebugStore;
@@ -1359,17 +1360,15 @@
// This must be initialized as early as possible to ensure availability for any
// downstream callers.
- if (com.android.internal.os.Flags.applicationSharedMemoryEnabled()) {
- ApplicationSharedMemory instance =
- ApplicationSharedMemory.fromFileDescriptor(
- applicationSharedMemoryFd, /* mutable= */ false);
- if (android.content.pm.Flags.cacheSdkSystemFeatures()) {
- SystemFeaturesCache.setInstance(
- new SystemFeaturesCache(instance.readSystemFeaturesCache()));
- }
- instance.closeFileDescriptor();
- ApplicationSharedMemory.setInstance(instance);
+ ApplicationSharedMemory instance =
+ ApplicationSharedMemory.fromFileDescriptor(
+ applicationSharedMemoryFd, /* mutable= */ false);
+ if (android.content.pm.Flags.cacheSdkSystemFeatures()) {
+ SystemFeaturesCache.setInstance(
+ new SystemFeaturesCache(instance.readSystemFeaturesCache()));
}
+ instance.closeFileDescriptor();
+ ApplicationSharedMemory.setInstance(instance);
setCoreSettings(coreSettings);
@@ -4822,11 +4821,14 @@
final SurfaceControl.Transaction transaction = new SurfaceControl.Transaction();
transaction.hide(startingWindowLeash);
startingWindowLeash.release();
-
+ final boolean syncTransactionOnDraw =
+ com.android.window.flags.Flags.splashScreenViewSyncTransaction();
+ if (syncTransactionOnDraw) {
+ decorView.getViewRootImpl().applyTransactionOnDraw(transaction);
+ }
view.syncTransferSurfaceOnDraw();
- if (com.android.window.flags.Flags.useRtFrameCallbackForSplashScreenTransfer()
- && decorView.isHardwareAccelerated()) {
+ if (decorView.isHardwareAccelerated()) {
decorView.getViewRootImpl().registerRtFrameCallback(
new HardwareRenderer.FrameDrawingCallback() {
@Override
@@ -4836,7 +4838,9 @@
int syncResult, long frame) {
return didProduceBuffer -> {
Trace.instant(Trace.TRACE_TAG_VIEW, "transferSplashscreenView");
- transaction.apply();
+ if (!syncTransactionOnDraw) {
+ transaction.apply();
+ }
// Tell server we can remove the starting window after frame commit.
decorView.postOnAnimation(() ->
reportSplashscreenViewShown(token, view));
@@ -4845,7 +4849,9 @@
});
} else {
Trace.instant(Trace.TRACE_TAG_VIEW, "transferSplashscreenView_software");
- decorView.getViewRootImpl().applyTransactionOnDraw(transaction);
+ if (!syncTransactionOnDraw) {
+ decorView.getViewRootImpl().applyTransactionOnDraw(transaction);
+ }
// Tell server we can remove the starting window after frame commit.
decorView.postOnAnimation(() -> reportSplashscreenViewShown(token, view));
}
@@ -7794,6 +7800,16 @@
// Propagate Content Capture options
app.setContentCaptureOptions(data.contentCaptureOptions);
+ if (android.view.contentcapture.flags.Flags.warmUpBackgroundThreadForContentCapture()
+ && data.contentCaptureOptions != null) {
+ if (data.contentCaptureOptions.enableReceiver
+ && !data.contentCaptureOptions.lite) {
+ // Warm up the background thread when:
+ // 1) app is launched with content capture enabled, and
+ // 2) the app is NOT launched with content capture lite enabled.
+ BackgroundThread.startIfNeeded();
+ }
+ }
sendMessage(H.SET_CONTENT_CAPTURE_OPTIONS_CALLBACK, data.appInfo.packageName);
mInitialApplication = app;
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index cd22dac1..4ac27a8 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -2244,9 +2244,6 @@
if (!android.content.pm.Flags.cacheSdkSystemFeatures()) {
return false;
}
- if (!com.android.internal.os.Flags.applicationSharedMemoryEnabled()) {
- return false;
- }
if (ActivityThread.isSystem() && !SystemFeaturesCache.hasInstance()) {
// There are a handful of utility "system" processes that are neither system_server nor
// bound as applications. For these processes, we don't have access to application
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index cf4380e..5d2229d 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -21,6 +21,7 @@
import static android.os.StrictMode.vmIncorrectContextUseEnabled;
import static android.permission.flags.Flags.shouldRegisterAttributionSource;
import static android.view.WindowManager.LayoutParams.WindowType;
+import static android.window.WindowContext.registerCleaner;
import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
@@ -3429,11 +3430,23 @@
// WindowContainer. We should detach from WindowContainer when the Context is finalized
// if this Context is not a WindowContext. WindowContext finalization is handled in
// WindowContext class.
- if (mToken instanceof WindowTokenClient && mOwnsToken) {
- WindowTokenClientController.getInstance().detachIfNeeded(
- (WindowTokenClient) mToken);
+ try {
+ if (!isCleanerEnabled() && mToken instanceof WindowTokenClient && mOwnsToken) {
+ WindowTokenClientController.getInstance()
+ .detachIfNeeded((WindowTokenClient) mToken);
+ }
+ } finally {
+ super.finalize();
}
- super.finalize();
+ }
+
+ /**
+ * Returns {@code true} if {@link WindowContext#registerCleaner} is enabled.
+ */
+ private static boolean isCleanerEnabled() {
+ return com.android.window.flags.Flags.cleanUpWindowContextWithCleaner()
+ // Cleaner only works on SystemUiContext or WindowContext.
+ && com.android.window.flags.Flags.trackSystemUiContextBeforeWms();
}
@UnsupportedAppUsage
@@ -3476,7 +3489,9 @@
WindowTokenClientController.getInstance().attachToDisplayContent(token, displayId);
context.mContextType = CONTEXT_TYPE_SYSTEM_OR_SYSTEM_UI;
context.mOwnsToken = true;
-
+ if (isCleanerEnabled()) {
+ registerCleaner(systemUiContext);
+ }
return systemUiContext;
}
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index 6c6709b..01b2953 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -369,11 +369,6 @@
in RemoteCallback navigationObserver, in BackAnimationAdapter adaptor);
/**
- * Registers a callback to be invoked when the system server requests a back gesture.
- */
- void registerBackGestureDelegate(in RemoteCallback monitor);
-
- /**
* registers a callback to be invoked when a background activity launch is aborted.
*
* @param observer callback to be registered.
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index d0949ad..b6a363b 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -969,6 +969,15 @@
}
}
+ List<String> nativeSharedLibraries = new ArrayList<>();
+ if (mApplicationInfo.sharedLibraryInfos != null) {
+ for (SharedLibraryInfo info : mApplicationInfo.sharedLibraryInfos) {
+ if (info.isNative()) {
+ nativeSharedLibraries.add(info.getName());
+ }
+ }
+ }
+
// If we're not asked to include code, we construct a classloader that has
// no code path included. We still need to set up the library search paths
// and permitted path because NativeActivity relies on it (it attempts to
@@ -977,10 +986,14 @@
if (!mIncludeCode) {
if (mDefaultClassLoader == null) {
StrictMode.ThreadPolicy oldPolicy = allowThreadDiskReads();
- mDefaultClassLoader = ApplicationLoaders.getDefault().getClassLoader(
- "" /* codePath */, mApplicationInfo.targetSdkVersion, isBundledApp,
- librarySearchPath, libraryPermittedPath, mBaseClassLoader,
- null /* classLoaderName */);
+ mDefaultClassLoader = ApplicationLoaders.getDefault()
+ .getClassLoaderWithSharedLibraries(
+ "" /* codePath */, mApplicationInfo.targetSdkVersion, isBundledApp,
+ librarySearchPath, libraryPermittedPath, mBaseClassLoader,
+ null /* classLoaderName */,
+ null /* sharedLibraries */,
+ nativeSharedLibraries,
+ null /* sharedLibrariesLoadedAfterApp */);
setThreadPolicy(oldPolicy);
mAppComponentFactory = AppComponentFactory.DEFAULT;
}
@@ -1030,15 +1043,6 @@
createSharedLibrariesLoaders(mApplicationInfo.sharedLibraryInfos, isBundledApp,
librarySearchPath, libraryPermittedPath);
- List<String> nativeSharedLibraries = new ArrayList<>();
- if (mApplicationInfo.sharedLibraryInfos != null) {
- for (SharedLibraryInfo info : mApplicationInfo.sharedLibraryInfos) {
- if (info.isNative()) {
- nativeSharedLibraries.add(info.getName());
- }
- }
- }
-
mDefaultClassLoader = ApplicationLoaders.getDefault().getClassLoaderWithSharedLibraries(
zip, mApplicationInfo.targetSdkVersion, isBundledApp, librarySearchPath,
libraryPermittedPath, mBaseClassLoader,
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 2c190a8..b636534 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -17,6 +17,7 @@
package android.app;
import static android.annotation.Dimension.DP;
+import static android.app.Flags.FLAG_NM_SUMMARIZATION;
import static android.app.Flags.evenlyDividedCallStyleActionLayout;
import static android.app.Flags.notificationsRedesignTemplates;
import static android.app.admin.DevicePolicyResources.Drawables.Source.NOTIFICATION;
@@ -1242,6 +1243,12 @@
static public IBinder processAllowlistToken;
/**
+ * @hide
+ */
+ public static final String EXTRA_CONTAINS_SUMMARIZATION
+ = "android.app.extra.contains_summarization";
+
+ /**
* {@link #extras} key: this is the title of the notification,
* as supplied to {@link Builder#setContentTitle(CharSequence)}.
*/
@@ -4647,6 +4654,18 @@
}
}
+ /**
+ * Sets whether this notification contains text that was computationally summarized from
+ * other sources. The OS may choose to display this notification with different styling or
+ * choose not to summarize this content if this is true.
+ */
+ @FlaggedApi(Flags.FLAG_NM_SUMMARIZATION)
+ @NonNull
+ public Builder setHasSummarizedContent(boolean hasSummarizedContent) {
+ mN.extras.putBoolean(EXTRA_CONTAINS_SUMMARIZATION, hasSummarizedContent);
+ return this;
+ }
+
private ContrastColorUtil getColorUtil() {
if (mColorUtil == null) {
mColorUtil = ContrastColorUtil.getInstance(mContext);
@@ -8188,6 +8207,14 @@
}
/**
+ * Returns whether this notification contains computationally summarized text.
+ */
+ @FlaggedApi(FLAG_NM_SUMMARIZATION)
+ public boolean hasSummarizedContent() {
+ return extras != null && extras.getBoolean(EXTRA_CONTAINS_SUMMARIZATION);
+ }
+
+ /**
* Returns #when, unless it's set to 0, which should be shown as/treated as a 'current'
* notification. 0 is treated as a special value because it was special in an old version of
* android, and some apps are still (incorrectly) using it.
@@ -15216,7 +15243,9 @@
mBackgroundColor = ctx.getColor(R.color.materialColorSurfaceContainerHigh);
mPrimaryTextColor = ctx.getColor(R.color.materialColorOnSurface);
- mSecondaryTextColor = ctx.getColor(R.color.materialColorOnSurfaceVariant);
+ mSecondaryTextColor = Flags.notificationsRedesignFonts()
+ ? mPrimaryTextColor
+ : ctx.getColor(R.color.materialColorOnSurfaceVariant);
mPrimaryAccentColor = ctx.getColor(R.color.materialColorPrimary);
mSecondaryAccentColor = ctx.getColor(R.color.materialColorSecondary);
mTertiaryAccentColor = ctx.getColor(R.color.materialColorTertiary);
diff --git a/core/java/android/app/PropertyInvalidatedCache.java b/core/java/android/app/PropertyInvalidatedCache.java
index 6e49576..4a9cdd2 100644
--- a/core/java/android/app/PropertyInvalidatedCache.java
+++ b/core/java/android/app/PropertyInvalidatedCache.java
@@ -1197,8 +1197,7 @@
@android.ravenwood.annotation.RavenwoodReplace
private static boolean isSharedMemoryAvailable() {
- return com.android.internal.os.Flags.applicationSharedMemoryEnabled()
- && android.app.Flags.picUsesSharedMemory();
+ return true;
}
private static boolean isSharedMemoryAvailable$ravenwood() {
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index f35cbc0..fc5dea1 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -71,6 +71,8 @@
import android.bluetooth.BluetoothFrameworkInitializer;
import android.companion.CompanionDeviceManager;
import android.companion.ICompanionDeviceManager;
+import android.companion.datatransfer.continuity.ITaskContinuityManager;
+import android.companion.datatransfer.continuity.TaskContinuityManager;
import android.companion.virtual.IVirtualDeviceManager;
import android.companion.virtual.VirtualDeviceManager;
import android.compat.Compatibility;
@@ -1426,6 +1428,21 @@
}
});
+ if (android.companion.Flags.enableTaskContinuity()) {
+ registerService(Context.TASK_CONTINUITY_SERVICE, TaskContinuityManager.class,
+ new CachedServiceFetcher<TaskContinuityManager>() {
+ @Override
+ public TaskContinuityManager createService(ContextImpl ctx)
+ throws ServiceNotFoundException {
+ IBinder iBinder = ServiceManager.getServiceOrThrow(
+ Context.TASK_CONTINUITY_SERVICE);
+ ITaskContinuityManager service =
+ ITaskContinuityManager.Stub.asInterface(iBinder);
+ return new TaskContinuityManager(ctx, service);
+ }
+ });
+ }
+
registerService(Context.CROSS_PROFILE_APPS_SERVICE, CrossProfileApps.class,
new CachedServiceFetcher<CrossProfileApps>() {
@Override
diff --git a/core/java/android/app/UiAutomation.java b/core/java/android/app/UiAutomation.java
index 718f74f..bf9430c 100644
--- a/core/java/android/app/UiAutomation.java
+++ b/core/java/android/app/UiAutomation.java
@@ -1386,21 +1386,25 @@
@Nullable
public Bitmap takeScreenshot(@NonNull Window window) {
if (window == null) {
+ Log.e(LOG_TAG, "Window is null");
return null;
}
View decorView = window.peekDecorView();
if (decorView == null) {
+ Log.e(LOG_TAG, "Decor view is null");
return null;
}
ViewRootImpl viewRoot = decorView.getViewRootImpl();
if (viewRoot == null) {
+ Log.e(LOG_TAG, "View root is null");
return null;
}
SurfaceControl sc = viewRoot.getSurfaceControl();
if (!sc.isValid()) {
+ Log.e(LOG_TAG, "ViewRootImpl SurfaceControl is not valid");
return null;
}
diff --git a/core/java/android/app/contextualsearch/ContextualSearchManager.java b/core/java/android/app/contextualsearch/ContextualSearchManager.java
index 4e5fa6b..b5582ce 100644
--- a/core/java/android/app/contextualsearch/ContextualSearchManager.java
+++ b/core/java/android/app/contextualsearch/ContextualSearchManager.java
@@ -261,7 +261,8 @@
* <li>Resolves the activity using the package name and intent filter. The package name
* is fetched from the config specified in ContextualSearchManagerService.
* The activity must have ACTION_LAUNCH_CONTEXTUAL_SEARCH specified in its manifest.
- * <li>Puts the required extras in the launch intent.
+ * <li>Puts the required extras in the launch intent, which may include a
+ * {@link android.media.projection.MediaProjection} session.
* <li>Launches the activity.
* </ul>
* </p>
diff --git a/core/java/android/app/contextualsearch/flags.aconfig b/core/java/android/app/contextualsearch/flags.aconfig
index 1a14b20..c26c2c6 100644
--- a/core/java/android/app/contextualsearch/flags.aconfig
+++ b/core/java/android/app/contextualsearch/flags.aconfig
@@ -46,6 +46,16 @@
}
flag {
+ name: "contextual_search_media_projection"
+ namespace: "sysui_integrations"
+ description: "Attach MediaProjection session to contextual search invocation intent."
+ bug: "395054309"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "include_audio_playing_status"
namespace: "sysui_integrations"
description: "Add audio playing status to the contextual search invocation intent."
diff --git a/core/java/android/app/notification.aconfig b/core/java/android/app/notification.aconfig
index 73177a8..c8e6284 100644
--- a/core/java/android/app/notification.aconfig
+++ b/core/java/android/app/notification.aconfig
@@ -26,6 +26,14 @@
bug: "378660052"
}
+flag {
+ name: "notifications_redesign_fonts"
+ namespace: "systemui"
+ description: "Notifications Redesign: Update text styles in notification templates"
+ bug: "409496809"
+ is_fixed_read_only: true
+}
+
# Flag for finalized API: In Nextfood but exported (and therefore must stay).
flag {
name: "modes_api"
@@ -108,13 +116,6 @@
}
flag {
- name: "check_autogroup_before_post"
- namespace: "systemui"
- description: "Does a check to see if notification should be autogrouped before posting, and if so groups before post."
- bug: "330214226"
-}
-
-flag {
name: "notification_expansion_optional"
namespace: "systemui"
description: "Experiment to restore the pre-S behavior where standard notifications are not expandable unless they have actions."
@@ -129,13 +130,6 @@
}
flag {
- name: "keyguard_private_notifications"
- namespace: "systemui"
- description: "Fixes the behavior of KeyguardManager#setPrivateNotificationsAllowed()"
- bug: "309920145"
-}
-
-flag {
name: "category_voicemail"
is_exported: true
namespace: "wear_sysui"
@@ -199,27 +193,6 @@
}
flag {
- name: "restrict_audio_attributes_call"
- namespace: "systemui"
- description: "Only CallStyle notifs can use USAGE_NOTIFICATION_RINGTONE"
- bug: "331793339"
-}
-
-flag {
- name: "restrict_audio_attributes_alarm"
- namespace: "systemui"
- description: "Only alarm category notifs can use USAGE_ALARM"
- bug: "331793339"
-}
-
-flag {
- name: "restrict_audio_attributes_media"
- namespace: "systemui"
- description: "No notifs can use USAGE_UNKNOWN or USAGE_MEDIA"
- bug: "331793339"
-}
-
-flag {
name: "clean_up_spans_and_new_lines"
namespace: "systemui"
description: "Cleans up spans and unnecessary new lines from standard notification templates"
@@ -227,13 +200,6 @@
}
flag {
- name: "compact_heads_up_notification"
- namespace: "systemui"
- description: "[Minimal HUN] Enables the compact heads up notification feature"
- bug: "270709257"
-}
-
-flag {
name: "compact_heads_up_notification_reply"
namespace: "systemui"
description: "[Minimal HUN] Enables the compact heads up notification reply capability for Conversation Notifications"
diff --git a/core/java/android/app/performance.aconfig b/core/java/android/app/performance.aconfig
index 82875eb..0b817d8 100644
--- a/core/java/android/app/performance.aconfig
+++ b/core/java/android/app/performance.aconfig
@@ -3,15 +3,6 @@
flag {
namespace: "system_performance"
- name: "pic_uses_shared_memory"
- is_exported: true
- is_fixed_read_only: true
- description: "PropertyInvalidatedCache uses shared memory for nonces."
- bug: "360897450"
-}
-
-flag {
- namespace: "system_performance"
name: "enforce_pic_testmode_protocol"
is_exported: true
is_fixed_read_only: true
diff --git a/core/java/android/app/supervision/ISupervisionManager.aidl b/core/java/android/app/supervision/ISupervisionManager.aidl
index 801162f..0989492 100644
--- a/core/java/android/app/supervision/ISupervisionManager.aidl
+++ b/core/java/android/app/supervision/ISupervisionManager.aidl
@@ -17,16 +17,19 @@
package android.app.supervision;
import android.content.Intent;
+import android.app.supervision.SupervisionRecoveryInfo;
/**
* Internal IPC interface to the supervision service.
* {@hide}
*/
interface ISupervisionManager {
- Intent createConfirmSupervisionCredentialsIntent();
+ Intent createConfirmSupervisionCredentialsIntent(int userId);
boolean isSupervisionEnabledForUser(int userId);
void setSupervisionEnabledForUser(int userId, boolean enabled);
String getActiveSupervisionAppPackage(int userId);
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS)")
boolean shouldAllowBypassingSupervisionRoleQualification();
+ oneway void setSupervisionRecoveryInfo(in SupervisionRecoveryInfo recoveryInfo);
+ SupervisionRecoveryInfo getSupervisionRecoveryInfo();
}
diff --git a/core/java/android/app/supervision/SupervisionAppService.java b/core/java/android/app/supervision/SupervisionAppService.java
index 93eb962..c49f14c 100644
--- a/core/java/android/app/supervision/SupervisionAppService.java
+++ b/core/java/android/app/supervision/SupervisionAppService.java
@@ -18,6 +18,8 @@
import android.annotation.FlaggedApi;
import android.annotation.Nullable;
+import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.SystemApi;
import android.app.Service;
import android.app.supervision.flags.Flags;
@@ -33,6 +35,16 @@
@SystemApi
@FlaggedApi(Flags.FLAG_ENABLE_SUPERVISION_APP_SERVICE)
public class SupervisionAppService extends Service {
+ /**
+ * Service action: Action for a service that the {@code
+ * android.app.role.RoleManager.ROLE_SYSTEM_SUPERVISION} role holder must implement.
+ *
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.SERVICE_ACTION)
+ public static final String ACTION_BIND_SUPERVISION_APP_SERVICE =
+ "android.app.action.BIND_SUPERVISION_APP_SERVICE";
+
private final ISupervisionAppService mBinder =
new ISupervisionAppService.Stub() {
@Override
diff --git a/core/java/android/app/supervision/SupervisionManager.java b/core/java/android/app/supervision/SupervisionManager.java
index 76a789d3..e6437bc 100644
--- a/core/java/android/app/supervision/SupervisionManager.java
+++ b/core/java/android/app/supervision/SupervisionManager.java
@@ -105,7 +105,8 @@
public Intent createConfirmSupervisionCredentialsIntent() {
if (mService != null) {
try {
- Intent result = mService.createConfirmSupervisionCredentialsIntent();
+ Intent result =
+ mService.createConfirmSupervisionCredentialsIntent(mContext.getUserId());
if (result != null) {
result.prepareToEnterProcess(
Intent.LOCAL_FLAG_FROM_SYSTEM, mContext.getAttributionSource());
@@ -195,11 +196,9 @@
return null;
}
-
/**
* @return {@code true} if bypassing the qualification is allowed for the specified role based
- * on the current state of the device.
- *
+ * on the current state of the device.
* @hide
*/
@SystemApi
@@ -215,4 +214,36 @@
}
return false;
}
+
+ /**
+ * Sets the supervision recovery information.
+ *
+ * @hide
+ */
+ public void setSupervisionRecoveryInfo(SupervisionRecoveryInfo recoveryInfo) {
+ if (mService != null) {
+ try {
+ mService.setSupervisionRecoveryInfo(recoveryInfo);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
+ * Returns the supervision recovery information or null if recovery is not setup.
+ *
+ * @hide
+ */
+ @Nullable
+ public SupervisionRecoveryInfo getSupervisionRecoveryInfo() {
+ if (mService != null) {
+ try {
+ return mService.getSupervisionRecoveryInfo();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ return null;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/model/DualShadeEducationalTooltipModel.kt b/core/java/android/app/supervision/SupervisionRecoveryInfo.aidl
similarity index 62%
copy from packages/SystemUI/src/com/android/systemui/scene/domain/model/DualShadeEducationalTooltipModel.kt
copy to core/java/android/app/supervision/SupervisionRecoveryInfo.aidl
index f63e132..b8e2042d 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/model/DualShadeEducationalTooltipModel.kt
+++ b/core/java/android/app/supervision/SupervisionRecoveryInfo.aidl
@@ -14,11 +14,18 @@
* limitations under the License.
*/
-package com.android.systemui.scene.domain.model
+package android.app.supervision;
-/** Enumerates the type of tooltip to show. */
-enum class DualShadeEducationalTooltipModel {
- None,
- ForNotificationsShade,
- ForQuickSettingsShade,
+/**
+ * A parcelable of the supervision recovery information. This stores information for recovery
+ * purposes.
+ *
+ * <p>Email: The email for recovery. ID: The account id for recovery.
+ *
+ * @hide
+ */
+@JavaDerive(equals = true, toString = true)
+parcelable SupervisionRecoveryInfo {
+ @nullable String email;
+ @nullable String id;
}
diff --git a/packages/SystemUI/src/com/android/systemui/topwindoweffects/data/entity/SqueezeEffectCornerResourceId.kt b/core/java/android/companion/datatransfer/continuity/ITaskContinuityManager.aidl
similarity index 77%
copy from packages/SystemUI/src/com/android/systemui/topwindoweffects/data/entity/SqueezeEffectCornerResourceId.kt
copy to core/java/android/companion/datatransfer/continuity/ITaskContinuityManager.aidl
index a55301e..c97ec40 100644
--- a/packages/SystemUI/src/com/android/systemui/topwindoweffects/data/entity/SqueezeEffectCornerResourceId.kt
+++ b/core/java/android/companion/datatransfer/continuity/ITaskContinuityManager.aidl
@@ -14,6 +14,12 @@
* limitations under the License.
*/
-package com.android.systemui.topwindoweffects.data.entity
+package android.companion.datatransfer.continuity;
-data class SqueezeEffectCornerResourceId(val top: Int, val bottom: Int)
+/**
+ * Interface for communication with the task continuity service.
+ * {@hide}
+ */
+interface ITaskContinuityManager {
+
+}
diff --git a/core/java/android/companion/datatransfer/continuity/OWNERS b/core/java/android/companion/datatransfer/continuity/OWNERS
new file mode 100644
index 0000000..4a4cb1e
--- /dev/null
+++ b/core/java/android/companion/datatransfer/continuity/OWNERS
@@ -0,0 +1,2 @@
+include /core/java/android/companion/OWNERS
+joeantonetti@google.com
diff --git a/core/java/android/companion/datatransfer/continuity/TaskContinuityManager.java b/core/java/android/companion/datatransfer/continuity/TaskContinuityManager.java
new file mode 100644
index 0000000..67ef00a
--- /dev/null
+++ b/core/java/android/companion/datatransfer/continuity/TaskContinuityManager.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.companion.datatransfer.continuity;
+
+import android.annotation.SystemService;
+import android.content.Context;
+
+/**
+ * This class facilitates task continuity between devices owned by the same user.
+ * This includes synchronizing lists of open tasks between a user's devices, as well as requesting
+ * to hand off a task from one device to another. Handing a task off to a device will resume the
+ * application on the receiving device, preserving the state of the task.
+ *
+ * @hide
+ */
+@SystemService(Context.TASK_CONTINUITY_SERVICE)
+public class TaskContinuityManager {
+ private final Context mContext;
+ private final ITaskContinuityManager mService;
+
+ public TaskContinuityManager(Context context, ITaskContinuityManager service) {
+ mContext = context;
+ mService = service;
+ }
+}
diff --git a/core/java/android/companion/flags.aconfig b/core/java/android/companion/flags.aconfig
index 2b9f700..1b63d8e 100644
--- a/core/java/android/companion/flags.aconfig
+++ b/core/java/android/companion/flags.aconfig
@@ -64,3 +64,10 @@
description: "Enable set device icon API"
bug: "341057668"
}
+
+flag {
+ name: "enable_task_continuity"
+ namespace: "desktop_better_together"
+ description: "Enable task continuity API"
+ bug: "400970610"
+}
diff --git a/core/java/android/companion/virtual/ViewConfigurationParams.java b/core/java/android/companion/virtual/ViewConfigurationParams.java
new file mode 100644
index 0000000..d1280ad9
--- /dev/null
+++ b/core/java/android/companion/virtual/ViewConfigurationParams.java
@@ -0,0 +1,388 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.companion.virtual;
+
+import android.annotation.FlaggedApi;
+import android.annotation.FloatRange;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.companion.virtualdevice.flags.Flags;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.view.ViewConfiguration;
+
+import java.time.Duration;
+import java.util.Objects;
+
+/**
+ * Parameters related to {@link ViewConfiguration} that can be configured when creating a virtual
+ * device. When these parameters are set, {@link ViewConfiguration} methods would return the
+ * configured values for any context associated with the virtual device.
+ *
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(Flags.FLAG_VIEWCONFIGURATION_APIS)
+public final class ViewConfigurationParams implements Parcelable {
+
+ /**
+ * @hide
+ */
+ public static final int INVALID_VALUE = -1;
+
+ private final float mTouchSlopDp;
+ private final float mMinimumFlingVelocityDpPerSecond;
+ private final float mMaximumFlingVelocityDpPerSecond;
+ private final float mScrollFriction;
+ private final int mTapTimeoutMillis;
+ private final int mDoubleTapTimeoutMillis;
+ private final int mDoubleTapMinTimeMillis;
+
+ private ViewConfigurationParams(float touchSlopDp, float minimumFlingVelocityDpPerSecond,
+ float maximumFlingVelocityDpPerSecond, float scrollFriction, int tapTimeoutMillis,
+ int doubleTapTimeoutMillis, int doubleTapMinTimeMillis) {
+ mTouchSlopDp = touchSlopDp;
+ mMinimumFlingVelocityDpPerSecond = minimumFlingVelocityDpPerSecond;
+ mMaximumFlingVelocityDpPerSecond = maximumFlingVelocityDpPerSecond;
+ mScrollFriction = scrollFriction;
+ mTapTimeoutMillis = tapTimeoutMillis;
+ mDoubleTapTimeoutMillis = doubleTapTimeoutMillis;
+ mDoubleTapMinTimeMillis = doubleTapMinTimeMillis;
+ }
+
+ private ViewConfigurationParams(Parcel in) {
+ mTouchSlopDp = in.readFloat();
+ mMinimumFlingVelocityDpPerSecond = in.readFloat();
+ mMaximumFlingVelocityDpPerSecond = in.readFloat();
+ mScrollFriction = in.readFloat();
+ mTapTimeoutMillis = in.readInt();
+ mDoubleTapTimeoutMillis = in.readInt();
+ mDoubleTapMinTimeMillis = in.readInt();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeFloat(mTouchSlopDp);
+ dest.writeFloat(mMinimumFlingVelocityDpPerSecond);
+ dest.writeFloat(mMaximumFlingVelocityDpPerSecond);
+ dest.writeFloat(mScrollFriction);
+ dest.writeInt(mTapTimeoutMillis);
+ dest.writeInt(mDoubleTapTimeoutMillis);
+ dest.writeInt(mDoubleTapMinTimeMillis);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof ViewConfigurationParams that)) return false;
+ return Float.compare(mTouchSlopDp, that.mTouchSlopDp) == 0
+ && Float.compare(mMinimumFlingVelocityDpPerSecond,
+ that.mMinimumFlingVelocityDpPerSecond) == 0
+ && Float.compare(mMaximumFlingVelocityDpPerSecond,
+ that.mMaximumFlingVelocityDpPerSecond) == 0
+ && Float.compare(mScrollFriction, that.mScrollFriction) == 0
+ && mTapTimeoutMillis == that.mTapTimeoutMillis
+ && mDoubleTapTimeoutMillis == that.mDoubleTapTimeoutMillis
+ && mDoubleTapMinTimeMillis == that.mDoubleTapMinTimeMillis;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mTouchSlopDp, mMinimumFlingVelocityDpPerSecond,
+ mMaximumFlingVelocityDpPerSecond, mScrollFriction, mTapTimeoutMillis,
+ mDoubleTapTimeoutMillis, mDoubleTapMinTimeMillis);
+ }
+
+ @Override
+ @NonNull
+ public String toString() {
+ return "ViewConfigurationParams("
+ + "mTouchSlopDp=" + mTouchSlopDp
+ + ", mMinimumFlingVelocityDpPerSecond=" + mMinimumFlingVelocityDpPerSecond
+ + ", mMaximumFlingVelocityDpPerSecond=" + mMaximumFlingVelocityDpPerSecond
+ + ", mScrollFriction=" + mScrollFriction
+ + ", mTapTimeoutMillis=" + mTapTimeoutMillis
+ + ", mDoubleTapTimeoutMillis=" + mDoubleTapTimeoutMillis
+ + ", mDoubleTapMinTimeMillis=" + mDoubleTapMinTimeMillis
+ + ')';
+ }
+
+ /**
+ * Returns the touch slop in density independent pixels (dp).
+ *
+ * @see ViewConfiguration#getScaledTouchSlop()
+ */
+ @FloatRange(from = 0)
+ public float getTouchSlopDp() {
+ return mTouchSlopDp;
+ }
+
+ /**
+ * Returns the minimum fling velocity in density independent pixels (dp) per second.
+ *
+ * @see ViewConfiguration#getScaledMinimumFlingVelocity()
+ */
+ @FloatRange(from = 0)
+ public float getMinimumFlingVelocityDpPerSecond() {
+ return mMinimumFlingVelocityDpPerSecond;
+ }
+
+ /**
+ * Returns the maximum fling velocity in density independent pixels (dp) per second.
+ *
+ * @see ViewConfiguration#getScaledMaximumFlingVelocity()
+ */
+ @FloatRange(from = 0)
+ public float getMaximumFlingVelocityDpPerSecond() {
+ return mMaximumFlingVelocityDpPerSecond;
+ }
+
+ /**
+ * Returns the scroll friction.
+ *
+ * @see ViewConfiguration#getScrollFrictionAmount()
+ */
+ @FloatRange(from = 0)
+ public float getScrollFriction() {
+ return mScrollFriction;
+ }
+
+ /**
+ * Returns a {@link Duration} representing the tap timeout.
+ *
+ * @see ViewConfiguration#getTapTimeoutMillis()
+ */
+ @NonNull
+ public Duration getTapTimeoutDuration() {
+ return Duration.ofMillis(mTapTimeoutMillis);
+ }
+
+ /**
+ * Returns a {@link Duration} representing the double tap timeout.
+ *
+ * @see ViewConfiguration#getDoubleTapTimeoutMillis()
+ */
+ @NonNull
+ public Duration getDoubleTapTimeoutDuration() {
+ return Duration.ofMillis(mDoubleTapTimeoutMillis);
+ }
+
+ /**
+ * Returns a {@link Duration} representing the double tap minimum time.
+ */
+ @NonNull
+ public Duration getDoubleTapMinTimeDuration() {
+ return Duration.ofMillis(mDoubleTapMinTimeMillis);
+ }
+
+ @NonNull
+ public static final Creator<ViewConfigurationParams> CREATOR =
+ new Creator<>() {
+ @Override
+ public ViewConfigurationParams createFromParcel(Parcel in) {
+ return new ViewConfigurationParams(in);
+ }
+
+ @Override
+ public ViewConfigurationParams[] newArray(int size) {
+ return new ViewConfigurationParams[size];
+ }
+ };
+
+ /**
+ * Builder for {@link ViewConfigurationParams}.
+ */
+ @FlaggedApi(Flags.FLAG_VIEWCONFIGURATION_APIS)
+ public static final class Builder {
+
+ private float mTouchSlopDp = INVALID_VALUE;
+ private float mMinimumFlingVelocityDpPerSecond = INVALID_VALUE;
+ private float mMaximumFlingVelocityDpPerSecond = INVALID_VALUE;
+ private float mScrollFriction = INVALID_VALUE;
+ private int mTapTimeoutMillis = INVALID_VALUE;
+ private int mDoubleTapTimeoutMillis = INVALID_VALUE;
+ private int mDoubleTapMinTimeMillis = INVALID_VALUE;
+
+ /**
+ * Sets the touch slop in density independent pixels (dp). When this is set,
+ * {@link ViewConfiguration#getScaledTouchSlop()} would return this value multiplied by
+ * the display density for any context associated with the virtual device.
+ *
+ * @throws IllegalArgumentException if the value is negative.
+ * @see ViewConfiguration#getScaledTouchSlop()
+ */
+ @NonNull
+ public Builder setTouchSlopDp(@FloatRange(from = 0) float touchSlopDp) {
+ if (touchSlopDp < 0) {
+ throw new IllegalArgumentException("Touch slop cannot be negative");
+ }
+ mTouchSlopDp = touchSlopDp;
+ return this;
+ }
+
+ /**
+ * Sets the minimum fling velocity in density independent pixels (dp) per second. When
+ * this is set, {@link ViewConfiguration#getScaledMinimumFlingVelocity()} would return this
+ * value multiplied by the display density for any context associated with the virtual
+ * device.
+ *
+ * @throws IllegalArgumentException if the value is negative.
+ * @see ViewConfiguration#getScaledMinimumFlingVelocity()
+ */
+ @NonNull
+ public Builder setMinimumFlingVelocityDpPerSecond(
+ @FloatRange(from = 0) float minimumFlingVelocityDpPerSecond) {
+ if (minimumFlingVelocityDpPerSecond < 0) {
+ throw new IllegalArgumentException("Minimum ling velocity cannot be negative");
+ }
+ mMinimumFlingVelocityDpPerSecond = minimumFlingVelocityDpPerSecond;
+ return this;
+ }
+
+ /**
+ * Sets the maximum fling velocity in density independent pixels (dp) per second. When
+ * this is set, {@link ViewConfiguration#getScaledMaximumFlingVelocity()} would return this
+ * value multiplied by the display density for any context associated with the virtual
+ * device.
+ *
+ * @throws IllegalArgumentException if the value is negative.
+ * @see ViewConfiguration#getScaledMaximumFlingVelocity()
+ */
+ @NonNull
+ public Builder setMaximumFlingVelocityDpPerSecond(
+ @FloatRange(from = 0) float maximumFlingVelocityDpPerSecond) {
+ if (maximumFlingVelocityDpPerSecond < 0) {
+ throw new IllegalArgumentException("Maximum fling velocity cannot be negative");
+ }
+ mMaximumFlingVelocityDpPerSecond = maximumFlingVelocityDpPerSecond;
+ return this;
+ }
+
+ /**
+ * Sets the scroll friction.
+ *
+ * @throws IllegalArgumentException if the value is negative.
+ * @see ViewConfiguration#getScrollFrictionAmount()
+ */
+ @NonNull
+ public Builder setScrollFriction(@FloatRange(from = 0) float scrollFriction) {
+ if (scrollFriction < 0) {
+ throw new IllegalArgumentException("Scroll friction cannot be negative");
+ }
+ mScrollFriction = scrollFriction;
+ return this;
+ }
+
+ /**
+ * Sets the tap timeout as {@link Duration}.
+ *
+ * @throws IllegalArgumentException if the corresponding milliseconds value is negative, or
+ * greater than {@link Integer#MAX_VALUE}.
+ * @see ViewConfiguration#getTapTimeoutMillis()
+ */
+ @NonNull
+ public Builder setTapTimeoutDuration(@NonNull Duration duration) {
+ Objects.requireNonNull(duration);
+ if (duration.isNegative()) {
+ throw new IllegalArgumentException("Tap timeout cannot be negative");
+ }
+ long millis = duration.toMillis();
+ if (millis > Integer.MAX_VALUE) {
+ throw new IllegalArgumentException(
+ "Tap timeout cannot be larger than " + Integer.MAX_VALUE);
+ }
+ mTapTimeoutMillis = (int) millis;
+ return this;
+ }
+
+ /**
+ * Sets the double tap timeout as {@link Duration}.
+ *
+ * @throws IllegalArgumentException if the corresponding milliseconds value is negative, or
+ * greater than {@link Integer#MAX_VALUE}.
+ * @see ViewConfiguration#getDoubleTapTimeoutMillis()
+ */
+ @NonNull
+ public Builder setDoubleTapTimeoutDuration(@NonNull Duration duration) {
+ Objects.requireNonNull(duration);
+ if (duration.isNegative()) {
+ throw new IllegalArgumentException("Double tap timeout cannot be negative");
+ }
+ long millis = duration.toMillis();
+ if (millis > Integer.MAX_VALUE) {
+ throw new IllegalArgumentException(
+ "Double tap timeout cannot be larger than " + Integer.MAX_VALUE);
+ }
+ mDoubleTapTimeoutMillis = (int) millis;
+ return this;
+ }
+
+ /**
+ * Sets the double tap minimum time as {@link Duration}.
+ *
+ * @throws IllegalArgumentException if the corresponding milliseconds value is negative, or
+ * greater than {@link Integer#MAX_VALUE}.
+ */
+ @NonNull
+ public Builder setDoubleTapMinTimeDuration(@NonNull Duration duration) {
+ Objects.requireNonNull(duration);
+ if (duration.isNegative()) {
+ throw new IllegalArgumentException("Double tap min time cannot be negative");
+ }
+ long millis = duration.toMillis();
+ if (millis > Integer.MAX_VALUE) {
+ throw new IllegalArgumentException(
+ "Double tap min time cannot be larger than " + Integer.MAX_VALUE);
+ }
+ mDoubleTapMinTimeMillis = (int) millis;
+ return this;
+ }
+
+ /**
+ * Builds the {@link ViewConfigurationParams} instance.
+ *
+ * @throws IllegalArgumentException if there's no parameter set, or if the minimum fling
+ * velocity is greater than the maximum fling velocity.
+ */
+ @NonNull
+ public ViewConfigurationParams build() {
+ if (mTouchSlopDp == INVALID_VALUE
+ && mMinimumFlingVelocityDpPerSecond == INVALID_VALUE
+ && mMaximumFlingVelocityDpPerSecond == INVALID_VALUE
+ && mScrollFriction == INVALID_VALUE
+ && mTapTimeoutMillis == INVALID_VALUE
+ && mDoubleTapTimeoutMillis == INVALID_VALUE
+ && mDoubleTapMinTimeMillis == INVALID_VALUE) {
+ throw new IllegalArgumentException("None of the parameters are set");
+ }
+ if (mMinimumFlingVelocityDpPerSecond != INVALID_VALUE
+ && mMaximumFlingVelocityDpPerSecond != INVALID_VALUE
+ && mMinimumFlingVelocityDpPerSecond > mMaximumFlingVelocityDpPerSecond) {
+ throw new IllegalArgumentException(
+ "Minimum fling velocity cannot be greater than the maximum fling velocity");
+ }
+ return new ViewConfigurationParams(mTouchSlopDp, mMinimumFlingVelocityDpPerSecond,
+ mMaximumFlingVelocityDpPerSecond, mScrollFriction, mTapTimeoutMillis,
+ mDoubleTapTimeoutMillis, mDoubleTapMinTimeMillis);
+ }
+ }
+}
diff --git a/core/java/android/companion/virtual/VirtualDeviceParams.java b/core/java/android/companion/virtual/VirtualDeviceParams.java
index 6994947..6ad2b02 100644
--- a/core/java/android/companion/virtual/VirtualDeviceParams.java
+++ b/core/java/android/companion/virtual/VirtualDeviceParams.java
@@ -60,7 +60,7 @@
import java.util.concurrent.Executor;
/**
- * Params that can be configured when creating virtual devices.
+ * Parameters that can be configured when creating virtual devices.
*
* @hide
*/
@@ -331,6 +331,7 @@
private final int mAudioRecordingSessionId;
private final long mDimDuration;
private final long mScreenOffTimeout;
+ @Nullable private final ViewConfigurationParams mViewConfigurationParams;
private VirtualDeviceParams(
@LockState int lockState,
@@ -348,7 +349,8 @@
int audioPlaybackSessionId,
int audioRecordingSessionId,
long dimDuration,
- long screenOffTimeout) {
+ long screenOffTimeout,
+ @Nullable ViewConfigurationParams viewConfigurationParams) {
mLockState = lockState;
mUsersWithMatchingAccounts =
new ArraySet<>(Objects.requireNonNull(usersWithMatchingAccounts));
@@ -368,6 +370,7 @@
mAudioRecordingSessionId = audioRecordingSessionId;
mDimDuration = dimDuration;
mScreenOffTimeout = screenOffTimeout;
+ mViewConfigurationParams = viewConfigurationParams;
}
@SuppressWarnings("unchecked")
@@ -390,6 +393,8 @@
mInputMethodComponent = parcel.readTypedObject(ComponentName.CREATOR);
mDimDuration = parcel.readLong();
mScreenOffTimeout = parcel.readLong();
+ mViewConfigurationParams = Flags.viewconfigurationApis()
+ ? parcel.readTypedObject(ViewConfigurationParams.CREATOR) : null;
}
/**
@@ -619,6 +624,15 @@
return mAudioRecordingSessionId;
}
+ /**
+ * Returns the (optional) {@link ViewConfigurationParams} for the virtual device.
+ */
+ @Nullable
+ @FlaggedApi(Flags.FLAG_VIEWCONFIGURATION_APIS)
+ public ViewConfigurationParams getViewConfigurationParams() {
+ return mViewConfigurationParams;
+ }
+
@Override
public int describeContents() {
return 0;
@@ -643,6 +657,9 @@
dest.writeTypedObject(mInputMethodComponent, flags);
dest.writeLong(mDimDuration);
dest.writeLong(mScreenOffTimeout);
+ if (Flags.viewconfigurationApis()) {
+ dest.writeTypedObject(mViewConfigurationParams, flags);
+ }
}
@Override
@@ -679,7 +696,8 @@
&& mAudioPlaybackSessionId == that.mAudioPlaybackSessionId
&& mAudioRecordingSessionId == that.mAudioRecordingSessionId
&& mDimDuration == that.mDimDuration
- && mScreenOffTimeout == that.mScreenOffTimeout;
+ && mScreenOffTimeout == that.mScreenOffTimeout
+ && Objects.equals(mViewConfigurationParams, that.mViewConfigurationParams);
}
@Override
@@ -688,7 +706,8 @@
mLockState, mUsersWithMatchingAccounts, mCrossTaskNavigationExemptions,
mDefaultNavigationPolicy, mActivityPolicyExemptions, mDefaultActivityPolicy, mName,
mDevicePolicies, mHomeComponent, mInputMethodComponent, mAudioPlaybackSessionId,
- mAudioRecordingSessionId, mDimDuration, mScreenOffTimeout);
+ mAudioRecordingSessionId, mDimDuration, mScreenOffTimeout,
+ mViewConfigurationParams);
for (int i = 0; i < mDevicePolicies.size(); i++) {
hashCode = 31 * hashCode + mDevicePolicies.keyAt(i);
hashCode = 31 * hashCode + mDevicePolicies.valueAt(i);
@@ -714,6 +733,7 @@
+ " mAudioRecordingSessionId=" + mAudioRecordingSessionId
+ " mDimDuration=" + mDimDuration
+ " mScreenOffTimeout=" + mScreenOffTimeout
+ + " mViewConfigurationParams=" + mViewConfigurationParams
+ ")";
}
@@ -737,6 +757,7 @@
pw.println(prefix + "mAudioRecordingSessionId=" + mAudioRecordingSessionId);
pw.println(prefix + "mDimDuration=" + mDimDuration);
pw.println(prefix + "mScreenOffTimeout=" + mScreenOffTimeout);
+ pw.println(prefix + "mViewConfigurationParams=" + mViewConfigurationParams);
}
@NonNull
@@ -782,6 +803,7 @@
@Nullable private ComponentName mInputMethodComponent;
private Duration mDimDuration = Duration.ZERO;
private Duration mScreenOffTimeout = Duration.ZERO;
+ @Nullable private ViewConfigurationParams mViewConfigurationParams;
private static class VirtualSensorCallbackDelegate extends IVirtualSensorCallback.Stub {
@NonNull
@@ -1236,6 +1258,19 @@
}
/**
+ * Sets the optional {@link ViewConfigurationParams} for the virtual device.
+ *
+ * @see ViewConfigurationParams
+ */
+ @NonNull
+ @FlaggedApi(Flags.FLAG_VIEWCONFIGURATION_APIS)
+ public Builder setViewConfigurationParams(
+ @Nullable ViewConfigurationParams viewConfigurationParams) {
+ mViewConfigurationParams = viewConfigurationParams;
+ return this;
+ }
+
+ /**
* Builds the {@link VirtualDeviceParams} instance.
*
* @throws IllegalArgumentException if there's mismatch between policy definition and
@@ -1352,7 +1387,8 @@
mAudioPlaybackSessionId,
mAudioRecordingSessionId,
mDimDuration.toMillis(),
- mScreenOffTimeout.toMillis());
+ mScreenOffTimeout.toMillis(),
+ mViewConfigurationParams);
}
}
}
diff --git a/core/java/android/companion/virtual/flags/flags.aconfig b/core/java/android/companion/virtual/flags/flags.aconfig
index c29f152..9e9391c 100644
--- a/core/java/android/companion/virtual/flags/flags.aconfig
+++ b/core/java/android/companion/virtual/flags/flags.aconfig
@@ -173,3 +173,10 @@
description: "Allow external virtual cameras visible only in the Context of the virtual device"
bug: "375609768"
}
+
+flag {
+ name: "virtual_camera_no_frame_duplication"
+ namespace: "virtual_devices"
+ description: "Do not duplicate frames if no input is received and the capture request is not PREVIEW"
+ bug: "383779587"
+}
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index 385d6cfd..9606cb3 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -2781,8 +2781,7 @@
if (userId != UserHandle.USER_CURRENT
// getUserIdFromAuthority can return USER_NULL when can't cast the userId to
// an int, which can cause high volume of binder calls.
- && (!android.multiuser.Flags.fixGetUserPropertyCache()
- || userId != UserHandle.USER_NULL)
+ && userId != UserHandle.USER_NULL
&& userId != mContext.getUserId()
// Since userId specified in content uri, the provider userId would be
// determined from it.
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index c11393e..2479e06 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -6696,6 +6696,15 @@
@SystemApi
public static final String WEARABLE_SENSING_SERVICE = "wearable_sensing";
+ /**
+ * Use with {@link #getSystemService(String)} to retrieve a
+ * {@link android.companion.datatransfer.continuity.TaskContinuityManager}.
+ *
+ * @see #getSystemService(String)
+ * @see TaskContinuityManager
+ * @hide
+ */
+ public static final String TASK_CONTINUITY_SERVICE = "task_continuity";
/**
* Use with {@link #getSystemService(String)} to retrieve a
diff --git a/core/java/android/content/pm/LauncherActivityInfo.java b/core/java/android/content/pm/LauncherActivityInfo.java
index 760224f..b25a473 100644
--- a/core/java/android/content/pm/LauncherActivityInfo.java
+++ b/core/java/android/content/pm/LauncherActivityInfo.java
@@ -85,16 +85,15 @@
}
/**
- * Retrieves the label for the activity.
+ * Retrieves the label for the activity. If the activity's label is invisible
+ * for the user, use the application's label instead. If the application's label
+ * is still invisible for the user, use the package name instead.
*
- * @return The label for the activity.
+ * @return The label for the activity. If the activity's label is invisible for the user,
+ * return the application's label instead. If the application's label
+ * is still invisible for the user, return the package name instead.
*/
public CharSequence getLabel() {
- if (!Flags.lightweightInvisibleLabelDetection()) {
- // TODO: Go through LauncherAppsService
- return getActivityInfo().loadLabel(mPm);
- }
-
CharSequence label = getActivityInfo().loadLabel(mPm).toString().trim();
// If the activity label is visible to the user, return the original activity label
if (isVisible(label)) {
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 53966b8..b083874 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -3471,7 +3471,7 @@
/**
* Feature for {@link #getSystemAvailableFeatures} and
* {@link #hasSystemFeature}: The device can communicate using Near-Field
- * Communications (NFC).
+ * Communications (NFC), acting as a reader.
*/
@SdkConstant(SdkConstantType.FEATURE)
public static final String FEATURE_NFC = "android.hardware.nfc";
@@ -5817,6 +5817,7 @@
* {@link Context#getPackageManager}
*/
@Deprecated
+ @android.ravenwood.annotation.RavenwoodKeep
public PackageManager() {}
/**
diff --git a/core/java/android/content/pm/flags.aconfig b/core/java/android/content/pm/flags.aconfig
index 6f564f5..89fa5e3 100644
--- a/core/java/android/content/pm/flags.aconfig
+++ b/core/java/android/content/pm/flags.aconfig
@@ -34,14 +34,6 @@
}
flag {
- name: "nullable_data_dir"
- namespace: "package_manager_service"
- description: "Feature flag to allow ApplicationInfo.dataDir to be null."
- bug: "302587814"
- is_fixed_read_only: true
-}
-
-flag {
name: "get_package_info"
is_exported: true
namespace: "package_manager_service"
@@ -91,13 +83,6 @@
}
flag {
- name: "lightweight_invisible_label_detection"
- namespace: "package_manager_service"
- description: "Feature flag to detect the invisible labels in Launcher Apps"
- bug: "299586370"
-}
-
-flag {
name: "read_install_info"
is_exported: true
namespace: "package_manager_service"
@@ -113,13 +98,6 @@
}
flag {
- name: "improve_install_dont_kill"
- namespace: "package_manager_service"
- description: "Feature flag to reduce app crashes caused by split installs with INSTALL_DONT_KILL"
- bug: "291212866"
-}
-
-flag {
name: "relative_reference_intent_filters"
is_exported: true
namespace: "package_manager_service"
@@ -181,13 +159,6 @@
}
flag {
- name: "encode_app_intent"
- namespace: "package_manager_service"
- description: "Feature flag to encode app intent."
- bug: "281848623"
-}
-
-flag {
name: "recoverability_detection"
is_exported: true
namespace: "package_manager_service"
@@ -197,21 +168,6 @@
}
flag {
- name: "fix_system_apps_first_install_time"
- namespace: "package_manager_service"
- description: "Feature flag to fix the first-install timestamps for system apps."
- bug: "321258605"
- is_fixed_read_only: true
-}
-
-flag {
- name: "allow_sdk_sandbox_query_intent_activities"
- namespace: "package_manager_service"
- description: "Feature flag to allow the sandbox SDK to query intent activities of the client app."
- bug: "295842134"
-}
-
-flag {
name: "emergency_install_permission"
is_exported: true
namespace: "permissions"
@@ -229,14 +185,6 @@
}
flag {
- name: "force_multi_arch_native_libs_match"
- namespace: "package_manager_service"
- description: "Feature flag to force an multiArch app's native libraries to match with the natively supported ABIs of the device"
- bug: "282783453"
- is_fixed_read_only: true
-}
-
-flag {
name: "set_pre_verified_domains"
is_exported: true
namespace: "package_manager_service"
@@ -263,14 +211,6 @@
}
flag {
- name: "wait_application_killed"
- namespace: "package_manager_service"
- description: "Feature flag to control whether to wait until the application is killed when clear application data"
- bug: "31009094"
- is_fixed_read_only: true
-}
-
-flag {
name: "component_state_changed_metrics"
namespace: "package_manager_service"
description: "Feature flag to log the metrics when the component state is changed."
@@ -279,14 +219,6 @@
}
flag {
- name: "package_restart_query_disabled_by_default"
- namespace: "package_manager_service"
- description: "Feature flag to register broadcast receiver only support package restart query."
- bug: "300309050"
- is_fixed_read_only: true
-}
-
-flag {
name: "get_package_info_with_fd"
is_exported: true
namespace: "package_manager_service"
diff --git a/core/java/android/content/pm/multiuser.aconfig b/core/java/android/content/pm/multiuser.aconfig
index fefa8ab..9dc4bab 100644
--- a/core/java/android/content/pm/multiuser.aconfig
+++ b/core/java/android/content/pm/multiuser.aconfig
@@ -47,13 +47,6 @@
}
flag {
- name: "add_ui_for_sounds_from_background_users"
- namespace: "multiuser"
- description: "Allow foreground user to dismiss sounds that are coming from background users"
- bug: "314907186"
-}
-
-flag {
name: "new_multiuser_settings_ux"
namespace: "multiuser"
description: "Update multiuser settings UI"
@@ -68,16 +61,6 @@
}
flag {
- name: "multiple_alarm_notifications_support"
- namespace: "multiuser"
- description: "Implement handling of multiple simultaneous alarms/timers on bg users"
- bug: "367615180"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
name: "enable_biometrics_to_unlock_private_space"
is_exported: true
namespace: "profile_experiences"
@@ -93,14 +76,6 @@
}
flag {
- name: "avatar_sync"
- namespace: "multiuser"
- description: "Implement new Avatar Picker outside of SetttingsLib with ability to select avatars from Google Account and synchronise to any changes."
- bug: "296829976"
- is_fixed_read_only: true
-}
-
-flag {
name: "allow_resolver_sheet_for_private_space"
namespace: "profile_experiences"
description: "Add support for Private Space in resolver sheet"
@@ -131,46 +106,6 @@
}
flag {
- name: "fix_avatar_picker_read_back_order"
- namespace: "multiuser"
- description: "Talkback focus doesn't move to the 'If you change your Google Account picture…' after swiping next to move the focus from 'Choose a picture'"
- bug: "330835921"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
- name: "fix_avatar_picker_selected_read_back"
- namespace: "multiuser"
- description: "Talkback doesn't announce 'selected' after double tapping the button in the picture list in 'Choose a picture' page."
- bug: "330840549"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
- name: "fix_avatar_concurrent_file_write"
- namespace: "multiuser"
- description: "Fix potential unexpected behavior due to concurrent file writing"
- bug: "339351031"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
- name: "fix_avatar_cross_user_leak"
- namespace: "multiuser"
- description: "Fix cross-user picture uri leak for avatar picker apps."
- bug: "341688848"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
name: "fix_avatar_content_provider_null_authority"
namespace: "multiuser"
description: "Fix crash when content provider authority is null."
@@ -181,36 +116,6 @@
}
flag {
- name: "fix_avatar_picker_not_responding_for_new_user"
- namespace: "multiuser"
- description: "Avatar picker is not responding after selecting photo for new user."
- bug: "358407488"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
- name: "fix_get_user_property_cache"
- namespace: "multiuser"
- description: "Cache is not optimised for getUserProperty for values below 0, eg. UserHandler.USER_NULL or UserHandle.USER_ALL"
- bug: "350416200"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
- name: "fix_disabling_of_mu_toggle_when_restriction_applied"
- namespace: "multiuser"
- description: "When no_user_switch is set but no EnforcedAdmin is present, the toggle has to be disabled"
- bug: "356387759"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
name: "place_add_user_dialog_within_activity"
namespace: "multiuser"
description: "Display dialog within activity to make it traversable by Accessibility"
@@ -231,29 +136,6 @@
is_fixed_read_only: true
}
-
-flag {
- name: "cache_profile_parent_read_only"
- namespace: "multiuser"
- description: "Cache getProfileParent to avoid unnecessary binder calls"
- bug: "350417399"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
- is_fixed_read_only: true
-}
-
-flag {
- name: "cache_profile_ids_read_only"
- namespace: "multiuser"
- description: "Cache getProfileIds to avoid unnecessary binder calls"
- bug: "350421409"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
- is_fixed_read_only: true
-}
-
flag {
name: "cache_profile_type_read_only"
namespace: "multiuser"
@@ -266,27 +148,6 @@
}
flag {
- name: "cache_profiles_read_only"
- namespace: "multiuser"
- description: "Cache getProfiles to avoid unnecessary binder calls"
- bug: "350419395"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
- is_fixed_read_only: true
-}
-
-flag {
- name: "cache_quiet_mode_state"
- namespace: "multiuser"
- description: "Optimise quiet mode state retrieval"
- bug: "350420769"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
name: "cache_user_properties_correctly_read_only"
namespace: "multiuser"
description: "UserProperties cache needs to take into account who the callingUid is."
@@ -298,49 +159,6 @@
}
flag {
- name: "cache_user_serial_number"
- namespace: "multiuser"
- description: "Optimise user serial number retrieval"
- bug: "340018451"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
- name: "cache_user_serial_number_read_only"
- namespace: "multiuser"
- description: "Optimise user serial number retrieval. Read only flag, so that it can be used before the flags are initialized."
- bug: "353134536"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
- is_fixed_read_only: true
-}
-
-flag {
- name: "invalidate_cache_on_users_changed_read_only"
- namespace: "multiuser"
- description: "Invalidate the cache when users are added or removed to improve caches."
- bug: "372383485"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
- is_fixed_read_only: true
-}
-
-flag {
- name: "caches_not_invalidated_at_start_read_only"
- namespace: "multiuser"
- description: "PIC need to be invalidated at start in order to work properly."
- bug: "356167673"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
- is_fixed_read_only: true
-}
-
-flag {
name: "cache_user_info_read_only"
namespace: "multiuser"
description: "Cache UserInfo to avoid unnecessary binder calls"
@@ -591,14 +409,6 @@
}
flag {
- name: "caching_development_improvements"
- namespace: "multiuser"
- description: "System API to simplify caching implamentations"
- bug: "364947162"
- is_fixed_read_only: true
-}
-
-flag {
name: "show_custom_unlock_title_inside_private_profile"
namespace: "profile_experiences"
description: "When private space is unlocked show dynamic title in unlock factor screens based on lock factor set for the profile"
@@ -663,3 +473,10 @@
description: "Allows any regular user to have profiles"
bug: "374832167"
}
+
+flag {
+ name: "perfetto_multiuser_table"
+ namespace: "multiuser"
+ description: "Generate file for perfetto, so it can create a users table"
+ bug: "391110239"
+}
diff --git a/packages/SystemUI/src/com/android/systemui/topwindoweffects/data/entity/SqueezeEffectCornerResourceId.kt b/core/java/android/content/res/AssetFileDescriptor.aidl
similarity index 81%
copy from packages/SystemUI/src/com/android/systemui/topwindoweffects/data/entity/SqueezeEffectCornerResourceId.kt
copy to core/java/android/content/res/AssetFileDescriptor.aidl
index a55301e..d7f2c3f 100644
--- a/packages/SystemUI/src/com/android/systemui/topwindoweffects/data/entity/SqueezeEffectCornerResourceId.kt
+++ b/core/java/android/content/res/AssetFileDescriptor.aidl
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package com.android.systemui.topwindoweffects.data.entity
+package android.content.res;
-data class SqueezeEffectCornerResourceId(val top: Int, val bottom: Int)
+parcelable AssetFileDescriptor;
diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java
index 008bf2f..fd55e9f 100644
--- a/core/java/android/content/res/AssetManager.java
+++ b/core/java/android/content/res/AssetManager.java
@@ -1190,7 +1190,20 @@
*/
public @NonNull XmlResourceParser openXmlResourceParser(int cookie, @NonNull String fileName)
throws IOException {
- try (XmlBlock block = openXmlBlockAsset(cookie, fileName, true)) {
+ return openXmlResourceParser(cookie, fileName, true);
+ }
+
+ /**
+ * Retrieve a parser for a compiled XML file.
+ *
+ * @param cookie Identifier of the package to be opened.
+ * @param fileName The name of the file to retrieve.
+ * @param hasFlags Whether the parser should check for flags on each element
+ * @hide
+ */
+ public @NonNull XmlResourceParser openXmlResourceParser(int cookie, @NonNull String fileName,
+ boolean hasFlags) throws IOException {
+ try (XmlBlock block = openXmlBlockAsset(cookie, fileName, hasFlags)) {
XmlResourceParser parser = block.newParser(ID_NULL, new Validator());
// If openXmlBlockAsset doesn't throw, it will always return an XmlBlock object with
// a valid native pointer, which makes newParser always return non-null. But let's
diff --git a/core/java/android/credentials/flags.aconfig b/core/java/android/credentials/flags.aconfig
index 73c4d66..854030e 100644
--- a/core/java/android/credentials/flags.aconfig
+++ b/core/java/android/credentials/flags.aconfig
@@ -164,4 +164,14 @@
metadata {
purpose: PURPOSE_BUGFIX
}
+}
+
+flag {
+ namespace: "credential_manager"
+ name: "is_credman_settings_expressive_design"
+ description: "settings design changes for credential manager"
+ bug: "373717708"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
}
\ No newline at end of file
diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java
index 8bff624..aac67b4 100644
--- a/core/java/android/database/sqlite/SQLiteDatabase.java
+++ b/core/java/android/database/sqlite/SQLiteDatabase.java
@@ -731,7 +731,6 @@
* }
* </pre>
*/
- @FlaggedApi(Flags.FLAG_SQLITE_APIS_35)
public void beginTransactionReadOnly() {
beginTransactionWithListenerReadOnly(null);
}
@@ -818,7 +817,6 @@
* </pre>
*/
@SuppressLint("ExecutorRegistration")
- @FlaggedApi(Flags.FLAG_SQLITE_APIS_35)
public void beginTransactionWithListenerReadOnly(
@Nullable SQLiteTransactionListener transactionListener) {
beginTransaction(transactionListener, SQLiteSession.TRANSACTION_MODE_DEFERRED);
@@ -2257,7 +2255,6 @@
* @throws IllegalStateException if a transaction is not in progress.
* @throws SQLiteException if the SQL cannot be compiled.
*/
- @FlaggedApi(Flags.FLAG_SQLITE_APIS_35)
@NonNull
public SQLiteRawStatement createRawStatement(@NonNull String sql) {
Objects.requireNonNull(sql);
@@ -2277,7 +2274,6 @@
* @return The ROWID of the last row to be inserted under this connection.
* @throws IllegalStateException if there is no current transaction.
*/
- @FlaggedApi(Flags.FLAG_SQLITE_APIS_35)
public long getLastInsertRowId() {
return getThreadSession().getLastInsertRowId();
}
@@ -2291,7 +2287,6 @@
* @return The number of rows changed by the most recent sql statement
* @throws IllegalStateException if there is no current transaction.
*/
- @FlaggedApi(Flags.FLAG_SQLITE_APIS_35)
public long getLastChangedRowCount() {
return getThreadSession().getLastChangedRowCount();
}
@@ -2319,7 +2314,6 @@
* @return The number of rows changed on the current connection.
* @throws IllegalStateException if there is no current transaction.
*/
- @FlaggedApi(Flags.FLAG_SQLITE_APIS_35)
public long getTotalChangedRowCount() {
return getThreadSession().getTotalChangedRowCount();
}
diff --git a/core/java/android/database/sqlite/SQLiteRawStatement.java b/core/java/android/database/sqlite/SQLiteRawStatement.java
index ce2334a..363ce4f9 100644
--- a/core/java/android/database/sqlite/SQLiteRawStatement.java
+++ b/core/java/android/database/sqlite/SQLiteRawStatement.java
@@ -71,7 +71,6 @@
*
* @see <a href="http://sqlite.org/c3ref/stmt.html">sqlite3_stmt</a>
*/
-@FlaggedApi(Flags.FLAG_SQLITE_APIS_35)
public final class SQLiteRawStatement implements Closeable {
private static final String TAG = "SQLiteRawStatement";
diff --git a/core/java/android/debug/AdbManagerInternal.java b/core/java/android/debug/AdbManagerInternal.java
index e448706..77e98f6 100644
--- a/core/java/android/debug/AdbManagerInternal.java
+++ b/core/java/android/debug/AdbManagerInternal.java
@@ -59,14 +59,4 @@
* reloaded.
*/
public abstract void notifyKeyFilesUpdated();
-
- /**
- * Starts adbd for a transport.
- */
- public abstract void startAdbdForTransport(byte transportType);
-
- /**
- * Stops adbd for a transport.
- */
- public abstract void stopAdbdForTransport(byte transportType);
}
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index 6a61c42..f3a9a89 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -1035,7 +1035,7 @@
AttributionSourceState contextAttributionSourceState =
contextAttributionSource.asState();
- if (Flags.dataDeliveryPermissionChecks() && useContextAttributionSource) {
+ if (useContextAttributionSource) {
return contextAttributionSourceState;
} else {
AttributionSourceState clientAttribution =
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index d2fcfd6..6c00fa9 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -2344,9 +2344,15 @@
* (triggered by {@link CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER android.control.aePrecaptureTrigger}) and
* may be fired for captures for which the
* {@link CaptureRequest#CONTROL_CAPTURE_INTENT android.control.captureIntent} field is set to
- * STILL_CAPTURE</p>
+ * STILL_CAPTURE.</p>
+ * <p>It's important to wait for the precapture sequence
+ * to complete (i.e., {@link CaptureResult#CONTROL_AE_STATE android.control.aeState} reaches
+ * FLASH_REQUIRED, CONVERGED, or LOCKED) before submitting a
+ * STILL_CAPTURE request. Otherwise, in low-light conditions,
+ * the image captures with flash fired won't have correct exposures.</p>
*
* @see CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER
+ * @see CaptureResult#CONTROL_AE_STATE
* @see CaptureRequest#CONTROL_CAPTURE_INTENT
* @see CaptureRequest#CONTROL_AE_MODE
*/
@@ -2360,9 +2366,15 @@
* (triggered by {@link CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER android.control.aePrecaptureTrigger}) and
* will always be fired for captures for which the
* {@link CaptureRequest#CONTROL_CAPTURE_INTENT android.control.captureIntent} field is set to
- * STILL_CAPTURE</p>
+ * STILL_CAPTURE.</p>
+ * <p>It's important to wait for the precapture sequence
+ * to complete (i.e., {@link CaptureResult#CONTROL_AE_STATE android.control.aeState} reaches
+ * FLASH_REQUIRED, CONVERGED, or LOCKED) Dbefore submitting a
+ * STILL_CAPTURE request. Otherwise, in low-light conditions,
+ * the image captures with flash fired won't have correct exposures.</p>
*
* @see CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER
+ * @see CaptureResult#CONTROL_AE_STATE
* @see CaptureRequest#CONTROL_CAPTURE_INTENT
* @see CaptureRequest#CONTROL_AE_MODE
*/
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index 56d2727..ee98eb7 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -422,11 +422,7 @@
}
mCameraId = cameraId;
mDeviceCallback = new ClientStateCallback(executor, callback);
- if (Flags.singleThreadExecutorNaming()) {
- mDeviceExecutor = Executors.newSingleThreadExecutor(sThreadFactory);
- } else {
- mDeviceExecutor = Executors.newSingleThreadExecutor();
- }
+ mDeviceExecutor = Executors.newSingleThreadExecutor(sThreadFactory);
mCharacteristics = characteristics;
mCameraManager = manager;
mAppTargetSdkVersion = appTargetSdkVersion;
@@ -1893,7 +1889,7 @@
}
// Allow RAW formats, even when not advertised.
- if (Flags.multiResRawReprocessing() && isRawFormat(inputFormat)) {
+ if (isRawFormat(inputFormat)) {
return;
}
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index 42df43e..f282c6d4 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -82,6 +82,8 @@
*
* @hide
*/
+@android.ravenwood.annotation.RavenwoodPartiallyAllowlisted
+@android.ravenwood.annotation.RavenwoodKeepPartialClass
public final class DisplayManagerGlobal {
private static final String TAG = "DisplayManager";
@@ -231,7 +233,7 @@
* before the display manager has been fully initialized.
*/
@UnsupportedAppUsage
- // @RavenwoodIgnore(value = "null")
+ @android.ravenwood.annotation.RavenwoodIgnore
public static DisplayManagerGlobal getInstance() {
synchronized (DisplayManagerGlobal.class) {
if (sInstance == null) {
diff --git a/core/java/android/hardware/display/DisplayTopology.java b/core/java/android/hardware/display/DisplayTopology.java
index ed5958d..f553aac 100644
--- a/core/java/android/hardware/display/DisplayTopology.java
+++ b/core/java/android/hardware/display/DisplayTopology.java
@@ -813,7 +813,13 @@
return a == b || (Float.isNaN(a) && Float.isNaN(b)) || Math.abs(a - b) < EPSILON;
}
- private Map<Integer, TreeNode> allNodesIdMap() {
+ /**
+ * Returns a map of all TreeNodes indexed by the display ID.
+ *
+ * @hide
+ */
+ @NonNull
+ public Map<Integer, TreeNode> allNodesIdMap() {
var pend = new ArrayDeque<TreeNode>();
var found = new HashMap<Integer, TreeNode>();
diff --git a/core/java/android/hardware/display/DisplayViewport.java b/core/java/android/hardware/display/DisplayViewport.java
index d2b66c3..67637ff 100644
--- a/core/java/android/hardware/display/DisplayViewport.java
+++ b/core/java/android/hardware/display/DisplayViewport.java
@@ -81,6 +81,9 @@
public @ViewportType int type;
+ // The logical display density which is the basis for density-independent pixels.
+ public int densityDpi;
+
public void copyFrom(DisplayViewport viewport) {
valid = viewport.valid;
isActive = viewport.isActive;
@@ -93,6 +96,7 @@
uniqueId = viewport.uniqueId;
physicalPort = viewport.physicalPort;
type = viewport.type;
+ densityDpi = viewport.densityDpi;
}
/**
@@ -125,7 +129,8 @@
&& deviceHeight == other.deviceHeight
&& TextUtils.equals(uniqueId, other.uniqueId)
&& Objects.equals(physicalPort, other.physicalPort)
- && type == other.type;
+ && type == other.type
+ && densityDpi == other.densityDpi;
}
@Override
@@ -145,6 +150,7 @@
result += prime * result + physicalPort.hashCode();
}
result += prime * result + type;
+ result += prime * result + densityDpi;
return result;
}
@@ -158,6 +164,7 @@
+ ", uniqueId='" + uniqueId + "'"
+ ", physicalPort=" + physicalPort
+ ", orientation=" + orientation
+ + ", densityDpi=" + densityDpi
+ ", logicalFrame=" + logicalFrame
+ ", physicalFrame=" + physicalFrame
+ ", deviceWidth=" + deviceWidth
diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl
index 5537135..3389b02 100644
--- a/core/java/android/hardware/input/IInputManager.aidl
+++ b/core/java/android/hardware/input/IInputManager.aidl
@@ -260,42 +260,42 @@
KeyGlyphMap getKeyGlyphMap(int deviceId);
- @PermissionManuallyEnforced
+ @EnforcePermission("MANAGE_KEY_GESTURES")
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = "
+ "android.Manifest.permission.MANAGE_KEY_GESTURES)")
void registerKeyGestureEventListener(IKeyGestureEventListener listener);
- @PermissionManuallyEnforced
+ @EnforcePermission("MANAGE_KEY_GESTURES")
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = "
+ "android.Manifest.permission.MANAGE_KEY_GESTURES)")
void unregisterKeyGestureEventListener(IKeyGestureEventListener listener);
- @PermissionManuallyEnforced
+ @EnforcePermission("MANAGE_KEY_GESTURES")
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = "
+ "android.Manifest.permission.MANAGE_KEY_GESTURES)")
void registerKeyGestureHandler(in int[] keyGesturesToHandle, IKeyGestureHandler handler);
- @PermissionManuallyEnforced
+ @EnforcePermission("MANAGE_KEY_GESTURES")
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = "
+ "android.Manifest.permission.MANAGE_KEY_GESTURES)")
void unregisterKeyGestureHandler(IKeyGestureHandler handler);
- @PermissionManuallyEnforced
+ @EnforcePermission("MANAGE_KEY_GESTURES")
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = "
+ "android.Manifest.permission.MANAGE_KEY_GESTURES)")
AidlInputGestureData getInputGesture(int userId, in AidlInputGestureData.Trigger trigger);
- @PermissionManuallyEnforced
+ @EnforcePermission("MANAGE_KEY_GESTURES")
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = "
+ "android.Manifest.permission.MANAGE_KEY_GESTURES)")
int addCustomInputGesture(int userId, in AidlInputGestureData data);
- @PermissionManuallyEnforced
+ @EnforcePermission("MANAGE_KEY_GESTURES")
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = "
+ "android.Manifest.permission.MANAGE_KEY_GESTURES)")
int removeCustomInputGesture(int userId, in AidlInputGestureData data);
- @PermissionManuallyEnforced
+ @EnforcePermission("MANAGE_KEY_GESTURES")
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = "
+ "android.Manifest.permission.MANAGE_KEY_GESTURES)")
void removeAllCustomInputGestures(int userId, int tag);
@@ -305,4 +305,6 @@
AidlInputGestureData[] getAppLaunchBookmarks();
void resetLockedModifierState();
+
+ void setMouseScalingEnabled(boolean enabled, int displayId);
}
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index a66ac76..beae130 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -1621,6 +1621,28 @@
}
/**
+ * Set whether all pointer scaling, including linear scaling based on the
+ * user's pointer speed setting, should be enabled or disabled for mice.
+ *
+ * Note that this only affects pointer movements from mice (that is, pointing devices which send
+ * relative motions, including trackballs and pointing sticks), not from other pointer devices
+ * such as touchpads and styluses.
+ *
+ * Scaling is enabled by default on new displays until it is explicitly disabled.
+ * @hide
+ */
+ @TestApi
+ @SuppressLint("UnflaggedApi") // @TestApi without associated feature.
+ @RequiresPermission(Manifest.permission.SET_POINTER_SPEED)
+ public void setMouseScalingEnabled(boolean enabled, int displayId) {
+ try {
+ mIm.setMouseScalingEnabled(enabled, displayId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* A callback used to be notified about battery state changes for an input device. The
* {@link #onBatteryStateChanged(int, long, BatteryState)} method will be called once after the
* listener is successfully registered to provide the initial battery state of the device.
diff --git a/core/java/android/hardware/input/InputSettings.java b/core/java/android/hardware/input/InputSettings.java
index 016e02c..fbabd8c5 100644
--- a/core/java/android/hardware/input/InputSettings.java
+++ b/core/java/android/hardware/input/InputSettings.java
@@ -26,7 +26,6 @@
import static com.android.hardware.input.Flags.touchpadSystemGestureDisable;
import static com.android.hardware.input.Flags.touchpadThreeFingerTapShortcut;
import static com.android.hardware.input.Flags.touchpadVisualizer;
-import static com.android.hardware.input.Flags.useKeyGestureEventHandlerMultiKeyGestures;
import static com.android.input.flags.Flags.FLAG_KEYBOARD_REPEAT_KEYS;
import static com.android.input.flags.Flags.keyboardRepeatKeys;
@@ -1299,13 +1298,4 @@
public static boolean isCustomizableInputGesturesFeatureFlagEnabled() {
return enableCustomizableInputGestures();
}
-
- /**
- * Whether multi-key gestures are supported using {@code KeyGestureEventHandler}
- *
- * @hide
- */
- public static boolean doesKeyGestureEventHandlerSupportMultiKeyGestures() {
- return useKeyGestureEventHandlerMultiKeyGestures();
- }
}
diff --git a/core/java/android/hardware/input/input_framework.aconfig b/core/java/android/hardware/input/input_framework.aconfig
index f23ff40..2a0f8b3 100644
--- a/core/java/android/hardware/input/input_framework.aconfig
+++ b/core/java/android/hardware/input/input_framework.aconfig
@@ -28,17 +28,6 @@
}
flag {
- name: "input_manager_lifecycle_support"
- namespace: "input"
- description: "Add support for Lifecycle support in input manager"
- bug: "362473586"
- is_fixed_read_only: true
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
namespace: "input_native"
name: "manage_key_gestures"
description: "Manage key gestures through Input APIs"
@@ -47,13 +36,6 @@
}
flag {
- namespace: "input"
- name: "use_key_gesture_event_handler_multi_key_gestures"
- description: "Use KeyGestureEvent handler APIs to control multi key press gestures"
- bug: "358569822"
-}
-
-flag {
name: "keyboard_repeat_keys"
namespace: "input_native"
description: "Allow configurable timeout before key repeat and repeat delay rate for key repeats"
diff --git a/core/java/android/net/Uri.java b/core/java/android/net/Uri.java
index caf963e..2cca62c 100644
--- a/core/java/android/net/Uri.java
+++ b/core/java/android/net/Uri.java
@@ -1985,7 +1985,7 @@
*/
public static String encodeIfNotEncoded(@Nullable String value, @Nullable String allow) {
if (value == null) return null;
- if (!Flags.encodeAppIntent() || isEncoded(value, allow)) return value;
+ if (isEncoded(value, allow)) return value;
return encode(value, allow);
}
@@ -2036,7 +2036,7 @@
*/
public static String decodeIfNeeded(@Nullable String value) {
if (value == null) return null;
- if (Flags.encodeAppIntent() && value.contains("%")) return decode(value);
+ if (value.contains("%")) return decode(value);
return value;
}
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index b2f2521..be1437a 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -802,12 +802,6 @@
@UnsupportedAppUsage
public abstract ArrayMap<String, ? extends Pkg> getPackageStats();
- /**
- * Returns the proportion of power consumed by the System Service
- * calls made by this UID.
- */
- public abstract double getProportionalSystemServiceUsage();
-
public abstract ControllerActivityCounter getWifiControllerActivity();
public abstract ControllerActivityCounter getBluetoothControllerActivity();
public abstract ControllerActivityCounter getModemControllerActivity();
@@ -3448,20 +3442,6 @@
public abstract int getDischargeAmountScreenDozeSinceCharge();
/**
- * Returns the approximate CPU time (in microseconds) spent by the system server handling
- * incoming service calls from apps. The result is returned as an array of longs,
- * organized as a sequence like this:
- * <pre>
- * cluster1-speed1, cluster1-speed2, ..., cluster2-speed1, cluster2-speed2, ...
- * </pre>
- *
- * @see com.android.internal.os.CpuScalingPolicies#getPolicies
- * @see com.android.internal.os.CpuScalingPolicies#getFrequencies
- */
- @Nullable
- public abstract long[] getSystemServiceTimeAtCpuSpeeds();
-
- /**
* Returns the total, last, or current battery uptime in microseconds.
*
* @param curTime the elapsed realtime in microseconds.
diff --git a/core/java/android/os/CombinedMessageQueue/MessageQueue.java b/core/java/android/os/CombinedMessageQueue/MessageQueue.java
index 8abbcfb..8754165 100644
--- a/core/java/android/os/CombinedMessageQueue/MessageQueue.java
+++ b/core/java/android/os/CombinedMessageQueue/MessageQueue.java
@@ -73,6 +73,16 @@
@UnsupportedAppUsage
private final boolean mQuitAllowed;
+ /**
+ * Used by all native methods.
+ *
+ * <p>In legacy mode, usage of this field (directly, or indirectly via native method
+ * invocations) must be guarded with the lock.
+ *
+ * <p>In concurrent mode, the Looper thread may access freely, but other threads must first call
+ * {@link #incrementMptrRefs()}, check the result, and if true then access the native
+ * object, followed by a call to {@link #decrementMptrRefs()}.
+ */
@UnsupportedAppUsage
@SuppressWarnings("unused")
private long mPtr; // used by native code
@@ -99,6 +109,7 @@
private int mAsyncMessageCount;
private final AtomicLong mMessageCount = new AtomicLong();
+ private final Thread mLooperThread;
private final String mThreadName;
private final long mTid;
@@ -130,6 +141,7 @@
// Not @FastNative since significant time is spent in the native code as it may invoke
// application callbacks.
private native void nativePollOnce(long ptr, int timeoutMillis); /*non-static for callbacks*/
+
@RavenwoodRedirect
@CriticalNative
private native static void nativeWake(long ptr);
@@ -147,7 +159,8 @@
mUseConcurrent = sIsProcessAllowedToUseConcurrent;
mQuitAllowed = quitAllowed;
mPtr = nativeInit();
- mThreadName = Thread.currentThread().getName();
+ mLooperThread = Thread.currentThread();
+ mThreadName = mLooperThread.getName();
mTid = Process.myTid();
}
@@ -236,25 +249,29 @@
private void decAndTraceMessageCount() {
mMessageCount.decrementAndGet();
- traceMessageCount();
+ if (PerfettoTrace.MQ_CATEGORY.isEnabled()) {
+ traceMessageCount();
+ }
}
private void incAndTraceMessageCount(Message msg, long when) {
mMessageCount.incrementAndGet();
- msg.mSendingThreadName = Thread.currentThread().getName();
- msg.mEventId.set(PerfettoTrace.getFlowId());
+ if (PerfettoTrace.MQ_CATEGORY.isEnabled()) {
+ msg.mSendingThreadName = Thread.currentThread().getName();
+ msg.mEventId.set(PerfettoTrace.getFlowId());
- traceMessageCount();
- PerfettoTrace.instant(PerfettoTrace.MQ_CATEGORY, "message_queue_send")
- .setFlow(msg.mEventId.get())
- .beginProto()
- .beginNested(2004 /* message_queue */)
- .addField(2 /* receiving_thread_name */, mThreadName)
- .addField(3 /* message_code */, msg.what)
- .addField(4 /* message_delay_ms */, when - SystemClock.uptimeMillis())
- .endNested()
- .endProto()
- .emit();
+ traceMessageCount();
+ PerfettoTrace.instant(PerfettoTrace.MQ_CATEGORY, "message_queue_send")
+ .setFlow(msg.mEventId.get())
+ .beginProto()
+ .beginNested(2004 /* message_queue */)
+ .addField(2 /* receiving_thread_name */, mThreadName)
+ .addField(3 /* message_code */, msg.what)
+ .addField(4 /* message_delay_ms */, when - SystemClock.uptimeMillis())
+ .endNested()
+ .endProto()
+ .emit();
+ }
}
/** @hide */
@@ -397,14 +414,14 @@
private boolean isPollingConcurrent() {
// If the loop is quitting then it must not be idling.
- if (!incrementQuittingState()) {
- return false;
+ if (incrementMptrRefs()) {
+ try {
+ return nativeIsPolling(mPtr);
+ } finally {
+ decrementMptrRefs();
+ }
}
- try {
- return nativeIsPolling(mPtr);
- } finally {
- decrementQuittingState();
- }
+ return false;
}
private boolean isPollingLegacy() {
@@ -560,11 +577,12 @@
record.mEvents = events;
record.mSeq += 1;
}
- nativeSetFileDescriptorEvents(mPtr, fdNum, events);
+ setFileDescriptorEvents(fdNum, events);
} else if (record != null) {
record.mEvents = 0;
mFileDescriptorRecords.removeAt(index);
- nativeSetFileDescriptorEvents(mPtr, fdNum, 0);
+
+ setFileDescriptorEvents(fdNum, 0);
}
}
@@ -781,8 +799,12 @@
*/
StateNode nextOp = sStackStateActive;
if (found == null) {
- if (next == null) {
- /* No message to deliver, sleep indefinitely */
+ if (getQuitting()) {
+ mNextPollTimeoutMillis = 0;
+ // State change will be Active->Active, so can immediately return here.
+ return null;
+ } else if (next == null) {
+ /* No message to deliver, sleep indefinitely, unless quitting */
mNextPollTimeoutMillis = -1;
nextOp = sStackStateParked;
if (DEBUG) {
@@ -1048,11 +1070,8 @@
}
if (mUseConcurrent) {
- if (!incrementQuittingState()) {
- return;
- }
- try {
- if (setQuitting()) {
+ if (incrementMptrRefsAndSetQuitting()) {
+ try {
if (safe) {
removeAllFutureMessages();
} else {
@@ -1061,9 +1080,9 @@
// We can assume mPtr != 0 while we hold a ref on our quitting state
nativeWake(mPtr);
+ } finally {
+ decrementMptrRefs();
}
- } finally {
- decrementQuittingState();
}
} else {
synchronized (this) {
@@ -1227,7 +1246,7 @@
Message m = first.mMessage;
if (m.target == null && m.arg1 == token) {
/* Wake up next() in case it was sleeping on this barrier. */
- nativeWake(mPtr);
+ concurrentWake();
}
} else if (!removed) {
throw new IllegalStateException("The specified message queue synchronization "
@@ -2657,47 +2676,107 @@
}
// Use MSB to indicate quitting state. Lower 63 bits hold ref count.
- private static final long QUITTING_MASK = ~(-1L >>> 1);
+ private static final long QUITTING_MASK = 1L << 63;
- private boolean incrementQuittingState() {
- long oldVal = (long)sQuittingRefCount.getAndAdd(this, 1);
- if ((oldVal & QUITTING_MASK) != 0) {
- // If we're quitting we need to drop our ref and indicate to the caller
- sQuittingRefCount.getAndAdd(this, -1);
- return false;
+ /**
+ * Increment the mPtr ref count.
+ *
+ * If this method returns true then the caller may use mPtr until they call
+ * {@link #decrementMptrRefs()}.
+ * If this method returns false then the caller must not use mPtr, and must
+ * instead assume that the MessageQueue is quitting or has already quit and
+ * act accordingly.
+ */
+ private boolean incrementMptrRefs() {
+ while (true) {
+ final long oldVal = mQuittingRefCountValue;
+ if ((oldVal & QUITTING_MASK) != 0) {
+ // If we're quitting then we're not allowed to increment the ref count.
+ return false;
+ }
+ if (sQuittingRefCount.compareAndSet(this, oldVal, oldVal + 1)) {
+ // Successfully incremented the ref count without quitting.
+ return true;
+ }
}
- return true;
}
- private void decrementQuittingState() {
+ /**
+ * Decrement the mPtr ref count.
+ *
+ * Call after {@link #incrementMptrRefs()} to release the ref on mPtr.
+ */
+ private void decrementMptrRefs() {
long oldVal = (long)sQuittingRefCount.getAndAdd(this, -1);
// If quitting and we were the last ref, wake up looper thread
- if ((oldVal & QUITTING_MASK) != 0 && (oldVal & ~QUITTING_MASK) == 1L) {
+ if (oldVal - 1 == QUITTING_MASK) {
LockSupport.unpark(mLooperThread);
}
}
- private boolean setQuitting() {
- long oldVal = (long)sQuittingRefCount.getAndBitwiseOr(this, QUITTING_MASK);
- if ((oldVal & QUITTING_MASK) != 0) {
- return false;
+ private boolean incrementMptrRefsAndSetQuitting() {
+ while (true) {
+ final long oldVal = mQuittingRefCountValue;
+ if ((oldVal & QUITTING_MASK) != 0) {
+ // If we're quitting then we're not allowed to increment the ref count.
+ return false;
+ }
+ if (sQuittingRefCount.compareAndSet(this, oldVal, (oldVal + 1) | QUITTING_MASK)) {
+ // Successfully incremented the ref count and set quitting.
+ return true;
+ }
}
- return true;
+ }
+
+ /**
+ * Wake the looper thread.
+ *
+ * {@link #nativeWake(long)} may be called directly only by the looper thread.
+ * Otherwise, call this method to ensure safe access to mPtr.
+ */
+ private void concurrentWake() {
+ if (incrementMptrRefs()) {
+ try {
+ nativeWake(mPtr);
+ } finally {
+ decrementMptrRefs();
+ }
+ }
+ }
+
+ private void setFileDescriptorEvents(int fdNum, int events) {
+ if (mUseConcurrent) {
+ if (incrementMptrRefs()) {
+ try {
+ nativeSetFileDescriptorEvents(mPtr, fdNum, events);
+ } finally {
+ decrementMptrRefs();
+ }
+ }
+ } else {
+ nativeSetFileDescriptorEvents(mPtr, fdNum, events);
+ }
}
private boolean getQuitting() {
return (mQuittingRefCountValue & QUITTING_MASK) != 0;
}
- private volatile Thread mLooperThread = null;
// Must only be called from looper thread
private boolean checkQuittingAndWaitForRefsToDrop() {
if (!getQuitting()) {
return false;
}
- mLooperThread = Thread.currentThread();
- while ((mQuittingRefCountValue & ~QUITTING_MASK) != 0) {
- LockSupport.park();
+ boolean wasInterrupted = false;
+ try {
+ while ((mQuittingRefCountValue & ~QUITTING_MASK) != 0) {
+ LockSupport.park();
+ wasInterrupted |= Thread.interrupted();
+ }
+ } finally {
+ if (wasInterrupted) {
+ mLooperThread.interrupt();
+ }
}
return true;
}
@@ -2890,7 +2969,7 @@
if (sState.compareAndSet(this, old, node)) {
if (inactive) {
if (wakeNeeded) {
- nativeWake(mPtr);
+ concurrentWake();
} else {
mMessageCounts.incrementQueued();
}
@@ -2943,7 +3022,7 @@
p.mMessage.recycleUnchecked();
decAndTraceMessageCount();
if (mMessageCounts.incrementCancelled()) {
- nativeWake(mPtr);
+ concurrentWake();
}
}
} else {
diff --git a/core/java/android/os/ConcurrentMessageQueue/MessageQueue.java b/core/java/android/os/ConcurrentMessageQueue/MessageQueue.java
index 25541ed..23aba34 100644
--- a/core/java/android/os/ConcurrentMessageQueue/MessageQueue.java
+++ b/core/java/android/os/ConcurrentMessageQueue/MessageQueue.java
@@ -242,47 +242,103 @@
}
// Use MSB to indicate quitting state. Lower 63 bits hold ref count.
- private static final long QUITTING_MASK = ~(-1L >>> 1);
+ private static final long QUITTING_MASK = 1L << 63;
- private boolean incrementQuittingState() {
- long oldVal = (long)sQuittingRefCount.getAndAdd(this, 1);
- if ((oldVal & QUITTING_MASK) != 0) {
- // If we're quitting we need to drop our ref and indicate to the caller
- sQuittingRefCount.getAndAdd(this, -1);
- return false;
+ /**
+ * Increment the mPtr ref count.
+ *
+ * If this method returns true then the caller may use mPtr until they call
+ * {@link #decrementMptrRefs()}.
+ * If this method returns false then the caller must not use mPtr, and must
+ * instead assume that the MessageQueue is quitting or has already quit and
+ * act accordingly.
+ */
+ private boolean incrementMptrRefs() {
+ while (true) {
+ final long oldVal = mQuittingRefCountValue;
+ if ((oldVal & QUITTING_MASK) != 0) {
+ // If we're quitting then we're not allowed to increment the ref count.
+ return false;
+ }
+ if (sQuittingRefCount.compareAndSet(this, oldVal, oldVal + 1)) {
+ // Successfully incremented the ref count without quitting.
+ return true;
+ }
}
- return true;
}
- private void decrementQuittingState() {
+ /**
+ * Decrement the mPtr ref count.
+ *
+ * Call after {@link #incrementMptrRefs()} to release the ref on mPtr.
+ */
+ private void decrementMptrRefs() {
long oldVal = (long)sQuittingRefCount.getAndAdd(this, -1);
// If quitting and we were the last ref, wake up looper thread
- if ((oldVal & QUITTING_MASK) != 0 && (oldVal & ~QUITTING_MASK) == 1L) {
+ if (oldVal - 1 == QUITTING_MASK) {
LockSupport.unpark(mLooperThread);
}
}
- private boolean setQuitting() {
- long oldVal = (long)sQuittingRefCount.getAndBitwiseOr(this, QUITTING_MASK);
- if ((oldVal & QUITTING_MASK) != 0) {
- return false;
+ private boolean incrementMptrRefsAndSetQuitting() {
+ while (true) {
+ final long oldVal = mQuittingRefCountValue;
+ if ((oldVal & QUITTING_MASK) != 0) {
+ // If we're quitting then we're not allowed to increment the ref count.
+ return false;
+ }
+ if (sQuittingRefCount.compareAndSet(this, oldVal, (oldVal + 1) | QUITTING_MASK)) {
+ // Successfully incremented the ref count and set quitting.
+ return true;
+ }
}
- return true;
+ }
+
+ /**
+ * Wake the looper thread.
+ *
+ * {@link #nativeWake(long)} may be called directly only by the looper thread.
+ * Otherwise, call this method to ensure safe access to mPtr.
+ */
+ private void concurrentWake() {
+ if (incrementMptrRefs()) {
+ try {
+ nativeWake(mPtr);
+ } finally {
+ decrementMptrRefs();
+ }
+ }
+ }
+
+ private void setFileDescriptorEvents(int fdNum, int events) {
+ if (incrementMptrRefs()) {
+ try {
+ nativeSetFileDescriptorEvents(mPtr, fdNum, events);
+ } finally {
+ decrementMptrRefs();
+ }
+ }
}
private boolean getQuitting() {
return (mQuittingRefCountValue & QUITTING_MASK) != 0;
}
- private volatile Thread mLooperThread = null;
// Must only be called from looper thread
private boolean checkQuittingAndWaitForRefsToDrop() {
if (!getQuitting()) {
return false;
}
- mLooperThread = Thread.currentThread();
- while ((mQuittingRefCountValue & ~QUITTING_MASK) != 0) {
- LockSupport.park();
+ boolean wasInterrupted = false;
+ try {
+ while ((mQuittingRefCountValue & ~QUITTING_MASK) != 0) {
+ LockSupport.park();
+ wasInterrupted |= Thread.interrupted();
+ }
+ } finally {
+ if (wasInterrupted) {
+ mLooperThread.interrupt();
+ }
}
return true;
}
@@ -372,6 +428,7 @@
}
private final MessageCounts mMessageCounts = new MessageCounts();
+ private final Thread mLooperThread;
private final Object mIdleHandlersLock = new Object();
@GuardedBy("mIdleHandlersLock")
@@ -414,6 +471,7 @@
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
mPtr = nativeInit();
+ mLooperThread = Thread.currentThread();
}
@android.ravenwood.annotation.RavenwoodReplace
@@ -558,14 +616,14 @@
*/
public boolean isPolling() {
// If the loop is quitting then it must not be idling.
- if (!incrementQuittingState()) {
- return false;
+ if (incrementMptrRefs()) {
+ try {
+ return nativeIsPolling(mPtr);
+ } finally {
+ decrementMptrRefs();
+ }
}
- try {
- return nativeIsPolling(mPtr);
- } finally {
- decrementQuittingState();
- }
+ return false;
}
/* Helper to choose the correct queue to insert into. */
@@ -771,7 +829,11 @@
*/
StateNode nextOp = sStackStateActive;
if (found == null) {
- if (next == null) {
+ if (getQuitting()) {
+ mNextPollTimeoutMillis = 0;
+ // State change will be Active->Active, so can immediately return here.
+ return null;
+ } else if (next == null) {
/* No message to deliver, sleep indefinitely */
mNextPollTimeoutMillis = -1;
nextOp = sStackStateParked;
@@ -904,22 +966,20 @@
throw new IllegalStateException("Main thread not allowed to quit.");
}
- if (!incrementQuittingState()) {
+ if (!incrementMptrRefsAndSetQuitting()) {
return;
}
try {
- if (setQuitting()) {
- if (safe) {
- removeAllFutureMessages();
- } else {
- removeAllMessages();
- }
-
- // We can assume mPtr != 0 while we hold a ref on our quitting state
- nativeWake(mPtr);
+ if (safe) {
+ removeAllFutureMessages();
+ } else {
+ removeAllMessages();
}
+
+ // We can assume mPtr != 0 while we hold a ref on our quitting state
+ nativeWake(mPtr);
} finally {
- decrementQuittingState();
+ decrementMptrRefs();
}
}
@@ -1020,7 +1080,7 @@
if (sState.compareAndSet(this, old, node)) {
if (inactive) {
if (wakeNeeded) {
- nativeWake(mPtr);
+ concurrentWake();
} else {
mMessageCounts.incrementQueued();
}
@@ -1121,7 +1181,7 @@
Message m = first.mMessage;
if (m.target == null && m.arg1 == token) {
/* Wake up next() in case it was sleeping on this barrier. */
- nativeWake(mPtr);
+ concurrentWake();
}
} else if (!removed) {
throw new IllegalStateException("The specified message queue synchronization "
@@ -1244,7 +1304,7 @@
if (p.removeFromStack()) {
p.mMessage.recycleUnchecked();
if (mMessageCounts.incrementCancelled()) {
- nativeWake(mPtr);
+ concurrentWake();
}
}
} else {
@@ -1684,11 +1744,11 @@
record.mEvents = events;
record.mSeq += 1;
}
- nativeSetFileDescriptorEvents(mPtr, fdNum, events);
+ setFileDescriptorEvents(fdNum, events);
} else if (record != null) {
record.mEvents = 0;
mFileDescriptorRecords.removeAt(index);
- nativeSetFileDescriptorEvents(mPtr, fdNum, 0);
+ setFileDescriptorEvents(fdNum, 0);
}
}
diff --git a/core/java/android/os/HandlerThread.java b/core/java/android/os/HandlerThread.java
index f852d3c..882f2d6 100644
--- a/core/java/android/os/HandlerThread.java
+++ b/core/java/android/os/HandlerThread.java
@@ -40,11 +40,11 @@
mPriority = Process.THREAD_PRIORITY_DEFAULT;
onCreated();
}
-
+
/**
* Constructs a HandlerThread.
* @param name
- * @param priority The priority to run the thread at. The value supplied must be from
+ * @param priority The priority to run the thread at. The value supplied must be from
* {@link android.os.Process} and not from java.lang.Thread.
*/
public HandlerThread(String name, int priority) {
@@ -85,7 +85,7 @@
Looper.loop();
mTid = -1;
}
-
+
/**
* This method returns the Looper associated with this thread. If this thread not been started
* or for any reason isAlive() returns false, this method will return null. If this thread
@@ -153,6 +153,9 @@
* </p><p>
* Any attempt to post messages to the queue after the looper is asked to quit will fail.
* For example, the {@link Handler#sendMessage(Message)} method will return false.
+ * </p><p>
+ * If {@link #quit} or {@link #quitSafely} is called multiple times, the first call
+ * will have an effect and the subsequent calls will be no-ops.
* </p><p class="note">
* Using this method may be unsafe because some messages may not be delivered
* before the looper terminates. Consider using {@link #quitSafely} instead to ensure
@@ -186,6 +189,9 @@
* If the thread has not been started or has finished (that is if
* {@link #getLooper} returns null), then false is returned.
* Otherwise the looper is asked to quit and true is returned.
+ * </p><p>
+ * If {@link #quit} or {@link #quitSafely} is called multiple times, the first call
+ * will have an effect and the subsequent calls will be no-ops.
* </p>
*
* @return True if the looper looper has been asked to quit or false if the
diff --git a/core/java/android/os/ITradeInMode.aidl b/core/java/android/os/ITradeInMode.aidl
index 5c29511..6912e97 100644
--- a/core/java/android/os/ITradeInMode.aidl
+++ b/core/java/android/os/ITradeInMode.aidl
@@ -107,4 +107,29 @@
*
*/
int getHingeLifeSpan(in int hingeId);
+ /**
+ * Information relating to internal screen panel part originality
+ * This is imported from composer HAL's screen part status
+ */
+ @VintfStability
+ @Backing(type="int")
+ enum ScreenPartStatus {
+ /**
+ * Device cannot differentiate an original screen from a replaced screen.
+ */
+ UNSUPPORTED = 0,
+ /**
+ * Device has the original screen it was manufactured with.
+ */
+ ORIGINAL = 1,
+ /**
+ * Device has a replaced screen.
+ */
+ REPLACED = 2,
+ }
+ /**
+ * Get ScreenPartStatus
+ *
+ */
+ ScreenPartStatus getScreenPartStatus();
}
diff --git a/core/java/android/os/IpcDataCache.java b/core/java/android/os/IpcDataCache.java
index 887a10c..ff9a9b9 100644
--- a/core/java/android/os/IpcDataCache.java
+++ b/core/java/android/os/IpcDataCache.java
@@ -616,7 +616,7 @@
* @hide
*/
public IpcDataCache(@NonNull Config config, @NonNull RemoteCall<Query, Result> remoteCall) {
- this(config, android.multiuser.Flags.cachingDevelopmentImprovements() ?
+ this(config,
new QueryHandler<Query, Result>() {
@Override
public Result apply(Query query) {
@@ -626,7 +626,7 @@
throw e.rethrowFromSystemServer();
}
}
- } : new SystemServerCallHandler<>(remoteCall));
+ });
}
@@ -638,7 +638,6 @@
* bypassed.
* @hide
*/
- @FlaggedApi(android.multiuser.Flags.FLAG_CACHING_DEVELOPMENT_IMPROVEMENTS)
public IpcDataCache(@NonNull Config config,
@NonNull RemoteCall<Query, Result> remoteCall,
@NonNull BypassCall<Query> bypass) {
diff --git a/core/java/android/os/Looper.java b/core/java/android/os/Looper.java
index 3ff6e05..a7b46fe 100644
--- a/core/java/android/os/Looper.java
+++ b/core/java/android/os/Looper.java
@@ -199,14 +199,16 @@
return false;
}
- PerfettoTrace.begin(PerfettoTrace.MQ_CATEGORY, "message_queue_receive")
- .beginProto()
- .beginNested(2004 /* message_queue */)
- .addField(1 /* sending_thread_name */, msg.mSendingThreadName)
- .endNested()
- .endProto()
- .setTerminatingFlow(msg.mEventId.get())
- .emit();
+ if (PerfettoTrace.MQ_CATEGORY.isEnabled()) {
+ PerfettoTrace.begin(PerfettoTrace.MQ_CATEGORY, "message_queue_receive")
+ .beginProto()
+ .beginNested(2004 /* message_queue */)
+ .addField(1 /* sending_thread_name */, msg.mSendingThreadName)
+ .endNested()
+ .endProto()
+ .setTerminatingFlow(msg.mEventId.get())
+ .emit();
+ }
// This must be in a local variabe, in case a UI event sets the logger
final Printer logging = me.mLogging;
@@ -297,8 +299,10 @@
+ msg.target.getClass().getName() + " "
+ msg.callback + " what=" + msg.what);
}
+ if (PerfettoTrace.MQ_CATEGORY.isEnabled()) {
+ PerfettoTrace.end(PerfettoTrace.MQ_CATEGORY).emit();
+ }
- PerfettoTrace.end(PerfettoTrace.MQ_CATEGORY).emit();
msg.recycleUnchecked();
return true;
@@ -487,6 +491,9 @@
* </p><p>
* Any attempt to post messages to the queue after the looper is asked to quit will fail.
* For example, the {@link Handler#sendMessage(Message)} method will return false.
+ * </p><p>
+ * If {@link #quit} or {@link #quitSafely} is called multiple times, the first call
+ * will have an effect and the subsequent calls will be no-ops.
* </p><p class="note">
* Using this method may be unsafe because some messages may not be delivered
* before the looper terminates. Consider using {@link #quitSafely} instead to ensure
@@ -509,6 +516,9 @@
* </p><p>
* Any attempt to post messages to the queue after the looper is asked to quit will fail.
* For example, the {@link Handler#sendMessage(Message)} method will return false.
+ * </p><p>
+ * If {@link #quit} or {@link #quitSafely} is called multiple times, the first call
+ * will have an effect and the subsequent calls will be no-ops.
* </p>
*/
public void quitSafely() {
diff --git a/core/java/android/os/PowerManagerInternal.java b/core/java/android/os/PowerManagerInternal.java
index 2736b60..857a9f4 100644
--- a/core/java/android/os/PowerManagerInternal.java
+++ b/core/java/android/os/PowerManagerInternal.java
@@ -209,6 +209,14 @@
public abstract void uidIdle(int uid);
/**
+ * Used to notify the power manager that wakelocks should be disabled.
+ *
+ * @param force {@code true} to activate force disable wakelocks, {@code false} to turn it off.
+ */
+
+ public abstract void setForceDisableWakelocks(boolean force);
+
+ /**
* Boost: It is sent when user interacting with the device, for example,
* touchscreen events are incoming.
* Defined in hardware/interfaces/power/aidl/android/hardware/power/Boost.aidl
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index b68b9a7..654474d 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -1179,6 +1179,20 @@
public static final native void setProcessFrozen(int pid, int uid, boolean frozen);
/**
+ * Return true if the process is frozen. It returns false if the pid/uid combination is not
+ * found or if the caller does not have permission to query the pid.
+ *
+ * @param pid Identifier of the process to query.
+ * @param uid Identifier of the user the process is running under.
+ * @return true if the process is frozen, false otherwise.
+ * @throws IllegalArgumentException if the uid is negative.
+ *
+ * @hide
+ */
+ public static final native boolean isProcessFrozen(int pid, int uid)
+ throws IllegalArgumentException;
+
+ /**
* Return the scheduling group of requested process.
*
* @hide
diff --git a/core/java/android/os/ServiceManagerNative.java b/core/java/android/os/ServiceManagerNative.java
index a5aa1b3..d155a44 100644
--- a/core/java/android/os/ServiceManagerNative.java
+++ b/core/java/android/os/ServiceManagerNative.java
@@ -133,6 +133,11 @@
return mServiceManager.getServiceDebugInfo();
}
+ public boolean checkServiceAccess(CallerContext callerCtx, String service,
+ String access) throws RemoteException {
+ return mServiceManager.checkServiceAccess(callerCtx, service, access);
+ }
+
/**
* Same as mServiceManager but used by apps.
*
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index edeb75b..5257a19 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -1379,7 +1379,7 @@
* @hide
*/
@UnsupportedAppUsage
- @android.ravenwood.annotation.RavenwoodReplace
+ @android.ravenwood.annotation.RavenwoodIgnore
public static @ThreadPolicyMask int getThreadPolicyMask() {
final BlockGuard.Policy policy = BlockGuard.getThreadPolicy();
if (policy instanceof AndroidBlockGuardPolicy) {
@@ -1389,12 +1389,6 @@
}
}
- /** @hide */
- public static @ThreadPolicyMask int getThreadPolicyMask$ravenwood() {
- // Ravenwood currently doesn't support any detection modes
- return 0;
- }
-
/** Returns the current thread's policy. */
public static ThreadPolicy getThreadPolicy() {
// TODO: this was a last minute Gingerbread API change (to
@@ -2690,15 +2684,11 @@
* (Java) thread-local policy value.
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- @android.ravenwood.annotation.RavenwoodReplace
+ @android.ravenwood.annotation.RavenwoodIgnore
private static void onBinderStrictModePolicyChange(@ThreadPolicyMask int newPolicy) {
setBlockGuardPolicy(newPolicy);
}
- private static void onBinderStrictModePolicyChange$ravenwood(@ThreadPolicyMask int newPolicy) {
- /* no-op */
- }
-
/**
* A tracked, critical time span. (e.g. during an animation.)
*
@@ -2864,6 +2854,7 @@
*
* @param name a short string for the exception stack trace that's built if when this fires.
*/
+ @android.ravenwood.annotation.RavenwoodIgnore
public static void noteSlowCall(String name) {
BlockGuard.Policy policy = BlockGuard.getThreadPolicy();
if (!(policy instanceof AndroidBlockGuardPolicy)) {
diff --git a/core/java/android/os/SystemClock.java b/core/java/android/os/SystemClock.java
index 8ef62e3..b937705 100644
--- a/core/java/android/os/SystemClock.java
+++ b/core/java/android/os/SystemClock.java
@@ -19,8 +19,6 @@
import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.app.IAlarmManager;
-import android.app.time.UnixEpochTime;
-import android.app.timedetector.ITimeDetectorService;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.location.ILocationManager;
@@ -111,7 +109,6 @@
private static final String TAG = "SystemClock";
private static volatile IAlarmManager sIAlarmManager;
- private static volatile ITimeDetectorService sITimeDetectorService;
/**
* Since {@code nanoTime()} is arbitrary, anchor our Ravenwood clocks against it.
@@ -191,14 +188,6 @@
return sIAlarmManager;
}
- private static ITimeDetectorService getITimeDetectorService() {
- if (sITimeDetectorService == null) {
- sITimeDetectorService = ITimeDetectorService.Stub
- .asInterface(ServiceManager.getService(Context.TIME_DETECTOR_SERVICE));
- }
- return sITimeDetectorService;
- }
-
/**
* Returns milliseconds since boot, not counting time spent in deep sleep.
*
@@ -345,36 +334,10 @@
* @hide
*/
public static long currentNetworkTimeMillis() {
- if (com.android.internal.os.Flags.applicationSharedMemoryEnabled()
- && Flags.networkTimeUsesSharedMemory()) {
- final long latestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis =
- ApplicationSharedMemory.getInstance()
- .getLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis();
- return latestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis + elapsedRealtime();
- } else {
- ITimeDetectorService timeDetectorService = getITimeDetectorService();
- if (timeDetectorService == null) {
- throw new RuntimeException(new DeadSystemException());
- }
-
- UnixEpochTime time;
- try {
- time = timeDetectorService.latestNetworkTime();
- } catch (ParcelableException e) {
- e.maybeRethrow(DateTimeException.class);
- throw new RuntimeException(e);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- if (time == null) {
- // This is not expected.
- throw new DateTimeException("Network based time is not available.");
- }
-
- long currentMillis = elapsedRealtime();
- long deltaMs = currentMillis - time.getElapsedRealtimeMillis();
- return time.getUnixEpochTimeMillis() + deltaMs;
- }
+ final long latestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis =
+ ApplicationSharedMemory.getInstance()
+ .getLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis();
+ return latestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis + elapsedRealtime();
}
/**
diff --git a/core/java/android/os/Trace.java b/core/java/android/os/Trace.java
index bf4c4d1..e956e65 100644
--- a/core/java/android/os/Trace.java
+++ b/core/java/android/os/Trace.java
@@ -18,6 +18,7 @@
import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.compat.annotation.UnsupportedAppUsage;
@@ -388,6 +389,29 @@
}
/**
+ * Writes a trace message to indicate that a given section of code has
+ * begun. Must be followed by a call to {@link #asyncTraceForTrackEnd} using the same
+ * track name and cookie.
+ *
+ * Events with the same trackName and cookie nest inside each other in the
+ * same way as calls to {@link #traceBegin(long, String)} and
+ * {@link #traceEnd(long)}.
+ *
+ * @param trackName The track where the event should appear in the trace.
+ * @param methodName The method name to appear in the trace.
+ * @param cookie Unique identifier used for nesting events on a single
+ * track. Events which overlap without nesting on the same
+ * track must have different values for cookie.
+ * @hide
+ */
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_ASYNC_TRACE_FOR_TRACK)
+ public static void asyncTraceForTrackBegin(
+ @NonNull String trackName, @NonNull String methodName, int cookie) {
+ asyncTraceForTrackBegin(TRACE_TAG_APP, trackName, methodName, cookie);
+ }
+
+ /**
* Writes a trace message to indicate that the current method has ended.
* Must be called exactly once for each call to
* {@link #asyncTraceForTrackBegin(long, String, String, int)}
@@ -412,6 +436,27 @@
}
/**
+ * Writes a trace message to indicate that the current method has ended.
+ * Must be called exactly once for each call to
+ * {@link #asyncTraceForTrackBegin(String, String, int)}
+ * using the same track name, and cookie.
+ *
+ * See the documentation for {@link #asyncTraceForTrackBegin(String, String, int)}.
+ * for intended usage of this method.
+ *
+ * @param trackName The track where the event should appear in the trace.
+ * @param cookie Unique identifier used for nesting events on a single
+ * track. Events which overlap without nesting on the same
+ * track must have different values for cookie.
+ * @hide
+ */
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_ASYNC_TRACE_FOR_TRACK)
+ public static void asyncTraceForTrackEnd(@NonNull String trackName, int cookie) {
+ asyncTraceForTrackEnd(TRACE_TAG_APP, trackName, cookie);
+ }
+
+ /**
* Writes a trace message to indicate that a given section of code was invoked.
*
* @param traceTag The trace tag.
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index f1b65a9..acce89e 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -753,8 +753,7 @@
* <p>This restriction can only be set by a <a href="https://developers.google.com/android/work/terminology#device_owner_do">
* device owner</a> or a <a href="https://developers.google.com/android/work/terminology#profile_owner_po">
* profile owner</a> on the primary user's profile or a profile owner of an organization-owned
- * <a href="https://developers.google.com/android/work/terminology#managed_profile">
- * managed profile</a> on the parent profile.
+ * <a href="https://developers.google.com/android/work/terminology#managed_profile">managed profile</a> on the parent profile.
* When it is set by a device owner, it applies globally. When it is set by a profile owner
* on the primary user or by a profile owner of an organization-owned managed profile on
* the parent profile, it disables the primary user from transferring files over USB. No other
@@ -1566,18 +1565,17 @@
/**
* Specifies that the managed profile is not allowed to have unified lock screen challenge with
- * the primary user.
+ * the profile parent.
*
* <p>To ensure that there is a separate work profile password, IT admins
* have to:
* <ol>
* <li>Enforce {@link UserManager#DISALLOW_UNIFIED_PASSWORD}</li>
* <li>Verify that {@link DevicePolicyManager#isUsingUnifiedPassword(ComponentName)}
- * returns true. This indicates that there is now a separate work
+ * returns false. This indicates that there is now a separate work
* profile password configured and the set up is completed.</li>
- * <li>In case {@link DevicePolicyManager#isUsingUnifiedPassword(ComponentName)}
- * returns false, invoke {@link DevicePolicyManager#ACTION_SET_NEW_PASSWORD}
- * intent and then verify again
+ * <li>In case {@link DevicePolicyManager#isUsingUnifiedPassword(ComponentName)} returns true,
+ * invoke {@link DevicePolicyManager#ACTION_SET_NEW_PASSWORD} intent and then verify again
* {@link DevicePolicyManager#isUsingUnifiedPassword(ComponentName)}.</li>
* </ol>
* </p>
@@ -1587,7 +1585,7 @@
*
* <p>Holders of the permission
* {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_LOCK_CREDENTIALS}
- * can set this restriction using the DevicePolicyManager APIs mentioned below.
+ * can set this restriction using the {@link DevicePolicyManager} APIs mentioned below.
*
* <p>Key for user restrictions.
* <p>Type: Boolean
@@ -4029,7 +4027,7 @@
@CannotBeSpecialUser @NonNull UserHandle userHandle) {
final int userId = userHandle.getIdentifier();
- if (userId < 0 && android.multiuser.Flags.fixGetUserPropertyCache()) {
+ if (userId < 0) {
// Avoid calling into system server for invalid user ids.
throw new IllegalArgumentException("Cannot access properties for user " + userId);
}
@@ -5442,16 +5440,9 @@
Manifest.permission.QUERY_USERS}, conditional = true)
@CachedProperty(api = "user_manager_user_data")
public List<UserInfo> getProfiles(@UserIdInt int userId) {
- if (android.multiuser.Flags.cacheProfilesReadOnly()) {
return UserManagerCache.getProfiles(
(Integer userIdentifier) -> mService.getProfiles(userIdentifier, false),
userId);
- }
- try {
- return mService.getProfiles(userId, false /* enabledOnly */);
- } catch (RemoteException re) {
- throw re.rethrowFromSystemServer();
- }
}
/**
@@ -5654,15 +5645,7 @@
Manifest.permission.CREATE_USERS,
Manifest.permission.QUERY_USERS}, conditional = true)
public @NonNull int[] getProfileIds(@UserIdInt int userId, boolean enabledOnly) {
- if (android.multiuser.Flags.cacheProfileIdsReadOnly()) {
return enabledOnly ? getEnabledProfileIds(userId) : getProfileIdsWithDisabled(userId);
- } else {
- try {
- return mService.getProfileIds(userId, enabledOnly);
- } catch (RemoteException re) {
- throw re.rethrowFromSystemServer();
- }
- }
}
/**
@@ -5676,12 +5659,8 @@
Manifest.permission.QUERY_USERS}, conditional = true)
@CachedProperty(api = "user_manager_users")
public int[] getProfileIdsWithDisabled(@UserIdInt int userId) {
- if (android.multiuser.Flags.cacheProfileIdsReadOnly()) {
- return UserManagerCache.getProfileIdsWithDisabled(
- (Integer userIdentifuer) -> mService.getProfileIds(userIdentifuer, false), userId);
- } else {
- return getProfileIds(userId, false /* enabledOnly */);
- }
+ return UserManagerCache.getProfileIdsWithDisabled(
+ (Integer userIdentifier) -> mService.getProfileIds(userIdentifier, false), userId);
}
/**
@@ -5694,19 +5673,13 @@
Manifest.permission.QUERY_USERS}, conditional = true)
@CachedProperty(api = "user_manager_users_enabled")
public int[] getEnabledProfileIds(@UserIdInt int userId) {
- if (android.multiuser.Flags.cacheProfileIdsReadOnly()) {
- return UserManagerCache.getEnabledProfileIds(
- (Integer userIdentifuer) -> mService.getProfileIds(userIdentifuer, true), userId);
- } else {
- return getProfileIds(userId, true /* enabledOnly */);
- }
+ return UserManagerCache.getEnabledProfileIds(
+ (Integer userIdentifier) -> mService.getProfileIds(userIdentifier, true), userId);
}
/** @hide */
public static final void invalidateEnabledProfileIds() {
- if (android.multiuser.Flags.cacheProfileIdsReadOnly()) {
- UserManagerCache.invalidateEnabledProfileIds();
- }
+ UserManagerCache.invalidateEnabledProfileIds();
}
/**
@@ -5779,28 +5752,20 @@
})
@CachedProperty(api = "user_manager_users")
public @Nullable UserHandle getProfileParent(@NonNull UserHandle user) {
- if (android.multiuser.Flags.cacheProfileParentReadOnly()) {
- final UserHandle userHandle = UserManagerCache.getProfileParent(
- (UserHandle query) -> {
- UserInfo info = getProfileParent(query.getIdentifier());
- // TODO: Remove when b/372923336 is fixed
- if (info == null) {
- return UserHandle.of(UserHandle.USER_NULL);
- }
- return UserHandle.of(info.id);
- },
- user);
- if (userHandle.getIdentifier() == UserHandle.USER_NULL) {
- return null;
- }
- return userHandle;
- } else {
- UserInfo info = getProfileParent(user.getIdentifier());
- if (info == null) {
- return null;
- }
- return UserHandle.of(info.id);
+ final UserHandle userHandle = UserManagerCache.getProfileParent(
+ (UserHandle query) -> {
+ UserInfo info = getProfileParent(query.getIdentifier());
+ // TODO: Remove when b/372923336 is fixed
+ if (info == null) {
+ return UserHandle.of(UserHandle.USER_NULL);
+ }
+ return UserHandle.of(info.id);
+ },
+ user);
+ if (userHandle.getIdentifier() == UserHandle.USER_NULL) {
+ return null;
}
+ return userHandle;
}
/**
@@ -5903,19 +5868,12 @@
*/
@CachedProperty(mods = {})
public boolean isQuietModeEnabled(UserHandle userHandle) {
- if (android.multiuser.Flags.cacheQuietModeState()) {
- final int userId = userHandle.getIdentifier();
- if (userId < 0) {
- return false;
- }
- return ((UserManagerCache) mIpcDataCache).isQuietModeEnabled(
- (UserHandle uh) -> mService.isQuietModeEnabled(uh.getIdentifier()), userHandle);
+ final int userId = userHandle.getIdentifier();
+ if (userId < 0) {
+ return false;
}
- try {
- return mService.isQuietModeEnabled(userHandle.getIdentifier());
- } catch (RemoteException re) {
- throw re.rethrowFromSystemServer();
- }
+ return ((UserManagerCache) mIpcDataCache).isQuietModeEnabled(
+ (UserHandle uh) -> mService.isQuietModeEnabled(uh.getIdentifier()), userHandle);
}
/**
@@ -6615,9 +6573,7 @@
*/
public static final void invalidateCacheOnUserListChange() {
UserManagerCache.invalidateUserSerialNumber();
- if (android.multiuser.Flags.cacheProfileParentReadOnly()) {
- UserManagerCache.invalidateProfileParent();
- }
+ UserManagerCache.invalidateProfileParent();
invalidateCacheOnUserDataChanged();
invalidateEnabledProfileIds();
invalidateUserRestriction();
@@ -6645,8 +6601,7 @@
* @hide
*/
public static final void invalidateCacheOnUserDataChanged() {
- if (android.multiuser.Flags.cacheProfilesReadOnly()
- || android.multiuser.Flags.cacheUserInfoReadOnly()) {
+ if (android.multiuser.Flags.cacheUserInfoReadOnly()) {
// TODO(b/383175685): Rename the invalidation call to make it clearer that it
// invalidates the caches for both getProfiles and getUserInfo (since they both use the
// same user_manager_user_data CachedProperty.api).
@@ -6665,24 +6620,13 @@
@UnsupportedAppUsage
@CachedProperty(mods = {}, api = "user_manager_users")
public int getUserSerialNumber(@UserIdInt int userId) {
- // Read only flag should is to fix early access to this API
- // cacheUserSerialNumber to be removed after the
- // cacheUserSerialNumberReadOnly is fully rolled out
- if (android.multiuser.Flags.cacheUserSerialNumberReadOnly()
- || android.multiuser.Flags.cacheUserSerialNumber()) {
- // System user serial number is always 0, and it always exists.
- // There is no need to call binder for that.
- if (userId == UserHandle.USER_SYSTEM) {
- return UserHandle.USER_SERIAL_SYSTEM;
- }
- return ((UserManagerCache) mIpcDataCache).getUserSerialNumber(
+ // System user serial number is always 0, and it always exists.
+ // There is no need to call binder for that.
+ if (userId == UserHandle.USER_SYSTEM) {
+ return UserHandle.USER_SERIAL_SYSTEM;
+ }
+ return ((UserManagerCache) mIpcDataCache).getUserSerialNumber(
mService::getUserSerialNumber, userId);
- }
- try {
- return mService.getUserSerialNumber(userId);
- } catch (RemoteException re) {
- throw re.rethrowFromSystemServer();
- }
}
/**
diff --git a/core/java/android/os/VibrationEffect.java b/core/java/android/os/VibrationEffect.java
index 0a0e806..836d0977 100644
--- a/core/java/android/os/VibrationEffect.java
+++ b/core/java/android/os/VibrationEffect.java
@@ -32,8 +32,6 @@
import android.content.ContentResolver;
import android.content.Context;
import android.hardware.vibrator.IVibrator;
-import android.hardware.vibrator.V1_0.EffectStrength;
-import android.hardware.vibrator.V1_3.Effect;
import android.net.Uri;
import android.os.vibrator.BasicPwleSegment;
import android.os.vibrator.Flags;
@@ -60,6 +58,7 @@
import java.util.StringJoiner;
import java.util.function.BiFunction;
import java.util.function.Function;
+import java.util.stream.IntStream;
/**
* A VibrationEffect describes a haptic effect to be performed by a {@link Vibrator}.
@@ -91,40 +90,46 @@
/**
* A click effect. Use this effect as a baseline, as it's the most common type of click effect.
*/
- public static final int EFFECT_CLICK = Effect.CLICK;
+ // Internally this maps to the HAL constant Effect::CLICK
+ public static final int EFFECT_CLICK = 0;
/**
* A double click effect.
*/
- public static final int EFFECT_DOUBLE_CLICK = Effect.DOUBLE_CLICK;
+ // Internally this maps to the HAL constant Effect::DOUBLE_CLICK
+ public static final int EFFECT_DOUBLE_CLICK = 1;
/**
* A tick effect. This effect is less strong compared to {@link #EFFECT_CLICK}.
*/
- public static final int EFFECT_TICK = Effect.TICK;
+ // Internally this maps to the HAL constant Effect::TICK
+ public static final int EFFECT_TICK = 2;
/**
* A thud effect.
* @see #get(int)
* @hide
*/
+ // Internally this maps to the HAL constant Effect::THUD
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@TestApi
- public static final int EFFECT_THUD = Effect.THUD;
+ public static final int EFFECT_THUD = 3;
/**
* A pop effect.
* @see #get(int)
* @hide
*/
+ // Internally this maps to the HAL constant Effect::POP
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@TestApi
- public static final int EFFECT_POP = Effect.POP;
+ public static final int EFFECT_POP = 4;
/**
* A heavy click effect. This effect is stronger than {@link #EFFECT_CLICK}.
*/
- public static final int EFFECT_HEAVY_CLICK = Effect.HEAVY_CLICK;
+ // Internally this maps to the HAL constant Effect::HEAVY_CLICK
+ public static final int EFFECT_HEAVY_CLICK = 5;
/**
* A texture effect meant to replicate soft ticks.
@@ -136,20 +141,24 @@
* @see #get(int)
* @hide
*/
+ // Internally this maps to the HAL constant Effect::TEXTURE_TICK
@TestApi
- public static final int EFFECT_TEXTURE_TICK = Effect.TEXTURE_TICK;
+ public static final int EFFECT_TEXTURE_TICK = 21;
/** {@hide} */
+ // Internally this maps to the HAL constant EffectStrength::LIGHT
@TestApi
- public static final int EFFECT_STRENGTH_LIGHT = EffectStrength.LIGHT;
+ public static final int EFFECT_STRENGTH_LIGHT = 0;
/** {@hide} */
+ // Internally this maps to the HAL constant EffectStrength::MEDIUM
@TestApi
- public static final int EFFECT_STRENGTH_MEDIUM = EffectStrength.MEDIUM;
+ public static final int EFFECT_STRENGTH_MEDIUM = 1;
/** {@hide} */
+ // Internally this maps to the HAL constant EffectStrength::STRONG
@TestApi
- public static final int EFFECT_STRENGTH_STRONG = EffectStrength.STRONG;
+ public static final int EFFECT_STRENGTH_STRONG = 2;
/**
* Ringtone patterns. They may correspond with the device's ringtone audio, or may just be a
@@ -158,25 +167,10 @@
* @see #get(Uri, Context)
* @hide
*/
+ // Internally this maps to the HAL constant Effect::RINGTONE_*
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@TestApi
- public static final int[] RINGTONES = {
- Effect.RINGTONE_1,
- Effect.RINGTONE_2,
- Effect.RINGTONE_3,
- Effect.RINGTONE_4,
- Effect.RINGTONE_5,
- Effect.RINGTONE_6,
- Effect.RINGTONE_7,
- Effect.RINGTONE_8,
- Effect.RINGTONE_9,
- Effect.RINGTONE_10,
- Effect.RINGTONE_11,
- Effect.RINGTONE_12,
- Effect.RINGTONE_13,
- Effect.RINGTONE_14,
- Effect.RINGTONE_15
- };
+ public static final int[] RINGTONES = IntStream.rangeClosed(6, 20).toArray();
/** @hide */
@IntDef(prefix = { "EFFECT_" }, value = {
@@ -1513,32 +1507,39 @@
*
* @hide
*/
+ // Internally this maps to the HAL constant CompositePrimitive::NOOP
public static final int PRIMITIVE_NOOP = 0;
/**
* This effect should produce a sharp, crisp click sensation.
*/
+ // Internally this maps to the HAL constant CompositePrimitive::CLICK
public static final int PRIMITIVE_CLICK = 1;
/**
* A haptic effect that simulates downwards movement with gravity. Often
* followed by extra energy of hitting and reverberation to augment
* physicality.
*/
+ // Internally this maps to the HAL constant CompositePrimitive::THUD
public static final int PRIMITIVE_THUD = 2;
/**
* A haptic effect that simulates spinning momentum.
*/
+ // Internally this maps to the HAL constant CompositePrimitive::SPIN
public static final int PRIMITIVE_SPIN = 3;
/**
* A haptic effect that simulates quick upward movement against gravity.
*/
+ // Internally this maps to the HAL constant CompositePrimitive::QUICK_RISE
public static final int PRIMITIVE_QUICK_RISE = 4;
/**
* A haptic effect that simulates slow upward movement against gravity.
*/
+ // Internally this maps to the HAL constant CompositePrimitive::SLOW_RISE
public static final int PRIMITIVE_SLOW_RISE = 5;
/**
* A haptic effect that simulates quick downwards movement with gravity.
*/
+ // Internally this maps to the HAL constant CompositePrimitive::QUICK_FALL
public static final int PRIMITIVE_QUICK_FALL = 6;
/**
* This very short effect should produce a light crisp sensation intended
diff --git a/core/java/android/os/flags.aconfig b/core/java/android/os/flags.aconfig
index 482c1e5..fc41434 100644
--- a/core/java/android/os/flags.aconfig
+++ b/core/java/android/os/flags.aconfig
@@ -158,6 +158,14 @@
}
flag {
+ name: "async_trace_for_track"
+ is_exported: true
+ namespace: "perfetto"
+ description: "Enable async trace for track."
+ bug: "407831889"
+}
+
+flag {
name: "battery_part_status_api"
is_exported: true
namespace: "phoenix"
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 24f8672..d7836af 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -29,7 +29,6 @@
import android.annotation.BytesLong;
import android.annotation.CallbackExecutor;
-import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -61,7 +60,6 @@
import android.os.Build;
import android.os.Environment;
import android.os.FileUtils;
-import android.os.Flags;
import android.os.Handler;
import android.os.IInstalld;
import android.os.IVold;
@@ -3019,7 +3017,6 @@
* @return Percentage of the remaining useful lifetime of the internal storage device.
* @hide
*/
- @FlaggedApi(Flags.FLAG_STORAGE_LIFETIME_API)
@SystemApi
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public int getInternalStorageRemainingLifetime() {
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index c36bd54..7cb5ee0 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -2407,6 +2407,15 @@
= "android.settings.NOTIFICATION_HISTORY";
/**
+ * Activity Action: Show notification bundling settings screen
+ *
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_NOTIFICATION_BUNDLES
+ = "android.settings.NOTIFICATION_BUNDLES";
+
+ /**
* Activity Action: Show app listing settings, filtered by those that send notifications.
*
*/
@@ -3553,6 +3562,10 @@
arg.putBoolean(CALL_METHOD_OVERRIDEABLE_BY_RESTORE_KEY, true);
}
IContentProvider cp = mProviderHolder.getProvider(cr);
+ if (cp == null) {
+ Log.w(TAG, "Can't set key " + name + " in " + mUri + " because cp is null");
+ return false;
+ }
cp.call(cr.getAttributionSource(),
mProviderHolder.mUri.getAuthority(), mCallSetCommand, name, arg);
} catch (RemoteException e) {
@@ -3573,6 +3586,10 @@
args.putString(CALL_METHOD_PREFIX_KEY, prefix);
args.putSerializable(CALL_METHOD_FLAGS_KEY, keyValues);
IContentProvider cp = mProviderHolder.getProvider(cr);
+ if (cp == null) {
+ Log.w(TAG, "Can't set strings for prefix " + prefix + " because cp is null");
+ return SET_ALL_RESULT_FAILURE;
+ }
Bundle bundle = cp.call(cr.getAttributionSource(),
mProviderHolder.mUri.getAuthority(),
mCallSetAllCommand, null, args);
@@ -3588,6 +3605,10 @@
Bundle arg = new Bundle();
arg.putInt(CALL_METHOD_USER_KEY, userHandle);
IContentProvider cp = mProviderHolder.getProvider(cr);
+ if (cp == null) {
+ Log.w(TAG, "Can't delete key " + name + " because cp is null");
+ return false;
+ }
cp.call(cr.getAttributionSource(),
mProviderHolder.mUri.getAuthority(), mCallDeleteCommand, name, arg);
} catch (RemoteException e) {
@@ -3673,6 +3694,10 @@
}
IContentProvider cp = mProviderHolder.getProvider(cr);
+ if (cp == null) {
+ Log.w(TAG, "Can't get key " + name + " because cp is null");
+ return null; // Return null, but don't cache it.
+ }
// Try the fast path first, not using query(). If this
// fails (alternate Settings provider that doesn't support
@@ -3887,6 +3912,10 @@
Log.i(TAG, "Cache miss for prefix:" + prefix);
}
IContentProvider cp = mProviderHolder.getProvider(cr);
+ if (cp == null) {
+ Log.w(TAG, "Can't get strings for prefix " + prefix + " because cp is null");
+ return keyValues;
+ }
try {
Bundle args = new Bundle();
@@ -4459,6 +4488,11 @@
}
arg.putInt(CALL_METHOD_RESET_MODE_KEY, mode);
IContentProvider cp = sProviderHolder.getProvider(resolver);
+ if (cp == null) {
+ Log.w(TAG, "Can't reset to defaults for " + CONTENT_URI
+ + " because cp is null");
+ return;
+ }
cp.call(resolver.getAttributionSource(),
sProviderHolder.mUri.getAuthority(), CALL_METHOD_RESET_SYSTEM, null, arg);
} catch (RemoteException e) {
@@ -7372,6 +7406,11 @@
}
arg.putInt(CALL_METHOD_RESET_MODE_KEY, mode);
IContentProvider cp = sProviderHolder.getProvider(resolver);
+ if (cp == null) {
+ Log.w(TAG, "Can't reset to defaults for " + CONTENT_URI
+ + " because cp is null");
+ return;
+ }
cp.call(resolver.getAttributionSource(),
sProviderHolder.mUri.getAuthority(), CALL_METHOD_RESET_SECURE, null, arg);
} catch (RemoteException e) {
@@ -8686,6 +8725,19 @@
"search_content_filters_enabled";
/**
+ * Setting to indicate that themes should be enabled in related app.
+ *
+ * <ul>
+ * <li>0 = Off
+ * <li>1 = Enable themes
+ * </ul>
+ *
+ * @hide
+ */
+ public static final String PACK_THEME_FEATURE_ENABLED =
+ "pack_theme_feature_enabled";
+
+ /**
* Set by the system to track if the user needs to see the call to action for
* the lockscreen notification policy.
* @hide
@@ -12671,6 +12723,14 @@
"accessibility_magnification_two_finger_triple_tap_enabled";
/**
+ * Whether the magnify navigation bar and keyboard feature is enabled.
+ *
+ * @hide
+ */
+ public static final String ACCESSIBILITY_MAGNIFICATION_MAGNIFY_NAV_AND_IME =
+ "accessibility_magnification_magnify_nav_and_ime";
+
+ /**
* For pinch to zoom anywhere feature.
*
* If true, you should be able to pinch to magnify the window anywhere.
@@ -18821,6 +18881,11 @@
}
arg.putInt(CALL_METHOD_RESET_MODE_KEY, mode);
IContentProvider cp = sProviderHolder.getProvider(resolver);
+ if (cp == null) {
+ Log.w(TAG, "Can't reset to defaults for " + CONTENT_URI
+ + " because cp is null");
+ return;
+ }
cp.call(resolver.getAttributionSource(),
sProviderHolder.mUri.getAuthority(), CALL_METHOD_RESET_GLOBAL, null, arg);
} catch (RemoteException e) {
@@ -21071,6 +21136,10 @@
Bundle arg = new Bundle();
arg.putInt(Settings.CALL_METHOD_USER_KEY, resolver.getUserId());
IContentProvider cp = sProviderHolder.getProvider(resolver);
+ if (cp == null) {
+ Log.w(TAG, "Can't get all strings because cp is null");
+ return allFlags;
+ }
if (Flags.reduceBinderTransactionSizeForGetAllProperties()) {
Bundle b = cp.call(resolver.getAttributionSource(),
@@ -21248,6 +21317,11 @@
arg.putString(Settings.CALL_METHOD_PREFIX_KEY, createPrefix(namespace));
}
IContentProvider cp = sProviderHolder.getProvider(resolver);
+ if (cp == null) {
+ Log.w(TAG, "Can't reset to defaults for " + CONTENT_URI
+ + " because cp is null");
+ return;
+ }
cp.call(resolver.getAttributionSource(),
sProviderHolder.mUri.getAuthority(), CALL_METHOD_RESET_CONFIG, null, arg);
} catch (RemoteException e) {
@@ -21270,6 +21344,11 @@
Bundle args = new Bundle();
args.putInt(CALL_METHOD_SYNC_DISABLED_MODE_KEY, disableSyncMode);
IContentProvider cp = sProviderHolder.getProvider(resolver);
+ if (cp == null) {
+ Log.w(TAG, "Can't set sync disabled mode for " + CONTENT_URI
+ + " because cp is null");
+ return;
+ }
cp.call(resolver.getAttributionSource(), sProviderHolder.mUri.getAuthority(),
CALL_METHOD_SET_SYNC_DISABLED_MODE_CONFIG, null, args);
} catch (RemoteException e) {
@@ -21291,6 +21370,11 @@
ContentResolver resolver = getContentResolver();
Bundle args = Bundle.EMPTY;
IContentProvider cp = sProviderHolder.getProvider(resolver);
+ if (cp == null) {
+ Log.w(TAG, "Can't query sync disabled mode for " + CONTENT_URI
+ + " because cp is null");
+ return -1;
+ }
Bundle bundle = cp.call(resolver.getAttributionSource(),
sProviderHolder.mUri.getAuthority(),
CALL_METHOD_GET_SYNC_DISABLED_MODE_CONFIG,
@@ -21332,6 +21416,10 @@
Bundle arg = new Bundle();
arg.putInt(CALL_METHOD_USER_KEY, resolver.getUserId());
IContentProvider cp = sProviderHolder.getProvider(resolver);
+ if (cp == null) {
+ Log.w(TAG, "Can't clear config monitor callback because cp is null");
+ return;
+ }
cp.call(resolver.getAttributionSource(),
sProviderHolder.mUri.getAuthority(),
CALL_METHOD_UNREGISTER_MONITOR_CALLBACK_CONFIG, null, arg);
@@ -21403,6 +21491,10 @@
handleMonitorCallback(result, executor, callback);
}));
IContentProvider cp = sProviderHolder.getProvider(resolver);
+ if (cp == null) {
+ Log.w(TAG, "Can't set config monitor callback because cp is null");
+ return;
+ }
cp.call(resolver.getAttributionSource(),
sProviderHolder.mUri.getAuthority(),
CALL_METHOD_REGISTER_MONITOR_CALLBACK_CONFIG, null, arg);
diff --git a/core/java/android/security/net/config/NetworkSecurityConfig.java b/core/java/android/security/net/config/NetworkSecurityConfig.java
index 410c68b..11d3a56 100644
--- a/core/java/android/security/net/config/NetworkSecurityConfig.java
+++ b/core/java/android/security/net/config/NetworkSecurityConfig.java
@@ -16,6 +16,14 @@
package android.security.net.config;
+import static android.sdk.Flags.majorMinorVersioningScheme;
+
+import static com.android.org.conscrypt.net.flags.Flags.certificateTransparencyDefaultEnabled;
+
+import android.annotation.FlaggedApi;
+import android.app.compat.CompatChanges;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledAfter;
import android.content.pm.ApplicationInfo;
import android.os.Build;
import android.util.ArrayMap;
@@ -38,8 +46,21 @@
public static final boolean DEFAULT_CLEARTEXT_TRAFFIC_PERMITTED = true;
/** @hide */
public static final boolean DEFAULT_HSTS_ENFORCED = false;
+
+ /**
+ * Enable Certificate Transparency verification checks by default on all TLS connections. Apps
+ * can still opt-out via their Network Security Config.
+ */
+ @ChangeId
+ @FlaggedApi(android.sdk.Flags.FLAG_MAJOR_MINOR_VERSIONING_SCHEME)
+ @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.BAKLAVA)
+ static final long DEFAULT_ENABLE_CERTIFICATE_TRANSPARENCY = 407952621L;
+
/** @hide */
- public static final boolean DEFAULT_CERTIFICATE_TRANSPARENCY_VERIFICATION_REQUIRED = false;
+ public static final boolean DEFAULT_CERTIFICATE_TRANSPARENCY_VERIFICATION_REQUIRED =
+ certificateTransparencyDefaultEnabled()
+ && majorMinorVersioningScheme()
+ && CompatChanges.isChangeEnabled(DEFAULT_ENABLE_CERTIFICATE_TRANSPARENCY);
private final boolean mCleartextTrafficPermitted;
private final boolean mHstsEnforced;
diff --git a/core/java/android/service/chooser/ChooserSession.java b/core/java/android/service/chooser/ChooserSession.java
deleted file mode 100644
index 8909e6f..0000000
--- a/core/java/android/service/chooser/ChooserSession.java
+++ /dev/null
@@ -1,432 +0,0 @@
-/*
- * Copyright 2025 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.chooser;
-
-import android.content.Intent;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.os.RemoteException;
-import android.util.Log;
-
-import androidx.annotation.MainThread;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-/**
- * <p>A class that represents an interactive Chooser session.</p>
- * <p>An instance of the class can be used as a value for <em>an</em> {@link Intent#ACTION_CHOOSER}
- * extra to establish a bi-directional communication channel with Chooser.
- * <p>A {@link ChooserSessionUpdateListener} callback can be used to receive updates about the
- * session and communication from Chooser.</p>
- *
- * @hide
- *
- */
-public final class ChooserSession implements Parcelable {
-
- private static final String TAG = "ChooserSession";
-
- private final IChooserControllerCallback mSessionCallbackBinder;
-
- // mChooserSession is expected to be null only on the Chooser side
- @Nullable
- private final ChooserSessionImpl mChooserSession;
-
- /**
- * An alias for {@code ChooserSession(Looper.getMainLooper())}.
- */
- public ChooserSession() {
- this(new Handler(Looper.getMainLooper()));
- }
-
- /**
- * @param handler a thread {@link ChooserSessionUpdateListener} callbacks will be delivered on.
- */
- public ChooserSession(Handler handler) {
- this(new ChooserSessionImpl(handler));
- }
-
- private ChooserSession(IChooserControllerCallback sessionBinder) {
- mSessionCallbackBinder = sessionBinder;
- mChooserSession = (sessionBinder instanceof ChooserSessionImpl)
- ? (ChooserSessionImpl) sessionBinder
- : null;
- }
-
- /**
- * @return true if the session is active: i.e. is not being cancelled by the client
- * (see {@link #cancel()}) or closed by the Chooser.
- */
- public boolean isActive() {
- return mChooserSession != null && mChooserSession.isActive();
- }
-
- /**
- * Cancel the session and close the Chooser.
- */
- public void cancel() {
- if (mChooserSession != null) {
- mChooserSession.cancel();
- }
- }
-
- /**
- * @return underlying {@link IChooserControllerCallback} binder.
- *
- * @hide
- */
- public IChooserControllerCallback getSessionCallbackBinder() {
- return mSessionCallbackBinder;
- }
-
- /**
- * <p>Get the active {@link ChooserController} or {@code null} if none is available.</p>
- * A chooser controller becomes available after the Chooser has registered it and stays
- * available while the session is active and the Chooser process is alive. It is possible for a
- * session to remain active without a Chooser process. For example, this could happen when the
- * client launches another activity on top of the Chooser session and the system reclaims the
- * new backgrounded chooser process. In such example, upon navigating back to the session, a
- * restored Chooser should register a new {@link ChooserController}.
- */
- @Nullable
- public ChooserController getChooserController() {
- return mChooserSession == null ? null : mChooserSession.getChooserController();
- }
-
- /**
- * @param listener make sure that the callback is cleared at the end of a component's lifecycle
- * (e.g. Activity) or provide a properly maintained WeakReference wrapper to avoid memory leaks.
- */
- public void setChooserStateListener(@Nullable ChooserSessionUpdateListener listener) {
- if (mChooserSession != null) {
- mChooserSession.setChooserStateListener(
- listener == null
- ? null
- : new ChooserSessionUpdateListenerWrapper(this, listener));
- }
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(@NonNull Parcel dest, int flags) {
- if (mChooserSession != null) {
- synchronized (mChooserSession) {
- dest.writeStrongBinder(mChooserSession);
- }
- }
- }
-
- public static final Parcelable.Creator<ChooserSession> CREATOR = new Creator<>() {
- @Override
- public ChooserSession createFromParcel(Parcel source) {
- IChooserControllerCallback binder =
- IChooserControllerCallback.Stub.asInterface(
- source.readStrongBinder());
- return binder == null ? null : new ChooserSession(binder);
- }
-
- @Override
- public ChooserSession[] newArray(int size) {
- return new ChooserSession[size];
- }
- };
-
- /**
- * A callback interface for Chooser session state updates.
- */
- public interface ChooserSessionUpdateListener {
-
- /**
- * Gets invoked when a {@link ChooserController} becomes available.
- * @param session a reference this callback is registered to.
- * @param chooserController active chooser controller.
- */
- void onChooserConnected(ChooserSession session, ChooserController chooserController);
-
- /**
- * Gets invoked when a {@link ChooserController} becomes unavailable.
- */
- void onChooserDisconnected(ChooserSession session);
-
- /**
- * Gets invoked when the session is closed by the Chooser.
- */
- void onSessionClosed(ChooserSession session);
-
- /**
- * A temporary support method; expected to be replaced by some other WindowManager API.
- */
- void onDrawerVerticalOffsetChanged(ChooserSession session, int offset);
- }
-
- /**
- * An interface for updating the Chooser.
- */
- public interface ChooserController {
-
- /**
- * Update chooser intent in a Chooser session.
- */
- // TODO: list all the updatable parameters in the javadoc.
- void updateIntent(Intent intent) throws RemoteException;
- }
-
- // Just to hide Chooser binder object from the client.
- private static class ChooserControllerWrapper implements ChooserController {
- public final IChooserController controller;
-
- private ChooserControllerWrapper(IChooserController controller) {
- this.controller = controller;
- }
-
- @Override
- public void updateIntent(Intent intent) throws RemoteException {
- controller.updateIntent(intent);
- }
- }
-
- private static class ChooserSessionUpdateListenerWrapper {
- private final ChooserSession mSession;
- private final ChooserSessionUpdateListener mListener;
-
- ChooserSessionUpdateListenerWrapper(
- ChooserSession mSession, ChooserSessionUpdateListener mListener) {
- this.mSession = mSession;
- this.mListener = mListener;
- }
-
- public void onChooserConnected(ChooserController chooserController) {
- mListener.onChooserConnected(mSession, chooserController);
- }
-
- public void onChooserDisconnected() {
- mListener.onChooserDisconnected(mSession);
- }
-
- public void onSessionClosed() {
- mListener.onSessionClosed(mSession);
- }
-
- public void onDrawerVerticalOffsetChanged(int offset) {
- mListener.onDrawerVerticalOffsetChanged(mSession, offset);
- }
- }
-
- private static class ChooserSessionImpl extends IChooserControllerCallback.Stub {
- private final Handler mHandler;
- private volatile ChooserSessionUpdateListenerWrapper mListener;
- private volatile boolean mIsActive = true;
- @Nullable
- private volatile ChooserControllerWrapper mChooserController;
- @Nullable
- private IBinder.DeathRecipient mChooserControllerLinkToDeath;
-
- ChooserSessionImpl(Handler handler) {
- mHandler = handler;
- }
-
- @Override
- public void registerChooserController(
- @Nullable final IChooserController chooserController) {
- mHandler.post(() -> setChooserController(chooserController));
- }
-
- @Override
- public void onDrawerVerticalOffsetChanged(int offset) {
- mHandler.post(() -> notifyDrawerVerticalOffsetChanged(offset));
- }
-
- public boolean isActive() {
- return mIsActive;
- }
-
- public void cancel() {
- mIsActive = false;
- mListener = null;
- if (mHandler.getLooper().isCurrentThread()) {
- doClose();
- } else {
- mHandler.post(this::doClose);
- }
- }
-
- @Nullable
- public ChooserController getChooserController() {
- return mChooserController;
- }
-
- public void setChooserStateListener(
- @Nullable ChooserSessionUpdateListenerWrapper listener) {
- mListener = listener;
- publishState();
- }
-
- private void publishState() {
- if (mHandler.getLooper().isCurrentThread()) {
- if (!mIsActive) {
- notifySessionClosed();
- } else if (mChooserController == null) {
- notifyChooserDisconnected();
- } else {
- notifyChooserConnected(mChooserController);
- }
- } else {
- mHandler.post(this::publishState);
- }
- }
-
- private void doClose() {
- ChooserControllerWrapper controllerWrapper = mChooserController;
- if (controllerWrapper != null) {
- if (mChooserControllerLinkToDeath != null) {
- safeUnlinkToDeath(
- controllerWrapper.controller.asBinder(), mChooserControllerLinkToDeath);
- }
- safeUpdateChooserIntent(controllerWrapper.controller, null);
- }
- mChooserController = null;
- mChooserControllerLinkToDeath = null;
- }
-
- private void setChooserController(IChooserController chooserController) {
- Log.d(
- TAG,
- "setIntentUpdater; isOpen: " + mIsActive
- + ", chooserController: " + chooserController);
- if (!mIsActive && chooserController != null) {
- // close Chooser
- safeUpdateChooserIntent(chooserController, null);
- return;
- }
- ChooserControllerWrapper controllerWrapper = mChooserController;
- if (controllerWrapper != null
- && areEqual(controllerWrapper.controller, chooserController)) {
- return;
- }
-
- disconnectCurrentIntentUpdater();
-
- if (chooserController != null) {
- controllerWrapper = new ChooserControllerWrapper(chooserController);
- this.mChooserController = controllerWrapper;
- mChooserControllerLinkToDeath = createDeathRecipient(chooserController);
- try {
- chooserController.asBinder().linkToDeath(mChooserControllerLinkToDeath, 0);
- notifyChooserConnected(controllerWrapper);
- } catch (RemoteException e) {
- // binder has already died
- this.mChooserController = null;
- mChooserControllerLinkToDeath = null;
- }
- } else {
- mIsActive = false;
- notifySessionClosed();
- }
- }
-
- @MainThread
- private void disconnectCurrentIntentUpdater() {
- ChooserControllerWrapper controllerWrapper = mChooserController;
- if (controllerWrapper != null) {
- if (mChooserControllerLinkToDeath != null) {
- safeUnlinkToDeath(
- controllerWrapper.controller.asBinder(), mChooserControllerLinkToDeath);
- }
- mChooserController = null;
- mChooserControllerLinkToDeath = null;
- notifyChooserDisconnected();
- }
- }
-
- private IBinder.DeathRecipient createDeathRecipient(IChooserController chooserController) {
- return () -> {
- Log.d(TAG, "chooser died");
- mHandler.post(() -> {
- ChooserControllerWrapper controllerWrapper = this.mChooserController;
- if (areEqual(
- controllerWrapper == null ? null : controllerWrapper.controller,
- chooserController)) {
- this.mChooserController = null;
- mChooserControllerLinkToDeath = null;
- mListener.onChooserDisconnected();
- }
- });
- };
- }
-
- private void notifyDrawerVerticalOffsetChanged(int offset) {
- ChooserSessionUpdateListenerWrapper listener = mListener;
- if (listener != null) {
- listener.onDrawerVerticalOffsetChanged(offset);
- }
- }
-
- private void notifyChooserConnected(ChooserController chooserController) {
- ChooserSessionUpdateListenerWrapper listener = mListener;
- if (listener != null) {
- listener.onChooserConnected(chooserController);
- }
- }
-
- private void notifySessionClosed() {
- ChooserSessionUpdateListenerWrapper listener = mListener;
- if (listener != null) {
- listener.onSessionClosed();
- }
- }
-
- private void notifyChooserDisconnected() {
- ChooserSessionUpdateListenerWrapper listener = mListener;
- if (listener != null) {
- listener.onChooserDisconnected();
- }
- }
-
- private static void safeUpdateChooserIntent(
- IChooserController chooserController, @Nullable Intent chooserIntent) {
- try {
- chooserController.updateIntent(chooserIntent);
- } catch (RemoteException ignored) {
- }
- }
-
- private static void safeUnlinkToDeath(IBinder binder, IBinder.DeathRecipient linkToDeath) {
- try {
- binder.unlinkToDeath(linkToDeath, 0);
- } catch (Exception ignored) {
- }
- }
-
- private static boolean areEqual(
- @Nullable IChooserController left, @Nullable IChooserController right) {
- if (left == null && right == null) {
- return true;
- }
- if (left == null || right == null) {
- return false;
- }
- return left.asBinder().equals(right.asBinder());
- }
- }
-}
diff --git a/core/java/android/service/chooser/IChooserController.aidl b/core/java/android/service/chooser/IChooserController.aidl
deleted file mode 100644
index b3d8d81..0000000
--- a/core/java/android/service/chooser/IChooserController.aidl
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright 2025 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.chooser;
-
-import android.content.Intent;
-
-/** {@hide} */
-interface IChooserController {
- oneway void updateIntent(in Intent intent);
-}
diff --git a/core/java/android/service/chooser/IChooserControllerCallback.aidl b/core/java/android/service/chooser/IChooserControllerCallback.aidl
deleted file mode 100644
index 141253f..0000000
--- a/core/java/android/service/chooser/IChooserControllerCallback.aidl
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright 2025 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.chooser;
-
-import android.service.chooser.IChooserController;
-
-/** {@hide} */
-interface IChooserControllerCallback {
- oneway void registerChooserController(in IChooserController updater);
- oneway void onDrawerVerticalOffsetChanged(in int offset);
-}
diff --git a/core/java/android/tracing/perfetto/TracingContext.java b/core/java/android/tracing/perfetto/TracingContext.java
index 13af8e8..61458b8 100644
--- a/core/java/android/tracing/perfetto/TracingContext.java
+++ b/core/java/android/tracing/perfetto/TracingContext.java
@@ -104,6 +104,16 @@
mDataSource.stopDoneDataSourceInstance(mInstanceIndex);
}
+ /**
+ * Gets the datasource instance for this state with a lock.
+ * releaseDataSourceInstanceLocked must be called before this can be called again.
+ * @return The data source instance for this state.
+ * Null if the datasource instance no longer exists.
+ */
+ public DataSourceInstanceType getDataSourceInstanceLocked() {
+ return mDataSource.getDataSourceInstanceLocked(mInstanceIndex);
+ }
+
protected byte[][] getAndClearAllPendingTracePackets() {
byte[][] res = new byte[mTracePackets.size()][];
for (int i = 0; i < mTracePackets.size(); i++) {
diff --git a/core/java/android/util/ListenerGroup.java b/core/java/android/util/ListenerGroup.java
new file mode 100644
index 0000000..5ffc445
--- /dev/null
+++ b/core/java/android/util/ListenerGroup.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.util;
+
+import android.annotation.NonNull;
+import android.os.Handler;
+import android.ravenwood.annotation.RavenwoodKeepWholeClass;
+
+import java.util.Objects;
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
+
+/**
+ * A utility class to manage a list of {@link Consumer} and {@link Executor} pairs. This class
+ * is thread safe because all the effects are dispatched through the handler.
+ * @param <T> the type of the value to be reported.
+ * @hide
+ */
+@RavenwoodKeepWholeClass
+public class ListenerGroup<T> {
+
+ /**
+ * The set of listeners to be managed. All modifications should be done on {@link #mHandler}.
+ */
+ private final ArrayMap<Consumer<T>, Executor> mListeners =
+ new ArrayMap<>();
+ @NonNull
+ private T mLastValue;
+ @NonNull
+ private final Handler mHandler;
+
+ /**
+ * Constructs a {@link ListenerGroup} that will replay the last reported value whenever a new
+ * listener is registered.
+ * @param value the initial value
+ * @param handler a handler to synchronize access to shared resources.
+ */
+ public ListenerGroup(@NonNull T value, @NonNull Handler handler) {
+ mLastValue = Objects.requireNonNull(value);
+ mHandler = Objects.requireNonNull(handler);
+ }
+
+ /**
+ * Relays the value to all the registered {@link java.util.function.Consumer}. The relay is
+ * initiated on the {@link Handler} provided in the constructor and then switched to the
+ * {@link Executor} that was registered with the {@link Consumer}.
+ */
+ public void accept(@NonNull T value) {
+ Objects.requireNonNull(value);
+ mHandler.post(() -> {
+ mLastValue = value;
+ for (int i = 0; i < mListeners.size(); i++) {
+ final Consumer<T> consumer = mListeners.keyAt(i);
+ final Executor executor = mListeners.get(consumer);
+ executor.execute(() -> consumer.accept(value));
+ }
+ });
+ }
+
+ /**
+ * Adds a {@link Consumer} to the group and replays the last reported value. The replay is
+ * initiated from the {@link Handler} provided in the constructor and run on the
+ * {@link Executor}. If the {@link Consumer} is already present then this is a no op.
+ */
+ public void addListener(@NonNull Executor executor, @NonNull Consumer<T> consumer) {
+ Objects.requireNonNull(executor, "Executor must not be null.");
+ Objects.requireNonNull(consumer, "Consumer must not be null.");
+ mHandler.post(() -> {
+ if (mListeners.containsKey(consumer)) {
+ return;
+ }
+ mListeners.put(consumer, executor);
+ executor.execute(() -> consumer.accept(mLastValue));
+ });
+ }
+
+ /**
+ * Removes a {@link Consumer} from the group. If the {@link Consumer} was not present then this
+ * is a no op.
+ */
+ public void removeListener(@NonNull Consumer<T> consumer) {
+ mHandler.post(() -> {
+ mListeners.remove(consumer);
+ });
+ }
+}
diff --git a/core/java/android/view/ContentRecordingSession.java b/core/java/android/view/ContentRecordingSession.java
index 952c63b..806581d 100644
--- a/core/java/android/view/ContentRecordingSession.java
+++ b/core/java/android/view/ContentRecordingSession.java
@@ -24,6 +24,7 @@
import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
+import android.os.Process;
import com.android.internal.util.DataClass;
@@ -51,6 +52,11 @@
* A single Task is being recorded. Recording may also be paused.
*/
public static final int RECORD_CONTENT_TASK = 1;
+ /**
+ * Content is recorded if it is below an overlay window belonging to the owner of the
+ * MediaProjection session.
+ */
+ public static final int RECORD_CONTENT_BELOW_OVERLAY = 2;
/** Full screen sharing (app is not selected). */
public static final int TARGET_UID_FULL_SCREEN = -1;
@@ -68,6 +74,12 @@
private int mTaskId = TASK_ID_UNKNOWN;
/**
+ * UID of the owner of the recording session. Only set if
+ * {@link #RECORD_CONTENT_BELOW_OVERLAY} is used.
+ */
+ private int mRecordingOwnerUid;
+
+ /**
* Unique logical identifier of the {@link android.hardware.display.VirtualDisplay} that has
* recorded content rendered to its surface.
*/
@@ -136,6 +148,16 @@
.setTaskId(taskId);
}
+
+ /** Returns an instance initialized for "below overlay" recording. */
+ public static ContentRecordingSession createOverlaySession(int displayToMirror,
+ int recordingOwnerUid) {
+ return new ContentRecordingSession()
+ .setDisplayToRecord(displayToMirror)
+ .setContentToRecord(RECORD_CONTENT_BELOW_OVERLAY)
+ .setRecordingOwnerUid(recordingOwnerUid);
+ }
+
/**
* Returns {@code true} if this is a valid session.
*
@@ -146,12 +168,20 @@
if (session == null) {
return false;
}
+ if (session.getVirtualDisplayId() == INVALID_DISPLAY) {
+ return false;
+ }
+
final boolean isValidTaskSession = session.getContentToRecord() == RECORD_CONTENT_TASK
&& session.getTokenToRecord() != null;
- final boolean isValidDisplaySession = session.getContentToRecord() == RECORD_CONTENT_DISPLAY
- && session.getDisplayToRecord() > INVALID_DISPLAY;
- return session.getVirtualDisplayId() > INVALID_DISPLAY
- && (isValidTaskSession || isValidDisplaySession);
+ final boolean isValidDisplaySession =
+ session.getContentToRecord() == RECORD_CONTENT_DISPLAY
+ && session.getDisplayToRecord() > INVALID_DISPLAY;
+ final boolean isValidOverlaySession =
+ session.getContentToRecord() == RECORD_CONTENT_BELOW_OVERLAY
+ && session.getDisplayToRecord() > INVALID_DISPLAY
+ && session.getRecordingOwnerUid() > Process.INVALID_UID;
+ return isValidTaskSession || isValidDisplaySession || isValidOverlaySession;
}
/**
@@ -182,7 +212,8 @@
@IntDef(prefix = "RECORD_CONTENT_", value = {
RECORD_CONTENT_DISPLAY,
- RECORD_CONTENT_TASK
+ RECORD_CONTENT_TASK,
+ RECORD_CONTENT_BELOW_OVERLAY
})
@Retention(RetentionPolicy.SOURCE)
@DataClass.Generated.Member
@@ -195,6 +226,8 @@
return "RECORD_CONTENT_DISPLAY";
case RECORD_CONTENT_TASK:
return "RECORD_CONTENT_TASK";
+ case RECORD_CONTENT_BELOW_OVERLAY:
+ return "RECORD_CONTENT_BELOW_OVERLAY";
default: return Integer.toHexString(value);
}
}
@@ -221,6 +254,7 @@
@DataClass.Generated.Member
/* package-private */ ContentRecordingSession(
int taskId,
+ int recordingOwnerUid,
int virtualDisplayId,
@RecordContent int contentToRecord,
int displayToRecord,
@@ -228,15 +262,18 @@
boolean waitingForConsent,
int targetUid) {
this.mTaskId = taskId;
+ this.mRecordingOwnerUid = recordingOwnerUid;
this.mVirtualDisplayId = virtualDisplayId;
this.mContentToRecord = contentToRecord;
if (!(mContentToRecord == RECORD_CONTENT_DISPLAY)
- && !(mContentToRecord == RECORD_CONTENT_TASK)) {
+ && !(mContentToRecord == RECORD_CONTENT_TASK)
+ && !(mContentToRecord == RECORD_CONTENT_BELOW_OVERLAY)) {
throw new java.lang.IllegalArgumentException(
"contentToRecord was " + mContentToRecord + " but must be one of: "
+ "RECORD_CONTENT_DISPLAY(" + RECORD_CONTENT_DISPLAY + "), "
- + "RECORD_CONTENT_TASK(" + RECORD_CONTENT_TASK + ")");
+ + "RECORD_CONTENT_TASK(" + RECORD_CONTENT_TASK + "), "
+ + "RECORD_CONTENT_BELOW_OVERLAY(" + RECORD_CONTENT_BELOW_OVERLAY + ")");
}
this.mDisplayToRecord = displayToRecord;
@@ -257,6 +294,15 @@
}
/**
+ * UID of the owner of the recording session. Only set if
+ * {@link #RECORD_CONTENT_BELOW_OVERLAY} is used.
+ */
+ @DataClass.Generated.Member
+ public int getRecordingOwnerUid() {
+ return mRecordingOwnerUid;
+ }
+
+ /**
* Unique logical identifier of the {@link android.hardware.display.VirtualDisplay} that has
* recorded content rendered to its surface.
*/
@@ -325,6 +371,16 @@
}
/**
+ * UID of the owner of the recording session. Only set if
+ * {@link #RECORD_CONTENT_BELOW_OVERLAY} is used.
+ */
+ @DataClass.Generated.Member
+ public @NonNull ContentRecordingSession setRecordingOwnerUid( int value) {
+ mRecordingOwnerUid = value;
+ return this;
+ }
+
+ /**
* Unique logical identifier of the {@link android.hardware.display.VirtualDisplay} that has
* recorded content rendered to its surface.
*/
@@ -342,11 +398,13 @@
mContentToRecord = value;
if (!(mContentToRecord == RECORD_CONTENT_DISPLAY)
- && !(mContentToRecord == RECORD_CONTENT_TASK)) {
+ && !(mContentToRecord == RECORD_CONTENT_TASK)
+ && !(mContentToRecord == RECORD_CONTENT_BELOW_OVERLAY)) {
throw new java.lang.IllegalArgumentException(
"contentToRecord was " + mContentToRecord + " but must be one of: "
+ "RECORD_CONTENT_DISPLAY(" + RECORD_CONTENT_DISPLAY + "), "
- + "RECORD_CONTENT_TASK(" + RECORD_CONTENT_TASK + ")");
+ + "RECORD_CONTENT_TASK(" + RECORD_CONTENT_TASK + "), "
+ + "RECORD_CONTENT_BELOW_OVERLAY(" + RECORD_CONTENT_BELOW_OVERLAY + ")");
}
return this;
@@ -405,6 +463,7 @@
return "ContentRecordingSession { " +
"taskId = " + mTaskId + ", " +
+ "recordingOwnerUid = " + mRecordingOwnerUid + ", " +
"virtualDisplayId = " + mVirtualDisplayId + ", " +
"contentToRecord = " + recordContentToString(mContentToRecord) + ", " +
"displayToRecord = " + mDisplayToRecord + ", " +
@@ -428,6 +487,7 @@
//noinspection PointlessBooleanExpression
return true
&& mTaskId == that.mTaskId
+ && mRecordingOwnerUid == that.mRecordingOwnerUid
&& mVirtualDisplayId == that.mVirtualDisplayId
&& mContentToRecord == that.mContentToRecord
&& mDisplayToRecord == that.mDisplayToRecord
@@ -444,6 +504,7 @@
int _hash = 1;
_hash = 31 * _hash + mTaskId;
+ _hash = 31 * _hash + mRecordingOwnerUid;
_hash = 31 * _hash + mVirtualDisplayId;
_hash = 31 * _hash + mContentToRecord;
_hash = 31 * _hash + mDisplayToRecord;
@@ -459,11 +520,12 @@
// You can override field parcelling by defining methods like:
// void parcelFieldName(Parcel dest, int flags) { ... }
- byte flg = 0;
- if (mWaitingForConsent) flg |= 0x20;
- if (mTokenToRecord != null) flg |= 0x10;
- dest.writeByte(flg);
+ int flg = 0;
+ if (mWaitingForConsent) flg |= 0x40;
+ if (mTokenToRecord != null) flg |= 0x20;
+ dest.writeInt(flg);
dest.writeInt(mTaskId);
+ dest.writeInt(mRecordingOwnerUid);
dest.writeInt(mVirtualDisplayId);
dest.writeInt(mContentToRecord);
dest.writeInt(mDisplayToRecord);
@@ -482,25 +544,29 @@
// You can override field unparcelling by defining methods like:
// static FieldType unparcelFieldName(Parcel in) { ... }
- byte flg = in.readByte();
- boolean waitingForConsent = (flg & 0x20) != 0;
+ int flg = in.readInt();
+ boolean waitingForConsent = (flg & 0x40) != 0;
int taskId = in.readInt();
+ int recordingOwnerUid = in.readInt();
int virtualDisplayId = in.readInt();
int contentToRecord = in.readInt();
int displayToRecord = in.readInt();
- IBinder tokenToRecord = (flg & 0x10) == 0 ? null : (IBinder) in.readStrongBinder();
+ IBinder tokenToRecord = (flg & 0x20) == 0 ? null : (IBinder) in.readStrongBinder();
int targetUid = in.readInt();
this.mTaskId = taskId;
+ this.mRecordingOwnerUid = recordingOwnerUid;
this.mVirtualDisplayId = virtualDisplayId;
this.mContentToRecord = contentToRecord;
if (!(mContentToRecord == RECORD_CONTENT_DISPLAY)
- && !(mContentToRecord == RECORD_CONTENT_TASK)) {
+ && !(mContentToRecord == RECORD_CONTENT_TASK)
+ && !(mContentToRecord == RECORD_CONTENT_BELOW_OVERLAY)) {
throw new java.lang.IllegalArgumentException(
"contentToRecord was " + mContentToRecord + " but must be one of: "
+ "RECORD_CONTENT_DISPLAY(" + RECORD_CONTENT_DISPLAY + "), "
- + "RECORD_CONTENT_TASK(" + RECORD_CONTENT_TASK + ")");
+ + "RECORD_CONTENT_TASK(" + RECORD_CONTENT_TASK + "), "
+ + "RECORD_CONTENT_BELOW_OVERLAY(" + RECORD_CONTENT_BELOW_OVERLAY + ")");
}
this.mDisplayToRecord = displayToRecord;
@@ -533,6 +599,7 @@
public static final class Builder {
private int mTaskId;
+ private int mRecordingOwnerUid;
private int mVirtualDisplayId;
private @RecordContent int mContentToRecord;
private int mDisplayToRecord;
@@ -542,7 +609,16 @@
private long mBuilderFieldsSet = 0L;
- public Builder() {
+ /**
+ * Creates a new Builder.
+ *
+ * @param recordingOwnerUid
+ * UID of the owner of the recording session. Only set if
+ * {@link #RECORD_CONTENT_BELOW_OVERLAY} is used.
+ */
+ public Builder(
+ int recordingOwnerUid) {
+ mRecordingOwnerUid = recordingOwnerUid;
}
/**
@@ -558,13 +634,25 @@
}
/**
+ * UID of the owner of the recording session. Only set if
+ * {@link #RECORD_CONTENT_BELOW_OVERLAY} is used.
+ */
+ @DataClass.Generated.Member
+ public @NonNull Builder setRecordingOwnerUid(int value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x2;
+ mRecordingOwnerUid = value;
+ return this;
+ }
+
+ /**
* Unique logical identifier of the {@link android.hardware.display.VirtualDisplay} that has
* recorded content rendered to its surface.
*/
@DataClass.Generated.Member
public @NonNull Builder setVirtualDisplayId(int value) {
checkNotUsed();
- mBuilderFieldsSet |= 0x2;
+ mBuilderFieldsSet |= 0x4;
mVirtualDisplayId = value;
return this;
}
@@ -575,7 +663,7 @@
@DataClass.Generated.Member
public @NonNull Builder setContentToRecord(@RecordContent int value) {
checkNotUsed();
- mBuilderFieldsSet |= 0x4;
+ mBuilderFieldsSet |= 0x8;
mContentToRecord = value;
return this;
}
@@ -589,7 +677,7 @@
@DataClass.Generated.Member
public @NonNull Builder setDisplayToRecord(int value) {
checkNotUsed();
- mBuilderFieldsSet |= 0x8;
+ mBuilderFieldsSet |= 0x10;
mDisplayToRecord = value;
return this;
}
@@ -603,7 +691,7 @@
@DataClass.Generated.Member
public @NonNull Builder setTokenToRecord(@NonNull IBinder value) {
checkNotUsed();
- mBuilderFieldsSet |= 0x10;
+ mBuilderFieldsSet |= 0x20;
mTokenToRecord = value;
return this;
}
@@ -617,7 +705,7 @@
@DataClass.Generated.Member
public @NonNull Builder setWaitingForConsent(boolean value) {
checkNotUsed();
- mBuilderFieldsSet |= 0x20;
+ mBuilderFieldsSet |= 0x40;
mWaitingForConsent = value;
return this;
}
@@ -628,7 +716,7 @@
@DataClass.Generated.Member
public @NonNull Builder setTargetUid(int value) {
checkNotUsed();
- mBuilderFieldsSet |= 0x40;
+ mBuilderFieldsSet |= 0x80;
mTargetUid = value;
return this;
}
@@ -636,31 +724,32 @@
/** Builds the instance. This builder should not be touched after calling this! */
public @NonNull ContentRecordingSession build() {
checkNotUsed();
- mBuilderFieldsSet |= 0x80; // Mark builder used
+ mBuilderFieldsSet |= 0x100; // Mark builder used
if ((mBuilderFieldsSet & 0x1) == 0) {
mTaskId = TASK_ID_UNKNOWN;
}
- if ((mBuilderFieldsSet & 0x2) == 0) {
+ if ((mBuilderFieldsSet & 0x4) == 0) {
mVirtualDisplayId = INVALID_DISPLAY;
}
- if ((mBuilderFieldsSet & 0x4) == 0) {
+ if ((mBuilderFieldsSet & 0x8) == 0) {
mContentToRecord = RECORD_CONTENT_DISPLAY;
}
- if ((mBuilderFieldsSet & 0x8) == 0) {
+ if ((mBuilderFieldsSet & 0x10) == 0) {
mDisplayToRecord = INVALID_DISPLAY;
}
- if ((mBuilderFieldsSet & 0x10) == 0) {
+ if ((mBuilderFieldsSet & 0x20) == 0) {
mTokenToRecord = null;
}
- if ((mBuilderFieldsSet & 0x20) == 0) {
+ if ((mBuilderFieldsSet & 0x40) == 0) {
mWaitingForConsent = false;
}
- if ((mBuilderFieldsSet & 0x40) == 0) {
+ if ((mBuilderFieldsSet & 0x80) == 0) {
mTargetUid = TARGET_UID_UNKNOWN;
}
ContentRecordingSession o = new ContentRecordingSession(
mTaskId,
+ mRecordingOwnerUid,
mVirtualDisplayId,
mContentToRecord,
mDisplayToRecord,
@@ -671,7 +760,7 @@
}
private void checkNotUsed() {
- if ((mBuilderFieldsSet & 0x80) != 0) {
+ if ((mBuilderFieldsSet & 0x100) != 0) {
throw new IllegalStateException(
"This Builder should not be reused. Use a new Builder instance instead");
}
@@ -679,10 +768,10 @@
}
@DataClass.Generated(
- time = 1716481148184L,
+ time = 1744196578914L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/view/ContentRecordingSession.java",
- inputSignatures = "public static final int RECORD_CONTENT_DISPLAY\npublic static final int RECORD_CONTENT_TASK\npublic static final int TARGET_UID_FULL_SCREEN\npublic static final int TARGET_UID_UNKNOWN\npublic static final int TASK_ID_UNKNOWN\nprivate int mTaskId\nprivate int mVirtualDisplayId\nprivate @android.view.ContentRecordingSession.RecordContent int mContentToRecord\nprivate int mDisplayToRecord\nprivate @android.annotation.Nullable android.os.IBinder mTokenToRecord\nprivate boolean mWaitingForConsent\nprivate int mTargetUid\npublic static android.view.ContentRecordingSession createDisplaySession(int)\npublic static android.view.ContentRecordingSession createTaskSession(android.os.IBinder)\npublic static android.view.ContentRecordingSession createTaskSession(android.os.IBinder,int)\npublic static boolean isValid(android.view.ContentRecordingSession)\npublic static boolean isProjectionOnSameDisplay(android.view.ContentRecordingSession,android.view.ContentRecordingSession)\nclass ContentRecordingSession extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genToString=true, genSetters=true, genEqualsHashCode=true)")
+ inputSignatures = "public static final int RECORD_CONTENT_DISPLAY\npublic static final int RECORD_CONTENT_TASK\npublic static final int RECORD_CONTENT_BELOW_OVERLAY\npublic static final int TARGET_UID_FULL_SCREEN\npublic static final int TARGET_UID_UNKNOWN\npublic static final int TASK_ID_UNKNOWN\nprivate int mTaskId\nprivate int mRecordingOwnerUid\nprivate int mVirtualDisplayId\nprivate @android.view.ContentRecordingSession.RecordContent int mContentToRecord\nprivate int mDisplayToRecord\nprivate @android.annotation.Nullable android.os.IBinder mTokenToRecord\nprivate boolean mWaitingForConsent\nprivate int mTargetUid\npublic static android.view.ContentRecordingSession createDisplaySession(int)\npublic static android.view.ContentRecordingSession createTaskSession(android.os.IBinder)\npublic static android.view.ContentRecordingSession createTaskSession(android.os.IBinder,int)\npublic static android.view.ContentRecordingSession createOverlaySession(int,int)\npublic static boolean isValid(android.view.ContentRecordingSession)\npublic static boolean isProjectionOnSameDisplay(android.view.ContentRecordingSession,android.view.ContentRecordingSession)\nclass ContentRecordingSession extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genToString=true, genSetters=true, genEqualsHashCode=true)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/view/ISurfaceControlViewHost.aidl b/core/java/android/view/ISurfaceControlViewHost.aidl
index 83aceb3..fd4b329 100644
--- a/core/java/android/view/ISurfaceControlViewHost.aidl
+++ b/core/java/android/view/ISurfaceControlViewHost.aidl
@@ -21,7 +21,6 @@
import android.view.InsetsState;
import android.view.ISurfaceControlViewHostParent;
import android.window.ISurfaceSyncGroup;
-import android.window.InputTransferToken;
/**
* API from content embedder back to embedded content in SurfaceControlViewHost
@@ -33,7 +32,6 @@
* APIs that are blocking
*/
oneway void onConfigurationChanged(in Configuration newConfig);
- oneway void onDispatchAttachedToWindow(in InputTransferToken token);
oneway void onDispatchDetachedFromWindow();
oneway void onInsetsChanged(in InsetsState state, in Rect insetFrame);
ISurfaceSyncGroup getSurfaceSyncGroup();
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index b3ed544..363c4d9 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -466,7 +466,7 @@
/**
* Used only for assist -- request a screenshot of the current application.
*/
- boolean requestAssistScreenshot(IAssistDataReceiver receiver);
+ void requestAssistScreenshot(IAssistDataReceiver receiver);
/**
* Called by System UI to notify Window Manager to hide transient bars.
@@ -999,7 +999,7 @@
*
* @param clientToken the window context's token
*/
- void detachWindowContext(IBinder clientToken);
+ oneway void detachWindowContext(IBinder clientToken);
/**
* Reparents the {@link android.window.WindowContext} to the
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index a029303..d06e578 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -229,7 +229,7 @@
/**
* Notifies that a rectangle on the screen has been requested.
*/
- oneway void onRectangleOnScreenRequested(IBinder token, in Rect rectangle);
+ oneway void onRectangleOnScreenRequested(IBinder token, in Rect rectangle, int source);
IWindowId getWindowId(IBinder window);
@@ -313,9 +313,8 @@
/**
* Update the flags on an input channel associated with a particular surface.
*/
- oneway void updateInputChannel(in IBinder channelToken,
- in @nullable InputTransferToken hostInputTransferToken,
- int displayId, in SurfaceControl surface, int flags, int privateFlags, int inputFeatures,
+ oneway void updateInputChannel(in IBinder channelToken, int displayId,
+ in SurfaceControl surface, int flags, int privateFlags, int inputFeatures,
in Region region);
/**
diff --git a/core/java/android/view/InputEventCompatProcessor.java b/core/java/android/view/InputEventCompatProcessor.java
index b3d8f98..fd2453d 100644
--- a/core/java/android/view/InputEventCompatProcessor.java
+++ b/core/java/android/view/InputEventCompatProcessor.java
@@ -17,11 +17,9 @@
package android.view;
import android.content.Context;
-import android.os.Build;
import android.os.Handler;
import android.view.input.LetterboxScrollProcessor;
-
-import com.android.window.flags.Flags;
+import android.view.input.StylusButtonCompatibility;
import java.util.ArrayList;
import java.util.List;
@@ -36,6 +34,7 @@
protected Context mContext;
protected int mTargetSdkVersion;
+ private final StylusButtonCompatibility mStylusButtonCompatibility;
private final LetterboxScrollProcessor mLetterboxScrollProcessor;
/** List of events to be used to return the processed events */
@@ -48,7 +47,12 @@
public InputEventCompatProcessor(Context context, Handler handler) {
mContext = context;
mTargetSdkVersion = context.getApplicationInfo().targetSdkVersion;
- if (Flags.scrollingFromLetterbox()) {
+ if (StylusButtonCompatibility.isCompatibilityNeeded(context)) {
+ mStylusButtonCompatibility = new StylusButtonCompatibility();
+ } else {
+ mStylusButtonCompatibility = null;
+ }
+ if (LetterboxScrollProcessor.isCompatibilityNeeded()) {
mLetterboxScrollProcessor = new LetterboxScrollProcessor(mContext, handler);
} else {
mLetterboxScrollProcessor = null;
@@ -73,7 +77,7 @@
final InputEvent stylusCompatEvent = processStylusButtonCompatibility(inputEvent);
// Process the event for LetterboxScrollCompatibility.
- List<MotionEvent> letterboxScrollCompatEvents = processLetterboxScrollCompatibility(
+ List<InputEvent> letterboxScrollCompatEvents = processLetterboxScrollCompatibility(
stylusCompatEvent != null ? stylusCompatEvent : inputEvent);
// If no adjustments are needed for LetterboxCompatibility.
@@ -100,9 +104,9 @@
* @return The InputEvent to finish, or null if it should not be finished.
*/
public InputEvent processInputEventBeforeFinish(InputEvent inputEvent) {
- if (mLetterboxScrollProcessor != null && inputEvent instanceof MotionEvent motionEvent) {
+ if (mLetterboxScrollProcessor != null) {
// LetterboxScrollProcessor may have generated events while processing motion events.
- return mLetterboxScrollProcessor.processMotionEventBeforeFinish(motionEvent);
+ return mLetterboxScrollProcessor.processInputEventBeforeFinish(inputEvent);
}
// No changes needed
@@ -110,27 +114,16 @@
}
- private List<MotionEvent> processLetterboxScrollCompatibility(InputEvent inputEvent) {
- if (mLetterboxScrollProcessor != null
- && inputEvent instanceof MotionEvent motionEvent
- && motionEvent.getAction() != MotionEvent.ACTION_OUTSIDE) {
- return mLetterboxScrollProcessor.processMotionEvent(motionEvent);
+ private List<InputEvent> processLetterboxScrollCompatibility(InputEvent inputEvent) {
+ if (mLetterboxScrollProcessor != null) {
+ return mLetterboxScrollProcessor.processInputEventForCompatibility(inputEvent);
}
return null;
}
-
private InputEvent processStylusButtonCompatibility(InputEvent inputEvent) {
- if (mTargetSdkVersion < Build.VERSION_CODES.M && inputEvent instanceof MotionEvent) {
- MotionEvent motion = (MotionEvent) inputEvent;
- final int mask =
- MotionEvent.BUTTON_STYLUS_PRIMARY | MotionEvent.BUTTON_STYLUS_SECONDARY;
- final int buttonState = motion.getButtonState();
- final int compatButtonState = (buttonState & mask) >> 4;
- if (compatButtonState != 0) {
- motion.setButtonState(buttonState | compatButtonState);
- }
- return motion;
+ if (mStylusButtonCompatibility != null) {
+ return mStylusButtonCompatibility.processInputEventForCompatibility(inputEvent);
}
return null;
}
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 053c915..53d97c7 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -862,9 +862,7 @@
}
public boolean onStateChanged(InsetsState state) {
- boolean stateChanged = !mState.equals(state, false /* excludesCaptionBar */,
- false /* excludesInvisibleIme */);
- if (!stateChanged && mLastDispatchedState.equals(state)) {
+ if (mState.equals(state) && mLastDispatchedState.equals(state)) {
return false;
}
if (DEBUG) Log.d(TAG, "onStateChanged: " + state);
@@ -875,7 +873,7 @@
updateCompatSysUiVisibility();
if (!mState.equals(lastState, false /* excludesCaptionBar */,
- true /* excludesInvisibleIme */)) {
+ true /* excludesInvisibleIme */, true /* excludesInvalidSource */)) {
if (DEBUG) Log.d(TAG, "onStateChanged, notifyInsetsChanged");
mHost.notifyInsetsChanged();
if (mLastDispatchedState.getDisplayFrame().equals(state.getDisplayFrame())) {
@@ -2067,7 +2065,7 @@
if ((types & ime()) != 0) {
final InsetsSourceControl imeControl = mImeSourceConsumer.getControl();
// Skip showing animation once that made by system for some reason.
- // (e.g. starting window with IME snapshot)
+ // (e.g. starting window with hasImeSurface)
if (imeControl != null) {
skipsAnim = imeControl.getAndClearSkipAnimationOnce() && show
&& mImeSourceConsumer.hasViewFocusWhenWindowFocusGain();
diff --git a/core/java/android/view/InsetsSource.java b/core/java/android/view/InsetsSource.java
index ba20839..e5f605e 100644
--- a/core/java/android/view/InsetsSource.java
+++ b/core/java/android/view/InsetsSource.java
@@ -114,13 +114,19 @@
*/
public static final int FLAG_FORCE_CONSUMING_OPAQUE_CAPTION_BAR = 1 << 4;
+ /**
+ * Indicates whether the insets source is valid.
+ */
+ public static final int FLAG_INVALID = 1 << 5;
+
@Retention(RetentionPolicy.SOURCE)
@IntDef(flag = true, prefix = "FLAG_", value = {
FLAG_SUPPRESS_SCRIM,
FLAG_INSETS_ROUNDED_CORNER,
FLAG_FORCE_CONSUMING,
FLAG_ANIMATE_RESIZING,
- FLAG_FORCE_CONSUMING_OPAQUE_CAPTION_BAR
+ FLAG_FORCE_CONSUMING_OPAQUE_CAPTION_BAR,
+ FLAG_INVALID,
})
public @interface Flags {}
@@ -594,6 +600,9 @@
if ((flags & FLAG_FORCE_CONSUMING_OPAQUE_CAPTION_BAR) != 0) {
joiner.add("FORCE_CONSUMING_OPAQUE_CAPTION_BAR");
}
+ if ((flags & FLAG_INVALID) != 0) {
+ joiner.add("INVALID");
+ }
return joiner.toString();
}
diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java
index f9d0026..d2e4401 100644
--- a/core/java/android/view/InsetsSourceConsumer.java
+++ b/core/java/android/view/InsetsSourceConsumer.java
@@ -325,7 +325,7 @@
}
}
final InsetsSource source = mState.peekSource(mId);
- if (source == null) {
+ if (source == null || source.hasFlags(InsetsSource.FLAG_INVALID)) {
return false;
}
final boolean requestedVisible = (mController.getRequestedVisibleTypes() & mType) != 0;
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
index bc1612b..4b05671 100644
--- a/core/java/android/view/InsetsState.java
+++ b/core/java/android/view/InsetsState.java
@@ -21,6 +21,7 @@
import static android.view.InsetsSource.FLAG_FORCE_CONSUMING;
import static android.view.InsetsSource.FLAG_FORCE_CONSUMING_OPAQUE_CAPTION_BAR;
import static android.view.InsetsSource.FLAG_INSETS_ROUNDED_CORNER;
+import static android.view.InsetsSource.FLAG_INVALID;
import static android.view.InsetsStateProto.DISPLAY_CUTOUT;
import static android.view.InsetsStateProto.DISPLAY_FRAME;
import static android.view.InsetsStateProto.SOURCES;
@@ -55,8 +56,6 @@
import android.view.WindowInsets.Type.InsetsType;
import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
-import com.android.internal.annotations.VisibleForTesting;
-
import java.io.PrintWriter;
import java.util.Objects;
import java.util.StringJoiner;
@@ -703,21 +702,23 @@
@Override
public boolean equals(@Nullable Object o) {
- return equals(o, false, false);
+ return equals(o, false, false, false);
}
/**
* An equals method can exclude the caption insets. This is useful because we assemble the
* caption insets information on the client side, and when we communicate with server, it's
* excluded.
- * @param excludesCaptionBar If {@link Type#captionBar()}} should be ignored.
- * @param excludesInvisibleIme If {@link WindowInsets.Type#ime()} should be ignored when IME is
+ *
+ * @param excludesCaptionBar If {@link Type#captionBar()}} should be ignored.
+ * @param excludesInvisibleIme If {@link Type#ime()} should be ignored when IME is
* not visible.
+ * @param excludesInvalidSource If a source should be ignored if it has
+ * {@link InsetsSource#FLAG_INVALID}.
* @return {@code true} if the two InsetsState objects are equal, {@code false} otherwise.
*/
- @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
public boolean equals(@Nullable Object o, boolean excludesCaptionBar,
- boolean excludesInvisibleIme) {
+ boolean excludesInvisibleIme, boolean excludesInvalidSource) {
if (this == o) { return true; }
if (o == null || getClass() != o.getClass()) { return false; }
@@ -735,7 +736,7 @@
final SparseArray<InsetsSource> thisSources = mSources;
final SparseArray<InsetsSource> thatSources = state.mSources;
- if (!excludesCaptionBar && !excludesInvisibleIme) {
+ if (!excludesCaptionBar && !excludesInvisibleIme && !excludesInvalidSource) {
return thisSources.contentEquals(thatSources);
} else {
final int thisSize = thisSources.size();
@@ -749,9 +750,11 @@
// Seek to the next non-excluding source of ours.
while (thisSource != null
- && (excludesCaptionBar && thisSource.getType() == captionBar()
- || excludesInvisibleIme && thisSource.getType() == ime()
- && !thisSource.isVisible())) {
+ && ((excludesCaptionBar && thisSource.getType() == captionBar())
+ || (excludesInvisibleIme && thisSource.getType() == ime()
+ && !thisSource.isVisible())
+ || (excludesInvalidSource
+ && thisSource.hasFlags(FLAG_INVALID)))) {
thisIndex++;
thisSource = thisIndex < thisSize ? thisSources.valueAt(thisIndex) : null;
}
@@ -762,9 +765,11 @@
// Seek to the next non-excluding source of theirs.
while (thatSource != null
- && (excludesCaptionBar && thatSource.getType() == captionBar()
- || excludesInvisibleIme && thatSource.getType() == ime()
- && !thatSource.isVisible())) {
+ && ((excludesCaptionBar && thatSource.getType() == captionBar())
+ || (excludesInvisibleIme && thatSource.getType() == ime()
+ && !thatSource.isVisible())
+ || (excludesInvalidSource
+ && thatSource.hasFlags(FLAG_INVALID)))) {
thatIndex++;
thatSource = thatIndex < thatSize ? thatSources.valueAt(thatIndex) : null;
}
diff --git a/core/java/android/view/KeyCharacterMap.java b/core/java/android/view/KeyCharacterMap.java
index 48dfdd4..c7422e9 100644
--- a/core/java/android/view/KeyCharacterMap.java
+++ b/core/java/android/view/KeyCharacterMap.java
@@ -313,8 +313,8 @@
private static native KeyCharacterMap nativeObtainEmptyKeyCharacterMap(int deviceId);
private static native boolean nativeEquals(long ptr1, long ptr2);
- private static native void nativeApplyOverlay(long ptr, String layoutDescriptor,
- String overlay);
+ private static native KeyCharacterMap nativeObtainMapWithOverlay(long ptrForBaseMap,
+ String layoutDescriptor, String overlay);
private static native int nativeGetMappedKey(long ptr, int scanCode);
private KeyCharacterMap(Parcel in) {
@@ -387,13 +387,13 @@
* @hide
*/
public static KeyCharacterMap load(@NonNull String layoutDescriptor, @NonNull String overlay) {
- KeyCharacterMap kcm = KeyCharacterMap.load(VIRTUAL_KEYBOARD);
- kcm.applyOverlay(layoutDescriptor, overlay);
- return kcm;
- }
-
- private void applyOverlay(@NonNull String layoutDescriptor, @NonNull String overlay) {
- nativeApplyOverlay(mPtr, layoutDescriptor, overlay);
+ KeyCharacterMap virtualKcm = KeyCharacterMap.load(VIRTUAL_KEYBOARD);
+ KeyCharacterMap kcmWithOverlay = nativeObtainMapWithOverlay(virtualKcm.mPtr,
+ layoutDescriptor, overlay);
+ if (kcmWithOverlay == null) {
+ return virtualKcm;
+ }
+ return kcmWithOverlay;
}
/**
diff --git a/core/java/android/view/ListenerGroup.java b/core/java/android/view/ListenerGroup.java
deleted file mode 100644
index 889caec..0000000
--- a/core/java/android/view/ListenerGroup.java
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Copyright (C) 2025 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view;
-
-import android.annotation.NonNull;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Objects;
-import java.util.concurrent.Executor;
-import java.util.function.Consumer;
-
-/**
- * A utility class to manage a list of {@link ListenerWrapper}. This class is not thread safe.
- * @param <T> the type of the value to be reported.
- * @hide
- */
-public class ListenerGroup<T> {
- private final List<ListenerWrapper<T>> mListeners = new ArrayList<>();
- @NonNull
- private T mLastValue;
-
- /**
- * Constructs a {@link ListenerGroup} that will replay the last reported value whenever a new
- * listener is registered.
- * @param value the initial value
- */
- public ListenerGroup(@NonNull T value) {
- mLastValue = value;
- }
-
- /**
- * Relays the value to all the registered {@link java.util.function.Consumer}
- */
- public void accept(@NonNull T value) {
- mLastValue = Objects.requireNonNull(value);
- for (int i = 0; i < mListeners.size(); i++) {
- mListeners.get(i).accept(value);
- }
- }
-
- /**
- * Adds a {@link Consumer} to the group and replays the last reported value. If the
- * {@link Consumer} is already present then this is a no op.
- */
- public void addListener(@NonNull Executor executor, @NonNull Consumer<T> consumer) {
- if (isConsumerPresent(consumer)) {
- return;
- }
- final ListenerWrapper<T> listenerWrapper = new ListenerWrapper<>(executor, consumer);
- mListeners.add(listenerWrapper);
- listenerWrapper.accept(mLastValue);
- }
-
- /**
- * Removes a {@link Consumer} from the group. If the {@link Consumer} was not present then this
- * is a no op.
- */
- public void removeListener(@NonNull Consumer<T> consumer) {
- final int index = computeIndex(consumer);
- if (index > -1) {
- mListeners.remove(index);
- }
- }
-
- /**
- * Returns {@code true} if the {@link Consumer} is present in the list, {@code false}
- * otherwise.
- */
- public boolean isConsumerPresent(Consumer<T> consumer) {
- return computeIndex(consumer) > -1;
- }
-
- /**
- * Returns the index of the matching {@link ListenerWrapper} if present, {@code -1} otherwise.
- */
- private int computeIndex(Consumer<T> consumer) {
- for (int i = 0; i < mListeners.size(); i++) {
- if (mListeners.get(i).isConsumerSame(consumer)) {
- return i;
- }
- }
- return -1;
- }
-}
diff --git a/core/java/android/view/ListenerWrapper.java b/core/java/android/view/ListenerWrapper.java
deleted file mode 100644
index fcf3fdb..0000000
--- a/core/java/android/view/ListenerWrapper.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 2025 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view;
-
-import android.annotation.NonNull;
-
-import java.util.Objects;
-import java.util.concurrent.Executor;
-import java.util.function.Consumer;
-
-/**
- * A utilty class to bundle a {@link Consumer} and an {@link Executor}
- * @param <T> the type of value to be reported.
- * @hide
- */
-public class ListenerWrapper<T> {
-
- @NonNull
- private final Consumer<T> mConsumer;
- @NonNull
- private final Executor mExecutor;
-
- public ListenerWrapper(@NonNull Executor executor, @NonNull Consumer<T> consumer) {
- mExecutor = Objects.requireNonNull(executor);
- mConsumer = Objects.requireNonNull(consumer);
- }
-
- /**
- * Relays the new value to the {@link Consumer} using the {@link Executor}
- */
- public void accept(@NonNull T value) {
- mExecutor.execute(() -> mConsumer.accept(value));
- }
-
- /**
- * Returns {@code true} if the consumer matches the one provided in the constructor,
- * {@code false} otherwise.
- */
- public boolean isConsumerSame(@NonNull Consumer<T> consumer) {
- return mConsumer.equals(consumer);
- }
-}
diff --git a/core/java/android/view/OWNERS b/core/java/android/view/OWNERS
index 4159c59..5b81a66 100644
--- a/core/java/android/view/OWNERS
+++ b/core/java/android/view/OWNERS
@@ -115,3 +115,6 @@
per-file *ScrollCapture*.aidl = file:/packages/SystemUI/src/com/android/systemui/screenshot/OWNERS
per-file *ScrollCapture*.java = file:/packages/SystemUI/src/com/android/systemui/screenshot/OWNERS
per-file *CaptureHelper*.java = file:/packages/SystemUI/src/com/android/systemui/screenshot/OWNERS
+
+# Protolog
+per-file ViewProtoLogGroups.java = file:/tools/protologtool/OWNERS
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index fae70eb..b3090c9 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -120,7 +120,7 @@
private static native void nativeDisconnect(long nativeObject);
private static native void nativeUpdateDefaultBufferSize(long nativeObject, int width, int height);
- private static native long nativeMirrorSurface(long mirrorOfObject);
+ private static native long nativeMirrorSurface(long mirrorOfObject, long stopAtObject);
private static native long nativeCreateTransaction();
private static native long nativeGetNativeTransactionFinalizer();
private static native void nativeApplyTransaction(long transactionObj, boolean sync,
@@ -1908,6 +1908,7 @@
public boolean secure;
public DeviceProductInfo deviceProductInfo;
public @Surface.Rotation int installOrientation;
+ public int screenPartStatus;
@Override
public String toString() {
@@ -1915,7 +1916,8 @@
+ ", density=" + density
+ ", secure=" + secure
+ ", deviceProductInfo=" + deviceProductInfo
- + ", installOrientation=" + installOrientation + "}";
+ + ", installOrientation=" + installOrientation
+ + ", screenPartStatus=" + screenPartStatus + "}";
}
@Override
@@ -1927,12 +1929,14 @@
&& density == that.density
&& secure == that.secure
&& Objects.equals(deviceProductInfo, that.deviceProductInfo)
- && installOrientation == that.installOrientation;
+ && installOrientation == that.installOrientation
+ && screenPartStatus == that.screenPartStatus;
}
@Override
public int hashCode() {
- return Objects.hash(isInternal, density, secure, deviceProductInfo, installOrientation);
+ return Objects.hash(isInternal, density, secure, deviceProductInfo,
+ installOrientation, screenPartStatus);
}
}
@@ -2774,7 +2778,39 @@
* @hide
*/
public static SurfaceControl mirrorSurface(SurfaceControl mirrorOf) {
- long nativeObj = nativeMirrorSurface(mirrorOf.mNativeObject);
+ return mirrorSurface(mirrorOf, null);
+ }
+
+ /**
+ * Creates a mirrored hierarchy for the mirrorOf {@link SurfaceControl}.
+ *
+ * Real Hierarchy Mirror
+ * SC (value that's returned)
+ * |
+ * A A'
+ * | |
+ * B B'
+ *
+ * With stopAt specified as layer B:
+ *
+ * Real Hierarchy Mirror
+ * A SC
+ * | |
+ * B A'
+ * |
+ * C
+ *
+ * @param mirrorOf The root of the hierarchy that should be mirrored.
+ * @param stopAt An optional SurfaceControl. When non-null the mirrored
+ * hierarchy won't include the specified SurfaceControl or anything z-ordered
+ * above it.
+ * @return A SurfaceControl that's the parent of the root of the mirrored hierarchy.
+ *
+ * @hide
+ */
+ public static SurfaceControl mirrorSurface(SurfaceControl mirrorOf, SurfaceControl stopAt) {
+ long stopAtObj = stopAt != null ? stopAt.mNativeObject : 0;
+ long nativeObj = nativeMirrorSurface(mirrorOf.mNativeObject, stopAtObj);
SurfaceControl sc = new SurfaceControl();
sc.mName = mirrorOf.mName + " (mirror)";
sc.assignNativeObject(nativeObj, "mirrorSurface");
diff --git a/core/java/android/view/SurfaceControlViewHost.java b/core/java/android/view/SurfaceControlViewHost.java
index ecbf1f6..bbe2d0f 100644
--- a/core/java/android/view/SurfaceControlViewHost.java
+++ b/core/java/android/view/SurfaceControlViewHost.java
@@ -61,6 +61,11 @@
private final WindowlessWindowManager mWm;
private SurfaceControl mSurfaceControl;
+
+ // True if this SurfaceControlViewHost created mSurfaceControl and is responsible for releasing
+ // it.
+ private final boolean mOwnsSurfaceControl;
+
private IAccessibilityEmbeddedConnection mAccessibilityEmbeddedConnection;
private boolean mReleased = false;
@@ -79,20 +84,6 @@
}
@Override
- public void onDispatchAttachedToWindow(InputTransferToken hostInputTransferToken) {
- boolean hostInputTransferTokenChanged =
- !Objects.equals(hostInputTransferToken, mWm.mHostInputTransferToken);
- if (!hostInputTransferTokenChanged) {
- return;
- }
-
- mWm.setHostInputTransferToken(hostInputTransferToken);
- if (mViewRoot != null && mViewRoot.mView != null) {
- mWm.updateInputChannel(getWindowToken().asBinder());
- }
- }
-
- @Override
public void onDispatchDetachedFromWindow() {
if (mViewRoot == null) {
return;
@@ -339,6 +330,7 @@
public SurfaceControlViewHost(@NonNull Context c, @NonNull Display d,
@NonNull WindowlessWindowManager wwm, @NonNull String callsite) {
mSurfaceControl = wwm.mRootSurface;
+ mOwnsSurfaceControl = false;
mWm = wwm;
mViewRoot = new ViewRootImpl(c, d, mWm, new WindowlessWindowLayout());
mCloseGuard.openWithCallSite("release", callsite);
@@ -407,6 +399,7 @@
.setName("SurfaceControlViewHost")
.setCallsite("SurfaceControlViewHost[" + callsite + "]")
.build();
+ mOwnsSurfaceControl = true;
mWm = new WindowlessWindowManager(context.getResources().getConfiguration(),
mSurfaceControl, hostToken);
@@ -559,7 +552,6 @@
* and render the object unusable.
*/
public void release() {
- // ViewRoot will release mSurfaceControl for us.
doRelease(true /* immediate */);
}
@@ -571,6 +563,9 @@
mViewRoot.die(immediate);
WindowManagerGlobal.getInstance().removeWindowlessRoot(mViewRoot);
+ if (Flags.scvhSurfaceControlLifetimeFix() && mOwnsSurfaceControl) {
+ mSurfaceControl.release();
+ }
mReleased = true;
mCloseGuard.close();
}
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 94e9aa7..29008d3 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -16,7 +16,6 @@
package android.view;
-
import static android.view.flags.Flags.FLAG_DEPRECATE_SURFACE_VIEW_Z_ORDER_APIS;
import static android.view.flags.Flags.FLAG_SURFACE_VIEW_GET_SURFACE_PACKAGE;
import static android.view.flags.Flags.FLAG_SURFACE_VIEW_SET_COMPOSITION_ORDER;
@@ -24,8 +23,6 @@
import static android.view.WindowManagerPolicyConstants.APPLICATION_MEDIA_OVERLAY_SUBLAYER;
import static android.view.WindowManagerPolicyConstants.APPLICATION_MEDIA_SUBLAYER;
import static android.view.WindowManagerPolicyConstants.APPLICATION_PANEL_SUBLAYER;
-import static android.view.flags.Flags.FLAG_SURFACE_VIEW_GET_SURFACE_PACKAGE;
-import static android.view.flags.Flags.FLAG_SURFACE_VIEW_SET_COMPOSITION_ORDER;
import android.annotation.FlaggedApi;
import android.annotation.FloatRange;
@@ -62,7 +59,6 @@
import android.view.SurfaceControl.Transaction;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.IAccessibilityEmbeddedConnection;
-import android.window.InputTransferToken;
import android.window.SurfaceSyncGroup;
import com.android.graphics.hwui.flags.Flags;
@@ -351,7 +347,7 @@
sv.mSurfacePackage.getRemoteInterface().attachParentInterface(this);
mSurfaceView = sv;
} catch (RemoteException e) {
- Log.d(TAG, "Failed to attach parent interface to SCVH. Likely SCVH is already "
+ Log.d(TAG, "Failed to attach parent interface to SCVH. Likely SCVH is alraedy "
+ "dead.");
}
}
@@ -496,37 +492,10 @@
mTag = "SV[" + System.identityHashCode(this) + windowName + "]";
}
- private void dispatchScvhAttachedToHost() {
- final ViewRootImpl viewRoot = getViewRootImpl();
- if (viewRoot == null) {
- return;
- }
-
- IBinder inputToken = viewRoot.getInputToken();
- if (inputToken == null) {
- // We don't have an input channel so we can't transfer focus or active
- // touch gestures to embedded.
- return;
- }
-
- try {
- mSurfacePackage
- .getRemoteInterface()
- .onDispatchAttachedToWindow(new InputTransferToken(inputToken));
- } catch (RemoteException e) {
- Log.d(TAG,
- "Failed to onDispatchAttachedToWindow to SCVH. Likely SCVH is already "
- + "dead.");
- }
- }
-
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
setTag();
- if (mSurfacePackage != null) {
- dispatchScvhAttachedToHost();
- }
getViewRootImpl().addSurfaceChangedCallback(this);
mWindowStopped = false;
mViewVisibility = getVisibility() == VISIBLE;
@@ -2220,7 +2189,6 @@
applyTransactionOnVriDraw(transaction);
}
mSurfacePackage = p;
- dispatchScvhAttachedToHost();
mSurfaceControlViewHostParent.attach(this);
if (isFocused()) {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 800c641..cbcf464 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -32,6 +32,7 @@
import static android.view.accessibility.AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED;
import static android.view.accessibility.Flags.a11ySequentialFocusStartingPoint;
import static android.view.accessibility.Flags.FLAG_DEPRECATE_ACCESSIBILITY_ANNOUNCEMENT_APIS;
+import static android.view.accessibility.Flags.FLAG_REQUEST_RECTANGLE_WITH_SOURCE;
import static android.view.accessibility.Flags.FLAG_SUPPLEMENTAL_DESCRIPTION;
import static android.view.accessibility.Flags.removeChildHoverCheckForTouchExploration;
import static android.view.accessibility.Flags.supplementalDescription;
@@ -5854,6 +5855,50 @@
private int mSizeBasedFrameRateCategoryAndReason;
/**
+ * @hide
+ */
+ @IntDef(prefix = { "RECTANGLE_ON_SCREEN_REQUEST_SOURCE_" }, value = {
+ RECTANGLE_ON_SCREEN_REQUEST_SOURCE_UNDEFINED,
+ RECTANGLE_ON_SCREEN_REQUEST_SOURCE_SCROLL_ONLY,
+ RECTANGLE_ON_SCREEN_REQUEST_SOURCE_TEXT_CURSOR,
+ RECTANGLE_ON_SCREEN_REQUEST_SOURCE_INPUT_FOCUS,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface RectangleOnScreenRequestSource {}
+
+ /**
+ * Represents that the user interaction that is requesting a rectangle on screen is
+ * doing so via the original {@link #requestRectangleOnScreen(Rect)} or
+ * {@link #requestRectangleOnScreen(Rect, boolean)} APIs that do not define the source.
+ * RECTANGLE_ON_SCREEN_REQUEST_SOURCE_UNDEFINED should be reserved for backward
+ * compatibility and should only be provided from calls to the original API.
+ */
+ @FlaggedApi(FLAG_REQUEST_RECTANGLE_WITH_SOURCE)
+ public static final int RECTANGLE_ON_SCREEN_REQUEST_SOURCE_UNDEFINED = 0x00000000;
+
+ /**
+ * Represents that the user interaction that is requesting a rectangle on screen is doing so
+ * only to scroll the View on screen, and the rectangle is not associated with a text cursor or
+ * keyboard focus.
+ */
+ @FlaggedApi(FLAG_REQUEST_RECTANGLE_WITH_SOURCE)
+ public static final int RECTANGLE_ON_SCREEN_REQUEST_SOURCE_SCROLL_ONLY = 0x00000001;
+
+ /**
+ * Represents that the user interaction that is requesting a rectangle on screen is
+ * doing so because the View contains a text cursor (caret).
+ */
+ @FlaggedApi(FLAG_REQUEST_RECTANGLE_WITH_SOURCE)
+ public static final int RECTANGLE_ON_SCREEN_REQUEST_SOURCE_TEXT_CURSOR = 0x00000002;
+
+ /**
+ * Represents that the user interaction that is requesting a rectangle on screen is
+ * doing so because the View has input/keyboard focus.
+ */
+ @FlaggedApi(FLAG_REQUEST_RECTANGLE_WITH_SOURCE)
+ public static final int RECTANGLE_ON_SCREEN_REQUEST_SOURCE_INPUT_FOCUS = 0x00000003;
+
+ /**
* Simple constructor to use when creating a view from code.
*
* @param context The Context the view is running in, through which it can
@@ -8530,6 +8575,42 @@
* @return Whether any parent scrolled.
*/
public boolean requestRectangleOnScreen(Rect rectangle, boolean immediate) {
+ return requestRectangleOnScreen(rectangle, immediate,
+ RECTANGLE_ON_SCREEN_REQUEST_SOURCE_UNDEFINED);
+ }
+
+ /**
+ * Request that a rectangle of this view be visible on the screen,
+ * scrolling if necessary just enough.
+ *
+ * <p>A View should call this if it maintains some notion of which part
+ * of its content is interesting. For example, a text editing view
+ * should call this when its cursor moves.
+ * <p>The Rectangle passed into this method should be in the View's content coordinate space.
+ * It should not be affected by which part of the View is currently visible or its scroll
+ * position.
+ * <p>When <code>immediate</code> is set to true, scrolling will not be
+ * animated.
+ * <p> The <code>source</code> parameter is used to differentiate behaviors of certain
+ * system features, like focus-following with display magnification, based on user
+ * preferences and the source of requests to show content on-screen. Callers are
+ * encouraged to provide one of the following request sources, when applicable,
+ * instead of using {@link #requestRectangleOnScreen(Rect)} or
+ * {@link #requestRectangleOnScreen(Rect, boolean)}:
+ * <ol>
+ * <li>{@link #RECTANGLE_ON_SCREEN_REQUEST_SOURCE_SCROLL_ONLY}</li>
+ * <li>{@link #RECTANGLE_ON_SCREEN_REQUEST_SOURCE_TEXT_CURSOR}</li>
+ * <li>{@link #RECTANGLE_ON_SCREEN_REQUEST_SOURCE_INPUT_FOCUS}</li>
+ * </ol>
+ *
+ * @param rectangle The rectangle in the View's content coordinate space
+ * @param immediate True to forbid animated scrolling, false otherwise
+ * @param source The type of user interaction that requested this rectangle
+ * @return Whether any parent scrolled.
+ */
+ @FlaggedApi(FLAG_REQUEST_RECTANGLE_WITH_SOURCE)
+ public boolean requestRectangleOnScreen(@NonNull Rect rectangle, boolean immediate,
+ @RectangleOnScreenRequestSource int source) {
if (mParent == null) {
return false;
}
@@ -8544,8 +8625,7 @@
while (parent != null) {
rectangle.set((int) position.left, (int) position.top,
(int) position.right, (int) position.bottom);
-
- scrolled |= parent.requestChildRectangleOnScreen(child, rectangle, immediate);
+ scrolled |= parent.requestChildRectangleOnScreen(child, rectangle, immediate, source);
if (!(parent instanceof View)) {
break;
@@ -8775,6 +8855,13 @@
viewRoot.getHandwritingInitiator().onEditorFocused(this);
}
}
+
+ if (android.view.accessibility.Flags.requestRectangleWithSource()) {
+ final Rect r = mAttachInfo.mTmpInvalRect;
+ getLocalVisibleRect(r);
+ requestRectangleOnScreen(r, false,
+ RECTANGLE_ON_SCREEN_REQUEST_SOURCE_INPUT_FOCUS);
+ }
}
invalidate(true);
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 7e0818a..8f6a8a1 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -19,8 +19,10 @@
import static android.view.WindowInsetsAnimation.Callback.DISPATCH_MODE_CONTINUE_ON_SUBTREE;
import static android.view.WindowInsetsAnimation.Callback.DISPATCH_MODE_STOP;
import static android.view.flags.Flags.FLAG_TOOLKIT_VIEWGROUP_SET_REQUESTED_FRAME_RATE_API;
-import static android.view.flags.Flags.toolkitViewgroupSetRequestedFrameRateApi;
import static android.view.flags.Flags.scrollCaptureTargetZOrderFix;
+import static android.view.flags.Flags.toolkitViewgroupSetRequestedFrameRateApi;
+
+import static com.android.window.flags.Flags.interceptMotionFromMoveToCancel;
import android.animation.LayoutTransition;
import android.annotation.CallSuper;
@@ -88,7 +90,6 @@
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Predicate;
-
/**
* <p>
* A <code>ViewGroup</code> is a special view that can contain other views
@@ -2674,7 +2675,8 @@
ViewRootImpl viewRootImpl = getViewRootImpl();
if (actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null) {
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
- final boolean isBackGestureInProgress = (viewRootImpl != null
+ final boolean isBackGestureInProgress = !interceptMotionFromMoveToCancel()
+ && (viewRootImpl != null
&& viewRootImpl.getOnBackInvokedDispatcher().isBackGestureInProgress());
if (!disallowIntercept || isBackGestureInProgress) {
// Allow back to intercept touch
diff --git a/core/java/android/view/ViewParent.java b/core/java/android/view/ViewParent.java
index 54bc348..cb335fb 100644
--- a/core/java/android/view/ViewParent.java
+++ b/core/java/android/view/ViewParent.java
@@ -16,6 +16,9 @@
package android.view;
+import static android.view.accessibility.Flags.FLAG_REQUEST_RECTANGLE_WITH_SOURCE;
+
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.Matrix;
@@ -322,8 +325,8 @@
* positioned onto the screen. {@link ViewGroup}s overriding this can trust
* that:
* <ul>
- * <li>child will be a direct child of this group</li>
- * <li>rectangle will be in the child's content coordinates</li>
+ * <li>{@code child} will be a direct child of this group</li>
+ * <li>{@code rectangle} will be in the child's content coordinates</li>
* </ul>
*
* <p>{@link ViewGroup}s overriding this should uphold the contract:</p>
@@ -331,7 +334,7 @@
* <li>nothing will change if the rectangle is already visible</li>
* <li>the view port will be scrolled only just enough to make the
* rectangle visible</li>
- * <ul>
+ * </ul>
*
* @param child The direct child making the request.
* @param rectangle The rectangle in the child's coordinates the child
@@ -344,6 +347,36 @@
boolean immediate);
/**
+ * Called when a child of this group wants a particular rectangle to be
+ * positioned onto the screen. {@link ViewGroup}s overriding this can trust
+ * that:
+ * <ul>
+ * <li>{@code child} will be a direct child of this group</li>
+ * <li>{@code rectangle} will be in the child's content coordinates</li>
+ * </ul>
+ *
+ * <p>{@link ViewGroup}s overriding this should uphold the contract:</p>
+ * <ul>
+ * <li>nothing will change if the rectangle is already visible</li>
+ * <li>the view port will be scrolled only just enough to make the
+ * rectangle visible</li>
+ * </ul>
+ *
+ * @param child The direct child making the request.
+ * @param rectangle The rectangle in the child's coordinates the child
+ * wishes to be on the screen.
+ * @param immediate True to forbid animated or delayed scrolling,
+ * false otherwise
+ * @param source The parameter for the source of this request.
+ * @return Whether the group scrolled to handle the operation
+ */
+ @FlaggedApi(FLAG_REQUEST_RECTANGLE_WITH_SOURCE)
+ default boolean requestChildRectangleOnScreen(@NonNull View child, @NonNull Rect rectangle,
+ boolean immediate, @View.RectangleOnScreenRequestSource int source) {
+ return requestChildRectangleOnScreen(child, rectangle, immediate);
+ }
+
+ /**
* Called by a child to request from its parent to send an {@link AccessibilityEvent}.
* The child has already populated a record for itself in the event and is delegating
* to its parent to send the event. The parent can optionally add a record for itself.
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 1c753cd..98a8a6c 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -37,8 +37,8 @@
import static android.view.Surface.FRAME_RATE_CATEGORY_LOW;
import static android.view.Surface.FRAME_RATE_CATEGORY_NORMAL;
import static android.view.Surface.FRAME_RATE_CATEGORY_NO_PREFERENCE;
-import static android.view.Surface.FRAME_RATE_COMPATIBILITY_AT_LEAST;
import static android.view.Surface.FRAME_RATE_COMPATIBILITY_FIXED_SOURCE;
+import static android.view.Surface.FRAME_RATE_COMPATIBILITY_AT_LEAST;
import static android.view.View.FRAME_RATE_CATEGORY_REASON_BOOST;
import static android.view.View.FRAME_RATE_CATEGORY_REASON_CONFLICTED;
import static android.view.View.FRAME_RATE_CATEGORY_REASON_INTERMITTENT;
@@ -136,10 +136,10 @@
import static com.android.text.flags.Flags.disableHandwritingInitiatorForIme;
import static com.android.window.flags.Flags.enableBufferTransformHintFromDisplay;
import static com.android.window.flags.Flags.enableWindowContextResourcesUpdateOnConfigChange;
+import static com.android.window.flags.Flags.fixViewRootCallTrace;
import static com.android.window.flags.Flags.predictiveBackSwipeEdgeNoneApi;
import static com.android.window.flags.Flags.reduceChangedExclusionRectsMsgs;
import static com.android.window.flags.Flags.setScPropertiesInClient;
-import static com.android.window.flags.Flags.fixViewRootCallTrace;
import android.Manifest;
import android.accessibilityservice.AccessibilityService;
@@ -191,7 +191,6 @@
import android.graphics.RectF;
import android.graphics.Region;
import android.graphics.RenderNode;
-import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
import android.hardware.SyncFence;
@@ -295,7 +294,6 @@
import com.android.internal.policy.DecorView;
import com.android.internal.policy.PhoneFallbackEventHandler;
import com.android.internal.protolog.ProtoLog;
-import com.android.internal.util.ContrastColorUtil;
import com.android.internal.util.FastPrintWriter;
import com.android.internal.view.BaseSurfaceHolder;
import com.android.internal.view.RootViewSurfaceTaker;
@@ -2089,21 +2087,12 @@
// preference for dark mode in configuration.uiMode. Instead, we assume that both
// force invert and the system's dark theme are enabled.
if (shouldApplyForceInvertDark()) {
- // TODO: b/368725782 - Use hwui color area detection instead of / in
- // addition to these heuristics.
+ // We will use HWUI color area detection to determine if it should actually be
+ // inverted. Checking light theme simply gives the developer a way to "opt-out"
+ // of force invert.
final boolean isLightTheme =
a.getBoolean(R.styleable.Theme_isLightTheme, false);
- final boolean isBackgroundColorLight;
- if (mView != null && mView.getBackground()
- instanceof ColorDrawable colorDrawable) {
- isBackgroundColorLight =
- !ContrastColorUtil.isColorDarkLab(colorDrawable.getColor());
- } else {
- // Treat unknown as light, so that only isLightTheme is used to determine
- // force dark treatment.
- isBackgroundColorLight = true;
- }
- if (isLightTheme && isBackgroundColorLight) {
+ if (isLightTheme) {
return ForceDarkType.FORCE_INVERT_COLOR_DARK;
} else {
return ForceDarkType.NONE;
@@ -2703,8 +2692,7 @@
mStopped = stopped;
final ThreadedRenderer renderer = mAttachInfo.mThreadedRenderer;
if (renderer != null) {
- if (DEBUG_DRAW)
- Log.d(mTag, "WindowStopped on " + getTitle() + " set to " + mStopped);
+ if (DEBUG_DRAW) Log.d(mTag, "WindowStopped on " + getTitle() + " set to " + mStopped);
renderer.setStopped(mStopped);
}
if (!mStopped) {
@@ -11370,6 +11358,13 @@
@Override
public boolean requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate) {
+ return requestChildRectangleOnScreen(child, rectangle, immediate,
+ View.RECTANGLE_ON_SCREEN_REQUEST_SOURCE_UNDEFINED);
+ }
+
+ @Override
+ public boolean requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate,
+ @View.RectangleOnScreenRequestSource int source) {
if (rectangle == null) {
return scrollToRectOrFocus(null, immediate);
}
@@ -11380,7 +11375,7 @@
mTempRect.offset(0, -mCurScrollY);
mTempRect.offset(mAttachInfo.mWindowLeft, mAttachInfo.mWindowTop);
try {
- mWindowSession.onRectangleOnScreenRequested(mWindow, mTempRect);
+ mWindowSession.onRectangleOnScreenRequested(mWindow, mTempRect, source);
} catch (RemoteException re) {
/* ignore */
}
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index 9097849..a80d82f 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -878,7 +878,7 @@
if (wm == null) {
wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
}
- mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
+ mWindowManager = wm.createLocalWindowManager(this);
}
void adjustLayoutParamsForSubWindow(WindowManager.LayoutParams wp) {
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 30056fc..67fa354 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -6779,4 +6779,30 @@
@NonNull Consumer<@ScreenRecordingState Integer> callback) {
throw new UnsupportedOperationException();
}
+
+ /**
+ * Sets the parent window to this {@code WindowManager}.
+ * This is necessary to attach sub-windows.
+ *
+ * @param parentWindow the parent window to be attached.
+ *
+ * @see android.window.WindowContext#attachWindow(View)
+ *
+ * @hide
+ */
+ default void setParentWindow(@NonNull Window parentWindow) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Creates a new instance of {@link WindowManager} with {@code parentWindow} attached.
+ *
+ * @param parentWindow the parent window to be attached.
+ * @return a new instance of {@link WindowManager} with {@code parentWindow} attached
+ *
+ * @hide
+ */
+ default WindowManager createLocalWindowManager(@NonNull Window parentWindow) {
+ throw new UnsupportedOperationException();
+ }
}
diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java
index 366ea20..666f4c1 100644
--- a/core/java/android/view/WindowManagerGlobal.java
+++ b/core/java/android/view/WindowManagerGlobal.java
@@ -32,6 +32,7 @@
import android.graphics.HardwareRenderer;
import android.os.Binder;
import android.os.Build;
+import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;
@@ -40,6 +41,7 @@
import android.util.AndroidRuntimeException;
import android.util.ArrayMap;
import android.util.ArraySet;
+import android.util.ListenerGroup;
import android.util.Log;
import android.util.Pair;
import android.util.SparseArray;
@@ -153,9 +155,8 @@
* The {@link ListenerGroup} that is associated to {@link #mViews}.
* @hide
*/
- @GuardedBy("mLock")
private final ListenerGroup<List<View>> mWindowViewsListenerGroup =
- new ListenerGroup<>(new ArrayList<>());
+ new ListenerGroup<>(new ArrayList<>(), new Handler(Looper.getMainLooper()));
@UnsupportedAppUsage
private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
@UnsupportedAppUsage
@@ -336,9 +337,6 @@
public void addWindowViewsListener(@NonNull Executor executor,
@NonNull Consumer<List<View>> consumer) {
synchronized (mLock) {
- if (mWindowViewsListenerGroup.isConsumerPresent(consumer)) {
- return;
- }
mWindowViewsListenerGroup.addListener(executor, consumer);
}
}
diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java
index f6a3041..7ee0d93 100644
--- a/core/java/android/view/WindowManagerImpl.java
+++ b/core/java/android/view/WindowManagerImpl.java
@@ -81,12 +81,18 @@
* provides a window manager for adding windows that are associated with that
* activity -- the window manager will not normally allow you to add arbitrary
* windows that are not associated with an activity.
+ * <p>
+ * Note that extending {@code WindowManagerImpl} for {@link WindowManager} customization may lead to
+ * crashes since {@link Window} and {@link WindowContext} may also customize
+ * {@code WindowManagerImpl}, such as providing {@link #mParentWindow}
+ * or {@link #mWindowContextToken}. Users should customize {@link WindowManager} via
+ * {@link WindowManagerWrapper}.
*
* @see WindowManager
* @see WindowManagerGlobal
* @hide
*/
-public class WindowManagerImpl implements WindowManager {
+public final class WindowManagerImpl implements WindowManager {
private static final String TAG = "WindowManager";
@UnsupportedAppUsage
@@ -129,14 +135,11 @@
mWindowMetricsController = new WindowMetricsController(mContext);
}
- public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
+ @Override
+ public WindowManager createLocalWindowManager(Window parentWindow) {
return new WindowManagerImpl(mContext, parentWindow, mWindowContextToken);
}
- public WindowManagerImpl createPresentationWindowManager(Context displayContext) {
- return new WindowManagerImpl(displayContext, mParentWindow, mWindowContextToken);
- }
-
/** Creates a {@link WindowManager} for a {@link WindowContext}. */
public static WindowManager createWindowContextWindowManager(Context context) {
final IBinder clientToken = context.getWindowContextToken();
@@ -153,9 +156,7 @@
mDefaultToken = token;
}
- /**
- * Sets the parent window.
- */
+ @Override
public void setParentWindow(@NonNull Window parentWindow) {
mParentWindow = parentWindow;
}
diff --git a/core/java/android/view/WindowManagerWrapper.java b/core/java/android/view/WindowManagerWrapper.java
new file mode 100644
index 0000000..cf2023b
--- /dev/null
+++ b/core/java/android/view/WindowManagerWrapper.java
@@ -0,0 +1,328 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import static com.android.server.display.feature.flags.Flags.FLAG_ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT;
+
+import android.Manifest;
+import android.annotation.FlaggedApi;
+import android.annotation.RequiresPermission;
+import android.content.ComponentName;
+import android.graphics.Bitmap;
+import android.graphics.Region;
+import android.os.IBinder;
+import android.os.Looper;
+import android.window.InputTransferToken;
+import android.window.TaskFpsCallback;
+import android.window.TrustedPresentationThresholds;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.window.flags.Flags;
+
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
+import java.util.function.IntConsumer;
+
+/**
+ * A wrapper that wraps a base {@link WindowManager}.
+ * <p>
+ * Similar to {@link android.content.ContextWrapper}, it enables to customize behavior without
+ * touching the original {@link WindowManager}.
+ *
+ * @hide
+ */
+public class WindowManagerWrapper implements WindowManager {
+
+ @NonNull
+ private final WindowManager mBase;
+
+ public WindowManagerWrapper(@NonNull WindowManager base) {
+ mBase = base;
+ }
+
+ @Override
+ public void addView(View view, ViewGroup.LayoutParams params) {
+ mBase.addView(view, params);
+ }
+
+ @Override
+ public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
+ mBase.updateViewLayout(view, params);
+ }
+
+ @Override
+ public void removeView(View view) {
+ mBase.removeView(view);
+ }
+
+ @Deprecated
+ @Override
+ public Display getDefaultDisplay() {
+ return mBase.getDefaultDisplay();
+ }
+
+ @Override
+ public void removeViewImmediate(View view) {
+ mBase.removeViewImmediate(view);
+ }
+
+ @NonNull
+ @Override
+ public WindowMetrics getCurrentWindowMetrics() {
+ return mBase.getCurrentWindowMetrics();
+ }
+
+ @NonNull
+ @Override
+ public WindowMetrics getMaximumWindowMetrics() {
+ return mBase.getMaximumWindowMetrics();
+ }
+
+ @NonNull
+ @Override
+ public Set<WindowMetrics> getPossibleMaximumWindowMetrics(int displayId) {
+ return mBase.getPossibleMaximumWindowMetrics(displayId);
+ }
+
+ @Override
+ public void requestAppKeyboardShortcuts(KeyboardShortcutsReceiver receiver,
+ int deviceId) {
+ mBase.requestAppKeyboardShortcuts(receiver, deviceId);
+ }
+
+ @Override
+ public KeyboardShortcutGroup getApplicationLaunchKeyboardShortcuts(int deviceId) {
+ return mBase.getApplicationLaunchKeyboardShortcuts(deviceId);
+ }
+
+ @Override
+ public void requestImeKeyboardShortcuts(KeyboardShortcutsReceiver receiver, int deviceId) {
+ mBase.requestImeKeyboardShortcuts(receiver, deviceId);
+ }
+
+ @RequiresPermission(android.Manifest.permission.RESTRICTED_VR_ACCESS)
+ @Override
+ public Region getCurrentImeTouchRegion() {
+ return mBase.getCurrentImeTouchRegion();
+ }
+
+ @Override
+ public void setShouldShowWithInsecureKeyguard(int displayId, boolean shouldShow) {
+ mBase.setShouldShowWithInsecureKeyguard(displayId, shouldShow);
+ }
+
+ @Override
+ public void setShouldShowSystemDecors(int displayId, boolean shouldShow) {
+ mBase.setShouldShowSystemDecors(displayId, shouldShow);
+ }
+
+ @Override
+ public boolean shouldShowSystemDecors(int displayId) {
+ return mBase.shouldShowSystemDecors(displayId);
+ }
+
+ @Override
+ public void setDisplayImePolicy(int displayId, int imePolicy) {
+ mBase.setDisplayImePolicy(displayId, imePolicy);
+ }
+
+ @FlaggedApi(FLAG_ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT)
+ @Override
+ public boolean isEligibleForDesktopMode(int displayId) {
+ return mBase.isEligibleForDesktopMode(displayId);
+ }
+
+ @Override
+ public int getDisplayImePolicy(int displayId) {
+ return mBase.getDisplayImePolicy(displayId);
+ }
+
+ @Override
+ public boolean isGlobalKey(int keyCode) {
+ return mBase.isGlobalKey(keyCode);
+ }
+
+ @Override
+ public boolean isCrossWindowBlurEnabled() {
+ return mBase.isCrossWindowBlurEnabled();
+ }
+
+ @Override
+ public void addCrossWindowBlurEnabledListener(@NonNull Consumer<Boolean> listener) {
+ mBase.addCrossWindowBlurEnabledListener(listener);
+ }
+
+ @Override
+ public void addCrossWindowBlurEnabledListener(@NonNull Executor executor,
+ @NonNull Consumer<Boolean> listener) {
+ mBase.addCrossWindowBlurEnabledListener(executor, listener);
+ }
+
+ @Override
+ public void removeCrossWindowBlurEnabledListener(@NonNull Consumer<Boolean> listener) {
+ mBase.removeCrossWindowBlurEnabledListener(listener);
+ }
+
+ @Override
+ public void addProposedRotationListener(@NonNull Executor executor,
+ @NonNull IntConsumer listener) {
+ mBase.addProposedRotationListener(executor, listener);
+ }
+
+ @Override
+ public void removeProposedRotationListener(@NonNull IntConsumer listener) {
+ mBase.removeProposedRotationListener(listener);
+ }
+
+ @Override
+ public void holdLock(IBinder token, int durationMs) {
+ mBase.holdLock(token, durationMs);
+ }
+
+ @Override
+ public boolean isTaskSnapshotSupported() {
+ return mBase.isTaskSnapshotSupported();
+ }
+
+ @Override
+ public void registerTaskFpsCallback(int taskId, @NonNull Executor executor,
+ @NonNull TaskFpsCallback callback) {
+ mBase.registerTaskFpsCallback(taskId, executor, callback);
+ }
+
+ @Override
+ public void unregisterTaskFpsCallback(@NonNull TaskFpsCallback callback) {
+ mBase.unregisterTaskFpsCallback(callback);
+ }
+
+ @Nullable
+ @Override
+ public Bitmap snapshotTaskForRecents(int taskId) {
+ return mBase.snapshotTaskForRecents(taskId);
+ }
+
+ @NonNull
+ @Override
+ public List<ComponentName> notifyScreenshotListeners(int displayId) {
+ return mBase.notifyScreenshotListeners(displayId);
+ }
+
+ @RequiresPermission(Manifest.permission.ACCESS_SURFACE_FLINGER)
+ @Override
+ public boolean replaceContentOnDisplayWithMirror(int displayId, @NonNull Window window) {
+ return mBase.replaceContentOnDisplayWithMirror(displayId, window);
+ }
+
+ @RequiresPermission(Manifest.permission.ACCESS_SURFACE_FLINGER)
+ @Override
+ public boolean replaceContentOnDisplayWithSc(int displayId, @NonNull SurfaceControl sc) {
+ return mBase.replaceContentOnDisplayWithSc(displayId, sc);
+ }
+
+ @FlaggedApi(Flags.FLAG_TRUSTED_PRESENTATION_LISTENER_FOR_WINDOW)
+ @Override
+ public void registerTrustedPresentationListener(@NonNull IBinder window,
+ @NonNull TrustedPresentationThresholds thresholds, @NonNull Executor executor,
+ @NonNull Consumer<Boolean> listener) {
+ mBase.registerTrustedPresentationListener(window, thresholds, executor, listener);
+ }
+
+ @FlaggedApi(Flags.FLAG_TRUSTED_PRESENTATION_LISTENER_FOR_WINDOW)
+ @Override
+ public void unregisterTrustedPresentationListener(@NonNull Consumer<Boolean> listener) {
+ mBase.unregisterTrustedPresentationListener(listener);
+ }
+
+ @FlaggedApi(Flags.FLAG_SURFACE_CONTROL_INPUT_RECEIVER)
+ @NonNull
+ @Override
+ public InputTransferToken registerBatchedSurfaceControlInputReceiver(
+ @NonNull InputTransferToken hostInputTransferToken,
+ @NonNull SurfaceControl surfaceControl, @NonNull Choreographer choreographer,
+ @NonNull SurfaceControlInputReceiver receiver) {
+ return mBase.registerBatchedSurfaceControlInputReceiver(
+ hostInputTransferToken, surfaceControl, choreographer, receiver);
+ }
+
+ @FlaggedApi(Flags.FLAG_SURFACE_CONTROL_INPUT_RECEIVER)
+ @NonNull
+ @Override
+ public InputTransferToken registerUnbatchedSurfaceControlInputReceiver(
+ @NonNull InputTransferToken hostInputTransferToken,
+ @NonNull SurfaceControl surfaceControl, @NonNull Looper looper,
+ @NonNull SurfaceControlInputReceiver receiver) {
+ return mBase.registerUnbatchedSurfaceControlInputReceiver(
+ hostInputTransferToken, surfaceControl, looper, receiver);
+ }
+
+ @FlaggedApi(Flags.FLAG_SURFACE_CONTROL_INPUT_RECEIVER)
+ @Override
+ public void unregisterSurfaceControlInputReceiver(@NonNull SurfaceControl surfaceControl) {
+ mBase.unregisterSurfaceControlInputReceiver(surfaceControl);
+ }
+
+ @FlaggedApi(Flags.FLAG_SURFACE_CONTROL_INPUT_RECEIVER)
+ @Nullable
+ @Override
+ public IBinder getSurfaceControlInputClientToken(@NonNull SurfaceControl surfaceControl) {
+ return mBase.getSurfaceControlInputClientToken(surfaceControl);
+ }
+
+ @FlaggedApi(Flags.FLAG_SURFACE_CONTROL_INPUT_RECEIVER)
+ @Override
+ public boolean transferTouchGesture(@NonNull InputTransferToken transferFromToken,
+ @NonNull InputTransferToken transferToToken) {
+ return mBase.transferTouchGesture(transferFromToken, transferToToken);
+ }
+
+ @NonNull
+ @Override
+ public IBinder getDefaultToken() {
+ return mBase.getDefaultToken();
+ }
+
+ @FlaggedApi(Flags.FLAG_SCREEN_RECORDING_CALLBACKS)
+ @RequiresPermission(Manifest.permission.DETECT_SCREEN_RECORDING)
+ @Override
+ public @ScreenRecordingState int addScreenRecordingCallback(@NonNull Executor executor,
+ @NonNull Consumer<@ScreenRecordingState Integer> callback) {
+ return mBase.addScreenRecordingCallback(executor, callback);
+ }
+
+ @FlaggedApi(Flags.FLAG_SCREEN_RECORDING_CALLBACKS)
+ @RequiresPermission(Manifest.permission.DETECT_SCREEN_RECORDING)
+ @Override
+ public void removeScreenRecordingCallback(
+ @NonNull Consumer<@ScreenRecordingState Integer> callback) {
+ mBase.removeScreenRecordingCallback(callback);
+ }
+
+ @Override
+ public WindowManager createLocalWindowManager(@NonNull Window parentWindow) {
+ final WindowManager newBase = mBase.createLocalWindowManager(parentWindow);
+ return new WindowManagerWrapper(newBase);
+ }
+
+ @Override
+ public void setParentWindow(@NonNull Window parentWindow) {
+ mBase.setParentWindow(parentWindow);
+ }
+}
diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java
index 5af4abd..9f310f4 100644
--- a/core/java/android/view/WindowlessWindowManager.java
+++ b/core/java/android/view/WindowlessWindowManager.java
@@ -89,7 +89,7 @@
protected final SurfaceControl mRootSurface;
private final Configuration mConfiguration;
private final IWindowSession mRealWm;
- InputTransferToken mHostInputTransferToken;
+ final InputTransferToken mHostInputTransferToken;
private final InputTransferToken mInputTransferToken = new InputTransferToken();
private InsetsState mInsetsState;
private final ClientWindowFrames mTmpFrames = new ClientWindowFrames();
@@ -128,11 +128,9 @@
return null;
}
- void setHostInputTransferToken(InputTransferToken token) {
- mHostInputTransferToken = token;
- }
-
- /** Utility API. */
+ /**
+ * Utility API.
+ */
void setCompletionCallback(IBinder window, ResizeCompleteCallback callback) {
if (mResizeCompletionForWindow.get(window) != null) {
Log.w(TAG, "Unsupported overlapping resizes");
@@ -153,25 +151,11 @@
return;
}
state.mInputRegion = region != null ? new Region(region) : null;
- updateInputChannel(window);
- }
- }
-
- protected void updateInputChannel(IBinder window) {
- State state;
- synchronized (this) {
- // Do everything while locked so that we synchronize with relayout. This should be a
- // very infrequent operation.
- state = mStateForWindow.get(window);
- if (state == null) {
- return;
- }
if (state.mInputChannelToken != null) {
try {
- mRealWm.updateInputChannel(state.mInputChannelToken, mHostInputTransferToken,
- state.mDisplayId, state.mSurfaceControl, state.mParams.flags,
- state.mParams.privateFlags, state.mParams.inputFeatures,
- state.mInputRegion);
+ mRealWm.updateInputChannel(state.mInputChannelToken, state.mDisplayId,
+ state.mSurfaceControl, state.mParams.flags, state.mParams.privateFlags,
+ state.mParams.inputFeatures, state.mInputRegion);
} catch (RemoteException e) {
Log.e(TAG, "Failed to update surface input channel: ", e);
}
@@ -190,7 +174,9 @@
}
}
- /** IWindowSession implementation. */
+ /**
+ * IWindowSession implementation.
+ */
@Override
public int addToDisplay(IWindow window, WindowManager.LayoutParams attrs,
int viewVisibility, int displayId, @InsetsType int requestedVisibleTypes,
@@ -451,15 +437,14 @@
if ((attrChanges & inputChangeMask) != 0 && state.mInputChannelToken != null) {
try {
if (mRealWm instanceof IWindowSession.Stub) {
- mRealWm.updateInputChannel(state.mInputChannelToken, mHostInputTransferToken,
- state.mDisplayId,
+ mRealWm.updateInputChannel(state.mInputChannelToken, state.mDisplayId,
new SurfaceControl(sc, "WindowlessWindowManager.relayout"),
attrs.flags, attrs.privateFlags, attrs.inputFeatures,
state.mInputRegion);
} else {
- mRealWm.updateInputChannel(state.mInputChannelToken, mHostInputTransferToken,
- state.mDisplayId, sc, attrs.flags, attrs.privateFlags,
- attrs.inputFeatures, state.mInputRegion);
+ mRealWm.updateInputChannel(state.mInputChannelToken, state.mDisplayId, sc,
+ attrs.flags, attrs.privateFlags, attrs.inputFeatures,
+ state.mInputRegion);
}
} catch (RemoteException e) {
Log.e(TAG, "Failed to update surface input channel: ", e);
@@ -573,7 +558,7 @@
@Override
public void onRectangleOnScreenRequested(android.os.IBinder token,
- android.graphics.Rect rectangle) {
+ android.graphics.Rect rectangle, int source) {
}
@Override
@@ -639,9 +624,8 @@
}
@Override
- public void updateInputChannel(IBinder channelToken, InputTransferToken hostInputToken,
- int displayId, SurfaceControl surface, int flags, int privateFlags, int inputFeatures,
- Region region) {
+ public void updateInputChannel(IBinder channelToken, int displayId, SurfaceControl surface,
+ int flags, int privateFlags, int inputFeatures, Region region) {
}
@Override
diff --git a/core/java/android/view/accessibility/flags/accessibility_flags.aconfig b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
index 9e03c1e..7cd6037 100644
--- a/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
+++ b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
@@ -44,13 +44,6 @@
}
flag {
- name: "a11y_qs_shortcut"
- namespace: "accessibility"
- description: "Add Quick Setting as one of the a11y shortcut options"
- bug: "297554934"
-}
-
-flag {
name: "a11y_selection_api"
namespace: "accessibility"
description: "Enables new APIs for an AccessibilityService to control selection across nodes."
@@ -250,16 +243,6 @@
}
flag {
- name: "restore_a11y_secure_settings_on_hsum_device"
- namespace: "accessibility"
- description: "Grab the a11y settings and send the settings restored broadcast for current visible foreground user"
- bug: "381294327"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
name: "restore_a11y_shortcut_target_service"
namespace: "accessibility"
description: "Perform merging and other bug fixes for SettingsProvider restore of ACCESSIBILITY_SHORTCUT_TARGET_SERVICES secure setting"
@@ -300,6 +283,13 @@
}
flag {
+ name: "text_cursor_blink_interval"
+ namespace: "accessibility"
+ description: "Configure the text cursor blink interval"
+ bug: "402134144"
+}
+
+flag {
name: "tri_state_checked"
namespace: "accessibility"
description: "Feature flag for adding tri-state checked api"
@@ -313,13 +303,3 @@
description: "Updates the Always-On A11yService state when the user changes the enablement of the shortcut."
bug: "298869916"
}
-
-flag {
- name: "warning_use_default_dialog_type"
- namespace: "accessibility"
- description: "Uses the default type for the A11yService warning dialog, instead of SYSTEM_ALERT_DIALOG"
- bug: "336719951"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
diff --git a/core/java/android/view/contentcapture/flags/content_capture_flags.aconfig b/core/java/android/view/contentcapture/flags/content_capture_flags.aconfig
index 3780db3..a3c1eab 100644
--- a/core/java/android/view/contentcapture/flags/content_capture_flags.aconfig
+++ b/core/java/android/view/contentcapture/flags/content_capture_flags.aconfig
@@ -17,6 +17,13 @@
}
flag {
+ name: "enable_system_ui_underlay"
+ namespace: "ailabs"
+ description: "Feature flag to enable the connection between content capture and system UI underlay"
+ bug: "403422950"
+}
+
+flag {
name: "flush_after_each_frame"
namespace: "pixel_state_server"
description: "Feature flag to send a flush event after each frame"
@@ -27,3 +34,13 @@
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "warm_up_background_thread_for_content_capture"
+ namespace: "system_performance"
+ description: "Feature flag to warm up the background thread for content capture"
+ bug: "408273598"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/core/java/android/view/input/LetterboxScrollProcessor.java b/core/java/android/view/input/LetterboxScrollProcessor.java
index ea0fc39..9513ec9 100644
--- a/core/java/android/view/input/LetterboxScrollProcessor.java
+++ b/core/java/android/view/input/LetterboxScrollProcessor.java
@@ -16,8 +16,7 @@
package android.view.input;
-import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
-
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.graphics.Rect;
@@ -27,9 +26,7 @@
import android.view.InputEvent;
import android.view.MotionEvent;
-import androidx.annotation.NonNull;
-
-import com.android.internal.annotations.VisibleForTesting;
+import com.android.window.flags.Flags;
import java.util.ArrayList;
import java.util.HashSet;
@@ -38,12 +35,11 @@
import java.util.Set;
/**
- * {@link MotionEvent} processor that forwards scrolls on the letterbox area to the app's view
+ * {@link InputEvent} processor that forwards scrolls on the letterbox area to the app's view
* hierarchy by translating the coordinates to app's inbound area.
*
* @hide
*/
-@VisibleForTesting(visibility = PACKAGE)
public class LetterboxScrollProcessor {
private enum LetterboxScrollState {
@@ -53,11 +49,15 @@
SCROLLING_STARTED_OUTSIDE_APP
}
- @NonNull private LetterboxScrollState mState = LetterboxScrollState.AWAITING_GESTURE_START;
- @NonNull private final List<MotionEvent> mProcessedEvents = new ArrayList<>();
+ @NonNull
+ private LetterboxScrollState mState = LetterboxScrollState.AWAITING_GESTURE_START;
+ @NonNull
+ private final List<InputEvent> mProcessedEvents = new ArrayList<>();
- @NonNull private final GestureDetector mScrollDetector;
- @NonNull private final Context mContext;
+ @NonNull
+ private final GestureDetector mScrollDetector;
+ @NonNull
+ private final Context mContext;
/** IDs of events generated from this class */
private final Set<Integer> mGeneratedEventIds = new HashSet<>();
@@ -67,24 +67,32 @@
mScrollDetector = new GestureDetector(context, new ScrollListener(), handler);
}
+ public static boolean isCompatibilityNeeded() {
+ return Flags.scrollingFromLetterbox();
+ }
+
/**
- * Processes the MotionEvent. If the gesture is started in the app's bounds, or moves over the
+ * Processes the InputEvent. If the gesture is started in the app's bounds, or moves over the
* app then the motion events are not adjusted. Motion events from outside the app's
* bounds that are detected as a scroll gesture are adjusted to be over the app's bounds.
* Otherwise (if the events are outside the app's bounds and not part of a scroll gesture), the
* motion events are ignored.
*
- * @param motionEvent The MotionEvent to process.
+ * @param inputEvent The InputEvent to process.
* @return The list of adjusted events, or null if no adjustments are needed. The list is empty
* if the event should be ignored. Do not keep a reference to the output as the list is reused.
*/
@Nullable
- public List<MotionEvent> processMotionEvent(@NonNull MotionEvent motionEvent) {
- if (!motionEvent.isFromSource(InputDevice.SOURCE_CLASS_POINTER)) {
- // This is a non-pointer event that doesn't correspond to any location on the screen.
- // Ignore it.
+ public List<InputEvent> processInputEventForCompatibility(@NonNull InputEvent inputEvent) {
+ if (!(inputEvent instanceof MotionEvent motionEvent)
+ || motionEvent.getAction() == MotionEvent.ACTION_OUTSIDE
+ || !motionEvent.isFromSource(InputDevice.SOURCE_CLASS_POINTER)) {
return null;
}
+ return processMotionEvent(motionEvent);
+ }
+
+ private List<InputEvent> processMotionEvent(@NonNull MotionEvent motionEvent) {
mProcessedEvents.clear();
final Rect appBounds = getAppBounds();
@@ -146,13 +154,12 @@
* Processes the InputEvent for compatibility before it is finished by calling
* InputEventReceiver#finishInputEvent().
*
- * @param motionEvent The MotionEvent to process.
+ * @param inputEvent The InputEvent to process.
* @return The motionEvent to finish, or null if it should not be finished.
*/
@Nullable
- @VisibleForTesting(visibility = PACKAGE)
- public InputEvent processMotionEventBeforeFinish(@NonNull MotionEvent motionEvent) {
- return mGeneratedEventIds.remove(motionEvent.getId()) ? null : motionEvent;
+ public InputEvent processInputEventBeforeFinish(@NonNull InputEvent inputEvent) {
+ return mGeneratedEventIds.remove(inputEvent.getId()) ? null : inputEvent;
}
@NonNull
diff --git a/core/java/android/view/input/StylusButtonCompatibility.java b/core/java/android/view/input/StylusButtonCompatibility.java
new file mode 100644
index 0000000..1f04984
--- /dev/null
+++ b/core/java/android/view/input/StylusButtonCompatibility.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.input;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.os.Build;
+import android.view.InputEvent;
+import android.view.MotionEvent;
+
+/**
+ * This rewrites stylus button events for an application targeting older SDK.
+ *
+ * @hide
+ */
+public class StylusButtonCompatibility {
+ private static final int STYLUS_BUTTONS_MASK =
+ MotionEvent.BUTTON_STYLUS_PRIMARY | MotionEvent.BUTTON_STYLUS_SECONDARY;
+
+ /**
+ * Returns {@code true} if this compatibility is required based on the given context.
+ */
+ public static boolean isCompatibilityNeeded(Context context) {
+ return context.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.M;
+ }
+
+ /**
+ * Returns a rewritten event if compatibility is applied.
+ * Returns a null if the event is not modified.
+ */
+ @Nullable
+ public InputEvent processInputEventForCompatibility(@NonNull InputEvent inputEvent) {
+ if (!(inputEvent instanceof MotionEvent motion)) {
+ return null;
+ }
+ final int buttonState = motion.getButtonState();
+ // BUTTON_STYLUS_PRIMARY and BUTTON_STYLUS_SECONDARY are mapped to
+ // BUTTON_SECONDARY and BUTTON_TERTIARY respectively.
+ final int compatButtonState = (buttonState & STYLUS_BUTTONS_MASK) >> 4;
+ if (compatButtonState == 0) {
+ // No need to rewrite.
+ return null;
+ }
+ motion.setButtonState(buttonState | compatButtonState);
+ return motion;
+ }
+}
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 7d7570d..38bf572 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -1117,7 +1117,8 @@
private boolean startInputOnWindowFocusGainInternal(@StartInputReason int startInputReason,
View focusedView, @StartInputFlags int startInputFlags,
- @SoftInputModeFlags int softInputMode, int windowFlags) {
+ @SoftInputModeFlags int softInputMode,
+ @WindowManager.LayoutParams.Flags int windowFlags) {
synchronized (mH) {
mCurrentEditorInfo = null;
mCompletions = null;
@@ -3400,7 +3401,8 @@
@RequiresPermission(value = Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional = true)
private boolean startInputInner(@StartInputReason int startInputReason,
@Nullable IBinder windowGainingFocus, @StartInputFlags int startInputFlags,
- @SoftInputModeFlags int softInputMode, int windowFlags) {
+ @SoftInputModeFlags int softInputMode,
+ @WindowManager.LayoutParams.Flags int windowFlags) {
final View view;
synchronized (mH) {
view = getServedViewLocked();
@@ -3673,7 +3675,7 @@
@StartInputFlags int startInputFlags,
@StartInputReason int startInputReason,
@SoftInputModeFlags int softInputMode,
- int windowFlags) {
+ @WindowManager.LayoutParams.Flags int windowFlags) {
return (startInputFlags & StartInputFlags.WINDOW_GAINED_FOCUS) == 0
&& (startInputFlags & StartInputFlags.IS_TEXT_EDITOR) == 0
&& previousViewFocusParameters != null
@@ -5079,6 +5081,7 @@
+ " mCursorSelEnd=" + mCursorSelEnd
+ " mCursorCandStart=" + mCursorCandStart
+ " mCursorCandEnd=" + mCursorCandEnd);
+ mImeDispatcher.dump(p, " ");
}
/**
diff --git a/core/java/android/view/inputmethod/ViewFocusParameterInfo.java b/core/java/android/view/inputmethod/ViewFocusParameterInfo.java
index 44c33fa..7a13e5c 100644
--- a/core/java/android/view/inputmethod/ViewFocusParameterInfo.java
+++ b/core/java/android/view/inputmethod/ViewFocusParameterInfo.java
@@ -17,6 +17,7 @@
package android.view.inputmethod;
import android.annotation.Nullable;
+import android.view.WindowManager;
import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
import com.android.internal.inputmethod.StartInputFlags;
@@ -34,13 +35,13 @@
@StartInputFlags final int mPreviousStartInputFlags;
@StartInputReason final int mPreviousStartInputReason;
@SoftInputModeFlags final int mPreviousSoftInputMode;
- final int mPreviousWindowFlags;
+ @WindowManager.LayoutParams.Flags final int mPreviousWindowFlags;
ViewFocusParameterInfo(@Nullable EditorInfo previousEditorInfo,
@StartInputFlags int previousStartInputFlags,
@StartInputReason int previousStartInputReason,
@SoftInputModeFlags int previousSoftInputMode,
- int previousWindowFlags) {
+ @WindowManager.LayoutParams.Flags int previousWindowFlags) {
mPreviousEditorInfo = previousEditorInfo;
mPreviousStartInputFlags = previousStartInputFlags;
mPreviousStartInputReason = previousStartInputReason;
@@ -52,7 +53,7 @@
@StartInputFlags int startInputFlags,
@StartInputReason int startInputReason,
@SoftInputModeFlags int softInputMode,
- int windowFlags) {
+ @WindowManager.LayoutParams.Flags int windowFlags) {
return mPreviousStartInputFlags == startInputFlags
&& mPreviousStartInputReason == startInputReason
&& mPreviousSoftInputMode == softInputMode
diff --git a/core/java/android/view/inputmethod/flags.aconfig b/core/java/android/view/inputmethod/flags.aconfig
index cdca410..f3cb55b 100644
--- a/core/java/android/view/inputmethod/flags.aconfig
+++ b/core/java/android/view/inputmethod/flags.aconfig
@@ -129,17 +129,6 @@
}
flag {
- name: "defer_show_soft_input_until_session_creation"
- namespace: "input_method"
- description: "Defers showSoftInput until the IME session has been created."
- bug: "337766845"
- is_fixed_read_only: true
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
name: "compatchange_for_zerojankproxy"
namespace: "input_method"
description: "Version-gate the sync/async nature of IMM#show/hideSoftInput() when using zeroJankProxy."
@@ -235,3 +224,10 @@
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "disable_ime_restore_on_activity_create"
+ namespace: "input_method"
+ description: "Disables restoring the IME visibility after activity recreation (e.g. rotation), without an explicit show request."
+ bug: "391859600"
+}
diff --git a/core/java/android/view/inspector/WindowInspector.java b/core/java/android/view/inspector/WindowInspector.java
index f0cc011..6409f33 100644
--- a/core/java/android/view/inspector/WindowInspector.java
+++ b/core/java/android/view/inspector/WindowInspector.java
@@ -43,7 +43,9 @@
/**
* Adds a listener that is notified whenever the value of {@link #getGlobalWindowViews()}
- * changes. The current value is provided immediately using the provided {@link Executor}.
+ * changes. The current value is provided immediately using the provided {@link Executor}. The
+ * value is initially relayed using the main thread and then posted to the {@link Executor}.
+ * Using a direct {@link Executor} and doing heavy calculations can cause performance issues.
* If this {@link Consumer} is already registered, then this method is a no op.
* @see #getGlobalWindowViews()
*/
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 1e0c490..575f5ff 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -1294,6 +1294,15 @@
: mItems
: mCollectionCache.getItemsForId(mIntentId);
+ if (items.hasLegacyNullItems()) {
+ Log.e(LOG_TAG, "Legacy null RemoteViews encountered in "
+ + "RemoteViewsFactory#getRemoteCollectionItems. Widget should not provide "
+ + "null RemoteViews in collections. Use setRemoteAdapter(viewId, items) "
+ + "instead to provide items.");
+ throw new ActionException("Null entries encountered in "
+ + "RemoteViewsFactory#getRemoteCollectionItems");
+ }
+
// Ensure that we are applying to an AppWidget root
if (!(rootParent instanceof AppWidgetHostView)) {
Log.e(LOG_TAG, "setRemoteAdapter can only be used for "
@@ -9529,15 +9538,23 @@
private final RemoteViews[] mViews;
private final boolean mHasStableIds;
private final int mViewTypeCount;
+ private final boolean mHasLegacyNullItems;
private HierarchyRootData mHierarchyRootData;
RemoteCollectionItems(
long[] ids, RemoteViews[] views, boolean hasStableIds, int viewTypeCount) {
+ this(ids, views, hasStableIds, viewTypeCount, /* hasLegacyNullItems= */ false);
+ }
+
+ RemoteCollectionItems(
+ long[] ids, RemoteViews[] views, boolean hasStableIds, int viewTypeCount,
+ boolean hasLegacyNullItems) {
mIds = ids;
mViews = views;
mHasStableIds = hasStableIds;
mViewTypeCount = viewTypeCount;
+ mHasLegacyNullItems = hasLegacyNullItems;
if (ids.length != views.length) {
throw new IllegalArgumentException(
"RemoteCollectionItems has different number of ids and views");
@@ -9566,6 +9583,7 @@
RemoteCollectionItems(@NonNull Parcel in, @Nullable HierarchyRootData hierarchyRootData) {
mHasStableIds = in.readBoolean();
mViewTypeCount = in.readInt();
+ mHasLegacyNullItems = in.readBoolean();
int length = in.readInt();
mIds = new long[length];
in.readLongArray(mIds);
@@ -9618,6 +9636,7 @@
dest.writeBoolean(mHasStableIds);
dest.writeInt(mViewTypeCount);
+ dest.writeBoolean(mHasLegacyNullItems);
dest.writeInt(mIds.length);
dest.writeLongArray(mIds);
@@ -9818,6 +9837,10 @@
return mHasStableIds;
}
+ boolean hasLegacyNullItems() {
+ return mHasLegacyNullItems;
+ }
+
@NonNull
public static final Creator<RemoteCollectionItems> CREATOR =
new Creator<RemoteCollectionItems>() {
@@ -9840,6 +9863,7 @@
private final List<RemoteViews> mViews = new ArrayList<>();
private boolean mHasStableIds;
private int mViewTypeCount;
+ private boolean mHasLegacyNullItems;
/**
* Adds a {@link RemoteViews} to the collection.
@@ -9891,6 +9915,18 @@
return this;
}
+ /**
+ * Set if the {@code RemoteCollectionItems} has null items. This can only happen during
+ * remote adapter conversion, and we will throw an exception when the action is applied.
+ *
+ * @hide
+ */
+ @NonNull
+ public Builder setHasLegacyNullItems(boolean hasLegacyNullItems) {
+ mHasLegacyNullItems = hasLegacyNullItems;
+ return this;
+ }
+
/** Creates the {@link RemoteCollectionItems} defined by this builder. */
@NonNull
public RemoteCollectionItems build() {
@@ -9906,7 +9942,8 @@
mIds.toArray(),
mViews.toArray(new RemoteViews[0]),
mHasStableIds,
- Math.max(mViewTypeCount, 1));
+ Math.max(mViewTypeCount, 1),
+ mHasLegacyNullItems);
}
}
diff --git a/core/java/android/widget/RemoteViewsService.java b/core/java/android/widget/RemoteViewsService.java
index 4045d42..9fb78b5 100644
--- a/core/java/android/widget/RemoteViewsService.java
+++ b/core/java/android/widget/RemoteViewsService.java
@@ -148,6 +148,10 @@
for (int i = 0; i < numOfEntries; i++) {
final long currentItemId = getItemId(i);
final RemoteViews currentView = getViewAt(i);
+ if (currentView == null) {
+ itemsBuilder.setHasLegacyNullItems(true);
+ break;
+ }
currentView.writeToParcel(capSizeTestParcel, 0);
if (capSizeTestParcel.dataSize() > capSize) {
break;
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 99fe0cb..e0e9e0b 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -12052,7 +12052,15 @@
getInterestingRect(mTempRect, line);
mTempRect.offset(mScrollX, mScrollY);
- if (requestRectangleOnScreen(mTempRect)) {
+ boolean requestRectangleOnScreenResult;
+ if (android.view.accessibility.Flags.requestRectangleWithSource()) {
+ requestRectangleOnScreenResult = requestRectangleOnScreen(mTempRect, false,
+ View.RECTANGLE_ON_SCREEN_REQUEST_SOURCE_TEXT_CURSOR);
+ } else {
+ requestRectangleOnScreenResult = requestRectangleOnScreen(mTempRect);
+ }
+
+ if (requestRectangleOnScreenResult) {
changed = true;
}
}
diff --git a/core/java/android/widget/Toast.java b/core/java/android/widget/Toast.java
index ead8887..c2c7f4f 100644
--- a/core/java/android/widget/Toast.java
+++ b/core/java/android/widget/Toast.java
@@ -207,11 +207,7 @@
INotificationManager service = getService();
String pkg = mContext.getOpPackageName();
TN tn = mTN;
- if (Flags.toastNoWeakref()) {
- tn.mNextView = mNextView;
- } else {
- tn.mNextViewWeakRef = new WeakReference<>(mNextView);
- }
+ tn.mNextView = mNextView;
final boolean isUiContext = mContext.isUiContext();
final int displayId = mContext.getDisplayId();
@@ -236,11 +232,9 @@
} catch (RemoteException e) {
// Empty
} finally {
- if (Flags.toastNoWeakref()) {
- if (!wasEnqueued) {
- tn.mNextViewWeakRef = null;
- tn.mNextView = null;
- }
+ if (!wasEnqueued) {
+ tn.mNextViewWeakRef = null;
+ tn.mNextView = null;
}
}
}
@@ -694,22 +688,14 @@
handleHide();
// Don't do this in handleHide() because it is also invoked by
// handleShow()
- if (Flags.toastNoWeakref()) {
- mNextView = null;
- } else {
- mNextViewWeakRef = null;
- }
+ mNextView = null;
break;
}
case CANCEL: {
handleHide();
// Don't do this in handleHide() because it is also invoked by
// handleShow()
- if (Flags.toastNoWeakref()) {
- mNextView = null;
- } else {
- mNextViewWeakRef = null;
- }
+ mNextView = null;
try {
getService().cancelToast(mPackageName, mToken);
} catch (RemoteException e) {
@@ -756,43 +742,23 @@
}
public void handleShow(IBinder windowToken) {
- if (Flags.toastNoWeakref()) {
- if (localLOGV) {
- Log.v(TAG, "HANDLE SHOW: " + this + " mView=" + mView
- + " mNextView=" + mNextView);
- }
- } else {
- if (localLOGV) {
- Log.v(TAG, "HANDLE SHOW: " + this + " mView=" + mView
- + " mNextView=" + mNextViewWeakRef);
- }
+ if (localLOGV) {
+ Log.v(TAG, "HANDLE SHOW: " + this + " mView=" + mView
+ + " mNextView=" + mNextView);
}
// If a cancel/hide is pending - no need to show - at this point
// the window token is already invalid and no need to do any work.
if (mHandler.hasMessages(CANCEL) || mHandler.hasMessages(HIDE)) {
return;
}
- if (Flags.toastNoWeakref()) {
- if (mNextView != null && mView != mNextView) {
- // remove the old view if necessary
- handleHide();
- mView = mNextView;
- if (mView != null) {
- mPresenter.show(mView, mToken, windowToken, mDuration, mGravity, mX, mY,
- mHorizontalMargin, mVerticalMargin,
- new CallbackBinder(getCallbacks(), mHandler));
- }
- }
- } else {
- if (mNextViewWeakRef != null && mView != mNextViewWeakRef.get()) {
- // remove the old view if necessary
- handleHide();
- mView = mNextViewWeakRef.get();
- if (mView != null) {
- mPresenter.show(mView, mToken, windowToken, mDuration, mGravity, mX, mY,
- mHorizontalMargin, mVerticalMargin,
- new CallbackBinder(getCallbacks(), mHandler));
- }
+ if (mNextView != null && mView != mNextView) {
+ // remove the old view if necessary
+ handleHide();
+ mView = mNextView;
+ if (mView != null) {
+ mPresenter.show(mView, mToken, windowToken, mDuration, mGravity, mX, mY,
+ mHorizontalMargin, mVerticalMargin,
+ new CallbackBinder(getCallbacks(), mHandler));
}
}
}
@@ -818,11 +784,7 @@
*/
@VisibleForTesting
public View getNextView() {
- if (Flags.toastNoWeakref()) {
- return mNextView;
- } else {
- return (mNextViewWeakRef != null) ? mNextViewWeakRef.get() : null;
- }
+ return mNextView;
}
}
diff --git a/core/java/android/widget/flags/notification_widget_flags.aconfig b/core/java/android/widget/flags/notification_widget_flags.aconfig
index 732eabe..d902a01 100644
--- a/core/java/android/widget/flags/notification_widget_flags.aconfig
+++ b/core/java/android/widget/flags/notification_widget_flags.aconfig
@@ -29,16 +29,6 @@
}
flag {
- name: "toast_no_weakref"
- namespace: "systemui"
- description: "Do not use WeakReference for custom view Toast"
- bug: "321732224"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
name: "big_picture_style_discard_empty_icon_bitmap_drawables"
namespace: "systemui"
description: "BigPictureStyle: Ignore Icon BitmapDrawables without Bitmaps"
@@ -49,26 +39,6 @@
}
flag {
- name: "messaging_child_request_layout"
- namespace: "systemui"
- description: "MessagingChild always needs to be measured during MessagingLinearLayout onMeasure."
- bug: "324537506"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
- name: "conversation_layout_use_maximum_child_height"
- namespace: "systemui"
- description: "MessagingChild always needs to be measured during MessagingLinearLayout onMeasure."
- bug: "324537506"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
name: "drop_non_existing_messages"
namespace: "systemui"
description: "Drops all group and message entries that no longer exist."
diff --git a/core/java/android/window/BackAnimationAdapter.java b/core/java/android/window/BackAnimationAdapter.java
index 153e153..ccce0b6 100644
--- a/core/java/android/window/BackAnimationAdapter.java
+++ b/core/java/android/window/BackAnimationAdapter.java
@@ -16,6 +16,8 @@
package android.window;
+import static android.view.Display.INVALID_DISPLAY;
+
import android.annotation.NonNull;
import android.os.Parcel;
import android.os.Parcelable;
@@ -28,6 +30,8 @@
* @hide
*/
public class BackAnimationAdapter implements Parcelable {
+ /** The display id where the back gesture is started. */
+ public int mOriginDisplayId = INVALID_DISPLAY;
private final IBackAnimationRunner mRunner;
private int[] mSupportedAnimators;
@@ -39,6 +43,7 @@
mRunner = IBackAnimationRunner.Stub.asInterface(in.readStrongBinder());
mSupportedAnimators = new int[in.readInt()];
in.readIntArray(mSupportedAnimators);
+ mOriginDisplayId = in.readInt();
}
public IBackAnimationRunner getRunner() {
@@ -64,6 +69,7 @@
dest.writeStrongInterface(mRunner);
dest.writeInt(mSupportedAnimators.length);
dest.writeIntArray(mSupportedAnimators);
+ dest.writeInt(mOriginDisplayId);
}
public static final @android.annotation.NonNull Creator<BackAnimationAdapter> CREATOR =
diff --git a/core/java/android/window/BackNavigationInfo.java b/core/java/android/window/BackNavigationInfo.java
index 924bdbf..0d392d4 100644
--- a/core/java/android/window/BackNavigationInfo.java
+++ b/core/java/android/window/BackNavigationInfo.java
@@ -94,6 +94,13 @@
*/
public static final String KEY_TOUCH_GESTURE_TRANSFERRED = "TouchGestureTransferred";
+ /**
+ * Key to access the displayId passed in result bundle that represents on which display
+ * the back gesture has been triggered.
+ * @hide
+ */
+ public static final String KEY_DISPLAY_ID = "DisplayId";
+
/**
* Defines the type of back destinations a back even can lead to. This is used to define the
diff --git a/core/java/android/window/BackTouchTracker.java b/core/java/android/window/BackTouchTracker.java
index bda4d2e..d8a1d38 100644
--- a/core/java/android/window/BackTouchTracker.java
+++ b/core/java/android/window/BackTouchTracker.java
@@ -52,6 +52,7 @@
private int mSwipeEdge;
private boolean mShouldUpdateStartLocation = false;
private TouchTrackerState mState = TouchTrackerState.INITIAL;
+ private boolean mIsInterceptedMotionEvent;
/**
* Updates the tracker with a new motion event.
@@ -117,6 +118,20 @@
return mState == TouchTrackerState.FINISHED;
}
+ /**
+ * Returns whether current app should not receive motion event.
+ */
+ public boolean isInterceptedMotionEvent() {
+ return mIsInterceptedMotionEvent;
+ }
+
+ /**
+ * Marks the app will not receive motion event from current gesture.
+ */
+ public void setMotionEventIntercepted() {
+ mIsInterceptedMotionEvent = true;
+ }
+
/** Sets the start location of the back gesture. */
public void setGestureStartLocation(float touchX, float touchY, int swipeEdge) {
mInitTouchX = touchX;
@@ -154,6 +169,7 @@
mState = TouchTrackerState.INITIAL;
mSwipeEdge = BackEvent.EDGE_LEFT;
mShouldUpdateStartLocation = false;
+ mIsInterceptedMotionEvent = false;
}
/** Creates a start {@link BackMotionEvent}. */
diff --git a/core/java/android/window/ConfigurationChangeSetting.java b/core/java/android/window/ConfigurationChangeSetting.java
index a292fe8..7e325ab 100644
--- a/core/java/android/window/ConfigurationChangeSetting.java
+++ b/core/java/android/window/ConfigurationChangeSetting.java
@@ -30,7 +30,6 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.LocalServices;
-import com.android.window.flags.Flags;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -61,12 +60,6 @@
private final int mSettingType;
private ConfigurationChangeSetting(@SettingType int settingType) {
- if (!Flags.condenseConfigurationChangeForSimpleMode()) {
- throw new IllegalStateException(
- "ConfigurationChangeSetting cannot be instantiated because the "
- + "condenseConfigurationChangeForSimpleMode flag is not enabled. "
- + "Please ensure this flag is enabled.");
- }
mSettingType = settingType;
}
diff --git a/core/java/android/window/DesktopExperienceFlags.java b/core/java/android/window/DesktopExperienceFlags.java
index 8fad125..8ed2d9d 100644
--- a/core/java/android/window/DesktopExperienceFlags.java
+++ b/core/java/android/window/DesktopExperienceFlags.java
@@ -44,9 +44,6 @@
*/
public enum DesktopExperienceFlags {
// go/keep-sorted start
- ACTIVITY_EMBEDDING_SUPPORT_FOR_CONNECTED_DISPLAYS(
- Flags::activityEmbeddingSupportForConnectedDisplays, true,
- Flags.FLAG_ACTIVITY_EMBEDDING_SUPPORT_FOR_CONNECTED_DISPLAYS),
BASE_DENSITY_FOR_EXTERNAL_DISPLAYS(
com.android.server.display.feature.flags.Flags::baseDensityForExternalDisplays, true,
com.android.server.display.feature.flags.Flags.FLAG_BASE_DENSITY_FOR_EXTERNAL_DISPLAYS),
@@ -54,6 +51,9 @@
com.android.input.flags.Flags.FLAG_CONNECTED_DISPLAYS_CURSOR),
DISPLAY_TOPOLOGY(com.android.server.display.feature.flags.Flags::displayTopology, true,
com.android.server.display.feature.flags.Flags.FLAG_DISPLAY_TOPOLOGY),
+ ENABLE_ACTIVITY_EMBEDDING_SUPPORT_FOR_CONNECTED_DISPLAYS(
+ Flags::enableActivityEmbeddingSupportForConnectedDisplays, true,
+ Flags.FLAG_ENABLE_ACTIVITY_EMBEDDING_SUPPORT_FOR_CONNECTED_DISPLAYS),
ENABLE_BUG_FIXES_FOR_SECONDARY_DISPLAY(Flags::enableBugFixesForSecondaryDisplay, true,
Flags.FLAG_ENABLE_BUG_FIXES_FOR_SECONDARY_DISPLAY),
ENABLE_CONNECTED_DISPLAYS_DND(Flags::enableConnectedDisplaysDnd, true,
@@ -64,6 +64,10 @@
android.app.Flags.FLAG_ENABLE_CONNECTED_DISPLAYS_WALLPAPER),
ENABLE_CONNECTED_DISPLAYS_WINDOW_DRAG(Flags::enableConnectedDisplaysWindowDrag, true,
Flags.FLAG_ENABLE_CONNECTED_DISPLAYS_WINDOW_DRAG),
+ ENABLE_DEFAULT_DESK_WITHOUT_WARMUP_MIGRATION(Flags::defaultDeskWithoutWarmupMigration, false,
+ Flags.FLAG_DEFAULT_DESK_WITHOUT_WARMUP_MIGRATION),
+ ENABLE_DESKTOP_APP_LAUNCH_BUGFIX(Flags::enableDesktopAppLaunchBugfix, false,
+ Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_BUGFIX),
ENABLE_DESKTOP_CLOSE_TASK_ANIMATION_IN_DTC_BUGFIX(
Flags::enableDesktopCloseTaskAnimationInDtcBugfix, false,
Flags.FLAG_ENABLE_DESKTOP_CLOSE_TASK_ANIMATION_IN_DTC_BUGFIX),
@@ -75,6 +79,16 @@
ENABLE_DESKTOP_SWIPE_BACK_MINIMIZE_ANIMATION_BUGFIX(
Flags::enableDesktopSwipeBackMinimizeAnimationBugfix, false,
Flags.FLAG_ENABLE_DESKTOP_SWIPE_BACK_MINIMIZE_ANIMATION_BUGFIX),
+ ENABLE_DESKTOP_TAB_TEARING_LAUNCH_ANIMATION(
+ Flags::enableDesktopTabTearingLaunchAnimation, false,
+ Flags.FLAG_ENABLE_DESKTOP_TAB_TEARING_LAUNCH_ANIMATION),
+ ENABLE_DESKTOP_TASKBAR_ON_FREEFORM_DISPLAYS(Flags::enableDesktopTaskbarOnFreeformDisplays,
+ false, Flags.FLAG_ENABLE_DESKTOP_TASKBAR_ON_FREEFORM_DISPLAYS),
+ ENABLE_DESKTOP_TASK_LIMIT_SEPARATE_TRANSITION(
+ Flags::enableDesktopTaskLimitSeparateTransition, false,
+ Flags.FLAG_ENABLE_DESKTOP_TASK_LIMIT_SEPARATE_TRANSITION),
+ ENABLE_DESKTOP_WINDOWING_PIP(Flags::enableDesktopWindowingPip, false,
+ Flags.FLAG_ENABLE_DESKTOP_WINDOWING_PIP),
ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT(
com.android.server.display.feature.flags.Flags::enableDisplayContentModeManagement,
true, FLAG_ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT),
@@ -93,12 +107,17 @@
Flags.FLAG_ENABLE_DYNAMIC_RADIUS_COMPUTATION_BUGFIX),
ENABLE_FREEFORM_BOX_SHADOWS(Flags::enableFreeformBoxShadows, false,
Flags.FLAG_ENABLE_FREEFORM_BOX_SHADOWS),
+ ENABLE_INDEPENDENT_BACK_IN_PROJECTED(Flags::enableIndependentBackInProjected, false,
+ Flags.FLAG_ENABLE_INDEPENDENT_BACK_IN_PROJECTED),
ENABLE_KEYBOARD_SHORTCUTS_TO_SWITCH_DESKS(Flags::keyboardShortcutsToSwitchDesks, false,
Flags.FLAG_KEYBOARD_SHORTCUTS_TO_SWITCH_DESKS),
ENABLE_MOVE_TO_NEXT_DISPLAY_SHORTCUT(Flags::enableMoveToNextDisplayShortcut, true,
Flags.FLAG_ENABLE_MOVE_TO_NEXT_DISPLAY_SHORTCUT),
ENABLE_MULTIDISPLAY_TRACKPAD_BACK_GESTURE(Flags::enableMultidisplayTrackpadBackGesture, false,
Flags.FLAG_ENABLE_MULTIDISPLAY_TRACKPAD_BACK_GESTURE),
+ ENABLE_MULTIPLE_DESKTOPS_ACTIVATION_IN_DESKTOP_FIRST_DISPLAYS(
+ Flags::enableMultipleDesktopsDefaultActivationInDesktopFirstDisplays, false,
+ Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_DEFAULT_ACTIVATION_IN_DESKTOP_FIRST_DISPLAYS),
ENABLE_MULTIPLE_DESKTOPS_BACKEND(Flags::enableMultipleDesktopsBackend, false,
Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND),
ENABLE_MULTIPLE_DESKTOPS_FRONTEND(Flags::enableMultipleDesktopsFrontend, false,
@@ -110,6 +129,8 @@
false, Flags.FLAG_ENABLE_PER_DISPLAY_DESKTOP_WALLPAPER_ACTIVITY),
ENABLE_PROJECTED_DISPLAY_DESKTOP_MODE(Flags::enableProjectedDisplayDesktopMode, false,
Flags.FLAG_ENABLE_PROJECTED_DISPLAY_DESKTOP_MODE),
+ ENABLE_SEE_THROUGH_TASK_FRAGMENTS(Flags::enableSeeThroughTaskFragments,
+ false, Flags.FLAG_ENABLE_SEE_THROUGH_TASK_FRAGMENTS),
ENABLE_TASKBAR_CONNECTED_DISPLAYS(Flags::enableTaskbarConnectedDisplays, true,
Flags.FLAG_ENABLE_TASKBAR_CONNECTED_DISPLAYS),
ENTER_DESKTOP_BY_DEFAULT_ON_FREEFORM_DISPLAYS(Flags::enterDesktopByDefaultOnFreeformDisplays,
@@ -191,12 +212,7 @@
private static boolean checkIfFlagShouldBeOverridden(@Nullable String flagName,
boolean defaultValue) {
- if (!Flags.showDesktopWindowingDevOption()) return false;
- if (flagName == null || flagName.isEmpty()) return defaultValue;
- int lastDot = flagName.lastIndexOf('.');
- String baseName = lastDot >= 0 ? flagName.substring(lastDot + 1) : flagName;
- return SystemProperties.getBoolean(SYSTEM_PROPERTY_OVERRIDE_PREFIX + baseName,
- defaultValue);
+ return defaultValue;
}
private static boolean getToggleOverride() {
diff --git a/core/java/android/window/DesktopModeFlags.java b/core/java/android/window/DesktopModeFlags.java
index 680a1d4..71622fb 100644
--- a/core/java/android/window/DesktopModeFlags.java
+++ b/core/java/android/window/DesktopModeFlags.java
@@ -35,8 +35,8 @@
* windowing features which are aiming for developer preview before their release. It allows
* developer option to override the default behavior of these flags.
*
- * <p>NOTE: Flags should only be added to this enum when they have received Product and UX
- * alignment that the feature is ready for developer preview, otherwise just do a flag check.
+ * <p> Note: No new flag should be added to this class. For new features, please add the flags to
+ * {@link DesktopExperienceFlags}.
*
* @hide
*/
@@ -96,7 +96,6 @@
ENABLE_DESKTOP_WINDOWING_MULTI_INSTANCE_FEATURES(
Flags::enableDesktopWindowingMultiInstanceFeatures, true),
ENABLE_DESKTOP_WINDOWING_PERSISTENCE(Flags::enableDesktopWindowingPersistence, true),
- ENABLE_DESKTOP_WINDOWING_PIP(Flags::enableDesktopWindowingPip, false),
ENABLE_DESKTOP_WINDOWING_QUICK_SWITCH(Flags::enableDesktopWindowingQuickSwitch, true),
ENABLE_DESKTOP_WINDOWING_SCVH_CACHE(Flags::enableDesktopWindowingScvhCacheBugFix, true),
ENABLE_DESKTOP_WINDOWING_SIZE_CONSTRAINTS(Flags::enableDesktopWindowingSizeConstraints, true),
diff --git a/core/java/android/window/ImeOnBackInvokedDispatcher.java b/core/java/android/window/ImeOnBackInvokedDispatcher.java
index d478108..b18937be 100644
--- a/core/java/android/window/ImeOnBackInvokedDispatcher.java
+++ b/core/java/android/window/ImeOnBackInvokedDispatcher.java
@@ -29,6 +29,7 @@
import android.os.ResultReceiver;
import android.util.Log;
import android.util.Pair;
+import android.util.Printer;
import android.view.ViewRootImpl;
import com.android.internal.annotations.VisibleForTesting;
@@ -181,12 +182,26 @@
} else {
imeCallback = new ImeOnBackInvokedCallback(iCallback, callbackId, priority);
}
+ if (unregisterCallback(callbackId, receivingDispatcher)) {
+ Log.w(TAG, "Received IME callback that's already registered. Unregistering and "
+ + "reregistering. (callbackId: " + callbackId
+ + " current callbacks: " + mImeCallbacks.size() + ")");
+ }
mImeCallbacks.add(imeCallback);
receivingDispatcher.registerOnBackInvokedCallbackUnchecked(imeCallback, priority);
}
private void unregisterReceivedCallback(
- int callbackId, OnBackInvokedDispatcher receivingDispatcher) {
+ int callbackId, @NonNull OnBackInvokedDispatcher receivingDispatcher) {
+ if (!unregisterCallback(callbackId, receivingDispatcher)) {
+ Log.e(TAG, "Ime callback not found. Ignoring unregisterReceivedCallback. "
+ + "callbackId: " + callbackId
+ + " remaining callbacks: " + mImeCallbacks.size());
+ }
+ }
+
+ private boolean unregisterCallback(int callbackId,
+ @NonNull OnBackInvokedDispatcher receivingDispatcher) {
ImeOnBackInvokedCallback callback = null;
for (ImeOnBackInvokedCallback imeCallback : mImeCallbacks) {
if (imeCallback.getId() == callbackId) {
@@ -195,12 +210,11 @@
}
}
if (callback == null) {
- Log.e(TAG, "Ime callback not found. Ignoring unregisterReceivedCallback. "
- + "callbackId: " + callbackId);
- return;
+ return false;
}
receivingDispatcher.unregisterOnBackInvokedCallback(callback);
mImeCallbacks.remove(callback);
+ return true;
}
/**
@@ -243,6 +257,23 @@
mQueuedReceive.clear();
}
+ /**
+ * Dumps the registered IME callbacks.
+ *
+ * @param prefix prefix to be prepended to each line
+ * @param p printer to write the dump to
+ */
+ public void dump(@NonNull Printer p, @NonNull String prefix) {
+ if (mImeCallbacks.isEmpty()) {
+ p.println(prefix + TAG + " mImeCallbacks: []");
+ } else {
+ p.println(prefix + TAG + " mImeCallbacks:");
+ for (ImeOnBackInvokedCallback callback : mImeCallbacks) {
+ p.println(prefix + " " + callback);
+ }
+ }
+ }
+
@VisibleForTesting(visibility = PACKAGE)
public static class ImeOnBackInvokedCallback implements OnBackAnimationCallback {
@NonNull
@@ -339,7 +370,7 @@
* another {@link ViewRootImpl} on focus change.
*
* @param previous the previously focused {@link ViewRootImpl}.
- * @param current the currently focused {@link ViewRootImpl}.
+ * @param current the currently focused {@link ViewRootImpl}.
*/
public void switchRootView(ViewRootImpl previous, ViewRootImpl current) {
for (ImeOnBackInvokedCallback imeCallback : mImeCallbacks) {
diff --git a/core/java/android/window/OWNERS b/core/java/android/window/OWNERS
index 82d3724..d3bb0db 100644
--- a/core/java/android/window/OWNERS
+++ b/core/java/android/window/OWNERS
@@ -2,5 +2,5 @@
include /services/core/java/com/android/server/wm/OWNERS
-per-file DesktopModeFlags.java = file:/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/OWNERS
-per-file DesktopExperienceFlags.java = file:/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/OWNERS
+per-file DesktopModeFlags.java = pbdr@google.com, mariiasand@google.com, pragyabajoria@google.com
+per-file DesktopExperienceFlags.java = pbdr@google.com, mariiasand@google.com, pragyabajoria@google.com, jorgegil@google.com
diff --git a/core/java/android/window/WindowContext.java b/core/java/android/window/WindowContext.java
index 142271d..830d30c 100644
--- a/core/java/android/window/WindowContext.java
+++ b/core/java/android/window/WindowContext.java
@@ -42,6 +42,8 @@
import java.lang.ref.Reference;
+import sun.misc.Cleaner;
+
/**
* {@link WindowContext} is a context for non-activity windows such as
* {@link android.view.WindowManager.LayoutParams#TYPE_APPLICATION_OVERLAY} windows or system
@@ -92,9 +94,9 @@
mType = type;
mOptions = options;
mWindowManager = createWindowContextWindowManager(this);
- WindowTokenClient token = (WindowTokenClient) getWindowContextToken();
- mController = new WindowContextController(requireNonNull(token));
-
+ WindowTokenClient token = (WindowTokenClient) requireNonNull(getWindowContextToken());
+ mController = new WindowContextController(token);
+ registerCleaner(this);
Reference.reachabilityFence(this);
}
@@ -134,8 +136,13 @@
@Override
protected void finalize() throws Throwable {
- release();
- super.finalize();
+ try {
+ if (!Flags.cleanUpWindowContextWithCleaner()) {
+ release();
+ }
+ } finally {
+ super.finalize();
+ }
}
/** Used for test to invoke because we can't invoke finalize directly. */
@@ -261,7 +268,7 @@
/**
* The {@link WindowWrapper} constructor.
*
- * @param context the associated {@link WindowContext}`
+ * @param context the associated {@link WindowContext}`
* @param decorView the view to be wrapped as {@link Window}'s decor view.
*/
WindowWrapper(@NonNull @UiContext Context context, @NonNull View decorView) {
@@ -287,4 +294,47 @@
return mDecorView;
}
}
+
+ /**
+ * Registers a {@link WindowContext} or a {@link SystemUiContext} with a cleaner.
+ *
+ * @throws IllegalArgumentException if the context is not a {@link WindowContext} or
+ * {@link SystemUiContext}.
+ */
+ public static void registerCleaner(@NonNull Context context) {
+ if (!Flags.cleanUpWindowContextWithCleaner()) {
+ return;
+ }
+
+ if (!(context instanceof WindowContext) && !(context instanceof SystemUiContext)) {
+ throw new IllegalArgumentException("The context must be either WindowContext or"
+ + " SystemUiContext.");
+ }
+ final ContextWrapper wrapper = (ContextWrapper) context;
+ Cleaner.create(context, new WindowContextCleaner(wrapper));
+ }
+
+ /**
+ * A {@link WindowContext} cleaner that applies when the {@link WindowContext} is going to be
+ * garbage-collected.
+ */
+ private static class WindowContextCleaner implements Runnable {
+
+ @NonNull
+ private final Context mBaseContext;
+
+ WindowContextCleaner(@NonNull ContextWrapper wrapper) {
+ // Cache the base Context to prevent hold the reference of WindowContext. The cleaner
+ // will work only if all strong references of WindowContext are gone.
+ mBaseContext = requireNonNull(wrapper.getBaseContext());
+ }
+
+ @Override
+ public void run() {
+ final WindowTokenClient token =
+ (WindowTokenClient) requireNonNull(mBaseContext.getWindowContextToken());
+ WindowTokenClientController.getInstance().detachIfNeeded(token);
+ mBaseContext.destroy();
+ }
+ }
}
diff --git a/core/java/android/window/WindowOnBackInvokedDispatcher.java b/core/java/android/window/WindowOnBackInvokedDispatcher.java
index 2911b0a..85f87da 100644
--- a/core/java/android/window/WindowOnBackInvokedDispatcher.java
+++ b/core/java/android/window/WindowOnBackInvokedDispatcher.java
@@ -314,6 +314,24 @@
}
}
+ /**
+ * Returns whether current app should not receive motion event.
+ */
+ public boolean isInterceptedMotionEvent() {
+ synchronized (mLock) {
+ return mTouchTracker.isInterceptedMotionEvent();
+ }
+ }
+
+ /**
+ * Marks the app will not receive motion event from current gesture.
+ */
+ public void setMotionEventIntercepted() {
+ synchronized (mLock) {
+ mTouchTracker.setMotionEventIntercepted();
+ }
+ }
+
private void sendCancelledIfInProgress(@NonNull OnBackInvokedCallback callback) {
boolean isInProgress = mProgressAnimator.isBackAnimationInProgress();
if (isInProgress && callback instanceof OnBackAnimationCallback) {
@@ -322,9 +340,6 @@
if (DEBUG) {
Log.d(TAG, "sendCancelIfRunning: callback canceled");
}
- } else {
- Log.w(TAG, "sendCancelIfRunning: isInProgress=" + isInProgress
- + " callback=" + callback);
}
}
diff --git a/core/java/android/window/flags/accessibility.aconfig b/core/java/android/window/flags/accessibility.aconfig
index 733e3db..95e63ab 100644
--- a/core/java/android/window/flags/accessibility.aconfig
+++ b/core/java/android/window/flags/accessibility.aconfig
@@ -2,13 +2,6 @@
container: "system"
flag {
- name: "do_not_check_intersection_when_non_magnifiable_window_transitions"
- namespace: "accessibility"
- description: "The flag controls whether the intersection check for non-magnifiable windows is needed when onWindowTransition,"
- bug: "312624253"
-}
-
-flag {
name: "always_draw_magnification_fullscreen_border"
namespace: "accessibility"
description: "Always draw fullscreen orange border in fullscreen magnification"
@@ -17,23 +10,3 @@
purpose: PURPOSE_BUGFIX
}
}
-
-flag {
- name: "use_window_original_touchable_region_when_magnification_recompute_bounds"
- namespace: "accessibility"
- description: "The flag controls whether to use the window original touchable regions in accessibilityController recomputeBounds"
- bug: "323366243"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
- name: "delay_notification_to_magnification_when_recents_window_to_front_transition"
- namespace: "accessibility"
- description: "The flag controls whether the delaying of notification for recents window to-front transition is needed. In accessibilityController other callbacks will decide sending or canceling the delayed notification."
- bug: "324949652"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
diff --git a/core/java/android/window/flags/lse_desktop_experience.aconfig b/core/java/android/window/flags/lse_desktop_experience.aconfig
index 38b9737..7c01732 100644
--- a/core/java/android/window/flags/lse_desktop_experience.aconfig
+++ b/core/java/android/window/flags/lse_desktop_experience.aconfig
@@ -631,6 +631,16 @@
}
flag {
+ name: "enable_see_through_task_fragments"
+ namespace: "lse_desktop_experience"
+ description: "Let see-through TaskFragments keep their siblings (in lower Z order) resumed"
+ bug: "407602007"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "enable_multiple_desktops_backend"
namespace: "lse_desktop_experience"
description: "Enable multiple desktop sessions for desktop windowing (backend)."
@@ -638,6 +648,13 @@
}
flag {
+ name: "enable_multiple_desktops_default_activation_in_desktop_first_displays"
+ namespace: "lse_desktop_experience"
+ description: "Activate a default desk in desktop-first displays, with multiple desks enabled"
+ bug: "408521020"
+}
+
+flag {
name: "keyboard_shortcuts_to_switch_desks"
namespace: "lse_desktop_experience"
description: "Enable switching the active desk with keyboard shortcuts"
@@ -645,6 +662,16 @@
}
flag {
+ name: "default_desk_without_warmup_migration"
+ namespace: "lse_desktop_experience"
+ description: "Moves desk creation to coroutines to guarantee non-null default desk"
+ bug: "406890311"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "enable_connected_displays_dnd"
namespace: "lse_desktop_experience"
description: "Enable drag-and-drop between connected displays."
@@ -1027,6 +1054,13 @@
}
flag {
+ name: "enable_independent_back_in_projected"
+ namespace: "lse_desktop_experience"
+ description: "Enables the system to have independent back per-display in projected mode."
+ bug: "401530192"
+}
+
+flag {
name: "enable_desktop_ime_bugfix"
namespace: "lse_desktop_experience"
description: "Enables bugfix to handle IME interactions in desktop windowing."
@@ -1106,3 +1140,23 @@
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "enable_desktop_tab_tearing_launch_animation"
+ namespace: "lse_desktop_experience"
+ description: "Enables the animation that occurs when a new window is opened via tab tearing."
+ bug: "376389593"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
+ name: "enable_desktop_task_limit_separate_transition"
+ namespace: "lse_desktop_experience"
+ description: "Run task limit minimize transition separate from app launch transitions."
+ bug: "356337427"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/core/java/android/window/flags/responsible_apis.aconfig b/core/java/android/window/flags/responsible_apis.aconfig
index 017dc68..306fe69 100644
--- a/core/java/android/window/flags/responsible_apis.aconfig
+++ b/core/java/android/window/flags/responsible_apis.aconfig
@@ -23,13 +23,6 @@
}
flag {
- name: "bal_improve_real_caller_visibility_check"
- namespace: "responsible_apis"
- description: "Prevent a task to restart based on a visible window during task switch."
- bug: "171459802"
-}
-
-flag {
name: "bal_respect_app_switch_state_when_check_bound_by_foreground_uid"
namespace: "responsible_apis"
description: "Prevent BAL based on it is bound by foreground Uid but the app switch is stopped."
@@ -37,13 +30,6 @@
}
flag {
- name: "bal_improved_metrics"
- namespace: "responsible_apis"
- description: "Improved metrics."
- bug: "339245692"
-}
-
-flag {
name: "bal_additional_start_modes"
namespace: "responsible_apis"
description: "Introduce additional start modes."
diff --git a/core/java/android/window/flags/window_surfaces.aconfig b/core/java/android/window/flags/window_surfaces.aconfig
index 6c26555..9013602 100644
--- a/core/java/android/window/flags/window_surfaces.aconfig
+++ b/core/java/android/window/flags/window_surfaces.aconfig
@@ -136,13 +136,12 @@
}
flag {
- name: "update_host_input_transfer_token"
+ name: "scvh_surface_control_lifetime_fix"
namespace: "window_surfaces"
- description: "Update host InpuTransferToken on view attach"
+ description: "Fix lifetime issue with SurfaceControl's created by SCVH"
is_fixed_read_only: true
- is_exported: true
- bug: "392965431"
+ bug: "407989942"
metadata {
purpose: PURPOSE_BUGFIX
}
-}
\ No newline at end of file
+}
diff --git a/core/java/android/window/flags/windowing_frontend.aconfig b/core/java/android/window/flags/windowing_frontend.aconfig
index 6da50c1..e7fa33c 100644
--- a/core/java/android/window/flags/windowing_frontend.aconfig
+++ b/core/java/android/window/flags/windowing_frontend.aconfig
@@ -41,6 +41,17 @@
}
flag {
+ name: "respect_hierarchy_surface_visibility"
+ namespace: "windowing_frontend"
+ description: "Ensure consistent surface visibility with window hierarchy"
+ bug: "383241933"
+ is_fixed_read_only: true
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "use_cached_insets_for_display_switch"
namespace: "windowing_frontend"
description: "Reduce intermediate insets changes for display switch"
@@ -84,14 +95,6 @@
}
flag {
- name: "keyguard_going_away_timeout"
- namespace: "windowing_frontend"
- description: "Allow a maximum of 10 seconds with keyguardGoingAway=true before force-resetting"
- bug: "343598832"
- is_fixed_read_only: true
-}
-
-flag {
name: "close_to_square_config_includes_status_bar"
namespace: "windowing_frontend"
description: "On close to square display, when necessary, configuration includes status bar"
@@ -99,17 +102,6 @@
}
flag {
- name: "reduce_keyguard_transitions"
- namespace: "windowing_frontend"
- description: "Avoid setting keyguard transitions ready unless there are no other changes"
- bug: "354647472"
- is_fixed_read_only: true
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
name: "transit_tracker_plumbing"
namespace: "windowing_frontend"
description: "Plumb and collect on transition tracking object instead of singleton"
@@ -179,13 +171,6 @@
}
flag {
- name: "process_priority_policy_for_multi_window_mode"
- namespace: "windowing_frontend"
- description: "Use higher priority for top-like processes"
- bug: "200769420"
-}
-
-flag {
name: "universal_resizable_by_default"
namespace: "windowing_frontend"
description: "The orientation, aspect ratio, resizability of activity will follow system behavior by default"
@@ -210,13 +195,6 @@
}
flag {
- name: "respect_non_top_visible_fixed_orientation"
- namespace: "windowing_frontend"
- description: "If top activity is not opaque, respect the fixed orientation of activity behind it"
- bug: "283514860"
-}
-
-flag {
name: "insets_decoupled_configuration"
namespace: "windowing_frontend"
description: "Configuration decoupled from insets"
@@ -361,17 +339,6 @@
}
flag {
- name: "record_task_snapshots_before_shutdown"
- namespace: "windowing_frontend"
- description: "Record task snapshots before shutdown"
- bug: "376821232"
- is_fixed_read_only: true
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
name: "predictive_back_three_button_nav"
namespace: "systemui"
description: "Enable Predictive Back Animation for 3-button-nav"
@@ -398,17 +365,6 @@
}
flag {
- name: "remove_defer_hiding_client"
- namespace: "windowing_frontend"
- description: "Remove mDeferHidingClient since everything is in shell-transition."
- is_fixed_read_only: true
- bug: "382485959"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
name: "relative_insets"
namespace: "windowing_frontend"
description: "Support insets definition and calculation relative to task bounds."
@@ -417,17 +373,6 @@
}
flag {
- name: "exclude_drawing_app_theme_snapshot_from_lock"
- namespace: "windowing_frontend"
- description: "Do not hold wm lock when drawing app theme snapshot."
- is_fixed_read_only: true
- bug: "373502791"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
name: "reduce_changed_exclusion_rects_msgs"
namespace: "windowing_frontend"
description: "Don't send MSG_SYSTEM_GESTURE_EXCLUSION_CHANGED when there is no change"
@@ -439,28 +384,6 @@
}
flag {
- name: "keep_app_window_hide_while_locked"
- namespace: "windowing_frontend"
- description: "Do not let app window visible while device is locked"
- is_fixed_read_only: true
- bug: "378088391"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
- name: "use_rt_frame_callback_for_splash_screen_transfer"
- namespace: "windowing_frontend"
- description: "report SplashscreenView shown after RtFrame commit"
- is_fixed_read_only: true
- bug: "387231234"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
name: "port_window_size_animation"
namespace: "windowing_frontend"
description: "Port window-resize animation from legacy to shell"
@@ -475,16 +398,6 @@
}
flag {
- name: "check_disabled_snapshots_in_task_persister"
- namespace: "windowing_frontend"
- description: "Check for TaskSnapshots disabling in TaskSnapshotPersister."
- bug: "387915176"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
name: "clear_system_vibrator"
namespace: "windowing_frontend"
description: "Clears the system vibrator before attaching new window, to avoid leaks."
@@ -565,6 +478,17 @@
}
flag {
+ name: "splash_screen_view_sync_transaction"
+ namespace: "windowing_frontend"
+ description: "Fixes flickering when transfer splash screen view to client."
+ bug: "402644135"
+ is_fixed_read_only: true
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
namespace: "wear_frameworks"
name: "dispatch_first_keyguard_locked_state"
description: "Ensures the first keyguard locked state is dispatched to listeners reigstered through KeyguardManager.addKeyguardLockedStateListener"
@@ -574,3 +498,14 @@
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "intercept_motion_from_move_to_cancel"
+ namespace: "windowing_frontend"
+ description: "Ensure that the client receives ACTION_CANCEL when the back gesture is intercepted."
+ bug: "404173501"
+ is_fixed_read_only: true
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
\ No newline at end of file
diff --git a/core/java/android/window/flags/windowing_sdk.aconfig b/core/java/android/window/flags/windowing_sdk.aconfig
index dc58d1b..1869884 100644
--- a/core/java/android/window/flags/windowing_sdk.aconfig
+++ b/core/java/android/window/flags/windowing_sdk.aconfig
@@ -48,13 +48,6 @@
flag {
namespace: "windowing_sdk"
- name: "activity_embedding_animation_customization_flag"
- description: "Whether the animation customization feature for AE is enabled"
- bug: "293658614"
-}
-
-flag {
- namespace: "windowing_sdk"
name: "rear_display_disable_force_desktop_system_decorations"
description: "Block system decorations from being added to a rear display when desktop mode is forced"
bug: "346103150"
@@ -115,16 +108,6 @@
}
flag {
- namespace: "windowing_sdk"
- name: "condense_configuration_change_for_simple_mode"
- description: "Condense configuration change for simple mode"
- bug: "356738240"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
namespace: "car_framework"
name: "safe_region_letterboxing"
description: "Enables letterboxing for a safe region"
@@ -133,17 +116,6 @@
flag {
namespace: "windowing_sdk"
- name: "fix_layout_existing_task"
- description: "Layout the existing task to ensure the bounds are updated."
- bug: "390291971"
- is_fixed_read_only: true
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
- namespace: "windowing_sdk"
name: "cleanup_dispatch_pending_transactions_remote_exception"
description: "Refactor to cleanup for RemoteException from dispatchPendingTransactions"
bug: "323801078"
@@ -232,3 +204,14 @@
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ namespace: "windowing_sdk"
+ name: "clean_up_window_context_with_cleaner"
+ description: "Using Cleaner to clean up WindowContext instead of overriding finalize"
+ bug: "339727951"
+ is_fixed_read_only: true
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/core/java/com/android/internal/accessibility/common/ShortcutConstants.java b/core/java/com/android/internal/accessibility/common/ShortcutConstants.java
index 4a49bb6..8140102 100644
--- a/core/java/com/android/internal/accessibility/common/ShortcutConstants.java
+++ b/core/java/com/android/internal/accessibility/common/ShortcutConstants.java
@@ -31,6 +31,7 @@
import android.annotation.IntDef;
import android.content.ComponentName;
+import android.provider.Settings;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -109,6 +110,26 @@
// LINT.ThenChange(:shortcut_type_intdef)
};
+ /**
+ * A list of shortcut settings key accessibility features share
+ */
+ public static final String[] GENERAL_SHORTCUT_SETTINGS = {
+ Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS,
+ Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE,
+ Settings.Secure.ACCESSIBILITY_QS_TARGETS,
+ Settings.Secure.ACCESSIBILITY_GESTURE_TARGETS
+ };
+
+ /**
+ * A list of shortcut settings key magnification uses
+ */
+ public static final String[] MAGNIFICATION_SHORTCUT_SETTINGS = {
+ Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS,
+ Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE,
+ Settings.Secure.ACCESSIBILITY_GESTURE_TARGETS,
+ Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED,
+ Settings.Secure.ACCESSIBILITY_MAGNIFICATION_TWO_FINGER_TRIPLE_TAP_ENABLED
+ };
/**
* Annotation for the different accessibility fragment type.
diff --git a/core/java/com/android/internal/content/PackageMonitor.java b/core/java/com/android/internal/content/PackageMonitor.java
index 4663d62..8218bab 100644
--- a/core/java/com/android/internal/content/PackageMonitor.java
+++ b/core/java/com/android/internal/content/PackageMonitor.java
@@ -22,7 +22,6 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.pm.Flags;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
@@ -71,8 +70,7 @@
@UnsupportedAppUsage
public PackageMonitor() {
- // If the feature flag is enabled, set mSupportsPackageRestartQuery to false by default
- this(!Flags.packageRestartQueryDisabledByDefault());
+ this(/* supportsPackageRestartQuery */ false);
}
/**
diff --git a/core/java/com/android/internal/foldables/FoldGracePeriodProvider.java b/core/java/com/android/internal/foldables/FoldGracePeriodProvider.java
deleted file mode 100644
index 53164f3..0000000
--- a/core/java/com/android/internal/foldables/FoldGracePeriodProvider.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.foldables;
-
-import android.os.Build;
-import android.sysprop.FoldLockBehaviorProperties;
-import android.util.Slog;
-
-import com.android.internal.foldables.flags.Flags;
-
-import java.util.function.Supplier;
-
-/**
- * Wrapper class to access {@link FoldLockBehaviorProperties}.
- */
-public class FoldGracePeriodProvider {
-
- private static final String TAG = "FoldGracePeriodProvider";
- private final Supplier<Boolean> mFoldGracePeriodEnabled = Flags::foldGracePeriodEnabled;
-
- /**
- * Whether the fold grace period feature is enabled.
- */
- public boolean isEnabled() {
- if ((Build.IS_ENG || Build.IS_USERDEBUG)
- && FoldLockBehaviorProperties.fold_grace_period_enabled().orElse(false)) {
- return true;
- }
- try {
- return mFoldGracePeriodEnabled.get();
- } catch (Throwable ex) {
- Slog.i(TAG,
- "Flags not ready yet. Return false for "
- + Flags.FLAG_FOLD_GRACE_PERIOD_ENABLED,
- ex);
- return false;
- }
- }
-}
diff --git a/core/java/com/android/internal/foldables/fold_lock_setting_flags.aconfig b/core/java/com/android/internal/foldables/fold_lock_setting_flags.aconfig
index 03b6a5b..c00a2b5 100644
--- a/core/java/com/android/internal/foldables/fold_lock_setting_flags.aconfig
+++ b/core/java/com/android/internal/foldables/fold_lock_setting_flags.aconfig
@@ -8,11 +8,3 @@
bug: "274447767"
is_fixed_read_only: true
}
-
-flag {
- name: "fold_grace_period_enabled"
- namespace: "display_manager"
- description: "Feature flag for Folding Grace Period"
- bug: "308417021"
- is_fixed_read_only: true
-}
diff --git a/core/java/com/android/internal/jank/Cuj.java b/core/java/com/android/internal/jank/Cuj.java
index c25f6b1..a164114 100644
--- a/core/java/com/android/internal/jank/Cuj.java
+++ b/core/java/com/android/internal/jank/Cuj.java
@@ -332,8 +332,18 @@
*/
public static final int CUJ_STATUS_BAR_APP_RETURN_TO_CALL_CHIP = 130;
+ /**
+ * Track the animation of the smart action button or smart reply button of the notification.
+ * The button in the notification features a gradient animation along their outlines when the
+ * button is displayed.
+ *
+ * <p>Tracking starts when the button outline starts to animate.
+ * Tracking ends when the button outline animation is fully settled.</p>
+ */
+ public static final int CUJ_NOTIFICATIONS_ANIMATED_ACTION = 131;
+
// When adding a CUJ, update this and make sure to also update CUJ_TO_STATSD_INTERACTION_TYPE.
- @VisibleForTesting static final int LAST_CUJ = CUJ_STATUS_BAR_APP_RETURN_TO_CALL_CHIP;
+ @VisibleForTesting static final int LAST_CUJ = CUJ_NOTIFICATIONS_ANIMATED_ACTION;
/** @hide */
@IntDef({
@@ -455,7 +465,8 @@
CUJ_LAUNCHER_WORK_UTILITY_VIEW_SHRINK,
CUJ_DEFAULT_TASK_TO_TASK_ANIMATION,
CUJ_DESKTOP_MODE_MOVE_WINDOW_TO_DISPLAY,
- CUJ_STATUS_BAR_APP_RETURN_TO_CALL_CHIP
+ CUJ_STATUS_BAR_APP_RETURN_TO_CALL_CHIP,
+ CUJ_NOTIFICATIONS_ANIMATED_ACTION
})
@Retention(RetentionPolicy.SOURCE)
public @interface CujType {}
@@ -588,6 +599,7 @@
CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_DEFAULT_TASK_TO_TASK_ANIMATION] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__DEFAULT_TASK_TO_TASK_ANIMATION;
CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_DESKTOP_MODE_MOVE_WINDOW_TO_DISPLAY] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__DESKTOP_MODE_MOVE_WINDOW_TO_DISPLAY;
CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_STATUS_BAR_APP_RETURN_TO_CALL_CHIP] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__STATUS_BAR_APP_RETURN_TO_CALL_CHIP;
+ CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_NOTIFICATIONS_ANIMATED_ACTION] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__NOTIFICATIONS_ANIMATED_ACTION;
}
private Cuj() {
@@ -844,6 +856,8 @@
return "DESKTOP_MODE_MOVE_WINDOW_TO_DISPLAY";
case CUJ_STATUS_BAR_APP_RETURN_TO_CALL_CHIP:
return "STATUS_BAR_APP_RETURN_TO_CALL_CHIP";
+ case CUJ_NOTIFICATIONS_ANIMATED_ACTION:
+ return "NOTIFICATIONS_ANIMATED_ACTION";
}
return "UNKNOWN";
}
diff --git a/core/java/com/android/internal/os/BackgroundThread.java b/core/java/com/android/internal/os/BackgroundThread.java
index 79996e5..55de55d 100644
--- a/core/java/com/android/internal/os/BackgroundThread.java
+++ b/core/java/com/android/internal/os/BackgroundThread.java
@@ -40,10 +40,17 @@
super("android.bg", android.os.Process.THREAD_PRIORITY_BACKGROUND);
}
- private static void ensureThreadLocked() {
+ private static void ensureThreadStartedLocked() {
if (sInstance == null) {
sInstance = new BackgroundThread();
sInstance.start();
+ }
+ }
+
+ private static void ensureThreadReadyLocked() {
+ ensureThreadStartedLocked();
+ if (sHandler == null) {
+ // This will block until the looper is initialized on the background thread.
final Looper looper = sInstance.getLooper();
looper.setTraceTag(Trace.TRACE_TAG_SYSTEM_SERVER);
looper.setSlowLogThresholdMs(
@@ -54,10 +61,19 @@
}
}
+ /**
+ * Starts the thread if needed, but doesn't block on thread initialization or readiness.
+ */
+ public static void startIfNeeded() {
+ synchronized (BackgroundThread.class) {
+ ensureThreadStartedLocked();
+ }
+ }
+
@NonNull
public static BackgroundThread get() {
synchronized (BackgroundThread.class) {
- ensureThreadLocked();
+ ensureThreadReadyLocked();
return sInstance;
}
}
@@ -65,7 +81,7 @@
@NonNull
public static Handler getHandler() {
synchronized (BackgroundThread.class) {
- ensureThreadLocked();
+ ensureThreadReadyLocked();
return sHandler;
}
}
@@ -73,7 +89,7 @@
@NonNull
public static Executor getExecutor() {
synchronized (BackgroundThread.class) {
- ensureThreadLocked();
+ ensureThreadReadyLocked();
return sHandlerExecutor;
}
}
diff --git a/core/java/com/android/internal/os/TimeoutRecord.java b/core/java/com/android/internal/os/TimeoutRecord.java
index 9306956..5f6e82f 100644
--- a/core/java/com/android/internal/os/TimeoutRecord.java
+++ b/core/java/com/android/internal/os/TimeoutRecord.java
@@ -202,17 +202,25 @@
return TimeoutRecord.endingNow(TimeoutKind.APP_START, reason);
}
- /** Record the ID of the timer that expired. */
+ /**
+ * Record the timer that expired. The argument is an opaque handle. If an expired timer had
+ * already been set, close it now.
+ */
@NonNull
public TimeoutRecord setExpiredTimer(@Nullable AutoCloseable handle) {
+ // Close the current value of mExpiredTimer, if it not null.
+ closeExpiredTimer();
mExpiredTimer = handle;
return this;
}
- /** Close the ExpiredTimer, if one is present. */
+ /** Close the ExpiredTimer, if one is present. getExpiredTimer will return null after this. */
public void closeExpiredTimer() {
try {
- if (mExpiredTimer != null) mExpiredTimer.close();
+ if (mExpiredTimer != null) {
+ mExpiredTimer.close();
+ mExpiredTimer = null;
+ }
} catch (Exception e) {
// mExpiredTimer.close() should never, ever throw. If it does, just rethrow as a
// RuntimeException.
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 38dc198..c6cecce 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -421,13 +421,11 @@
null /*declaringPackage*/, null /*dependentPackages*/, null /*dependencies*/,
false /*isNative*/));
- if (Flags.enableApacheHttpLegacyPreload()) {
- libs.add(new SharedLibraryInfo(
- "/system/framework/org.apache.http.legacy.jar", null /*packageName*/,
- null /*codePaths*/, null /*name*/, 0 /*version*/,
- SharedLibraryInfo.TYPE_BUILTIN, null /*declaringPackage*/,
- null /*dependentPackages*/, null /*dependencies*/, false /*isNative*/));
- }
+ libs.add(new SharedLibraryInfo(
+ "/system/framework/org.apache.http.legacy.jar", null /*packageName*/,
+ null /*codePaths*/, null /*name*/, 0 /*version*/,
+ SharedLibraryInfo.TYPE_BUILTIN, null /*declaringPackage*/,
+ null /*dependentPackages*/, null /*dependencies*/, false /*isNative*/));
if (Flags.enableMediaAndLocationPreload()) {
// As these libraries are technically optional and not necessarily inherited from
diff --git a/core/java/com/android/internal/os/flags.aconfig b/core/java/com/android/internal/os/flags.aconfig
index 32cde50..62e96f544 100644
--- a/core/java/com/android/internal/os/flags.aconfig
+++ b/core/java/com/android/internal/os/flags.aconfig
@@ -44,15 +44,6 @@
}
flag {
- name: "enable_apache_http_legacy_preload"
- namespace: "system_performance"
- description: "Enables zygote preload of non-BCP org.apache.http.legacy.jar library."
- # Fixed read-only is required as the flag is read during zygote init.
- is_fixed_read_only: true
- bug: "241474956"
-}
-
-flag {
name: "enable_media_and_location_preload"
namespace: "system_performance"
description: "Enables zygote preload of non-BCP media and location libraries."
@@ -76,12 +67,4 @@
description: "If the debug store is enabled."
bug: "314735374"
is_fixed_read_only: true
-}
-
-flag {
- name: "application_shared_memory_enabled"
- namespace: "system_performance"
- description: "Whether ApplicationSharedMemory is enabled."
- bug: "365575551"
- is_fixed_read_only: true
}
\ No newline at end of file
diff --git a/core/java/com/android/internal/pm/pkg/parsing/ParsingPackageUtils.java b/core/java/com/android/internal/pm/pkg/parsing/ParsingPackageUtils.java
index db60e12..04a51da 100644
--- a/core/java/com/android/internal/pm/pkg/parsing/ParsingPackageUtils.java
+++ b/core/java/com/android/internal/pm/pkg/parsing/ParsingPackageUtils.java
@@ -672,8 +672,12 @@
return input.error(INSTALL_PARSE_FAILED_BAD_MANIFEST,
"Failed adding asset path: " + apkPath);
}
+ // We pass false for hasFlags because manifest flags are handled separately in the parsing
+ // code. If the xml parser also checks and removes flags, not only will it be slower but
+ // also PackageManager will not work properly because it won't collect the referenced flags
+ // and won't be able to invalidate the package cache when one of those flags changes.
try (XmlResourceParser parser = assets.openXmlResourceParser(cookie,
- ANDROID_MANIFEST_FILENAME)) {
+ ANDROID_MANIFEST_FILENAME, false)) {
Resources res = new Resources(assets, mDisplayMetrics, null);
ParseResult<ParsingPackage> parseResult = parseSplitApk(input, pkg, res,
parser, flags, splitIndex);
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index e20a52b..97d16d8 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -42,6 +42,7 @@
import static android.window.DesktopModeFlags.ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION_ALWAYS;
import static com.android.internal.policy.PhoneWindow.FEATURE_OPTIONS_PANEL;
+import static com.android.window.flags.Flags.interceptMotionFromMoveToCancel;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -120,6 +121,7 @@
import com.android.internal.widget.ActionBarContextView;
import com.android.internal.widget.BackgroundFallback;
import com.android.internal.widget.floatingtoolbar.FloatingToolbar;
+import com.android.window.flags.Flags;
import java.util.List;
import java.util.concurrent.Executor;
@@ -201,6 +203,13 @@
ActionMode mPrimaryActionMode;
private ActionMode mFloatingActionMode;
private ActionBarContextView mPrimaryActionModeView;
+
+ // Paddings loaded from R.attr.actionModeStyle.
+ private int mActionModeViewInternalPaddingLeft;
+ private int mActionModeViewInternalPaddingTop;
+ private int mActionModeViewInternalPaddingRight;
+ private int mActionModeViewInternalPaddingBottom;
+
private PopupWindow mPrimaryActionModePopup;
private Runnable mShowPrimaryActionModePopup;
private ViewTreeObserver.OnPreDrawListener mFloatingToolbarPreDrawListener;
@@ -428,11 +437,50 @@
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
+ if (interceptBackProgress(ev)) {
+ return true;
+ }
final Window.Callback cb = mWindow.getCallback();
return cb != null && !mWindow.isDestroyed() && mFeatureId < 0
? cb.dispatchTouchEvent(ev) : super.dispatchTouchEvent(ev);
}
+ private boolean interceptBackProgress(MotionEvent ev) {
+ if (!interceptMotionFromMoveToCancel()) {
+ return false;
+ }
+ final ViewRootImpl viewRootImpl = getViewRootImpl();
+ if (viewRootImpl == null) {
+ return false;
+ }
+ viewRootImpl.getOnBackInvokedDispatcher().onMotionEvent(ev);
+ // Intercept touch if back gesture is in progress.
+ boolean isBackGestureInProgress = viewRootImpl.getOnBackInvokedDispatcher()
+ .isBackGestureInProgress();
+ if (!isBackGestureInProgress && mWearGestureInterceptionDetector != null) {
+ boolean wasIntercepting = mWearGestureInterceptionDetector.isIntercepting();
+ boolean intercepting = mWearGestureInterceptionDetector.onInterceptTouchEvent(ev);
+ if (wasIntercepting != intercepting) {
+ viewRootImpl.updateDecorViewGestureInterception(intercepting);
+ }
+ if (intercepting) {
+ isBackGestureInProgress = true;
+ }
+ }
+
+ if (!isBackGestureInProgress) {
+ return false;
+ }
+ // Intercept touch if back gesture is in progress.
+ if (!viewRootImpl.getOnBackInvokedDispatcher().isInterceptedMotionEvent()) {
+ viewRootImpl.getOnBackInvokedDispatcher().setMotionEventIntercepted();
+ ev.setAction(MotionEvent.ACTION_CANCEL);
+ // Return false to deliver the first CANCEL.
+ return false;
+ }
+ return true;
+ }
+
@Override
public boolean dispatchTrackballEvent(MotionEvent ev) {
final Window.Callback cb = mWindow.getCallback();
@@ -511,22 +559,25 @@
}
}
- ViewRootImpl viewRootImpl = getViewRootImpl();
- if (viewRootImpl != null) {
- viewRootImpl.getOnBackInvokedDispatcher().onMotionEvent(event);
- // Intercept touch if back gesture is in progress.
- if (viewRootImpl.getOnBackInvokedDispatcher().isBackGestureInProgress()) {
- return true;
+ if (!interceptMotionFromMoveToCancel()) {
+ ViewRootImpl viewRootImpl = getViewRootImpl();
+ if (viewRootImpl != null) {
+ viewRootImpl.getOnBackInvokedDispatcher().onMotionEvent(event);
+ // Intercept touch if back gesture is in progress.
+ if (viewRootImpl.getOnBackInvokedDispatcher().isBackGestureInProgress()) {
+ return true;
+ }
}
- }
- if (viewRootImpl != null && mWearGestureInterceptionDetector != null) {
- boolean wasIntercepting = mWearGestureInterceptionDetector.isIntercepting();
- boolean intercepting = mWearGestureInterceptionDetector.onInterceptTouchEvent(event);
- if (wasIntercepting != intercepting) {
- viewRootImpl.updateDecorViewGestureInterception(intercepting);
- }
- if (intercepting) {
- return true;
+ if (viewRootImpl != null && mWearGestureInterceptionDetector != null) {
+ boolean wasIntercepting = mWearGestureInterceptionDetector.isIntercepting();
+ boolean intercepting = mWearGestureInterceptionDetector
+ .onInterceptTouchEvent(event);
+ if (wasIntercepting != intercepting) {
+ viewRootImpl.updateDecorViewGestureInterception(intercepting);
+ }
+ if (intercepting) {
+ return true;
+ }
}
}
@@ -1003,7 +1054,8 @@
public void onWindowSystemUiVisibilityChanged(int visible) {
updateColorViews(null /* insets */, true /* animate */);
- if (mStatusGuard != null && mStatusGuard.getVisibility() == VISIBLE) {
+ if (!Flags.actionModeEdgeToEdge()
+ && mStatusGuard != null && mStatusGuard.getVisibility() == VISIBLE) {
updateStatusGuardColor();
}
}
@@ -1040,7 +1092,7 @@
}
mFrameOffsets.set(insets.getSystemWindowInsetsAsRect());
insets = updateColorViews(insets, true /* animate */);
- insets = updateStatusGuard(insets);
+ insets = updateActionModeInsets(insets);
if (getForeground() != null) {
drawableChanged();
}
@@ -1592,7 +1644,7 @@
}
}
- private WindowInsets updateStatusGuard(WindowInsets insets) {
+ private WindowInsets updateActionModeInsets(WindowInsets insets) {
boolean showStatusGuard = false;
// Show the status guard when the non-overlay contextual action bar is showing
if (mPrimaryActionModeView != null) {
@@ -1608,54 +1660,80 @@
final Rect rect = mTempRect;
// Apply the insets that have not been applied by the contentParent yet.
- WindowInsets innerInsets =
+ final WindowInsets innerInsets =
mWindow.mContentParent.computeSystemWindowInsets(insets, rect);
- int newTopMargin = innerInsets.getSystemWindowInsetTop();
- int newLeftMargin = innerInsets.getSystemWindowInsetLeft();
- int newRightMargin = innerInsets.getSystemWindowInsetRight();
+ final boolean consumesSystemWindowInsetsTop;
+ if (Flags.actionModeEdgeToEdge()) {
+ final Insets systemWindowInsets = innerInsets.getSystemWindowInsets();
+ final Insets newMargin = innerInsets.getInsets(
+ WindowInsets.Type.navigationBars());
- // Must use root window insets for the guard, because the color views consume
- // the navigation bar inset if the window does not request LAYOUT_HIDE_NAV - but
- // the status guard is attached at the root.
- WindowInsets rootInsets = getRootWindowInsets();
- int newGuardLeftMargin = rootInsets.getSystemWindowInsetLeft();
- int newGuardRightMargin = rootInsets.getSystemWindowInsetRight();
+ // Don't extend into navigation bar area so the width can align with status
+ // bar color view.
+ if (mlp.leftMargin != newMargin.left
+ || mlp.rightMargin != newMargin.right) {
+ mlpChanged = true;
+ mlp.leftMargin = newMargin.left;
+ mlp.rightMargin = newMargin.right;
+ }
- if (mlp.topMargin != newTopMargin || mlp.leftMargin != newLeftMargin
- || mlp.rightMargin != newRightMargin) {
- mlpChanged = true;
- mlp.topMargin = newTopMargin;
- mlp.leftMargin = newLeftMargin;
- mlp.rightMargin = newRightMargin;
- }
+ mPrimaryActionModeView.setPadding(
+ mActionModeViewInternalPaddingLeft + systemWindowInsets.left
+ - newMargin.left,
+ mActionModeViewInternalPaddingTop + systemWindowInsets.top,
+ mActionModeViewInternalPaddingRight + systemWindowInsets.right
+ - newMargin.right,
+ mActionModeViewInternalPaddingBottom);
+ consumesSystemWindowInsetsTop = systemWindowInsets.top > 0;
+ } else {
+ int newTopMargin = innerInsets.getSystemWindowInsetTop();
+ int newLeftMargin = innerInsets.getSystemWindowInsetLeft();
+ int newRightMargin = innerInsets.getSystemWindowInsetRight();
- if (newTopMargin > 0 && mStatusGuard == null) {
- mStatusGuard = new View(mContext);
- mStatusGuard.setVisibility(GONE);
- final LayoutParams lp = new LayoutParams(MATCH_PARENT,
- mlp.topMargin, Gravity.LEFT | Gravity.TOP);
- lp.leftMargin = newGuardLeftMargin;
- lp.rightMargin = newGuardRightMargin;
- addView(mStatusGuard, indexOfChild(mStatusColorViewState.view), lp);
- } else if (mStatusGuard != null) {
- final LayoutParams lp = (LayoutParams)
- mStatusGuard.getLayoutParams();
- if (lp.height != mlp.topMargin || lp.leftMargin != newGuardLeftMargin
- || lp.rightMargin != newGuardRightMargin) {
- lp.height = mlp.topMargin;
+ // Must use root window insets for the guard, because the color views
+ // consume the navigation bar inset if the window does not request
+ // LAYOUT_HIDE_NAV - but the status guard is attached at the root.
+ WindowInsets rootInsets = getRootWindowInsets();
+ int newGuardLeftMargin = rootInsets.getSystemWindowInsetLeft();
+ int newGuardRightMargin = rootInsets.getSystemWindowInsetRight();
+
+ if (mlp.topMargin != newTopMargin || mlp.leftMargin != newLeftMargin
+ || mlp.rightMargin != newRightMargin) {
+ mlpChanged = true;
+ mlp.topMargin = newTopMargin;
+ mlp.leftMargin = newLeftMargin;
+ mlp.rightMargin = newRightMargin;
+ }
+
+ if (newTopMargin > 0 && mStatusGuard == null) {
+ mStatusGuard = new View(mContext);
+ mStatusGuard.setVisibility(GONE);
+ final LayoutParams lp = new LayoutParams(MATCH_PARENT,
+ mlp.topMargin, Gravity.LEFT | Gravity.TOP);
lp.leftMargin = newGuardLeftMargin;
lp.rightMargin = newGuardRightMargin;
- mStatusGuard.setLayoutParams(lp);
+ addView(mStatusGuard, indexOfChild(mStatusColorViewState.view), lp);
+ } else if (mStatusGuard != null) {
+ final LayoutParams lp = (LayoutParams)
+ mStatusGuard.getLayoutParams();
+ if (lp.height != mlp.topMargin || lp.leftMargin != newGuardLeftMargin
+ || lp.rightMargin != newGuardRightMargin) {
+ lp.height = mlp.topMargin;
+ lp.leftMargin = newGuardLeftMargin;
+ lp.rightMargin = newGuardRightMargin;
+ mStatusGuard.setLayoutParams(lp);
+ }
}
- }
- // The action mode's theme may differ from the app, so
- // always show the status guard above it if we have one.
- showStatusGuard = mStatusGuard != null;
+ // The action mode's theme may differ from the app, so
+ // always show the status guard above it if we have one.
+ showStatusGuard = mStatusGuard != null;
- if (showStatusGuard && mStatusGuard.getVisibility() != VISIBLE) {
- // If it wasn't previously shown, the color may be stale
- updateStatusGuardColor();
+ if (showStatusGuard && mStatusGuard.getVisibility() != VISIBLE) {
+ // If it wasn't previously shown, the color may be stale
+ updateStatusGuardColor();
+ }
+ consumesSystemWindowInsetsTop = showStatusGuard;
}
// We only need to consume the insets if the action
@@ -1664,14 +1742,16 @@
// screen_simple_overlay_action_mode.xml).
final boolean nonOverlay = (mWindow.getLocalFeaturesPrivate()
& (1 << Window.FEATURE_ACTION_MODE_OVERLAY)) == 0;
- if (nonOverlay && showStatusGuard) {
+ if (nonOverlay && consumesSystemWindowInsetsTop) {
insets = insets.inset(0, insets.getSystemWindowInsetTop(), 0, 0);
}
} else {
- // reset top margin
- if (mlp.topMargin != 0 || mlp.leftMargin != 0 || mlp.rightMargin != 0) {
- mlpChanged = true;
- mlp.topMargin = 0;
+ if (!Flags.actionModeEdgeToEdge()) {
+ // reset top margin
+ if (mlp.topMargin != 0 || mlp.leftMargin != 0 || mlp.rightMargin != 0) {
+ mlpChanged = true;
+ mlp.topMargin = 0;
+ }
}
}
if (mlpChanged) {
@@ -1679,7 +1759,7 @@
}
}
}
- if (mStatusGuard != null) {
+ if (!Flags.actionModeEdgeToEdge() && mStatusGuard != null) {
mStatusGuard.setVisibility(showStatusGuard ? VISIBLE : GONE);
}
return insets;
@@ -1987,6 +2067,7 @@
}
mPrimaryActionModeView = new ActionBarContextView(actionBarContext);
+ initializeActionModeViewInternalPadding();
mPrimaryActionModePopup = new PopupWindow(actionBarContext, null,
R.attr.actionModePopupWindowStyle);
mPrimaryActionModePopup.setWindowLayoutType(
@@ -2033,6 +2114,7 @@
ViewStub stub = findViewById(R.id.action_mode_bar_stub);
if (stub != null) {
mPrimaryActionModeView = (ActionBarContextView) stub.inflate();
+ initializeActionModeViewInternalPadding();
mPrimaryActionModePopup = null;
}
}
@@ -2047,6 +2129,13 @@
return null;
}
+ private void initializeActionModeViewInternalPadding() {
+ mActionModeViewInternalPaddingLeft = mPrimaryActionModeView.getPaddingLeft();
+ mActionModeViewInternalPaddingTop = mPrimaryActionModeView.getPaddingTop();
+ mActionModeViewInternalPaddingRight = mPrimaryActionModeView.getPaddingRight();
+ mActionModeViewInternalPaddingBottom = mPrimaryActionModeView.getPaddingBottom();
+ }
+
private void endOnGoingFadeAnimation() {
if (mFadeAnim != null) {
mFadeAnim.end();
@@ -2183,7 +2272,7 @@
for (int i = getChildCount() - 1; i >= 0; i--) {
View v = getChildAt(i);
if (v != mStatusColorViewState.view && v != mNavigationColorViewState.view
- && v != mStatusGuard) {
+ && (Flags.actionModeEdgeToEdge() || v != mStatusGuard)) {
removeViewAt(i);
}
}
diff --git a/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java b/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java
index c5a7f43..0e2ecb25 100644
--- a/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java
+++ b/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java
@@ -520,9 +520,6 @@
if (args != null) {
int argIndex = 0;
- LongArray longParams = new LongArray();
- ArrayList<Double> doubleParams = new ArrayList<>();
- ArrayList<Boolean> booleanParams = new ArrayList<>();
for (Object o : args) {
int type = LogDataType.bitmaskToLogDataType(message.getMessageMask(), argIndex);
try {
@@ -539,23 +536,26 @@
break;
case LogDataType.LONG:
if (o == null) {
- longParams.add(0);
+ os.write(SINT64_PARAMS, 0);
} else {
- longParams.add(((Number) o).longValue());
+ os.write(SINT64_PARAMS, ((Number) o).longValue());
}
break;
case LogDataType.DOUBLE:
if (o == null) {
- doubleParams.add(0d);
+ os.write(DOUBLE_PARAMS, 0d);
} else {
- doubleParams.add(((Number) o).doubleValue());
+ os.write(DOUBLE_PARAMS, ((Number) o).doubleValue());
}
break;
case LogDataType.BOOLEAN:
+ // Converting booleans to int because Perfetto doesn't yet support
+ // repeated booleans, so we use a repeated integers instead.
+ // (b/313651412)
if (o == null) {
- booleanParams.add(false);
+ os.write(BOOLEAN_PARAMS, 0);
} else {
- booleanParams.add((boolean) o);
+ os.write(BOOLEAN_PARAMS, (boolean) o ? 1 : 0);
}
break;
}
@@ -564,14 +564,6 @@
}
argIndex++;
}
-
- for (int i = 0; i < longParams.size(); ++i) {
- os.write(SINT64_PARAMS, longParams.get(i));
- }
- doubleParams.forEach(it -> os.write(DOUBLE_PARAMS, it));
- // Converting booleans to int because Perfetto doesn't yet support repeated
- // booleans, so we use a repeated integers instead (b/313651412).
- booleanParams.forEach(it -> os.write(BOOLEAN_PARAMS, it ? 1 : 0));
}
if (tlsState.getShouldCollectStacktrace(logGroup.name())) {
diff --git a/core/java/com/android/internal/widget/ActionBarContextView.java b/core/java/com/android/internal/widget/ActionBarContextView.java
index 80fc2188..e9961dd 100644
--- a/core/java/com/android/internal/widget/ActionBarContextView.java
+++ b/core/java/com/android/internal/widget/ActionBarContextView.java
@@ -15,8 +15,10 @@
*/
package com.android.internal.widget;
+import android.annotation.SuppressLint;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
+import android.content.res.Configuration;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.os.Build;
@@ -34,6 +36,7 @@
import com.android.internal.R;
import com.android.internal.view.menu.MenuBuilder;
+import com.android.window.flags.Flags;
/**
* @hide
@@ -54,16 +57,17 @@
private Drawable mSplitBackground;
private boolean mTitleOptional;
private int mCloseItemLayout;
+ private final int mInternalVerticalPadding;
public ActionBarContextView(Context context) {
this(context, null);
}
-
+
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public ActionBarContextView(Context context, AttributeSet attrs) {
this(context, attrs, com.android.internal.R.attr.actionModeStyle);
}
-
+
public ActionBarContextView(Context context, AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr, 0);
}
@@ -92,6 +96,8 @@
R.layout.action_mode_close_item);
a.recycle();
+
+ mInternalVerticalPadding = getPaddingTop() + getPaddingBottom();
}
@Override
@@ -103,6 +109,19 @@
}
}
+ @SuppressLint("CustomViewStyleable")
+ @Override
+ protected void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+
+ // Action mode can change size on configuration changes.
+ // Reread the desired height from the theme-specified style.
+ TypedArray a = getContext().obtainStyledAttributes(null, R.styleable.ActionMode,
+ R.attr.actionModeStyle, 0);
+ setContentHeight(a.getLayoutDimension(R.styleable.ActionMode_height, 0));
+ a.recycle();
+ }
+
@Override
public void setSplitToolbar(boolean split) {
if (mSplitActionBar != split) {
@@ -314,12 +333,15 @@
}
final int contentWidth = MeasureSpec.getSize(widthMeasureSpec);
-
- int maxHeight = mContentHeight > 0 ?
- mContentHeight : MeasureSpec.getSize(heightMeasureSpec);
+ int availableWidth = contentWidth - getPaddingLeft() - getPaddingRight();
final int verticalPadding = getPaddingTop() + getPaddingBottom();
- int availableWidth = contentWidth - getPaddingLeft() - getPaddingRight();
+ final int externalVerticalPadding = Math.max(0, verticalPadding - mInternalVerticalPadding);
+ final int maxHeight = mContentHeight > 0
+ ? Flags.actionModeEdgeToEdge()
+ ? mContentHeight + externalVerticalPadding
+ : mContentHeight
+ : MeasureSpec.getSize(heightMeasureSpec);
final int height = maxHeight - verticalPadding;
final int childSpecHeight = MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST);
diff --git a/core/java/com/android/internal/widget/ActionBarOverlayLayout.java b/core/java/com/android/internal/widget/ActionBarOverlayLayout.java
index ff57fd4..b601cbc 100644
--- a/core/java/com/android/internal/widget/ActionBarOverlayLayout.java
+++ b/core/java/com/android/internal/widget/ActionBarOverlayLayout.java
@@ -294,54 +294,24 @@
}
}
- private boolean applyInsets(View view, Rect insets, boolean toPadding,
- boolean left, boolean top, boolean right, boolean bottom) {
- boolean changed;
- if (toPadding) {
- changed = setMargin(view, EMPTY_RECT, left, top, right, bottom);
- changed |= setPadding(view, insets, left, top, right, bottom);
- } else {
- changed = setPadding(view, EMPTY_RECT, left, top, right, bottom);
- changed |= setMargin(view, insets, left, top, right, bottom);
- }
- return changed;
- }
-
- private boolean setPadding(View view, Rect insets,
- boolean left, boolean top, boolean right, boolean bottom) {
- if ((left && view.getPaddingLeft() != insets.left)
- || (top && view.getPaddingTop() != insets.top)
- || (right && view.getPaddingRight() != insets.right)
- || (bottom && view.getPaddingBottom() != insets.bottom)) {
- view.setPadding(
- left ? insets.left : view.getPaddingLeft(),
- top ? insets.top : view.getPaddingTop(),
- right ? insets.right : view.getPaddingRight(),
- bottom ? insets.bottom : view.getPaddingBottom());
- return true;
- }
- return false;
- }
-
- private boolean setMargin(View view, Rect insets,
- boolean left, boolean top, boolean right, boolean bottom) {
+ private boolean setMargin(View view, int left, int top, int right, int bottom) {
final LayoutParams lp = (LayoutParams) view.getLayoutParams();
boolean changed = false;
- if (left && lp.leftMargin != insets.left) {
+ if (lp.leftMargin != left) {
changed = true;
- lp.leftMargin = insets.left;
+ lp.leftMargin = left;
}
- if (top && lp.topMargin != insets.top) {
+ if (lp.topMargin != top) {
changed = true;
- lp.topMargin = insets.top;
+ lp.topMargin = top;
}
- if (right && lp.rightMargin != insets.right) {
+ if (lp.rightMargin != right) {
changed = true;
- lp.rightMargin = insets.right;
+ lp.rightMargin = right;
}
- if (bottom && lp.bottomMargin != insets.bottom) {
+ if (lp.bottomMargin != bottom) {
changed = true;
- lp.bottomMargin = insets.bottom;
+ lp.bottomMargin = bottom;
}
return changed;
}
@@ -367,12 +337,30 @@
final Insets sysInsets = insets.getSystemWindowInsets();
mSystemInsets.set(sysInsets.left, sysInsets.top, sysInsets.right, sysInsets.bottom);
- // The top and bottom action bars are always within the content area.
- boolean changed = applyInsets(mActionBarTop, mSystemInsets,
- mActionBarExtendsIntoSystemInsets, true, true, true, false);
- if (mActionBarBottom != null) {
- changed |= applyInsets(mActionBarBottom, mSystemInsets,
- mActionBarExtendsIntoSystemInsets, true, false, true, true);
+ boolean changed = false;
+ if (mActionBarExtendsIntoSystemInsets) {
+ // Don't extend into navigation bar area so the width can align with status bar
+ // color view.
+ final Insets navBarInsets = insets.getInsets(WindowInsets.Type.navigationBars());
+ final int paddingLeft = sysInsets.left - navBarInsets.left;
+ final int paddingRight = sysInsets.right - navBarInsets.right;
+ mActionBarTop.setPadding(paddingLeft, sysInsets.top, paddingRight, 0);
+ changed |= setMargin(
+ mActionBarTop, navBarInsets.left, 0, navBarInsets.right, 0);
+ if (mActionBarBottom != null) {
+ mActionBarBottom.setPadding(paddingLeft, 0, paddingRight, sysInsets.bottom);
+ changed |= setMargin(
+ mActionBarBottom, navBarInsets.left, 0, navBarInsets.right, 0);
+ }
+ } else {
+ mActionBarTop.setPadding(0, 0, 0, 0);
+ changed |= setMargin(
+ mActionBarTop, sysInsets.left, sysInsets.top, sysInsets.right, 0);
+ if (mActionBarBottom != null) {
+ mActionBarBottom.setPadding(0, 0, 0, 0);
+ changed |= setMargin(
+ mActionBarBottom, sysInsets.left, 0, sysInsets.right, sysInsets.bottom);
+ }
}
// Cannot use the result of computeSystemWindowInsets, because that consumes the
@@ -521,7 +509,12 @@
);
}
}
- setMargin(mContent, mContentInsets, true, true, true, true);
+ setMargin(
+ mContent,
+ mContentInsets.left,
+ mContentInsets.top,
+ mContentInsets.right,
+ mContentInsets.bottom);
if (!mLastInnerInsets.equals(mInnerInsets)) {
// If the inner insets have changed, we need to dispatch this down to
diff --git a/core/java/com/android/internal/widget/ConversationLayout.java b/core/java/com/android/internal/widget/ConversationLayout.java
index cbaba1c..c976fbf 100644
--- a/core/java/com/android/internal/widget/ConversationLayout.java
+++ b/core/java/com/android/internal/widget/ConversationLayout.java
@@ -17,7 +17,6 @@
package com.android.internal.widget;
import static android.app.Flags.notificationsRedesignTemplates;
-import static android.widget.flags.Flags.conversationLayoutUseMaximumChildHeight;
import static com.android.internal.widget.MessagingGroup.IMAGE_DISPLAY_LOCATION_EXTERNAL;
import static com.android.internal.widget.MessagingGroup.IMAGE_DISPLAY_LOCATION_INLINE;
@@ -1513,24 +1512,22 @@
// FrameLayout measures its match_parent children twice when any of FLs dimension is not
// specified. However, its sets its own dimensions before the second measurement pass.
// Content CutOff happens when children have bigger height on its second measurement.
- if (conversationLayoutUseMaximumChildHeight()) {
- int maxHeight = getMeasuredHeight();
- final int count = getChildCount();
+ int maxHeight = getMeasuredHeight();
+ final int count = getChildCount();
- for (int i = 0; i < count; i++) {
- final View child = getChildAt(i);
- if (child == null || child.getVisibility() == GONE) {
- continue;
- }
+ for (int i = 0; i < count; i++) {
+ final View child = getChildAt(i);
+ if (child == null || child.getVisibility() == GONE) {
+ continue;
+ }
- final LayoutParams lp = (LayoutParams) child.getLayoutParams();
- maxHeight = Math.max(maxHeight,
- child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
- }
- maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
- if (maxHeight != getMeasuredHeight()) {
- setMeasuredDimension(getMeasuredWidth(), maxHeight);
- }
+ final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+ maxHeight = Math.max(maxHeight,
+ child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
+ }
+ maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
+ if (maxHeight != getMeasuredHeight()) {
+ setMeasuredDimension(getMeasuredWidth(), maxHeight);
}
}
diff --git a/core/java/com/android/internal/widget/MessagingLinearLayout.java b/core/java/com/android/internal/widget/MessagingLinearLayout.java
index 727375a..9425435 100644
--- a/core/java/com/android/internal/widget/MessagingLinearLayout.java
+++ b/core/java/com/android/internal/widget/MessagingLinearLayout.java
@@ -17,7 +17,6 @@
package com.android.internal.widget;
import static android.app.Flags.notificationsRedesignTemplates;
-import static android.widget.flags.Flags.messagingChildRequestLayout;
import android.annotation.Nullable;
import android.annotation.Px;
@@ -104,9 +103,7 @@
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
lp.hide = true;
// Child always needs to be measured to calculate hide property correctly in onMeasure.
- if (messagingChildRequestLayout()) {
- child.requestLayout();
- }
+ child.requestLayout();
if (child instanceof MessagingChild) {
MessagingChild messagingChild = (MessagingChild) child;
// Whenever we encounter the message first, it's always first in the layout
diff --git a/core/java/com/android/internal/widget/remotecompose/accessibility/AndroidPlatformSemanticNodeApplier.java b/core/java/com/android/internal/widget/remotecompose/accessibility/AndroidPlatformSemanticNodeApplier.java
index 3fe6873..dbe1785 100644
--- a/core/java/com/android/internal/widget/remotecompose/accessibility/AndroidPlatformSemanticNodeApplier.java
+++ b/core/java/com/android/internal/widget/remotecompose/accessibility/AndroidPlatformSemanticNodeApplier.java
@@ -88,13 +88,14 @@
@Override
protected void setBoundsInScreen(AccessibilityNodeInfo nodeInfo, Rect bounds) {
- nodeInfo.setBoundsInParent(bounds);
+ nodeInfo.setBoundsInParent(new Rect(0, 0, 1, 1));
nodeInfo.setBoundsInScreen(bounds);
}
@Override
protected void setUniqueId(AccessibilityNodeInfo nodeInfo, String id) {
nodeInfo.setUniqueId(id);
+ nodeInfo.setSource(mPlayer, Integer.parseInt(id));
}
@Override
@@ -143,6 +144,7 @@
if (scrollDirection == RootContentBehavior.SCROLL_HORIZONTAL) {
nodeInfo.setClassName("android.widget.HorizontalScrollView");
} else {
+ nodeInfo.setCollectionInfo(AccessibilityNodeInfo.CollectionInfo.obtain(-1, 1, false));
nodeInfo.setClassName("android.widget.ScrollView");
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/accessibility/PlatformRemoteComposeTouchHelper.java b/core/java/com/android/internal/widget/remotecompose/accessibility/PlatformRemoteComposeTouchHelper.java
index da4e8d6..85b6731 100644
--- a/core/java/com/android/internal/widget/remotecompose/accessibility/PlatformRemoteComposeTouchHelper.java
+++ b/core/java/com/android/internal/widget/remotecompose/accessibility/PlatformRemoteComposeTouchHelper.java
@@ -94,19 +94,23 @@
* @param virtualViewIds The list to be populated with the visible virtual view IDs.
*/
@Override
- protected void getVisibleVirtualViews(IntArray virtualViewIds) {
+ public void getVisibleVirtualViews(IntArray virtualViewIds) {
+ List<Integer> children = getVisibleChildVirtualViews();
+ for (int child : children) {
+ virtualViewIds.add(child);
+ }
+ }
+
+ @SuppressWarnings("JdkImmutableCollections")
+ public List<Integer> getVisibleChildVirtualViews() {
Component rootComponent = mRemoteDocA11y.findComponentById(RootId);
if (rootComponent == null
|| !mRemoteDocA11y.semanticModifiersForComponent(rootComponent).isEmpty()) {
- virtualViewIds.add(RootId);
+ return List.of(RootId);
}
- List<Integer> children =
- mRemoteDocA11y.semanticallyRelevantChildComponents(rootComponent, false);
- for (int child : children) {
- virtualViewIds.add(child);
- }
+ return mRemoteDocA11y.semanticallyRelevantChildComponents(rootComponent, false);
}
@Override
diff --git a/core/java/com/google/android/util/AbstractMessageParser.java b/core/java/com/google/android/util/AbstractMessageParser.java
index 0da7607..102d506 100644
--- a/core/java/com/google/android/util/AbstractMessageParser.java
+++ b/core/java/com/google/android/util/AbstractMessageParser.java
@@ -22,6 +22,7 @@
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
+import java.util.Objects;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -528,7 +529,7 @@
// Append any necessary end tokens
for (Character key : seenCharacters.keySet()) {
- if (seenCharacters.get(key) == Boolean.TRUE) {
+ if (Objects.equals(seenCharacters.get(key), Boolean.TRUE)) {
Format end = new Format(key.charValue(), false);
end.setMatched(true);
addToken(end);
diff --git a/core/jni/android_hardware_Camera.cpp b/core/jni/android_hardware_Camera.cpp
index 003ebc0..2ae2e18 100644
--- a/core/jni/android_hardware_Camera.cpp
+++ b/core/jni/android_hardware_Camera.cpp
@@ -539,7 +539,7 @@
return false;
}
- if (!(useContextAttributionSource && flags::data_delivery_permission_checks())) {
+ if (!useContextAttributionSource) {
clientAttribution.uid = Camera::USE_CALLING_UID;
clientAttribution.pid = Camera::USE_CALLING_PID;
}
diff --git a/core/jni/android_hardware_display_DisplayTopology.cpp b/core/jni/android_hardware_display_DisplayTopology.cpp
index a11b3ce..3349a4e 100644
--- a/core/jni/android_hardware_display_DisplayTopology.cpp
+++ b/core/jni/android_hardware_display_DisplayTopology.cpp
@@ -96,10 +96,12 @@
return OK;
}
-DisplayTopologyGraph android_hardware_display_DisplayTopologyGraph_toNative(JNIEnv* env,
- jobject topologyObj) {
- DisplayTopologyGraph topology;
- topology.primaryDisplayId = ui::LogicalDisplayId{
+base::Result<const DisplayTopologyGraph> android_hardware_display_DisplayTopologyGraph_toNative(
+ JNIEnv* env, jobject topologyObj) {
+ std::unordered_map<ui::LogicalDisplayId, std::vector<DisplayTopologyAdjacentDisplay>>
+ topologyGraph;
+ std::unordered_map<ui::LogicalDisplayId, int> displaysDensity;
+ ui::LogicalDisplayId primaryDisplayId = ui::LogicalDisplayId{
env->GetIntField(topologyObj, gDisplayTopologyGraphClassInfo.primaryDisplayId)};
jobjectArray nodesArray = static_cast<jobjectArray>(
@@ -114,11 +116,12 @@
}
android_hardware_display_DisplayTopologyGraphNode_toNative(env, nodeObj.get(),
- topology.graph,
- topology.displaysDensity);
+ /*byRef*/ topologyGraph,
+ /*byRef*/ displaysDensity);
}
}
- return topology;
+ return DisplayTopologyGraph::create(primaryDisplayId, std::move(topologyGraph),
+ std::move(displaysDensity));
}
// ----------------------------------------------------------------------------
diff --git a/core/jni/android_hardware_display_DisplayTopology.h b/core/jni/android_hardware_display_DisplayTopology.h
index 390191f..795042e 100644
--- a/core/jni/android_hardware_display_DisplayTopology.h
+++ b/core/jni/android_hardware_display_DisplayTopology.h
@@ -16,6 +16,7 @@
#pragma once
+#include <android-base/result.h>
#include <input/DisplayTopologyGraph.h>
#include "jni.h"
@@ -26,7 +27,7 @@
* Copies the contents of a DVM DisplayTopology object to a new native DisplayTopology instance.
* Returns DisplayTopology.
*/
-extern DisplayTopologyGraph android_hardware_display_DisplayTopologyGraph_toNative(
- JNIEnv* env, jobject eventObj);
+extern base::Result<const DisplayTopologyGraph>
+android_hardware_display_DisplayTopologyGraph_toNative(JNIEnv* env, jobject eventObj);
} // namespace android
diff --git a/core/jni/android_hardware_display_DisplayViewport.cpp b/core/jni/android_hardware_display_DisplayViewport.cpp
index 5d7b33e..38ef68c 100644
--- a/core/jni/android_hardware_display_DisplayViewport.cpp
+++ b/core/jni/android_hardware_display_DisplayViewport.cpp
@@ -43,6 +43,7 @@
jfieldID uniqueId;
jfieldID physicalPort;
jfieldID type;
+ jfieldID densityDpi;
} gDisplayViewportClassInfo;
static struct {
@@ -82,6 +83,8 @@
viewport->type = static_cast<ViewportType>(env->GetIntField(viewportObj,
gDisplayViewportClassInfo.type));
+ viewport->densityDpi = env->GetIntField(viewportObj, gDisplayViewportClassInfo.densityDpi);
+
jobject logicalFrameObj =
env->GetObjectField(viewportObj, gDisplayViewportClassInfo.logicalFrame);
viewport->logicalLeft = env->GetIntField(logicalFrameObj, gRectClassInfo.left);
@@ -135,6 +138,9 @@
gDisplayViewportClassInfo.type = GetFieldIDOrDie(env,
gDisplayViewportClassInfo.clazz, "type", "I");
+ gDisplayViewportClassInfo.densityDpi =
+ GetFieldIDOrDie(env, gDisplayViewportClassInfo.clazz, "densityDpi", "I");
+
clazz = FindClassOrDie(env, "android/graphics/Rect");
gRectClassInfo.left = GetFieldIDOrDie(env, clazz, "left", "I");
gRectClassInfo.top = GetFieldIDOrDie(env, clazz, "top", "I");
diff --git a/core/jni/android_media_AudioDeviceAttributes.cpp b/core/jni/android_media_AudioDeviceAttributes.cpp
index 6879a60..e151bfc 100644
--- a/core/jni/android_media_AudioDeviceAttributes.cpp
+++ b/core/jni/android_media_AudioDeviceAttributes.cpp
@@ -32,6 +32,12 @@
namespace android {
+// Populates the AudioDeviceAttributes with internal type and address. This is important when
+// using AudioDeviceAttributes equals / hashCode (e.g.: when inserting as a key in a map)
+// to identify the correct device based on type and address alone, and not other features, such
+// as name, audio descriptors or profile.
+// Note: if other elements are needed as part of AudioDeviceAttributes please create a
+// different method.
jint createAudioDeviceAttributesFromNative(JNIEnv *env, jobject *jAudioDeviceAttributes,
const AudioDeviceTypeAddr *devTypeAddr) {
jint jStatus = (jint)AUDIO_JAVA_SUCCESS;
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index 506f224..01c18e4 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -2318,7 +2318,7 @@
jobject jAudioAttributes = NULL;
jobject jMixMatchCriterion = NULL;
jobject jValueInteger = NULL;
- switch (criteria.mRule) {
+ switch (criteria.mRule & ~RULE_EXCLUSION_MASK) {
case RULE_MATCH_UID:
jValueInteger = env->NewObject(gIntegerClass, gIntegerCstor, criteria.mValue.mUid);
jMixMatchCriterion = env->NewObject(gAudioMixMatchCriterionClass,
@@ -2355,6 +2355,9 @@
gAudioMixMatchCriterionAttrCstor,
jAudioAttributes, criteria.mRule);
break;
+ default:
+ ALOGE("Invalid rule type: %d ", criteria.mRule);
+ return AUDIO_JAVA_BAD_VALUE;
}
env->CallBooleanMethod(jAudioMixMatchCriterionList, gArrayListMethods.add,
jMixMatchCriterion);
@@ -3425,6 +3428,11 @@
return enabled;
}
+static int android_media_AudioSystem_setSimulateDeviceConnections(JNIEnv *env, jobject thiz,
+ jboolean enabled) {
+ return check_AudioSystem_Command(AudioSystem::setSimulateDeviceConnections(enabled));
+}
+
class JavaSystemPropertyListener {
public:
JavaSystemPropertyListener(JNIEnv* env, jobject javaCallback, std::string sysPropName) :
@@ -3703,6 +3711,7 @@
android_media_AudioSystem_listenForSystemPropertyChange),
MAKE_JNI_NATIVE_METHOD("triggerSystemPropertyUpdate", "(J)V",
android_media_AudioSystem_triggerSystemPropertyUpdate),
+ MAKE_AUDIO_SYSTEM_METHOD(setSimulateDeviceConnections),
};
static const JNINativeMethod gEventHandlerMethods[] =
diff --git a/core/jni/android_media_ImageReader.cpp b/core/jni/android_media_ImageReader.cpp
index a34cb39..1afd964 100644
--- a/core/jni/android_media_ImageReader.cpp
+++ b/core/jni/android_media_ImageReader.cpp
@@ -18,6 +18,7 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "ImageReader_JNI"
#define ATRACE_TAG ATRACE_TAG_CAMERA
+
#include <android/hardware_buffer_jni.h>
#include <android_runtime/AndroidRuntime.h>
#include <android_runtime/android_graphics_GraphicBuffer.h>
@@ -49,6 +50,7 @@
#include <utils/Trace.h>
#include <utils/misc.h>
+#include <cstdint>
#include <cstdio>
#include "android_media_Utils.h"
@@ -120,8 +122,18 @@
void setBufferConsumer(const sp<BufferItemConsumer>& consumer) { mConsumer = consumer; }
BufferItemConsumer* getBufferConsumer() { return mConsumer.get(); }
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_MEDIA_MIGRATION)
+ void setSurface(const sp<Surface>& surface) {
+ mSurface = surface;
+ }
+
+ Surface* getSurface() {
+ return mSurface.get();
+ }
+#else
void setProducer(const sp<IGraphicBufferProducer>& producer) { mProducer = producer; }
IGraphicBufferProducer* getProducer() { return mProducer.get(); }
+#endif
void setBufferFormat(int format) { mFormat = format; }
int getBufferFormat() { return mFormat; }
@@ -141,7 +153,11 @@
List<BufferItem*> mBuffers;
sp<BufferItemConsumer> mConsumer;
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_MEDIA_MIGRATION)
+ sp<Surface> mSurface;
+#else
sp<IGraphicBufferProducer> mProducer;
+#endif
jobject mWeakThiz;
jclass mClazz;
int mFormat;
@@ -257,8 +273,19 @@
return ctx;
}
-static IGraphicBufferProducer* ImageReader_getProducer(JNIEnv* env, jobject thiz)
-{
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_MEDIA_MIGRATION)
+static Surface* ImageReader_getSurfaceFromContext(JNIEnv* env, jobject thiz) {
+ ALOGV("%s:", __FUNCTION__);
+ JNIImageReaderContext* const ctx = ImageReader_getContext(env, thiz);
+ if (ctx == NULL) {
+ jniThrowRuntimeException(env, "ImageReaderContext is not initialized");
+ return NULL;
+ }
+
+ return ctx->getSurface();
+}
+#else
+static IGraphicBufferProducer* ImageReader_getProducer(JNIEnv* env, jobject thiz) {
ALOGV("%s:", __FUNCTION__);
JNIImageReaderContext* const ctx = ImageReader_getContext(env, thiz);
if (ctx == NULL) {
@@ -268,6 +295,7 @@
return ctx->getProducer();
}
+#endif
static void ImageReader_setNativeContext(JNIEnv* env,
jobject thiz, sp<JNIImageReaderContext> ctx)
@@ -409,19 +437,10 @@
uint64_t consumerUsage = 0;
#endif
-#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
- sp<BufferItemConsumer> bufferConsumer = new BufferItemConsumer(consumerUsage, maxImages,
- /*controlledByApp*/ true);
- sp<IGraphicBufferProducer> gbProducer =
- bufferConsumer->getSurface()->getIGraphicBufferProducer();
-#else
- sp<IGraphicBufferProducer> gbProducer;
- sp<IGraphicBufferConsumer> gbConsumer;
- BufferQueue::createBufferQueue(&gbProducer, &gbConsumer);
- sp<BufferItemConsumer> bufferConsumer;
- bufferConsumer = new BufferItemConsumer(gbConsumer, consumerUsage, maxImages,
- /*controlledByApp*/true);
-#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+ auto [bufferConsumer, surface] =
+ BufferItemConsumer::create((uint64_t)consumerUsage, (int)maxImages,
+ /*controlledByApp*/ true);
+ sp<IGraphicBufferProducer> gbProducer = surface->getIGraphicBufferProducer();
if (bufferConsumer == nullptr) {
jniThrowExceptionFmt(env, "java/lang/RuntimeException",
"Failed to allocate native buffer consumer for hal format 0x%x and usage 0x%x",
@@ -430,17 +449,17 @@
}
if (consumerUsage & GRALLOC_USAGE_PROTECTED) {
-#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
bufferConsumer->setConsumerIsProtected(true);
-#else
- gbConsumer->setConsumerIsProtected(true);
-#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
}
ctx->setBufferConsumer(bufferConsumer);
bufferConsumer->setName(consumerName);
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_MEDIA_MIGRATION)
+ ctx->setSurface(sp<Surface>::make(gbProducer));
+#else
ctx->setProducer(gbProducer);
+#endif
bufferConsumer->setFrameAvailableListener(ctx);
ImageReader_setNativeContext(env, thiz, ctx);
ctx->setBufferFormat(nativeHalFormat);
@@ -687,7 +706,11 @@
status_t res = OK;
Image_unlockIfLocked(env, image);
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_MEDIA_MIGRATION)
+ res = bufferConsumer->detachBuffer(buffer->mGraphicBuffer);
+#else
res = bufferConsumer->detachBuffer(buffer->mSlot);
+#endif
if (res != OK) {
ALOGE("Image detach failed: %s (%d)!!!", strerror(-res), res);
if ((bool) throwISEOnly) {
@@ -718,6 +741,20 @@
}
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_MEDIA_MIGRATION)
+static jobject ImageReader_getSurface(JNIEnv* env, jobject thiz) {
+ ALOGV("%s: ", __FUNCTION__);
+
+ Surface* surface = ImageReader_getSurfaceFromContext(env, thiz);
+ if (surface == NULL) {
+ jniThrowRuntimeException(env, "Buffer consumer is uninitialized");
+ return NULL;
+ }
+
+ // Wrap the IGBP in a Java-language Surface.
+ return android_view_Surface_createFromSurface(env, surface);
+}
+#else
static jobject ImageReader_getSurface(JNIEnv* env, jobject thiz)
{
ALOGV("%s: ", __FUNCTION__);
@@ -731,6 +768,7 @@
// Wrap the IGBP in a Java-language Surface.
return android_view_Surface_createFromIGraphicBufferProducer(env, gbp);
}
+#endif
static void Image_getLockedImage(JNIEnv* env, jobject thiz, LockedImage *image,
uint64_t ndkReaderUsage) {
diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp
index 67c9725..7617984 100644
--- a/core/jni/android_util_Process.cpp
+++ b/core/jni/android_util_Process.cpp
@@ -30,6 +30,7 @@
#include <processgroup/sched_policy.h>
#include <android-base/logging.h>
#include <android-base/unique_fd.h>
+#include <cutils/misc.h>
#include <algorithm>
#include <array>
@@ -332,6 +333,27 @@
}
}
+jboolean android_os_Process_isProcessFrozen(JNIEnv *env, jobject, jint pid, jint uid)
+{
+ if (uid < 0) {
+ jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", "uid is negative: %d", uid);
+ return false;
+ }
+
+ char const* type = (uid < FIRST_APPLICATION_UID) ? "system" : "apps";
+ char path[PATH_MAX];
+ snprintf(path, sizeof(path), "/sys/fs/cgroup/%s/uid_%d/pid_%d/cgroup.freeze", type, uid, pid);
+
+ int fd = ::open(path, O_RDONLY);
+ if (fd >= 0) {
+ char flag = 0;
+ ::read(fd, &flag, 1);
+ ::close(fd);
+ return flag == '1';
+ }
+ return false;
+}
+
jint android_os_Process_getProcessGroup(JNIEnv* env, jobject clazz, jint pid)
{
SchedPolicy sp;
@@ -1401,6 +1423,7 @@
{"sendSignalThrows", "(II)V", (void*)android_os_Process_sendSignalThrows},
{"sendTgSignalThrows", "(III)V", (void*)android_os_Process_sendTgSignalThrows},
{"setProcessFrozen", "(IIZ)V", (void*)android_os_Process_setProcessFrozen},
+ {"isProcessFrozen", "(II)Z", (void*)android_os_Process_isProcessFrozen},
{"getFreeMemory", "()J", (void*)android_os_Process_getFreeMemory},
{"getTotalMemory", "()J", (void*)android_os_Process_getTotalMemory},
{"readProcLines", "(Ljava/lang/String;[Ljava/lang/String;[J)V",
diff --git a/core/jni/android_view_KeyCharacterMap.cpp b/core/jni/android_view_KeyCharacterMap.cpp
index 2b19ddf..660a62d 100644
--- a/core/jni/android_view_KeyCharacterMap.cpp
+++ b/core/jni/android_view_KeyCharacterMap.cpp
@@ -242,21 +242,24 @@
return static_cast<jboolean>(*map1 == *map2);
}
-static void nativeApplyOverlay(JNIEnv* env, jobject clazz, jlong ptr, jstring nameObj,
- jstring overlayObj) {
- NativeKeyCharacterMap* map = reinterpret_cast<NativeKeyCharacterMap*>(ptr);
+static jobject nativeObtainMapWithOverlay(JNIEnv* env, jobject clazz, jlong baseMapPtr,
+ jstring nameObj, jstring overlayObj) {
+ NativeKeyCharacterMap* map = reinterpret_cast<NativeKeyCharacterMap*>(baseMapPtr);
if (!map || !map->getMap()) {
- return;
+ return nullptr;
}
ScopedUtfChars nameChars(env, nameObj);
ScopedUtfChars overlayChars(env, overlayObj);
base::Result<std::shared_ptr<KeyCharacterMap>> ret =
KeyCharacterMap::loadContents(nameChars.c_str(), overlayChars.c_str(),
KeyCharacterMap::Format::OVERLAY);
- if (ret.ok()) {
- std::shared_ptr<KeyCharacterMap> overlay = *ret;
- map->getMap()->combine(*overlay);
+ if (!ret.ok()) {
+ return nullptr;
}
+ std::shared_ptr<KeyCharacterMap> overlay = *ret;
+ std::unique_ptr<KeyCharacterMap> result = std::make_unique<KeyCharacterMap>(*map->getMap());
+ result->combine(*overlay);
+ return android_view_KeyCharacterMap_create(env, map->getDeviceId(), std::move(result));
}
static jint nativeGetMappedKey(JNIEnv* env, jobject clazz, jlong ptr, jint scanCode) {
@@ -292,8 +295,9 @@
{"nativeObtainEmptyKeyCharacterMap", "(I)Landroid/view/KeyCharacterMap;",
(void*)nativeObtainEmptyKeyCharacterMap},
{"nativeEquals", "(JJ)Z", (void*)nativeEquals},
- {"nativeApplyOverlay", "(JLjava/lang/String;Ljava/lang/String;)V",
- (void*)nativeApplyOverlay},
+ {"nativeObtainMapWithOverlay",
+ "(JLjava/lang/String;Ljava/lang/String;)Landroid/view/KeyCharacterMap;",
+ (void*)nativeObtainMapWithOverlay},
{"nativeGetMappedKey", "(JI)I", (void*)nativeGetMappedKey}};
int register_android_view_KeyCharacterMap(JNIEnv* env)
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 5e3c2e1..eec9f48 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -110,6 +110,7 @@
jfieldID secure;
jfieldID deviceProductInfo;
jfieldID installOrientation;
+ jfieldID screenPartStatus;
} gStaticDisplayInfoClassInfo;
static struct {
@@ -1480,6 +1481,8 @@
isInternal));
env->SetIntField(object, gStaticDisplayInfoClassInfo.installOrientation,
static_cast<uint32_t>(info.installOrientation));
+ env->SetIntField(object, gStaticDisplayInfoClassInfo.screenPartStatus,
+ static_cast<uint8_t>(info.screenPartStatus));
return object;
}
@@ -2094,10 +2097,11 @@
return reinterpret_cast<jlong>(transaction.release());
}
-static jlong nativeMirrorSurface(JNIEnv* env, jclass clazz, jlong mirrorOfObj) {
+static jlong nativeMirrorSurface(JNIEnv* env, jclass clazz, jlong mirrorOfObj, jlong stopAtObj) {
sp<SurfaceComposerClient> client = SurfaceComposerClient::getDefault();
SurfaceControl *mirrorOf = reinterpret_cast<SurfaceControl*>(mirrorOfObj);
- sp<SurfaceControl> surface = client->mirrorSurface(mirrorOf);
+ SurfaceControl* stopAt = reinterpret_cast<SurfaceControl*>(stopAtObj);
+ sp<SurfaceControl> surface = client->mirrorSurface(mirrorOf, stopAt);
surface->incStrong((void *)nativeCreate);
return reinterpret_cast<jlong>(surface.get());
@@ -2718,7 +2722,7 @@
(void*)nativeWriteTransactionToParcel },
{"nativeClearTransaction", "(J)V",
(void*)nativeClearTransaction },
- {"nativeMirrorSurface", "(J)J",
+ {"nativeMirrorSurface", "(JJ)J",
(void*)nativeMirrorSurface },
{"nativeSetGlobalShadowSettings", "([F[FFFF)V",
(void*)nativeSetGlobalShadowSettings },
@@ -2818,6 +2822,8 @@
gStaticDisplayInfoClassInfo.installOrientation =
GetFieldIDOrDie(env, infoClazz, "installOrientation", "I");
+ gStaticDisplayInfoClassInfo.screenPartStatus =
+ GetFieldIDOrDie(env, infoClazz, "screenPartStatus", "I");
jclass dynamicInfoClazz = FindClassOrDie(env, "android/view/SurfaceControl$DynamicDisplayInfo");
gDynamicDisplayInfoClassInfo.clazz = MakeGlobalRefOrDie(env, dynamicInfoClazz);
gDynamicDisplayInfoClassInfo.ctor = GetMethodIDOrDie(env, dynamicInfoClazz, "<init>", "()V");
diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto
index 5831a0b..6a37cb0 100644
--- a/core/proto/android/providers/settings/secure.proto
+++ b/core/proto/android/providers/settings/secure.proto
@@ -114,6 +114,7 @@
optional SettingProto autoclick_revert_to_left_click = 65 [ (android.privacy).dest = DEST_AUTOMATIC ];
// Setting for accessibility magnification for cursor following mode.
optional SettingProto accessibility_magnification_cursor_following_mode = 66 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ optional SettingProto accessibility_magnification_magnify_nav_and_ime = 67 [ (android.privacy).dest = DEST_AUTOMATIC ];
}
optional Accessibility accessibility = 2;
diff --git a/core/res/Android.bp b/core/res/Android.bp
index ee246ea..50785d8 100644
--- a/core/res/Android.bp
+++ b/core/res/Android.bp
@@ -174,6 +174,7 @@
"android.media.tv.flags-aconfig",
"android.security.flags-aconfig",
"device_policy_aconfig_flags",
+ "devicelock-aconfig-flags",
"android.xr.flags-aconfig",
"com.android.hardware.input.input-aconfig",
"aconfig_trade_in_mode_flags",
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index c42336a..ea27e8c 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -8652,8 +8652,7 @@
<p>Not for use by third-party applications.
@hide -->
<permission android:name="android.permission.MANAGE_KEY_GESTURES"
- android:protectionLevel="signature|recents"
- android:featureFlag="com.android.hardware.input.manage_key_gestures" />
+ android:protectionLevel="signature|recents" />
<!-- Allows applications to register listeners for key activeness through
InputManagerService.
@@ -8672,6 +8671,17 @@
<permission android:name="android.permission.MANAGE_DEVICE_LOCK_STATE"
android:protectionLevel="internal|role" />
+ <!-- Allows an app to get the device lock enrollment type.
+ <p>This permission is only granted to system applications.
+ <p>Protection level: signature|privileged
+ @SystemApi
+ @FlaggedApi(com.android.devicelock.flags.Flags.FLAG_GET_ENROLLMENT_TYPE)
+ @hide
+ -->
+ <permission android:name="android.permission.GET_DEVICE_LOCK_ENROLLMENT_TYPE"
+ android:protectionLevel="signature|privileged"
+ android:featureFlag="com.android.devicelock.flags.get_enrollment_type" />
+
<!-- @SystemApi Required by a WearableSensingService to
ensure that only the caller with this permission can bind to it.
<p> Protection level: signature
diff --git a/core/res/res/color-night/shade_panel_bg_color.xml b/core/res/res/color-night/shade_panel_bg_color.xml
new file mode 100644
index 0000000..b43bcb5
--- /dev/null
+++ b/core/res/res/color-night/shade_panel_bg_color.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2025 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:color="@android:color/system_neutral1_500"
+ android:lStar="4" android:alpha="0.32"/>
+</selector>
diff --git a/core/res/res/color-night/shade_panel_fg_color.xml b/core/res/res/color-night/shade_panel_fg_color.xml
new file mode 100644
index 0000000..1bde319
--- /dev/null
+++ b/core/res/res/color-night/shade_panel_fg_color.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2025 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:color="@android:color/system_accent1_800"
+ android:alpha="0.32"/>
+</selector>
diff --git a/core/res/res/color/shade_panel_bg_color.xml b/core/res/res/color/shade_panel_bg_color.xml
new file mode 100644
index 0000000..b5678f9
--- /dev/null
+++ b/core/res/res/color/shade_panel_bg_color.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2025 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:color="@android:color/system_neutral1_500"
+ android:lStar="98" android:alpha="0.32"/>
+</selector>
diff --git a/core/res/res/color/shade_panel_fg_color.xml b/core/res/res/color/shade_panel_fg_color.xml
new file mode 100644
index 0000000..7080008
--- /dev/null
+++ b/core/res/res/color/shade_panel_fg_color.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2025 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:color="@android:color/system_accent1_100"
+ android:alpha="0.32"/>
+</selector>
diff --git a/core/res/res/color/shade_panel_scrim_color.xml b/core/res/res/color/shade_panel_scrim_color.xml
new file mode 100644
index 0000000..76e45ac
--- /dev/null
+++ b/core/res/res/color/shade_panel_scrim_color.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2025 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:color="#404040"
+ android:alpha="0.40"/>
+</selector>
diff --git a/core/res/res/drawable/notification_2025_conversation_icon_background.xml b/core/res/res/drawable/notification_2025_conversation_icon_background.xml
new file mode 100644
index 0000000..25668ae
--- /dev/null
+++ b/core/res/res/drawable/notification_2025_conversation_icon_background.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2025 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT 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="oval">
+ <solid android:color="#26FFFFFF" />
+</shape>
diff --git a/core/res/res/layout/accessibility_autoclick_scroll_panel.xml b/core/res/res/layout/accessibility_autoclick_scroll_panel.xml
index 5ba29f5..174109b 100644
--- a/core/res/res/layout/accessibility_autoclick_scroll_panel.xml
+++ b/core/res/res/layout/accessibility_autoclick_scroll_panel.xml
@@ -16,16 +16,16 @@
-->
<com.android.server.accessibility.autoclick.AutoclickLinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/accessibility_autoclick_scroll_panel"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
+ android:layout_width="180dp"
+ android:layout_height="184dp"
android:layout_gravity="center"
- android:orientation="vertical">
+ android:orientation="vertical"
+ android:background="@drawable/accessibility_autoclick_scroll_panel_container">
<RelativeLayout
android:layout_width="180dp"
android:layout_height="180dp"
- android:padding="3dp"
- android:background="@drawable/accessibility_autoclick_scroll_panel_container">
+ android:padding="3dp">
<!-- Up arrow at the top -->
<ImageButton
diff --git a/core/res/res/layout/accessibility_autoclick_type_panel.xml b/core/res/res/layout/accessibility_autoclick_type_panel.xml
index 39b37dc..2c32d15 100644
--- a/core/res/res/layout/accessibility_autoclick_type_panel.xml
+++ b/core/res/res/layout/accessibility_autoclick_type_panel.xml
@@ -43,7 +43,8 @@
<LinearLayout
android:id="@+id/accessibility_autoclick_long_press_layout"
- style="@style/AccessibilityAutoclickPanelButtonLayoutStyle">
+ style="@style/AccessibilityAutoclickPanelButtonLayoutStyle"
+ android:tooltipText="@string/accessibility_autoclick_long_press" >
<ImageButton
android:id="@+id/accessibility_autoclick_long_press_button"
@@ -55,67 +56,67 @@
<LinearLayout
android:id="@+id/accessibility_autoclick_drag_layout"
- style="@style/AccessibilityAutoclickPanelButtonLayoutStyle">
+ style="@style/AccessibilityAutoclickPanelButtonLayoutStyle"
+ android:tooltipText="@string/accessibility_autoclick_drag" >
<ImageButton
android:id="@+id/accessibility_autoclick_drag_button"
style="@style/AccessibilityAutoclickPanelImageButtonStyle"
android:contentDescription="@string/accessibility_autoclick_drag"
android:src="@drawable/accessibility_autoclick_drag"
- android:clickable="false"
- android:tooltipText="@string/accessibility_autoclick_drag" />
+ android:clickable="false"/>
</LinearLayout>
<LinearLayout
android:id="@+id/accessibility_autoclick_double_click_layout"
- style="@style/AccessibilityAutoclickPanelButtonLayoutStyle">
+ style="@style/AccessibilityAutoclickPanelButtonLayoutStyle"
+ android:tooltipText="@string/accessibility_autoclick_double_click" >
<ImageButton
android:id="@+id/accessibility_autoclick_double_click_button"
style="@style/AccessibilityAutoclickPanelImageButtonStyle"
android:contentDescription="@string/accessibility_autoclick_double_click"
android:src="@drawable/accessibility_autoclick_double_click"
- android:clickable="false"
- android:tooltipText="@string/accessibility_autoclick_double_click" />
+ android:clickable="false"/>
</LinearLayout>
<LinearLayout
android:id="@+id/accessibility_autoclick_right_click_layout"
- style="@style/AccessibilityAutoclickPanelButtonLayoutStyle">
+ style="@style/AccessibilityAutoclickPanelButtonLayoutStyle"
+ android:tooltipText="@string/accessibility_autoclick_right_click" >
<ImageButton
android:id="@+id/accessibility_autoclick_right_click_button"
style="@style/AccessibilityAutoclickPanelImageButtonStyle"
android:contentDescription="@string/accessibility_autoclick_right_click"
android:src="@drawable/accessibility_autoclick_right_click"
- android:clickable="false"
- android:tooltipText="@string/accessibility_autoclick_right_click" />
+ android:clickable="false"/>
</LinearLayout>
<LinearLayout
android:id="@+id/accessibility_autoclick_scroll_layout"
- style="@style/AccessibilityAutoclickPanelButtonLayoutStyle">
+ style="@style/AccessibilityAutoclickPanelButtonLayoutStyle"
+ android:tooltipText="@string/accessibility_autoclick_scroll" >
<ImageButton
android:id="@+id/accessibility_autoclick_scroll_button"
style="@style/AccessibilityAutoclickPanelImageButtonStyle"
android:contentDescription="@string/accessibility_autoclick_scroll"
android:src="@drawable/accessibility_autoclick_scroll"
- android:clickable="false"
- android:tooltipText="@string/accessibility_autoclick_scroll" />
+ android:clickable="false"/>
</LinearLayout>
<LinearLayout
android:id="@+id/accessibility_autoclick_left_click_layout"
- style="@style/AccessibilityAutoclickPanelButtonLayoutStyle">
+ style="@style/AccessibilityAutoclickPanelButtonLayoutStyle"
+ android:tooltipText="@string/accessibility_autoclick_left_click" >
<ImageButton
android:id="@+id/accessibility_autoclick_left_click_button"
style="@style/AccessibilityAutoclickPanelImageButtonStyle"
android:contentDescription="@string/accessibility_autoclick_left_click"
android:src="@drawable/accessibility_autoclick_left_click"
- android:clickable="false"
- android:tooltipText="@string/accessibility_autoclick_left_click" />
+ android:clickable="false"/>
</LinearLayout>
</LinearLayout>
@@ -130,28 +131,28 @@
<LinearLayout
android:id="@+id/accessibility_autoclick_pause_layout"
style="@style/AccessibilityAutoclickPanelButtonLayoutStyle"
- android:layout_marginEnd="@dimen/accessibility_autoclick_type_panel_button_spacing">
+ android:layout_marginEnd="@dimen/accessibility_autoclick_type_panel_button_spacing"
+ android:tooltipText="@string/accessibility_autoclick_pause" >
<ImageButton
android:id="@+id/accessibility_autoclick_pause_button"
style="@style/AccessibilityAutoclickPanelImageButtonStyle"
android:contentDescription="@string/accessibility_autoclick_pause"
android:src="@drawable/accessibility_autoclick_pause"
- android:clickable="false"
- android:tooltipText="@string/accessibility_autoclick_pause" />
+ android:clickable="false"/>
</LinearLayout>
<LinearLayout
android:id="@+id/accessibility_autoclick_position_layout"
- style="@style/AccessibilityAutoclickPanelButtonLayoutStyle">
+ style="@style/AccessibilityAutoclickPanelButtonLayoutStyle"
+ android:tooltipText="@string/accessibility_autoclick_position" >
<ImageButton
android:id="@+id/accessibility_autoclick_position_button"
style="@style/AccessibilityAutoclickPanelImageButtonStyle"
android:contentDescription="@string/accessibility_autoclick_position"
android:src="@drawable/accessibility_autoclick_position"
- android:clickable="false"
- android:tooltipText="@string/accessibility_autoclick_position" />
+ android:clickable="false"/>
</LinearLayout>
</LinearLayout>
diff --git a/core/res/res/layout/notification_2025_conversation_icon_container.xml b/core/res/res/layout/notification_2025_conversation_icon_container.xml
index 16c9500..eaed654 100644
--- a/core/res/res/layout/notification_2025_conversation_icon_container.xml
+++ b/core/res/res/layout/notification_2025_conversation_icon_container.xml
@@ -40,7 +40,7 @@
android:id="@+id/conversation_icon"
android:layout_width="@dimen/notification_2025_icon_circle_size"
android:layout_height="@dimen/notification_2025_icon_circle_size"
- android:background="@drawable/notification_icon_circle"
+ android:background="@drawable/notification_2025_conversation_icon_background"
android:clipToOutline="true"
android:scaleType="centerCrop"
android:importantForAccessibility="no"
diff --git a/core/res/res/layout/notification_2025_template_compact_heads_up_messaging.xml b/core/res/res/layout/notification_2025_template_compact_heads_up_messaging.xml
index 268396f..c6b7137 100644
--- a/core/res/res/layout/notification_2025_template_compact_heads_up_messaging.xml
+++ b/core/res/res/layout/notification_2025_template_compact_heads_up_messaging.xml
@@ -41,7 +41,7 @@
android:layout_height="@dimen/notification_icon_circle_size"
android:layout_gravity="center_vertical|start"
android:layout_marginStart="@dimen/notification_icon_circle_start"
- android:background="@drawable/notification_icon_circle"
+ android:background="@drawable/notification_2025_conversation_icon_background"
android:clipToOutline="true"
android:maxDrawableWidth="@dimen/notification_icon_circle_size"
android:maxDrawableHeight="@dimen/notification_icon_circle_size"
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index 5661103..8920b44 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -1971,7 +1971,7 @@
<string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
<string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"Batterybespaarder skakel Donkertema aan en beperk of skakel agtergrondaktiwiteit, sommige visuele effekte, sekere kenmerke en sommige netwerkverbindings af"</string>
<string name="battery_saver_description" msgid="8518809702138617167">"Batterybespaarder skakel Donkertema aan en beperk of skakel agtergrondaktiwiteit, sommige visuele effekte, sekere kenmerke en sommige netwerkverbindings af"</string>
- <string name="data_saver_description" msgid="4995164271550590517">"Databespaarder verhoed sommige apps om data in die agtergrond te stuur of te aanvaar om datagebruik te help verminder. \'n App wat jy tans gebruik kan by data ingaan, maar sal dit dalk minder gereeld doen. Dit kan byvoorbeeld beteken dat prente nie wys voordat jy op hulle tik nie."</string>
+ <string name="data_saver_description" msgid="4995164271550590517">"Databespaarder verhoed sommige apps om data op die agtergrond te stuur of te ontvang om datagebruik te help verminder. ’n App wat jy tans gebruik kan by data ingaan, maar sal dit dalk minder gereeld doen. Dit kan byvoorbeeld beteken dat prente nie wys voordat jy op hulle tik nie."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Skakel Databespaarder aan?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Skakel aan"</string>
<string name="zen_mode_duration_minutes_summary" msgid="4555514757230849789">"{count,plural, =1{Een minuut lank (tot {formattedTime})}other{# minute lank (tot {formattedTime})}}"</string>
@@ -2050,7 +2050,7 @@
<string name="user_creation_account_exists" msgid="2239146360099708035">"Laat <xliff:g id="APP">%1$s</xliff:g> toe om \'n nuwe gebruiker met <xliff:g id="ACCOUNT">%2$s</xliff:g> te skep (\'n gebruiker met hierdie rekening bestaan reeds)?"</string>
<string name="user_creation_adding" msgid="7305185499667958364">"Laat <xliff:g id="APP">%1$s</xliff:g> toe om \'n nuwe gebruiker met <xliff:g id="ACCOUNT">%2$s</xliff:g> te skep?"</string>
<string name="supervised_user_creation_label" msgid="6884904353827427515">"Voeg gebruiker onder toesig by"</string>
- <string name="language_selection_title" msgid="52674936078683285">"Voeg \'n taal by"</string>
+ <string name="language_selection_title" msgid="52674936078683285">"Voeg ’n taal by"</string>
<string name="country_selection_title" msgid="5221495687299014379">"Streekvoorkeur"</string>
<string name="search_language_hint" msgid="7004225294308793583">"Voer taalnaam in"</string>
<string name="language_picker_section_suggested" msgid="6556199184638990447">"Voorgestel"</string>
@@ -2272,6 +2272,7 @@
<string name="accessibility_autoclick_double_click" msgid="2103826849116176478">"Dubbelklik"</string>
<string name="accessibility_autoclick_drag" msgid="1499559489796843224">"Sleep"</string>
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Rollees"</string>
+ <string name="accessibility_autoclick_long_press" msgid="8757561680164116951">"Langdruk"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Onderbreek"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Posisie"</string>
<string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Rollees op"</string>
@@ -2556,11 +2557,14 @@
<string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Apps"</string>
<string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Jou vingerafdrukke kan nie meer herken word nie. Stel Vingerafdrukslot weer op."</string>
<string name="usb_apm_usb_plugged_in_when_locked_notification_title" msgid="468577168569874967">"USB-toestel is ingeprop wanneer dit gesluit is"</string>
- <string name="usb_apm_usb_plugged_in_when_locked_notification_text" msgid="6695268246267993166">"USB-toestel is ingeprop wanneer jou Android gesluit is. Om die toestel te gebruik, moet jy eers jou Android ontsluit en dan weer die USB-toestel insit om dit te gebruik."</string>
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_notification_text (5561957202444135984) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_no_replug_notification_text (3844834116914726455) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_low_power_charge_notification_text (1264294871764444170) -->
+ <skip />
<string name="usb_apm_usb_suspicious_activity_notification_title" msgid="3461195995882871461">"Verdagte USB-aktiwiteit"</string>
<string name="usb_apm_usb_suspicious_activity_notification_text" msgid="6537085605929303187">"USB-datasein is gedeaktiveer."</string>
- <!-- no translation found for fingerprint_frr_notification_title (3668331918920659093) -->
- <skip />
- <!-- no translation found for fingerprint_frr_notification_msg (2162966846624639252) -->
- <skip />
+ <string name="fingerprint_frr_notification_title" msgid="3668331918920659093">"Sukkel jy met Vingerafdrukslot?"</string>
+ <string name="fingerprint_frr_notification_msg" msgid="2162966846624639252">"Tik om te kyk na wenke om jou ervaring met ontsluiting te verbeter"</string>
</resources>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index 10b1e75..3adbc33 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -1729,7 +1729,7 @@
<string name="display_manager_overlay_display_title" msgid="1480158037150469170">"<xliff:g id="NAME">%1$s</xliff:g>፦ <xliff:g id="WIDTH">%2$d</xliff:g>x<xliff:g id="HEIGHT">%3$d</xliff:g>፣ <xliff:g id="DPI">%4$d</xliff:g> dpi"</string>
<string name="display_manager_overlay_display_secure_suffix" msgid="2810034719482834679">"፣ የተጠበቀ"</string>
<string name="kg_forgot_pattern_button_text" msgid="406145459223122537">"ስርዓተ ጥለቱን እርሳ"</string>
- <string name="kg_wrong_pattern" msgid="1342812634464179931">"የተሳሳተ ስርዓተ ጥለት"</string>
+ <string name="kg_wrong_pattern" msgid="1342812634464179931">"የተሳሳተ ሥርዓተ ጥለት"</string>
<string name="kg_wrong_password" msgid="2384677900494439426">"የተሳሳተ ይለፍ ቃል"</string>
<string name="kg_wrong_pin" msgid="3680925703673166482">"የተሳሳተ ፒን"</string>
<string name="kg_pattern_instructions" msgid="8366024510502517748">"ስርዓተ ጥለትዎን ይሳሉ"</string>
@@ -2272,6 +2272,7 @@
<string name="accessibility_autoclick_double_click" msgid="2103826849116176478">"ድርብ ጠቅ አድርግ"</string>
<string name="accessibility_autoclick_drag" msgid="1499559489796843224">"ጎትት"</string>
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"ሸብልል"</string>
+ <string name="accessibility_autoclick_long_press" msgid="8757561680164116951">"ረዥም መጫን"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"ባለበት አቁም"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"አቀማመጥ"</string>
<string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"ወደ ላይ ሸብልል"</string>
@@ -2556,11 +2557,14 @@
<string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"መተግበሪያዎች"</string>
<string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"ከእንግዲህ የጣት አሻራዎችዎ ሊለዩ አይችሉም። በጣት አሻራ መክፈቻን እንደገና ያዋቅሩ።"</string>
<string name="usb_apm_usb_plugged_in_when_locked_notification_title" msgid="468577168569874967">"ሲቆለፍ የUSB መሣሪያ ተሰክቷል።"</string>
- <string name="usb_apm_usb_plugged_in_when_locked_notification_text" msgid="6695268246267993166">"Android ሲቆለፍ የUSB መሣሪያ ተሰክቷል። መሣሪያ ለመጠቀም እባክዎ መጀመሪያ Androidን ይክፈቱ እና ከዚያም እሱን ለመጠቀም የUSB መሣሪያ እንደገና ያስገቡ።"</string>
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_notification_text (5561957202444135984) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_no_replug_notification_text (3844834116914726455) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_low_power_charge_notification_text (1264294871764444170) -->
+ <skip />
<string name="usb_apm_usb_suspicious_activity_notification_title" msgid="3461195995882871461">"አጠራጣሪ የUSB እንቅስቃሴ"</string>
<string name="usb_apm_usb_suspicious_activity_notification_text" msgid="6537085605929303187">"የUSB የውሂብ ምልክት ተሰናክሏል።"</string>
- <!-- no translation found for fingerprint_frr_notification_title (3668331918920659093) -->
- <skip />
- <!-- no translation found for fingerprint_frr_notification_msg (2162966846624639252) -->
- <skip />
+ <string name="fingerprint_frr_notification_title" msgid="3668331918920659093">"በጣት አሻራ መክፈቻ ችግር እያጋጠመዎት ነው?"</string>
+ <string name="fingerprint_frr_notification_msg" msgid="2162966846624639252">"የመክፈት ተሞክሮዎን ለማሻሻል ጠቃሚ ምክሮችን ለመገምገም መታ ያድርጉ"</string>
</resources>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 0a655fe..1bd9d1d 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -1457,7 +1457,7 @@
<string name="usb_unsupported_audio_accessory_title" msgid="2335775548086533065">"تم اكتشاف ملحق صوتي تناظري"</string>
<string name="usb_unsupported_audio_accessory_message" msgid="1300168007129796621">"الجهاز الذي تم توصيله بالهاتف غير متوافق معه. انقر للحصول على المزيد من المعلومات."</string>
<string name="adb_active_notification_title" msgid="408390247354560331">"تم توصيل USB لتصحيح أخطاء الجهاز"</string>
- <string name="adb_active_notification_message" msgid="5617264033476778211">"انقر لإيقاف تصحيح أخطاء الجهاز عبر USB"</string>
+ <string name="adb_active_notification_message" msgid="5617264033476778211">"يُرجى النقر لإيقاف تصحيح أخطاء الجهاز عبر USB"</string>
<string name="adb_active_notification_message" product="tv" msgid="6624498401272780855">"اختيار إيقاف تصحيح أخطاء USB."</string>
<string name="adbwifi_active_notification_title" msgid="6147343659168302473">"تم تفعيل ميزة \"تصحيح الأخطاء اللاسلكي\"."</string>
<string name="adbwifi_active_notification_message" msgid="930987922852867972">"انقر لإيقاف ميزة \"تصحيح الأخطاء اللاسلكي\"."</string>
@@ -2008,7 +2008,7 @@
<string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"من <xliff:g id="START">%1$s</xliff:g> إلى <xliff:g id="END">%2$s</xliff:g>"</string>
<string name="zen_mode_trigger_summary_combined" msgid="6492381546327807669">"<xliff:g id="DAYS">%1$s</xliff:g>، <xliff:g id="TIMES">%2$s</xliff:g>"</string>
<string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"أي تقويم"</string>
- <string name="muted_by" msgid="91464083490094950">"يعمل <xliff:g id="THIRD_PARTY">%1$s</xliff:g> على كتم بعض الأصوات."</string>
+ <string name="muted_by" msgid="91464083490094950">"يتم كتم بعض الأصوات بواسطة <xliff:g id="THIRD_PARTY">%1$s</xliff:g>."</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"حدثت مشكلة داخلية في جهازك، وقد لا يستقر وضعه حتى إجراء إعادة الضبط على الإعدادات الأصلية."</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"حدثت مشكلة داخلية في جهازك. يمكنك الاتصال بالمصنِّع للحصول على تفاصيل."</string>
<string name="stk_cc_ussd_to_dial" msgid="3139884150741157610">"تم تغيير طلب USSD إلى مكالمة عادية."</string>
@@ -2215,7 +2215,7 @@
<string name="nas_upgrade_notification_enable_action" msgid="3046406808378726874">"حسنًا"</string>
<string name="nas_upgrade_notification_disable_action" msgid="3794833210043497982">"إيقاف"</string>
<string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"مزيد من المعلومات"</string>
- <string name="nas_upgrade_notification_learn_more_content" msgid="3735480566983530650">"تم إبدال الإشعارات التكيُّفية لنظام التشغيل Android في الإصدار 12 منه بالإشعارات المحسّنة. تعرض هذه الميزة إجراءات وردودًا مقترحة وتنظِّم الإشعارات.\n\nيمكن للإشعارات المحسّنة الوصول إلى محتوى الإشعارات، بما في ذلك المعلومات الشخصية، مثلاً أسماء جهات الاتصال والرسائل. يمكن لهذه الميزة أيضًا إغلاق الإشعارات أو الاستجابة لها، مثلاً الردّ على مكالمات الهاتف والتحكّم في ميزة \"عدم الإزعاج\"."</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="3735480566983530650">"تم استبدال \"الإشعارات التكيُّفية\" بـ \"الإشعارات المحسّنة\" في الإصدار 12 من Android. تعرض هذه الميزة إجراءات وردودًا مقترحة وتنظِّم الإشعارات.\n\nتستطيع \"الإشعارات المحسّنة\" الوصول إلى محتوى الإشعارات، بما في ذلك المعلومات الشخصية، مثلاً أسماء جهات الاتصال والرسائل. ويمكنها أيضًا إغلاق الإشعارات أو الاستجابة لها، مثلاً الردّ على مكالمات الهاتف والتحكّم في ميزة \"عدم الإزعاج\"."</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"إشعار معلومات \"وضع سلسلة الإجراءات\""</string>
<string name="dynamic_mode_notification_title" msgid="1388718452788985481">"تم تفعيل ميزة توفير شحن البطارية"</string>
<string name="dynamic_mode_notification_summary" msgid="1639031262484979689">"يتم تقليل استخدام البطارية لإطالة عمرها."</string>
@@ -2276,6 +2276,7 @@
<string name="accessibility_autoclick_double_click" msgid="2103826849116176478">"النقر مرّتين"</string>
<string name="accessibility_autoclick_drag" msgid="1499559489796843224">"سحب"</string>
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"الانتقال للأسفل أو للأعلى"</string>
+ <string name="accessibility_autoclick_long_press" msgid="8757561680164116951">"الضغط مع الاستمرار"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"إيقاف مؤقت"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"تعديل الموضع"</string>
<string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"الانتقال للأعلى"</string>
@@ -2560,7 +2561,12 @@
<string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"التطبيقات"</string>
<string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"لم يعد بالإمكان التعرّف على بصمات أصابعك. يجب ضبط ميزة \"فتح الجهاز ببصمة الإصبع\" مجددًا."</string>
<string name="usb_apm_usb_plugged_in_when_locked_notification_title" msgid="468577168569874967">"تم توصيل جهاز USB عندما كان الجهاز مقفلاً"</string>
- <string name="usb_apm_usb_plugged_in_when_locked_notification_text" msgid="6695268246267993166">"تم توصيل جهاز USB عندما كان جهاز Android مقفلاً. لاستخدام الجهاز، يُرجى فتح قفل جهاز Android أولاً ثم إعادة إدخال جهاز USB لاستخدامه."</string>
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_notification_text (5561957202444135984) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_no_replug_notification_text (3844834116914726455) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_low_power_charge_notification_text (1264294871764444170) -->
+ <skip />
<string name="usb_apm_usb_suspicious_activity_notification_title" msgid="3461195995882871461">"نشاط مريب في جهاز USB"</string>
<string name="usb_apm_usb_suspicious_activity_notification_text" msgid="6537085605929303187">"تم إيقاف مؤشر بيانات USB."</string>
<string name="fingerprint_frr_notification_title" msgid="3668331918920659093">"هل هناك مشاكل متعلقة بميزة \"فتح الجهاز ببصمة الإصبع\"؟"</string>
diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml
index 85ad6db..dd5c0b2 100644
--- a/core/res/res/values-as/strings.xml
+++ b/core/res/res/values-as/strings.xml
@@ -89,7 +89,7 @@
<string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"জৰুৰীকালীন কল কৰাৰ সুবিধা উপলব্ধ নহয়"</string>
<string name="emergency_calling_do_not_show_again" msgid="5034171343309733068">"পুনৰাই নেদেখুৱাব"</string>
<string name="EmergencyCallWarningSummary" msgid="9102799172089265268">"জৰুৰীকালীন কল কৰিবলৈ ম’বাইল নেটৱৰ্কৰ প্ৰয়োজন"</string>
- <string name="notification_channel_network_alert" msgid="4788053066033851841">"সতৰ্কবাণীসমূহ"</string>
+ <string name="notification_channel_network_alert" msgid="4788053066033851841">"সতৰ্কবার্তা"</string>
<string name="notification_channel_call_forward" msgid="8230490317314272406">"কল ফৰৱাৰ্ডিং"</string>
<string name="notification_channel_emergency_callback" msgid="54074839059123159">"জৰুৰীকালীন ক\'লবেক ম\'ড"</string>
<string name="notification_channel_mobile_data_status" msgid="1941911162076442474">"ম’বাইল ডেটাৰ স্থিতি"</string>
@@ -305,7 +305,7 @@
<string name="notification_channel_vpn" msgid="1628529026203808999">"ভিপিএনৰ স্থিতি"</string>
<string name="notification_channel_system_time" msgid="1660313368058030441">"সময় আৰু সময় মণ্ডল"</string>
<string name="notification_channel_device_admin" msgid="6384932669406095506">"আপোনাৰ আইটি প্ৰশাসকৰ পৰা অহা সতৰ্কবাণী"</string>
- <string name="notification_channel_alerts" msgid="5070241039583668427">"সতৰ্কবাণীসমূহ"</string>
+ <string name="notification_channel_alerts" msgid="5070241039583668427">"সতৰ্কবার্তা"</string>
<string name="notification_channel_retail_mode" msgid="3732239154256431213">"খুচুৰা ডেম\'"</string>
<string name="notification_channel_usb" msgid="1528280969406244896">"ইউএছবি সংযোগ"</string>
<string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"এপ্ চলি আছে"</string>
@@ -2272,6 +2272,7 @@
<string name="accessibility_autoclick_double_click" msgid="2103826849116176478">"দুবাৰ ক্লিক"</string>
<string name="accessibility_autoclick_drag" msgid="1499559489796843224">"টানি আনি এৰক"</string>
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"স্ক্ৰ’ল কৰক"</string>
+ <string name="accessibility_autoclick_long_press" msgid="8757561680164116951">"দীঘলীয়া টিপক"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"পজ কৰক"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"স্থান"</string>
<string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"ওপৰলৈ স্ক্ৰ’ল কৰক"</string>
@@ -2556,7 +2557,12 @@
<string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"এপ্লিকেশ্বন"</string>
<string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"আপোনাৰ ফিংগাৰপ্ৰিণ্ট আৰু চিনাক্ত কৰিব নোৱাৰি। ফিংগাৰপ্ৰিণ্ট আনলক পুনৰ ছেট আপ কৰক।"</string>
<string name="usb_apm_usb_plugged_in_when_locked_notification_title" msgid="468577168569874967">"লক হৈ থাকোঁতে USB ডিভাইচ প্লাগ ইন কৰা হৈছে"</string>
- <string name="usb_apm_usb_plugged_in_when_locked_notification_text" msgid="6695268246267993166">"Android লক হৈ থাকোঁতে USB ডিভাইচ প্লাগ ইন কৰা হৈছে। ডিভাইচ ব্যৱহাৰ কৰিবলৈ অনুগ্ৰহ কৰি প্ৰথমে Android আনলক কৰক আৰু তাৰ পাছত USB ডিভাইচটো ব্যৱহাৰ কৰিবলৈ সেইটো পুনৰ ভৰাওক।"</string>
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_notification_text (5561957202444135984) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_no_replug_notification_text (3844834116914726455) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_low_power_charge_notification_text (1264294871764444170) -->
+ <skip />
<string name="usb_apm_usb_suspicious_activity_notification_title" msgid="3461195995882871461">"সন্দেহজনক USBৰ কাৰ্যকলাপ"</string>
<string name="usb_apm_usb_suspicious_activity_notification_text" msgid="6537085605929303187">"USB ডেটা ছিগনেল অক্ষম কৰা হৈছে।"</string>
<string name="fingerprint_frr_notification_title" msgid="3668331918920659093">"ফিংগাৰপ্ৰিন্ট আনলকৰ ক্ষেত্ৰত কিবা অসুবিধা পাইছে নেকি?"</string>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index 6e8443b..09a59e2 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -2272,6 +2272,7 @@
<string name="accessibility_autoclick_double_click" msgid="2103826849116176478">"İki dəfə toxunun"</string>
<string name="accessibility_autoclick_drag" msgid="1499559489796843224">"Çəkin"</string>
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Sürüşdürün"</string>
+ <string name="accessibility_autoclick_long_press" msgid="8757561680164116951">"Basıb saxlayın"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Durdurun"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Mövqe"</string>
<string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Yuxarı sürüşdürün"</string>
@@ -2556,11 +2557,14 @@
<string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Tətbiqlər"</string>
<string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Barmaq izlərinizi artıq tanımaq mümkün deyil. Barmaqla Kilidaçmanı yenidən ayarlayın."</string>
<string name="usb_apm_usb_plugged_in_when_locked_notification_title" msgid="468577168569874967">"Kilidli olduqda USB cihazı qoşulu olur"</string>
- <string name="usb_apm_usb_plugged_in_when_locked_notification_text" msgid="6695268246267993166">"Android kilidləndikdə USB cihazı qoşulu olur. Cihazdan istifadə etmək üçün əvvəlcə Android-i kiliddən çıxarın və sonra USB cihazını yenidən taxaraq ondan istifadə edin."</string>
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_notification_text (5561957202444135984) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_no_replug_notification_text (3844834116914726455) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_low_power_charge_notification_text (1264294871764444170) -->
+ <skip />
<string name="usb_apm_usb_suspicious_activity_notification_title" msgid="3461195995882871461">"Şübhəli USB fəaliyyəti"</string>
<string name="usb_apm_usb_suspicious_activity_notification_text" msgid="6537085605929303187">"USB data siqnalı deaktiv edilib."</string>
- <!-- no translation found for fingerprint_frr_notification_title (3668331918920659093) -->
- <skip />
- <!-- no translation found for fingerprint_frr_notification_msg (2162966846624639252) -->
- <skip />
+ <string name="fingerprint_frr_notification_title" msgid="3668331918920659093">"Barmaqla Kiliddən Açma ilə bağlı problem var?"</string>
+ <string name="fingerprint_frr_notification_msg" msgid="2162966846624639252">"Kiliddən çıxarma təcrübənizi təkmilləşdirmək üçün toxunaraq məsləhətləri nəzərdən keçirin"</string>
</resources>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index 57df9d4..f0e3a4a 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -2273,6 +2273,7 @@
<string name="accessibility_autoclick_double_click" msgid="2103826849116176478">"Dvaput kliknite"</string>
<string name="accessibility_autoclick_drag" msgid="1499559489796843224">"Prevucite"</string>
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Skrolujte"</string>
+ <string name="accessibility_autoclick_long_press" msgid="8757561680164116951">"Dug pritisak"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Pauziraj"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Pozicija"</string>
<string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Skroluj nagore"</string>
@@ -2557,7 +2558,12 @@
<string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Aplikacije"</string>
<string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Otisci prstiju više ne mogu da se prepoznaju. Ponovo podesite otključavanje otiskom prsta."</string>
<string name="usb_apm_usb_plugged_in_when_locked_notification_title" msgid="468577168569874967">"USB uređaj je priključen kada je Android zaključan"</string>
- <string name="usb_apm_usb_plugged_in_when_locked_notification_text" msgid="6695268246267993166">"USB uređaj je priključen kada je Android zaključan. Da biste koristili uređaj, prvo otključajte Android, pa ponovo ubacite USB uređaj da biste ga koristili."</string>
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_notification_text (5561957202444135984) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_no_replug_notification_text (3844834116914726455) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_low_power_charge_notification_text (1264294871764444170) -->
+ <skip />
<string name="usb_apm_usb_suspicious_activity_notification_title" msgid="3461195995882871461">"Sumnjiva USB aktivnost"</string>
<string name="usb_apm_usb_suspicious_activity_notification_text" msgid="6537085605929303187">"Signal za prenos podataka sa USB-a je onemogućen."</string>
<string name="fingerprint_frr_notification_title" msgid="3668331918920659093">"Imate problema sa otključavanjem otiskom prsta?"</string>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index fda094d..b8ee881 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -2274,6 +2274,7 @@
<string name="accessibility_autoclick_double_click" msgid="2103826849116176478">"Двайное націсканне"</string>
<string name="accessibility_autoclick_drag" msgid="1499559489796843224">"Перацягванне"</string>
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Гартанне"</string>
+ <string name="accessibility_autoclick_long_press" msgid="8757561680164116951">"Доўгае націсканне"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Прыпыніць"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Пазіцыя"</string>
<string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Прагартаць уверх"</string>
@@ -2558,7 +2559,12 @@
<string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Праграмы"</string>
<string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Вашы адбіткі пальцаў больш не распазнаюцца. Паўторна наладзьце разблакіроўку адбіткам пальца."</string>
<string name="usb_apm_usb_plugged_in_when_locked_notification_title" msgid="468577168569874967">"USB-прылада падключана, калі прылада заблакіравана"</string>
- <string name="usb_apm_usb_plugged_in_when_locked_notification_text" msgid="6695268246267993166">"USB-прылада падключана, калі прылада Android заблакіравана. Каб выкарыстоўваць прыладу, разблакіруйце прыладу Android і паўторна ўстаўце USB-прыладу."</string>
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_notification_text (5561957202444135984) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_no_replug_notification_text (3844834116914726455) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_low_power_charge_notification_text (1264294871764444170) -->
+ <skip />
<string name="usb_apm_usb_suspicious_activity_notification_title" msgid="3461195995882871461">"Падазроная актыўнасць на USB-прыладзе"</string>
<string name="usb_apm_usb_suspicious_activity_notification_text" msgid="6537085605929303187">"Сігнал даных USB адключаны."</string>
<string name="fingerprint_frr_notification_title" msgid="3668331918920659093">"Узніклі праблемы з разблакіроўкай адбіткам пальца?"</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 224d440..6593ab4 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -2272,6 +2272,7 @@
<string name="accessibility_autoclick_double_click" msgid="2103826849116176478">"Кликване два пъти"</string>
<string name="accessibility_autoclick_drag" msgid="1499559489796843224">"Преместване с плъзгане"</string>
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Превъртане"</string>
+ <string name="accessibility_autoclick_long_press" msgid="8757561680164116951">"Продължително натискане"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Пауза"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Позиция"</string>
<string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Превъртане нагоре"</string>
@@ -2556,11 +2557,14 @@
<string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Приложения"</string>
<string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Отпечатъците ви вече не могат да бъдат разпознати. Настройте отново „Отключване с отпечатък“."</string>
<string name="usb_apm_usb_plugged_in_when_locked_notification_title" msgid="468577168569874967">"USB устройството е свързано, когато устройството е заключено"</string>
- <string name="usb_apm_usb_plugged_in_when_locked_notification_text" msgid="6695268246267993166">"USB устройството е включено, когато устройството с Android е заключено. За да използвате устройството, първо отключете Android и след това поставете отново USB устройството."</string>
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_notification_text (5561957202444135984) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_no_replug_notification_text (3844834116914726455) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_low_power_charge_notification_text (1264294871764444170) -->
+ <skip />
<string name="usb_apm_usb_suspicious_activity_notification_title" msgid="3461195995882871461">"Подозрителна активност на USB"</string>
<string name="usb_apm_usb_suspicious_activity_notification_text" msgid="6537085605929303187">"Сигналът за данни през USB е деактивиран."</string>
- <!-- no translation found for fingerprint_frr_notification_title (3668331918920659093) -->
- <skip />
- <!-- no translation found for fingerprint_frr_notification_msg (2162966846624639252) -->
- <skip />
+ <string name="fingerprint_frr_notification_title" msgid="3668331918920659093">"Имате проблеми с функцията „Отключване с отпечатък“?"</string>
+ <string name="fingerprint_frr_notification_msg" msgid="2162966846624639252">"Докоснете, за да прегледате съвети за подобряване на практическата работа при отключване"</string>
</resources>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index fc630a8..2c0eb30 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -1799,7 +1799,7 @@
<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>
- <string name="color_inversion_feature_name" msgid="2672824491933264951">"কালার ইনভার্সন"</string>
+ <string name="color_inversion_feature_name" msgid="2672824491933264951">"রঙ ইনভার্সন"</string>
<string name="color_correction_feature_name" msgid="7975133554160979214">"রঙ সংশোধন করা"</string>
<string name="one_handed_mode_feature_name" msgid="2334330034828094891">"এক হাতে ব্যবহার করার মোড"</string>
<string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"অতিরিক্ত কম উজ্জ্বলতা"</string>
@@ -2272,6 +2272,7 @@
<string name="accessibility_autoclick_double_click" msgid="2103826849116176478">"ডবল ক্লিক করুন"</string>
<string name="accessibility_autoclick_drag" msgid="1499559489796843224">"টেনে আনুন"</string>
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"স্ক্রল করুন"</string>
+ <string name="accessibility_autoclick_long_press" msgid="8757561680164116951">"বেশিক্ষণ প্রেস করে রাখুন"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"পজ করুন"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"পজিশন"</string>
<string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"উপর দিকে স্ক্রল করুন"</string>
@@ -2556,11 +2557,11 @@
<string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"অ্যাপ্লিকেশন"</string>
<string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"আপনার ফিঙ্গারপ্রিন্ট আর শনাক্ত করা যাবে না। \'ফিঙ্গারপ্রিন্ট আনলক\' ফিচার আবার সেট-আপ করুন।"</string>
<string name="usb_apm_usb_plugged_in_when_locked_notification_title" msgid="468577168569874967">"লক থাকাকালীন USB ডিভাইস প্লাগ-ইন করা হয়েছে"</string>
- <string name="usb_apm_usb_plugged_in_when_locked_notification_text" msgid="6695268246267993166">"Android লক থাকাকালীন USB ডিভাইস প্লাগ-ইন করা হয়েছে। ডিভাইস ব্যবহার করতে, প্রথমে Android আনলক করুন এবং তারপর সেটি ব্যবহার করতে USB ডিভাইস আবার যোগ করুন।"</string>
+ <string name="usb_apm_usb_plugged_in_when_locked_notification_text" msgid="5561957202444135984">"Android লক থাকাকালীন USB ডিভাইস প্লাগ-ইন করা হয়েছে। ডিভাইস ব্যবহার করার জন্য, প্রথমে Android আনলক করুন।"</string>
+ <string name="usb_apm_usb_plugged_in_when_locked_no_replug_notification_text" msgid="3844834116914726455">"Android লক থাকাকালীন USB ডিভাইস প্লাগ-ইন করা হয়েছে। ডিভাইস ব্যবহার করার জন্য, প্রথমে Android আনলক করুন এবং তারপর সেটি ব্যবহার করতে USB ডিভাইস আবার যোগ করুন।"</string>
+ <string name="usb_apm_usb_plugged_in_when_locked_low_power_charge_notification_text" msgid="1264294871764444170">"লক করা থাকলে চার্জ করার ক্ষমতা কমে যেতে পারে অথবা সেই সুবিধা উপলভ্য নাও থাকতে পারে।"</string>
<string name="usb_apm_usb_suspicious_activity_notification_title" msgid="3461195995882871461">"সন্দেহজনক USB অ্যাক্টিভিটি"</string>
<string name="usb_apm_usb_suspicious_activity_notification_text" msgid="6537085605929303187">"USB ডেটা সিগন্যাল বন্ধ করা হয়েছে।"</string>
- <!-- no translation found for fingerprint_frr_notification_title (3668331918920659093) -->
- <skip />
- <!-- no translation found for fingerprint_frr_notification_msg (2162966846624639252) -->
- <skip />
+ <string name="fingerprint_frr_notification_title" msgid="3668331918920659093">"\'ফিঙ্গারপ্রিন্ট আনলক\' ফিচারে সমস্যা হচ্ছে?"</string>
+ <string name="fingerprint_frr_notification_msg" msgid="2162966846624639252">"আপনার আনলক করার অভিজ্ঞতা উন্নত করতে রিভিউ সংক্রান্ত পরামর্শে ট্যাপ করুন"</string>
</resources>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index cc0d82c..fbe6675 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -1395,7 +1395,7 @@
<string name="network_switch_metered_detail" msgid="1358296010128405906">"Kada <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> nema pristup internetu, uređaj koristi mrežu <xliff:g id="NEW_NETWORK">%1$s</xliff:g>. Moguća je naplata usluge."</string>
<string name="network_switch_metered_toast" msgid="501662047275723743">"Prebačeno iz mreže <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> u <xliff:g id="NEW_NETWORK">%2$s</xliff:g> mrežu"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"prijenos podataka na mobilnoj mreži"</item>
+ <item msgid="2255670471736226365">"prenos podataka na mobilnoj mreži"</item>
<item msgid="5520925862115353992">"WiFi"</item>
<item msgid="1055487873974272842">"Bluetooth"</item>
<item msgid="1616528372438698248">"Ethernet"</item>
@@ -2212,7 +2212,7 @@
<string name="nas_upgrade_notification_enable_action" msgid="3046406808378726874">"Uredu"</string>
<string name="nas_upgrade_notification_disable_action" msgid="3794833210043497982">"Isključi"</string>
<string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"Saznajte više"</string>
- <string name="nas_upgrade_notification_learn_more_content" msgid="3735480566983530650">"Poboljšana obavještenja su zamijenila prilagodljiva obavještenja Androida u verziji Android 12. Ova funkcija prikazuje predložene radnje i odgovore te organizira vaša obavještenja.\n\nPoboljšana obavještenja mogu pristupiti sadržaju obavještenja, uključujući lične informacije kao što su imena kontakata i poruke. Ova funkcija također može odbacivati obavještenja ili reagirati na njih, npr. može odgovarati na telefonske pozive i kontrolirati funkciju Ne ometaj."</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="3735480566983530650">"Poboljšana obavještenja su zamijenila Androidova prilagodljiva obavještenja u Androidu 12. Ova funkcija prikazuje predložene radnje i odgovore te organizira vaša obavještenja.\n\nPoboljšana obavještenja mogu pristupiti sadržaju obavještenja, uključujući lične informacije kao što su imena kontakata i poruke. Ova funkcija također može odbacivati obavještenja ili reagirati na njih, npr. može odgovarati na telefonske pozive i kontrolirati funkciju Ne ometaj."</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Obavještenje za informacije Rutinskog načina"</string>
<string name="dynamic_mode_notification_title" msgid="1388718452788985481">"Ušteda baterije je uključena"</string>
<string name="dynamic_mode_notification_summary" msgid="1639031262484979689">"Smanjena je potrošnja baterije da se produži vijek trajanja baterije"</string>
@@ -2273,6 +2273,7 @@
<string name="accessibility_autoclick_double_click" msgid="2103826849116176478">"Dvostruki klik"</string>
<string name="accessibility_autoclick_drag" msgid="1499559489796843224">"Prevlačenje"</string>
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Klizanje"</string>
+ <string name="accessibility_autoclick_long_press" msgid="8757561680164116951">"Dugi pritisak"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Pauziraj"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Položaj"</string>
<string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Klizanje nagore"</string>
@@ -2557,11 +2558,14 @@
<string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Aplikacije"</string>
<string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Vaši otisci prstiju se više ne mogu prepoznavati. Ponovo postavite otključavanje otiskom prsta."</string>
<string name="usb_apm_usb_plugged_in_when_locked_notification_title" msgid="468577168569874967">"USB uređaj je priključen dok je uređaj bio zaključan"</string>
- <string name="usb_apm_usb_plugged_in_when_locked_notification_text" msgid="6695268246267993166">"USB uređaj je priključen dok je Android bio zaključan. Da koristite uređaj, prvo otključajte Android, a zatim ponovo umetnite USB uređaj da ga koristite."</string>
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_notification_text (5561957202444135984) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_no_replug_notification_text (3844834116914726455) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_low_power_charge_notification_text (1264294871764444170) -->
+ <skip />
<string name="usb_apm_usb_suspicious_activity_notification_title" msgid="3461195995882871461">"Sumnjiva aktivnost USB-a"</string>
<string name="usb_apm_usb_suspicious_activity_notification_text" msgid="6537085605929303187">"Podatkovni signal USB-a je onemogućen."</string>
- <!-- no translation found for fingerprint_frr_notification_title (3668331918920659093) -->
- <skip />
- <!-- no translation found for fingerprint_frr_notification_msg (2162966846624639252) -->
- <skip />
+ <string name="fingerprint_frr_notification_title" msgid="3668331918920659093">"Imate li problem s otključavanjem otiskom prsta?"</string>
+ <string name="fingerprint_frr_notification_msg" msgid="2162966846624639252">"Dodirnite da pregledate savjete za poboljšanje iskustva otključavanja"</string>
</resources>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 08b03bb..32e4888 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -867,11 +867,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Supervisar els intents de desbloqueig de la pantalla"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Supervisa el nombre de contrasenyes incorrectes introduïdes per desbloquejar la pantalla i bloqueja la tauleta o esborra\'n totes les dades si s\'introdueixen massa contrasenyes incorrectes."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Supervisa el nombre de contrasenyes incorrectes introduïdes per desbloquejar la pantalla i bloqueja el dispositiu Android TV o esborra\'n totes les dades si s\'introdueixen massa contrasenyes incorrectes."</string>
- <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Supervisa el nombre de contrasenyes incorrectes introduïdes per desbloquejar la pantalla i bloqueja el sistema d\'informació i entreteniment o esborra\'n totes les dades si s\'introdueixen massa contrasenyes incorrectes."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Supervisa el nombre de contrasenyes incorrectes introduïdes per desbloquejar la pantalla i bloqueja el sistema d\'infoentreteniment o esborra\'n totes les dades si s\'introdueixen massa contrasenyes incorrectes."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Supervisa el nombre de contrasenyes incorrectes introduïdes per desbloquejar la pantalla i bloqueja el telèfon o esborra\'n totes les dades si s\'introdueixen massa contrasenyes incorrectes."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Supervisa el nombre de contrasenyes incorrectes introduïdes per desbloquejar la pantalla i bloqueja la tauleta o esborra totes les dades d\'aquest usuari si s\'introdueixen massa contrasenyes incorrectes."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Supervisa el nombre de contrasenyes incorrectes introduïdes per desbloquejar la pantalla i bloqueja el dispositiu Android TV o esborra totes les dades d\'aquest usuari si s\'introdueixen massa contrasenyes incorrectes."</string>
- <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Supervisa el nombre de contrasenyes incorrectes introduïdes per desbloquejar la pantalla i bloqueja el sistema d\'informació i entreteniment o esborra totes les dades d\'aquest perfil si s\'introdueixen massa contrasenyes incorrectes."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Supervisa el nombre de contrasenyes incorrectes introduïdes per desbloquejar la pantalla i bloqueja el sistema d\'infoentreteniment o esborra totes les dades d\'aquest perfil si s\'introdueixen massa contrasenyes incorrectes."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Supervisa el nombre de contrasenyes incorrectes introduïdes per desbloquejar la pantalla i bloqueja el telèfon o esborra totes les dades d\'aquest usuari si s\'introdueixen massa contrasenyes incorrectes."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Canviar el bloqueig de pantalla"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Canvia el bloqueig de pantalla."</string>
@@ -880,13 +880,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Esborrar totes les dades"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Esborra les dades de la tauleta sense avisar mitjançant el restabliment de les dades de fàbrica."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Suprimeix les dades del dispositiu Android TV sense previ avís mitjançant el restabliment de les dades de fàbrica."</string>
- <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Esborra les dades del sistema d\'informació i entreteniment sense avisar mitjançant el restabliment de les dades de fàbrica."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Esborra les dades del sistema d\'infoentreteniment sense avisar mitjançant el restabliment de les dades de fàbrica."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Esborra les dades del telèfon sense avisar mitjançant el restabliment de les dades de fàbrica."</string>
<string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Esborra les dades del perfil"</string>
<string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Esborrar les dades de l\'usuari"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Esborra les dades de l\'usuari desades a la tauleta sense avisar-ne."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Esborra les dades de l\'usuari desades al dispositiu Android TV sense previ avís."</string>
- <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Esborra les dades del perfil d\'aquest sistema d\'informació i entreteniment sense avisar."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Esborra les dades del perfil d\'aquest sistema d\'infoentreteniment sense avisar."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Esborra les dades de l\'usuari desades al telèfon sense avisar-ne."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Definir el servidor intermediari global del dispositiu"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Si la política s\'activa, s\'utilitza el servidor intermediari global del dispositiu. Només el propietari del dispositiu el pot establir."</string>
@@ -2273,6 +2273,7 @@
<string name="accessibility_autoclick_double_click" msgid="2103826849116176478">"Fes doble clic"</string>
<string name="accessibility_autoclick_drag" msgid="1499559489796843224">"Arrossega"</string>
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Desplaça"</string>
+ <string name="accessibility_autoclick_long_press" msgid="8757561680164116951">"Mantén premut"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Posa en pausa"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Posició"</string>
<string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Desplaça\'t cap amunt"</string>
@@ -2557,11 +2558,14 @@
<string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Aplicacions"</string>
<string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Les teves empremtes digitals ja no es poden reconèixer. Torna a configurar Desbloqueig amb empremta digital."</string>
<string name="usb_apm_usb_plugged_in_when_locked_notification_title" msgid="468577168569874967">"S\'ha connectat un dispositiu USB quan el dispositiu estava bloquejat"</string>
- <string name="usb_apm_usb_plugged_in_when_locked_notification_text" msgid="6695268246267993166">"El dispositiu USB està connectat quan Android està bloquejat. Per utilitzar el dispositiu, primer desbloqueja Android i, a continuació, torna a inserir el dispositiu USB per utilitzar-lo."</string>
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_notification_text (5561957202444135984) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_no_replug_notification_text (3844834116914726455) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_low_power_charge_notification_text (1264294871764444170) -->
+ <skip />
<string name="usb_apm_usb_suspicious_activity_notification_title" msgid="3461195995882871461">"Activitat USB sospitosa"</string>
<string name="usb_apm_usb_suspicious_activity_notification_text" msgid="6537085605929303187">"El senyal de dades per USB s\'ha desactivat."</string>
- <!-- no translation found for fingerprint_frr_notification_title (3668331918920659093) -->
- <skip />
- <!-- no translation found for fingerprint_frr_notification_msg (2162966846624639252) -->
- <skip />
+ <string name="fingerprint_frr_notification_title" msgid="3668331918920659093">"Tens problemes amb Desbloqueig amb empremta digital?"</string>
+ <string name="fingerprint_frr_notification_msg" msgid="2162966846624639252">"Toca per revisar consells i millorar l\'experiència de desbloqueig"</string>
</resources>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 3a62ae4..9b00319 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -1805,7 +1805,7 @@
<string name="color_correction_feature_name" msgid="7975133554160979214">"Korekce barev"</string>
<string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Režim jedné ruky"</string>
<string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Velmi tmavé zobrazení"</string>
- <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Naslouchátka"</string>
+ <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Sluchové pomůcky"</string>
<string name="autoclick_feature_name" msgid="8149248738736949630">"Automatické kliknutí"</string>
<string name="hearing_device_status_disconnected" msgid="497547752953543832">"Odpojeno"</string>
<string name="hearing_device_status_connected" msgid="2149385149669918764">"Připojeno"</string>
@@ -2006,7 +2006,7 @@
<string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> – <xliff:g id="END">%2$s</xliff:g>"</string>
<string name="zen_mode_trigger_summary_combined" msgid="6492381546327807669">"<xliff:g id="DAYS">%1$s</xliff:g>, <xliff:g id="TIMES">%2$s</xliff:g>"</string>
<string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"V libovolném kalendáři"</string>
- <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> vypíná určité zvuky"</string>
+ <string name="muted_by" msgid="91464083490094950">"Hodinky <xliff:g id="THIRD_PARTY">%1$s</xliff:g> některé zvuky vypínají."</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"V zařízení došlo k internímu problému. Dokud neprovedete obnovení továrních dat, může být nestabilní."</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"V zařízení došlo k internímu problému. Další informace vám sdělí výrobce."</string>
<string name="stk_cc_ussd_to_dial" msgid="3139884150741157610">"Požadavek USSD byl změněn na běžný hovor"</string>
@@ -2274,6 +2274,7 @@
<string name="accessibility_autoclick_double_click" msgid="2103826849116176478">"Dvojité kliknutí"</string>
<string name="accessibility_autoclick_drag" msgid="1499559489796843224">"Přetažení"</string>
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Posunutí"</string>
+ <string name="accessibility_autoclick_long_press" msgid="8757561680164116951">"Dlouhé stisknutí"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Pozastavit"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Pozice"</string>
<string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Posunout nahoru"</string>
@@ -2558,11 +2559,14 @@
<string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Aplikace"</string>
<string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Vaše otisky prstů se nedaří rozpoznat. Nastavte odemknutí otiskem prstu znovu."</string>
<string name="usb_apm_usb_plugged_in_when_locked_notification_title" msgid="468577168569874967">"Zařízení USB bylo připojeno, když bylo zařízení zamknuté"</string>
- <string name="usb_apm_usb_plugged_in_when_locked_notification_text" msgid="6695268246267993166">"Zařízení USB bylo připojeno, když byl Android zamknutý. Pokud zařízení chcete používat, nejdříve Android odemkněte a pak zařízení USB připojte znovu."</string>
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_notification_text (5561957202444135984) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_no_replug_notification_text (3844834116914726455) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_low_power_charge_notification_text (1264294871764444170) -->
+ <skip />
<string name="usb_apm_usb_suspicious_activity_notification_title" msgid="3461195995882871461">"Podezřelá aktivita USB"</string>
<string name="usb_apm_usb_suspicious_activity_notification_text" msgid="6537085605929303187">"Datový signál USB byl deaktivován."</string>
- <!-- no translation found for fingerprint_frr_notification_title (3668331918920659093) -->
- <skip />
- <!-- no translation found for fingerprint_frr_notification_msg (2162966846624639252) -->
- <skip />
+ <string name="fingerprint_frr_notification_title" msgid="3668331918920659093">"Máte s odemknutím otiskem prstu potíže?"</string>
+ <string name="fingerprint_frr_notification_msg" msgid="2162966846624639252">"Klepnutím zobrazíte tipy pro snazší odemykání"</string>
</resources>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 080b885..c6249dc 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -1800,8 +1800,8 @@
<string name="disable_accessibility_shortcut" msgid="5806091378745232383">"Deaktiver genvej"</string>
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Brug genvej"</string>
<string name="color_inversion_feature_name" msgid="2672824491933264951">"Ombytning af farver"</string>
- <string name="color_correction_feature_name" msgid="7975133554160979214">"Farvekorrigering"</string>
- <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Enhåndstilstand"</string>
+ <string name="color_correction_feature_name" msgid="7975133554160979214">"Farvekorrigering"</string>
+ <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Enhåndstilstand"</string>
<string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Ekstra dæmpet belysning"</string>
<string name="hearing_aids_feature_name" msgid="1125892105105852542">"Høreapparater"</string>
<string name="autoclick_feature_name" msgid="8149248738736949630">"Autoklik"</string>
@@ -2272,6 +2272,7 @@
<string name="accessibility_autoclick_double_click" msgid="2103826849116176478">"Dobbeltklik"</string>
<string name="accessibility_autoclick_drag" msgid="1499559489796843224">"Træk"</string>
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Rul"</string>
+ <string name="accessibility_autoclick_long_press" msgid="8757561680164116951">"Langt tryk"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Sæt på pause"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Placering"</string>
<string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Rul op"</string>
@@ -2556,11 +2557,14 @@
<string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Apps"</string>
<string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Dine fingeraftryk kan ikke længere genkendes. Konfigurer fingeroplåsning igen."</string>
<string name="usb_apm_usb_plugged_in_when_locked_notification_title" msgid="468577168569874967">"USB-enheden er tilsluttet, når Android er låst"</string>
- <string name="usb_apm_usb_plugged_in_when_locked_notification_text" msgid="6695268246267993166">"USB-enheden er tilsluttet, når Android er låst. Hvis du vil bruge enheden, skal du først låse Android op og derefter tilslutte USB-enheden."</string>
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_notification_text (5561957202444135984) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_no_replug_notification_text (3844834116914726455) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_low_power_charge_notification_text (1264294871764444170) -->
+ <skip />
<string name="usb_apm_usb_suspicious_activity_notification_title" msgid="3461195995882871461">"Mistænkelig USB-aktivitet"</string>
<string name="usb_apm_usb_suspicious_activity_notification_text" msgid="6537085605929303187">"USB-datasignalet er blevet deaktiveret."</string>
- <!-- no translation found for fingerprint_frr_notification_title (3668331918920659093) -->
- <skip />
- <!-- no translation found for fingerprint_frr_notification_msg (2162966846624639252) -->
- <skip />
+ <string name="fingerprint_frr_notification_title" msgid="3668331918920659093">"Har du problemer med fingeroplåsning?"</string>
+ <string name="fingerprint_frr_notification_msg" msgid="2162966846624639252">"Tryk for at få tips til, hvordan du forbedrer oplåsningsoplevelsen"</string>
</resources>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index cff6a0b..839b357 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -1971,7 +1971,7 @@
<string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
<string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"Der Energiesparmodus aktiviert das dunkle Design. Hintergrundaktivitäten, einige Funktionen und optische Effekte und manche Netzwerkverbindungen werden eingeschränkt oder deaktiviert."</string>
<string name="battery_saver_description" msgid="8518809702138617167">"Der Energiesparmodus aktiviert das dunkle Design. Hintergrundaktivitäten, einige Funktionen und optische Effekte sowie manche Netzwerkverbindungen werden eingeschränkt oder deaktiviert."</string>
- <string name="data_saver_description" msgid="4995164271550590517">"Der Datensparmodus verhindert, dass manche Apps im Hintergrund Daten senden oder empfangen, sodass weniger Daten verbraucht werden. Auch werden die Datenzugriffe der gerade aktiven App eingeschränkt, was z. B. dazu führen kann, dass Bilder erst angetippt werden müssen, bevor sie sichtbar werden."</string>
+ <string name="data_saver_description" msgid="4995164271550590517">"Der Datensparmodus verhindert, dass manche Apps im Hintergrund Daten senden oder empfangen. So werden weniger Daten verbraucht. Auch die Datenzugriffe der gerade aktiven App werden eingeschränkt, was etwa dazu führen kann, dass Bilder erst angetippt werden müssen, bevor sie sichtbar werden."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Datensparmodus aktivieren?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Aktivieren"</string>
<string name="zen_mode_duration_minutes_summary" msgid="4555514757230849789">"{count,plural, =1{Für eine Minute (bis {formattedTime})}other{Für # Minuten (bis {formattedTime})}}"</string>
@@ -2004,7 +2004,7 @@
<string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> bis <xliff:g id="END">%2$s</xliff:g>"</string>
<string name="zen_mode_trigger_summary_combined" msgid="6492381546327807669">"<xliff:g id="DAYS">%1$s</xliff:g>, <xliff:g id="TIMES">%2$s</xliff:g>"</string>
<string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Alle Kalender"</string>
- <string name="muted_by" msgid="91464083490094950">"Einige Töne werden von <xliff:g id="THIRD_PARTY">%1$s</xliff:g> stummgeschaltet"</string>
+ <string name="muted_by" msgid="91464083490094950">"Stummschaltung einiger Töne durch <xliff:g id="THIRD_PARTY">%1$s</xliff:g>"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"Es liegt ein internes Problem mit deinem Gerät vor. Möglicherweise verhält es sich instabil, bis du es auf die Werkseinstellungen zurücksetzt."</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"Es liegt ein internes Problem mit deinem Gerät vor. Bitte wende dich diesbezüglich an den Hersteller."</string>
<string name="stk_cc_ussd_to_dial" msgid="3139884150741157610">"USSD-Anfrage wurde in normalen Anruf geändert"</string>
@@ -2272,6 +2272,7 @@
<string name="accessibility_autoclick_double_click" msgid="2103826849116176478">"Doppelklicken"</string>
<string name="accessibility_autoclick_drag" msgid="1499559489796843224">"Ziehen"</string>
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Scrollen"</string>
+ <string name="accessibility_autoclick_long_press" msgid="8757561680164116951">"Lange drücken"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Pausieren"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Position"</string>
<string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Nach oben scrollen"</string>
@@ -2556,11 +2557,14 @@
<string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Anwendungen"</string>
<string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Deine Fingerabdrücke können nicht mehr erkannt werden. Bitte richte die Entsperrung per Fingerabdruck neu ein."</string>
<string name="usb_apm_usb_plugged_in_when_locked_notification_title" msgid="468577168569874967">"USB-Gerät wurde angeschlossen, als das Android-Gerät gesperrt war"</string>
- <string name="usb_apm_usb_plugged_in_when_locked_notification_text" msgid="6695268246267993166">"Das USB-Gerät wurde angeschlossen, als das Android-Gerät gesperrt war. Du musst erst das Android-Gerät entsperren und dann das USB-Gerät noch einmal anschließen, damit du es verwenden kannst."</string>
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_notification_text (5561957202444135984) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_no_replug_notification_text (3844834116914726455) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_low_power_charge_notification_text (1264294871764444170) -->
+ <skip />
<string name="usb_apm_usb_suspicious_activity_notification_title" msgid="3461195995882871461">"Verdächtige USB-Aktivitäten"</string>
<string name="usb_apm_usb_suspicious_activity_notification_text" msgid="6537085605929303187">"USB-Datensignal wurde deaktiviert."</string>
- <!-- no translation found for fingerprint_frr_notification_title (3668331918920659093) -->
- <skip />
- <!-- no translation found for fingerprint_frr_notification_msg (2162966846624639252) -->
- <skip />
+ <string name="fingerprint_frr_notification_title" msgid="3668331918920659093">"Probleme mit der Entsperrung per Fingerabdruck?"</string>
+ <string name="fingerprint_frr_notification_msg" msgid="2162966846624639252">"Tippe, um Tipps zur optimalen Nutzung der Entsperrung zu erhalten"</string>
</resources>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 5c61fe1..6f96960 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -2272,6 +2272,7 @@
<string name="accessibility_autoclick_double_click" msgid="2103826849116176478">"Διπλό κλικ"</string>
<string name="accessibility_autoclick_drag" msgid="1499559489796843224">"Μεταφορά"</string>
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Κύλιση"</string>
+ <string name="accessibility_autoclick_long_press" msgid="8757561680164116951">"Παρατεταμένο πάτημα"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Παύση"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Θέση"</string>
<string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Κύλιση προς τα επάνω"</string>
@@ -2556,11 +2557,14 @@
<string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Εφαρμογές"</string>
<string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Δεν είναι δυνατή πλέον η αναγνώριση των δακτυλικών αποτυπωμάτων σας. Ρυθμίστε ξανά τη λειτουργία Ξεκλείδωμα με δακτυλικό αποτύπωμα."</string>
<string name="usb_apm_usb_plugged_in_when_locked_notification_title" msgid="468577168569874967">"Σύνδεση συσκευής USB σε κατάσταση κλειδώματος"</string>
- <string name="usb_apm_usb_plugged_in_when_locked_notification_text" msgid="6695268246267993166">"Η συσκευή USB είναι συνδεδεμένη, όταν το Android είναι κλειδωμένο. Για να χρησιμοποιήσετε τη συσκευή, ξεκλειδώστε πρώτα το Android και, στη συνέχεια, επανατοποθετήστε τη συσκευή USB για να τη χρησιμοποιήσετε."</string>
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_notification_text (5561957202444135984) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_no_replug_notification_text (3844834116914726455) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_low_power_charge_notification_text (1264294871764444170) -->
+ <skip />
<string name="usb_apm_usb_suspicious_activity_notification_title" msgid="3461195995882871461">"Ύποπτη δραστηριότητα USB"</string>
<string name="usb_apm_usb_suspicious_activity_notification_text" msgid="6537085605929303187">"Το σήμα δεδομένων USB έχει απενεργοποιηθεί."</string>
- <!-- no translation found for fingerprint_frr_notification_title (3668331918920659093) -->
- <skip />
- <!-- no translation found for fingerprint_frr_notification_msg (2162966846624639252) -->
- <skip />
+ <string name="fingerprint_frr_notification_title" msgid="3668331918920659093">"Αντιμετωπίζετε προβλήματα με τη λειτουργία Ξεκλείδωμα με δακτυλικό αποτύπωμα;"</string>
+ <string name="fingerprint_frr_notification_msg" msgid="2162966846624639252">"Πατήστε για να ελέγξετε συμβουλές για τη βελτίωση της εμπειρίας ξεκλειδώματος"</string>
</resources>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index 1ee5661..b30e074 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -2272,6 +2272,7 @@
<string name="accessibility_autoclick_double_click" msgid="2103826849116176478">"Double-click"</string>
<string name="accessibility_autoclick_drag" msgid="1499559489796843224">"Drag"</string>
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Scroll"</string>
+ <string name="accessibility_autoclick_long_press" msgid="8757561680164116951">"Long press"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Pause"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Position"</string>
<string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Scroll up"</string>
@@ -2556,11 +2557,14 @@
<string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Applications"</string>
<string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Your fingerprints can no longer be recognised. Set up Fingerprint Unlock again."</string>
<string name="usb_apm_usb_plugged_in_when_locked_notification_title" msgid="468577168569874967">"USB device plugged in when locked"</string>
- <string name="usb_apm_usb_plugged_in_when_locked_notification_text" msgid="6695268246267993166">"USB device is plugged in when Android is locked. To use the device, please unlock Android first and then reinsert the USB device to use it."</string>
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_notification_text (5561957202444135984) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_no_replug_notification_text (3844834116914726455) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_low_power_charge_notification_text (1264294871764444170) -->
+ <skip />
<string name="usb_apm_usb_suspicious_activity_notification_title" msgid="3461195995882871461">"Suspicious USB activity"</string>
<string name="usb_apm_usb_suspicious_activity_notification_text" msgid="6537085605929303187">"USB data signal has been disabled."</string>
- <!-- no translation found for fingerprint_frr_notification_title (3668331918920659093) -->
- <skip />
- <!-- no translation found for fingerprint_frr_notification_msg (2162966846624639252) -->
- <skip />
+ <string name="fingerprint_frr_notification_title" msgid="3668331918920659093">"Having trouble with Fingerprint Unlock?"</string>
+ <string name="fingerprint_frr_notification_msg" msgid="2162966846624639252">"Tap to review tips to improve your unlocking experience"</string>
</resources>
diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml
index 1a7dd72..9ce5756 100644
--- a/core/res/res/values-en-rCA/strings.xml
+++ b/core/res/res/values-en-rCA/strings.xml
@@ -2272,6 +2272,7 @@
<string name="accessibility_autoclick_double_click" msgid="2103826849116176478">"Double click"</string>
<string name="accessibility_autoclick_drag" msgid="1499559489796843224">"Drag"</string>
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Scroll"</string>
+ <string name="accessibility_autoclick_long_press" msgid="8757561680164116951">"Long press"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Pause"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Position"</string>
<string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Scroll Up"</string>
@@ -2556,7 +2557,9 @@
<string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Applications"</string>
<string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Your fingerprints can no longer be recognized. Set up Fingerprint Unlock again."</string>
<string name="usb_apm_usb_plugged_in_when_locked_notification_title" msgid="468577168569874967">"USB device plugged in when locked"</string>
- <string name="usb_apm_usb_plugged_in_when_locked_notification_text" msgid="6695268246267993166">"USB device is plugged in when Android is locked. To use device, please unlock Android first and then reinsert USB device to use it."</string>
+ <string name="usb_apm_usb_plugged_in_when_locked_notification_text" msgid="5561957202444135984">"USB device is plugged in when Android is locked. To use device, please unlock Android first."</string>
+ <string name="usb_apm_usb_plugged_in_when_locked_no_replug_notification_text" msgid="3844834116914726455">"USB device is plugged in when Android is locked. To use device, please unlock Android first and then reinsert USB device to use it."</string>
+ <string name="usb_apm_usb_plugged_in_when_locked_low_power_charge_notification_text" msgid="1264294871764444170">"Charging capability may be reduced or unavailable when locked."</string>
<string name="usb_apm_usb_suspicious_activity_notification_title" msgid="3461195995882871461">"Suspicious USB activity"</string>
<string name="usb_apm_usb_suspicious_activity_notification_text" msgid="6537085605929303187">"USB data signal has been disabled."</string>
<string name="fingerprint_frr_notification_title" msgid="3668331918920659093">"Having trouble with Fingerprint Unlock?"</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index 54887a8..56588f0 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -2272,6 +2272,7 @@
<string name="accessibility_autoclick_double_click" msgid="2103826849116176478">"Double-click"</string>
<string name="accessibility_autoclick_drag" msgid="1499559489796843224">"Drag"</string>
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Scroll"</string>
+ <string name="accessibility_autoclick_long_press" msgid="8757561680164116951">"Long press"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Pause"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Position"</string>
<string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Scroll up"</string>
@@ -2556,11 +2557,14 @@
<string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Applications"</string>
<string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Your fingerprints can no longer be recognised. Set up Fingerprint Unlock again."</string>
<string name="usb_apm_usb_plugged_in_when_locked_notification_title" msgid="468577168569874967">"USB device plugged in when locked"</string>
- <string name="usb_apm_usb_plugged_in_when_locked_notification_text" msgid="6695268246267993166">"USB device is plugged in when Android is locked. To use the device, please unlock Android first and then reinsert the USB device to use it."</string>
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_notification_text (5561957202444135984) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_no_replug_notification_text (3844834116914726455) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_low_power_charge_notification_text (1264294871764444170) -->
+ <skip />
<string name="usb_apm_usb_suspicious_activity_notification_title" msgid="3461195995882871461">"Suspicious USB activity"</string>
<string name="usb_apm_usb_suspicious_activity_notification_text" msgid="6537085605929303187">"USB data signal has been disabled."</string>
- <!-- no translation found for fingerprint_frr_notification_title (3668331918920659093) -->
- <skip />
- <!-- no translation found for fingerprint_frr_notification_msg (2162966846624639252) -->
- <skip />
+ <string name="fingerprint_frr_notification_title" msgid="3668331918920659093">"Having trouble with Fingerprint Unlock?"</string>
+ <string name="fingerprint_frr_notification_msg" msgid="2162966846624639252">"Tap to review tips to improve your unlocking experience"</string>
</resources>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index d2a2c6c..6399e98 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -2272,6 +2272,7 @@
<string name="accessibility_autoclick_double_click" msgid="2103826849116176478">"Double-click"</string>
<string name="accessibility_autoclick_drag" msgid="1499559489796843224">"Drag"</string>
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Scroll"</string>
+ <string name="accessibility_autoclick_long_press" msgid="8757561680164116951">"Long press"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Pause"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Position"</string>
<string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Scroll up"</string>
@@ -2556,11 +2557,14 @@
<string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Applications"</string>
<string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Your fingerprints can no longer be recognised. Set up Fingerprint Unlock again."</string>
<string name="usb_apm_usb_plugged_in_when_locked_notification_title" msgid="468577168569874967">"USB device plugged in when locked"</string>
- <string name="usb_apm_usb_plugged_in_when_locked_notification_text" msgid="6695268246267993166">"USB device is plugged in when Android is locked. To use the device, please unlock Android first and then reinsert the USB device to use it."</string>
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_notification_text (5561957202444135984) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_no_replug_notification_text (3844834116914726455) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_low_power_charge_notification_text (1264294871764444170) -->
+ <skip />
<string name="usb_apm_usb_suspicious_activity_notification_title" msgid="3461195995882871461">"Suspicious USB activity"</string>
<string name="usb_apm_usb_suspicious_activity_notification_text" msgid="6537085605929303187">"USB data signal has been disabled."</string>
- <!-- no translation found for fingerprint_frr_notification_title (3668331918920659093) -->
- <skip />
- <!-- no translation found for fingerprint_frr_notification_msg (2162966846624639252) -->
- <skip />
+ <string name="fingerprint_frr_notification_title" msgid="3668331918920659093">"Having trouble with Fingerprint Unlock?"</string>
+ <string name="fingerprint_frr_notification_msg" msgid="2162966846624639252">"Tap to review tips to improve your unlocking experience"</string>
</resources>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index a4b5e9f..5c87f26 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -2212,7 +2212,7 @@
<string name="nas_upgrade_notification_enable_action" msgid="3046406808378726874">"Aceptar"</string>
<string name="nas_upgrade_notification_disable_action" msgid="3794833210043497982">"Desactivar"</string>
<string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"Más información"</string>
- <string name="nas_upgrade_notification_learn_more_content" msgid="3735480566983530650">"Las notificaciones mejoradas reemplazaron a las notificaciones adaptables en Android 12. Esta función muestra respuestas y acciones sugeridas, y organiza tus notificaciones.\n\nLas notificaciones mejoradas pueden acceder a todo el contenido de notificaciones, incluida información personal, como nombres de contactos y mensajes. También pueden descartar o responder notificaciones (como contestar llamadas) y controlar la función No interrumpir."</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="3735480566983530650">"Las notificaciones mejoradas reemplazaron a las Notificaciones adaptativas en Android 12. Esta función muestra respuestas y acciones sugeridas, y organiza tus notificaciones.\n\nLas notificaciones mejoradas pueden acceder a todo el contenido de notificaciones, incluida información personal, como nombres de contactos y mensajes. También pueden descartar o responder notificaciones (como contestar llamadas) y controlar la función No interrumpir."</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Notificación de información del modo de Rutinas"</string>
<string name="dynamic_mode_notification_title" msgid="1388718452788985481">"Ahorro de batería activado"</string>
<string name="dynamic_mode_notification_summary" msgid="1639031262484979689">"Reduciendo el uso de la batería para extender su duración"</string>
@@ -2273,6 +2273,7 @@
<string name="accessibility_autoclick_double_click" msgid="2103826849116176478">"Hacer doble clic"</string>
<string name="accessibility_autoclick_drag" msgid="1499559489796843224">"Arrastrar"</string>
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Desplazamiento"</string>
+ <string name="accessibility_autoclick_long_press" msgid="8757561680164116951">"Mantener presionado"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Pausar"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Posición"</string>
<string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Desplazarse hacia arriba"</string>
@@ -2557,11 +2558,14 @@
<string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Aplicaciones"</string>
<string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Ya no se pueden reconocer tus huellas dactilares. Vuelve a configurar el Desbloqueo con huellas dactilares."</string>
<string name="usb_apm_usb_plugged_in_when_locked_notification_title" msgid="468577168569874967">"Dispositivo USB conectado cuando el dispositivo está bloqueado"</string>
- <string name="usb_apm_usb_plugged_in_when_locked_notification_text" msgid="6695268246267993166">"El dispositivo USB está conectado cuando Android está bloqueado. Para usar el dispositivo, primero desbloquea Android y, luego, vuelve a insertar el dispositivo USB."</string>
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_notification_text (5561957202444135984) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_no_replug_notification_text (3844834116914726455) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_low_power_charge_notification_text (1264294871764444170) -->
+ <skip />
<string name="usb_apm_usb_suspicious_activity_notification_title" msgid="3461195995882871461">"Actividad de USB sospechosa"</string>
<string name="usb_apm_usb_suspicious_activity_notification_text" msgid="6537085605929303187">"Se inhabilitó la señal de datos por USB."</string>
- <!-- no translation found for fingerprint_frr_notification_title (3668331918920659093) -->
- <skip />
- <!-- no translation found for fingerprint_frr_notification_msg (2162966846624639252) -->
- <skip />
+ <string name="fingerprint_frr_notification_title" msgid="3668331918920659093">"¿Tienes problemas con el Desbloqueo con huellas dactilares?"</string>
+ <string name="fingerprint_frr_notification_msg" msgid="2162966846624639252">"Presiona para revisar las sugerencias para mejorar la experiencia de desbloqueo"</string>
</resources>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index b24c88f..b755e76 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -2273,6 +2273,7 @@
<string name="accessibility_autoclick_double_click" msgid="2103826849116176478">"Hacer doble clic"</string>
<string name="accessibility_autoclick_drag" msgid="1499559489796843224">"Arrastrar"</string>
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Desplazarse"</string>
+ <string name="accessibility_autoclick_long_press" msgid="8757561680164116951">"Mantener pulsado"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Pausar"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Posición"</string>
<string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Desplazarse hacia arriba"</string>
@@ -2557,11 +2558,14 @@
<string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Aplicaciones"</string>
<string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Tus huellas digitales ya no pueden reconocerse. Vuelve a configurar Desbloqueo con huella digital."</string>
<string name="usb_apm_usb_plugged_in_when_locked_notification_title" msgid="468577168569874967">"Dispositivo USB conectado con el dispositivo bloqueado"</string>
- <string name="usb_apm_usb_plugged_in_when_locked_notification_text" msgid="6695268246267993166">"El dispositivo USB está conectado cuando Android está bloqueado. Para usar el dispositivo, desbloquea Android primero y, a continuación, vuelve a insertar el dispositivo USB para usarlo."</string>
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_notification_text (5561957202444135984) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_no_replug_notification_text (3844834116914726455) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_low_power_charge_notification_text (1264294871764444170) -->
+ <skip />
<string name="usb_apm_usb_suspicious_activity_notification_title" msgid="3461195995882871461">"Actividad USB sospechosa"</string>
<string name="usb_apm_usb_suspicious_activity_notification_text" msgid="6537085605929303187">"La señal de datos USB se ha inhabilitado."</string>
- <!-- no translation found for fingerprint_frr_notification_title (3668331918920659093) -->
- <skip />
- <!-- no translation found for fingerprint_frr_notification_msg (2162966846624639252) -->
- <skip />
+ <string name="fingerprint_frr_notification_title" msgid="3668331918920659093">"¿Tienes problemas con Desbloqueo con huella digital?"</string>
+ <string name="fingerprint_frr_notification_msg" msgid="2162966846624639252">"Toca para ver consejos para mejorar la experiencia de desbloqueo"</string>
</resources>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index 4ce102a..9480365 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -327,7 +327,7 @@
<string name="permgrouplab_contacts" msgid="4254143639307316920">"Kontaktid"</string>
<string name="permgroupdesc_contacts" msgid="9163927941244182567">"juurdepääs kontaktidele"</string>
<string name="permgrouplab_location" msgid="1858277002233964394">"Asukoht"</string>
- <string name="permgroupdesc_location" msgid="1995955142118450685">"pääseda juurde selle seadme asukohale"</string>
+ <string name="permgroupdesc_location" msgid="1995955142118450685">"Juurdepääs selle seadme asukohale"</string>
<string name="permgrouplab_calendar" msgid="6426860926123033230">"Kalender"</string>
<string name="permgroupdesc_calendar" msgid="6762751063361489379">"juurdepääs kalendrile"</string>
<string name="permgrouplab_sms" msgid="795737735126084874">"SMS"</string>
@@ -2272,6 +2272,7 @@
<string name="accessibility_autoclick_double_click" msgid="2103826849116176478">"Topeltklikk"</string>
<string name="accessibility_autoclick_drag" msgid="1499559489796843224">"Lohista"</string>
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Keri"</string>
+ <string name="accessibility_autoclick_long_press" msgid="8757561680164116951">"Pikk vajutus"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Peata"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Asukoht"</string>
<string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Keri üles"</string>
@@ -2556,7 +2557,12 @@
<string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Rakendused"</string>
<string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Teie sõrmejälgi ei saa enam tuvastada. Seadistage sõrmejäljega avamine uuesti."</string>
<string name="usb_apm_usb_plugged_in_when_locked_notification_title" msgid="468577168569874967">"USB-seade ühendati, kui seade oli lukus"</string>
- <string name="usb_apm_usb_plugged_in_when_locked_notification_text" msgid="6695268246267993166">"USB-seade ühendati, kui Android oli lukustatud. Seadme kasutamiseks avage esmalt Android ja ühendage siis USB-seade uuesti, et seda kasutada."</string>
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_notification_text (5561957202444135984) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_no_replug_notification_text (3844834116914726455) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_low_power_charge_notification_text (1264294871764444170) -->
+ <skip />
<string name="usb_apm_usb_suspicious_activity_notification_title" msgid="3461195995882871461">"Kahtlane tegevus USB-ga"</string>
<string name="usb_apm_usb_suspicious_activity_notification_text" msgid="6537085605929303187">"USB andmesignaal on keelatud."</string>
<string name="fingerprint_frr_notification_title" msgid="3668331918920659093">"Kas teil on sõrmejäljega avamisega probleeme?"</string>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index 6c23779..641572e 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -2272,6 +2272,7 @@
<string name="accessibility_autoclick_double_click" msgid="2103826849116176478">"Egin klik bikoitza"</string>
<string name="accessibility_autoclick_drag" msgid="1499559489796843224">"Arrastatu"</string>
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Egin gora eta behera"</string>
+ <string name="accessibility_autoclick_long_press" msgid="8757561680164116951">"Sakatu luze"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Pausatu"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Ezarri posizioan"</string>
<string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Egin gora"</string>
@@ -2556,11 +2557,14 @@
<string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Aplikazioak"</string>
<string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Zure hatz-markak ez dira ezagutzen jada. Konfiguratu berriro hatz-marka bidez desblokeatzeko eginbidea."</string>
<string name="usb_apm_usb_plugged_in_when_locked_notification_title" msgid="468577168569874967">"USB bidezko gailua blokeatuta zegoen bitartean entxufatu da"</string>
- <string name="usb_apm_usb_plugged_in_when_locked_notification_text" msgid="6695268246267993166">"USB bidezko gailua entxufatuta dago Android blokeatuta dagoenean. Gailua erabiltzeko, desblokeatu Android eta entxufatu berriro USB bidezko gailua hura erabiltzeko."</string>
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_notification_text (5561957202444135984) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_no_replug_notification_text (3844834116914726455) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_low_power_charge_notification_text (1264294871764444170) -->
+ <skip />
<string name="usb_apm_usb_suspicious_activity_notification_title" msgid="3461195995882871461">"USB bidezko jarduera susmagarriak"</string>
<string name="usb_apm_usb_suspicious_activity_notification_text" msgid="6537085605929303187">"USB bidezko datu-seinalea desgaitu da."</string>
- <!-- no translation found for fingerprint_frr_notification_title (3668331918920659093) -->
- <skip />
- <!-- no translation found for fingerprint_frr_notification_msg (2162966846624639252) -->
- <skip />
+ <string name="fingerprint_frr_notification_title" msgid="3668331918920659093">"Arazoak dituzu hatz-marka bidez desblokeatzeko eginbidearekin?"</string>
+ <string name="fingerprint_frr_notification_msg" msgid="2162966846624639252">"Sakatu hau desblokeatzeko aukera hobetzeko aholkuak berrikusteko"</string>
</resources>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 273031e..3f2ceb4 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -1446,7 +1446,7 @@
<string name="usb_ptp_notification_title" msgid="5043437571863443281">"PTP ازطریق USB روشن شد"</string>
<string name="usb_tether_notification_title" msgid="8828527870612663771">"اشتراکگذاری اینترنت با USB روشن شد"</string>
<string name="usb_midi_notification_title" msgid="7404506788950595557">"MIDI ازطریق USB روشن شد"</string>
- <string name="usb_uvc_notification_title" msgid="2030032862673400008">"دستگاه بهعنوان «وببین» متصل شده است"</string>
+ <string name="usb_uvc_notification_title" msgid="2030032862673400008">"دستگاه بهعنوان «وبکم» متصل شده است"</string>
<string name="usb_accessory_notification_title" msgid="1385394660861956980">"وسیله جانبی USB متصل است"</string>
<string name="usb_notification_message" msgid="4715163067192110676">"برای گزینههای بیشتر تکضرب بزنید."</string>
<string name="usb_power_notification_message" msgid="7284765627437897702">"درحال شارژ کردن دستگاه متصلشده. برای گزینههای بیشتر، تکضرب بزنید."</string>
@@ -2272,6 +2272,7 @@
<string name="accessibility_autoclick_double_click" msgid="2103826849116176478">"دوکلیک کردن"</string>
<string name="accessibility_autoclick_drag" msgid="1499559489796843224">"کشیدن"</string>
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"پیمایش"</string>
+ <string name="accessibility_autoclick_long_press" msgid="8757561680164116951">"فشار طولانی"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"توقف موقت"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"موقعیت"</string>
<string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"پیمایش به بالا"</string>
@@ -2556,11 +2557,14 @@
<string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"برنامهها"</string>
<string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"اثر انگشتانتان دیگر قابلشناسایی نیست. «قفلگشایی با اثر انگشت» را دوباره راهاندازی کنید."</string>
<string name="usb_apm_usb_plugged_in_when_locked_notification_title" msgid="468577168569874967">"دستگاه USB هنگام قفل بودن وصل شده است"</string>
- <string name="usb_apm_usb_plugged_in_when_locked_notification_text" msgid="6695268246267993166">"دستگاه USB هنگام قفل بودن Android متصل شده است. برای استفاده از دستگاه، لطفاً ابتدا قفل Android را باز کنید و سپس دستگاه USB را دوباره وارد کنید."</string>
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_notification_text (5561957202444135984) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_no_replug_notification_text (3844834116914726455) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_low_power_charge_notification_text (1264294871764444170) -->
+ <skip />
<string name="usb_apm_usb_suspicious_activity_notification_title" msgid="3461195995882871461">"فعالیت مشکوک USB"</string>
<string name="usb_apm_usb_suspicious_activity_notification_text" msgid="6537085605929303187">"نشانِ داده USB غیرفعال شده است."</string>
- <!-- no translation found for fingerprint_frr_notification_title (3668331918920659093) -->
- <skip />
- <!-- no translation found for fingerprint_frr_notification_msg (2162966846624639252) -->
- <skip />
+ <string name="fingerprint_frr_notification_title" msgid="3668331918920659093">"برای «قفلگشایی با اثر انگشت» با مشکل روبرو هستید؟"</string>
+ <string name="fingerprint_frr_notification_msg" msgid="2162966846624639252">"برای بازنگری نکات مربوط به بهبود تجربه قفلگشایی، تکضرب بزنید"</string>
</resources>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index ab9fe72..44aa36c 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -283,7 +283,7 @@
<string name="global_action_silent_mode_on_status" msgid="2371892537738632013">"Äänet ovat POISSA KÄYTÖSTÄ"</string>
<string name="global_action_silent_mode_off_status" msgid="6608006545950920042">"Äänet ovat KÄYTÖSSÄ"</string>
<string name="global_actions_toggle_airplane_mode" msgid="6911684460146916206">"Lentokonetila"</string>
- <string name="global_actions_airplane_mode_on_status" msgid="5508025516695361936">"Lentokonetila on KÄYTÖSSÄ"</string>
+ <string name="global_actions_airplane_mode_on_status" msgid="5508025516695361936">"Lentokonetila on PÄÄLLÄ"</string>
<string name="global_actions_airplane_mode_off_status" msgid="8522219771500505475">"Lentokonetila on POIS KÄYTÖSTÄ"</string>
<string name="global_action_settings" msgid="4671878836947494217">"Asetukset"</string>
<string name="global_action_assist" msgid="2517047220311505805">"Auta"</string>
@@ -305,7 +305,7 @@
<string name="notification_channel_vpn" msgid="1628529026203808999">"VPN-tila"</string>
<string name="notification_channel_system_time" msgid="1660313368058030441">"Aika ja aikavyöhykkeet"</string>
<string name="notification_channel_device_admin" msgid="6384932669406095506">"Ilmoitukset IT-järjestelmänvalvojalta"</string>
- <string name="notification_channel_alerts" msgid="5070241039583668427">"Ilmoitukset"</string>
+ <string name="notification_channel_alerts" msgid="5070241039583668427">"Varoitukset"</string>
<string name="notification_channel_retail_mode" msgid="3732239154256431213">"Esittelytila"</string>
<string name="notification_channel_usb" msgid="1528280969406244896">"USB-yhteys"</string>
<string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Sovellus käynnissä"</string>
@@ -2272,6 +2272,7 @@
<string name="accessibility_autoclick_double_click" msgid="2103826849116176478">"Kaksoisklikkaa"</string>
<string name="accessibility_autoclick_drag" msgid="1499559489796843224">"Vedä"</string>
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Vieritä"</string>
+ <string name="accessibility_autoclick_long_press" msgid="8757561680164116951">"Pitkä painallus"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Keskeytä"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Sijainti"</string>
<string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Vieritys ylös"</string>
@@ -2556,11 +2557,14 @@
<string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Sovellukset"</string>
<string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Sormenjälkiäsi ei voi enää tunnistaa. Ota sormenjälkiavaus uudelleen käyttöön."</string>
<string name="usb_apm_usb_plugged_in_when_locked_notification_title" msgid="468577168569874967">"USB-laite kytkettynä, kun lukitus on päällä"</string>
- <string name="usb_apm_usb_plugged_in_when_locked_notification_text" msgid="6695268246267993166">"USB-laite on kytkettynä, kun Android on lukittu. Jos haluat käyttää laitetta, avaa ensin Androidin lukitus ja kytke USB-laite uudelleen."</string>
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_notification_text (5561957202444135984) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_no_replug_notification_text (3844834116914726455) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_low_power_charge_notification_text (1264294871764444170) -->
+ <skip />
<string name="usb_apm_usb_suspicious_activity_notification_title" msgid="3461195995882871461">"Epäilyttävää USB-toimintaa"</string>
<string name="usb_apm_usb_suspicious_activity_notification_text" msgid="6537085605929303187">"USB-datasignaali on poistettu käytöstä."</string>
- <!-- no translation found for fingerprint_frr_notification_title (3668331918920659093) -->
- <skip />
- <!-- no translation found for fingerprint_frr_notification_msg (2162966846624639252) -->
- <skip />
+ <string name="fingerprint_frr_notification_title" msgid="3668331918920659093">"Onko sormenjälkiavauksessa ongelmia?"</string>
+ <string name="fingerprint_frr_notification_msg" msgid="2162966846624639252">"Katso napauttamalla vinkkejä lukituksen avaamiseen"</string>
</resources>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index 0813a15..a9af86d 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -963,7 +963,7 @@
<string name="phoneTypeAssistant" msgid="757550783842231039">"Assistant"</string>
<string name="phoneTypeMms" msgid="1799747455131365989">"MMS"</string>
<string name="eventTypeCustom" msgid="3257367158986466481">"Personnaliser"</string>
- <string name="eventTypeBirthday" msgid="7770026752793912283">"Date de naissance"</string>
+ <string name="eventTypeBirthday" msgid="7770026752793912283">"Anniversaire"</string>
<string name="eventTypeAnniversary" msgid="4684702412407916888">"Anniversaire"</string>
<string name="eventTypeOther" msgid="530671238533887997">"Autre"</string>
<string name="emailTypeCustom" msgid="1809435350482181786">"Personnaliser"</string>
@@ -1454,7 +1454,7 @@
<string name="usb_unsupported_audio_accessory_title" msgid="2335775548086533065">"Un accessoire audio analogique a été détecté"</string>
<string name="usb_unsupported_audio_accessory_message" msgid="1300168007129796621">"L\'appareil connecté n\'est pas compatible avec ce téléphone. Touchez ici en savoir plus."</string>
<string name="adb_active_notification_title" msgid="408390247354560331">"Débogage USB activé"</string>
- <string name="adb_active_notification_message" msgid="5617264033476778211">"Touchez pour désactiver le débogage USB"</string>
+ <string name="adb_active_notification_message" msgid="5617264033476778211">"Touchez pour le désactiver"</string>
<string name="adb_active_notification_message" product="tv" msgid="6624498401272780855">"Sélectionnez cette option pour désactiver le débogage USB."</string>
<string name="adbwifi_active_notification_title" msgid="6147343659168302473">"Débogage sans fil connecté"</string>
<string name="adbwifi_active_notification_message" msgid="930987922852867972">"Touchez l\'écran pour désactiver le débogage sans fil"</string>
@@ -1803,7 +1803,7 @@
<string name="color_inversion_feature_name" msgid="2672824491933264951">"Inversion des couleurs"</string>
<string name="color_correction_feature_name" msgid="7975133554160979214">"Correction des couleurs"</string>
<string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Mode Une main"</string>
- <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Très sombre"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Réduction supplémentaire de la luminosité"</string>
<string name="hearing_aids_feature_name" msgid="1125892105105852542">"Appareils auditifs"</string>
<string name="autoclick_feature_name" msgid="8149248738736949630">"Clic automatique"</string>
<string name="hearing_device_status_disconnected" msgid="497547752953543832">"Déconnecté"</string>
@@ -2005,7 +2005,7 @@
<string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"De <xliff:g id="START">%1$s</xliff:g> à <xliff:g id="END">%2$s</xliff:g>"</string>
<string name="zen_mode_trigger_summary_combined" msgid="6492381546327807669">"<xliff:g id="DAYS">%1$s</xliff:g>, <xliff:g id="TIMES">%2$s</xliff:g>"</string>
<string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"N\'importe quel agenda"</string>
- <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> désactive certains sons"</string>
+ <string name="muted_by" msgid="91464083490094950">"Des sons sont désactivés par : <xliff:g id="THIRD_PARTY">%1$s</xliff:g>"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"Un problème interne est survenu avec votre appareil. Il se peut qu\'il soit instable jusqu\'à ce que vous le réinitialisiez à ses paramètres par défaut."</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"Un problème interne est survenu avec votre appareil. Communiquez avec le fabricant pour obtenir plus de détails."</string>
<string name="stk_cc_ussd_to_dial" msgid="3139884150741157610">"La demande USSD a été remplacée par une demande d\'appel régulier"</string>
@@ -2212,7 +2212,7 @@
<string name="nas_upgrade_notification_enable_action" msgid="3046406808378726874">"OK"</string>
<string name="nas_upgrade_notification_disable_action" msgid="3794833210043497982">"Désactiver"</string>
<string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"En savoir plus"</string>
- <string name="nas_upgrade_notification_learn_more_content" msgid="3735480566983530650">"Les notifications améliorées ont remplacé les notifications adaptatives Android sous Android 12. Cette fonctionnalité vous présente des suggestions d\'actions et de réponse, et organise vos notifications.\n\nLes notifications améliorées peuvent accéder au contenu de toutes les notifications, y compris les renseignements personnels comme le nom des contacts et les messages. Cette fonctionnalité peut aussi fermer des notifications ou interagir avec elles, comme répondre aux appels téléphoniques et gérer le mode Ne pas déranger."</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="3735480566983530650">"Les notifications améliorées ont remplacé les notifications adaptatives Android sous Android 12. Cette fonctionnalité vous présente des suggestions d\'actions et de réponse, et organise vos notifications.\n\nLes notifications améliorées peuvent accéder au contenu des notifications, y compris les renseignements personnels comme le nom des contacts et les messages. Cette fonctionnalité peut aussi fermer des notifications ou interagir avec elles, comme répondre aux appels téléphoniques et gérer le mode Ne pas déranger."</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Notification d\'information du mode Routine"</string>
<string name="dynamic_mode_notification_title" msgid="1388718452788985481">"Économiseur de pile activé"</string>
<string name="dynamic_mode_notification_summary" msgid="1639031262484979689">"Réduction de l\'utilisation de la pile pour en prolonger l\'autonomie"</string>
@@ -2273,6 +2273,7 @@
<string name="accessibility_autoclick_double_click" msgid="2103826849116176478">"Double-cliquer"</string>
<string name="accessibility_autoclick_drag" msgid="1499559489796843224">"Glisser"</string>
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Faire défiler"</string>
+ <string name="accessibility_autoclick_long_press" msgid="8757561680164116951">"Appui prolongé"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Pause"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Position"</string>
<string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Faire défiler vers le haut"</string>
@@ -2557,11 +2558,14 @@
<string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Applications"</string>
<string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Vos empreintes digitales ne peuvent plus être reconnues. Reconfigurez le Déverrouillage par empreinte digitale."</string>
<string name="usb_apm_usb_plugged_in_when_locked_notification_title" msgid="468577168569874967">"L\'appareil USB est branché quand Android est verrouillé"</string>
- <string name="usb_apm_usb_plugged_in_when_locked_notification_text" msgid="6695268246267993166">"L\'appareil USB est branché quand Android est verrouillé. Pour utiliser l\'appareil, veuillez d\'abord déverrouiller Android, puis réinsérer l\'appareil USB."</string>
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_notification_text (5561957202444135984) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_no_replug_notification_text (3844834116914726455) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_low_power_charge_notification_text (1264294871764444170) -->
+ <skip />
<string name="usb_apm_usb_suspicious_activity_notification_title" msgid="3461195995882871461">"Activité USB suspecte"</string>
<string name="usb_apm_usb_suspicious_activity_notification_text" msgid="6537085605929303187">"Le signal de données USB a été désactivé."</string>
- <!-- no translation found for fingerprint_frr_notification_title (3668331918920659093) -->
- <skip />
- <!-- no translation found for fingerprint_frr_notification_msg (2162966846624639252) -->
- <skip />
+ <string name="fingerprint_frr_notification_title" msgid="3668331918920659093">"Vous rencontrez des problèmes avec le Déverrouillage par empreinte digitale?"</string>
+ <string name="fingerprint_frr_notification_msg" msgid="2162966846624639252">"Touchez l\'écran pour consulter des conseils afin d\'améliorer votre expérience de déverrouillage"</string>
</resources>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 9f6d9a9..59c4e0a 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -1972,7 +1972,7 @@
<string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
<string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"L\'économiseur de batterie active le thème sombre et limite ou désactive l\'activité en arrière-plan ainsi que certains effets visuels, fonctionnalités et connexions réseau."</string>
<string name="battery_saver_description" msgid="8518809702138617167">"L\'économiseur de batterie active le thème sombre et limite ou désactive les activités en arrière-plan ainsi que certains effets visuels, fonctionnalités et connexions réseau."</string>
- <string name="data_saver_description" msgid="4995164271550590517">"Pour réduire la consommation des données, l\'Économiseur de données empêche certaines applis d\'envoyer ou de recevoir des données en arrière-plan. Les applis que vous utiliserez pourront toujours accéder aux données, mais le feront moins fréquemment. Par exemple, il est possible que les images ne s\'afficheront pas tant que vous n\'aurez pas appuyé dessus."</string>
+ <string name="data_saver_description" msgid="4995164271550590517">"Pour réduire la consommation des données, l\'Économiseur de données empêche certaines applis d\'envoyer ou de recevoir des données en arrière-plan. Les applis que vous utiliserez pourront toujours accéder aux données, mais le feront moins fréquemment. Par exemple, il se peut que les images ne s\'affichent pas tant que vous n\'appuyez pas dessus."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Activer l\'Économiseur de données ?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Activer"</string>
<string name="zen_mode_duration_minutes_summary" msgid="4555514757230849789">"{count,plural, =1{Pendant 1 minute (jusqu\'à {formattedTime})}one{Pendant # minute (jusqu\'à {formattedTime})}many{Pendant # minutes (jusqu\'à {formattedTime})}other{Pendant # minutes (jusqu\'à {formattedTime})}}"</string>
@@ -2212,7 +2212,7 @@
<string name="nas_upgrade_notification_enable_action" msgid="3046406808378726874">"OK"</string>
<string name="nas_upgrade_notification_disable_action" msgid="3794833210043497982">"Désactiver"</string>
<string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"En savoir plus"</string>
- <string name="nas_upgrade_notification_learn_more_content" msgid="3735480566983530650">"Les notifications améliorées ont remplacé les notifications intelligentes dans Android 12. Cette fonctionnalité affiche des suggestions d\'actions et de réponses, et organise vos notifications.\n\nElle a accès au contenu des notifications, y compris à des infos personnelles tels que les noms et les messages des contacts. Elle peut aussi fermer les notifications, ou y répondre (répondre aux appels téléphoniques, par exemple), et contrôler Ne pas déranger."</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="3735480566983530650">"Les notifications améliorées ont remplacé les notifications adaptatives dans Android 12. Cette fonctionnalité affiche des suggestions d\'actions et de réponses, et organise vos notifications.\n\nElle a accès au contenu des notifications, y compris à des infos personnelles comme les noms et les messages des contacts. Elle peut aussi fermer les notifications ou y répondre (répondre aux appels téléphoniques, par exemple), et contrôler Ne pas déranger."</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Notification d\'information du mode Routine"</string>
<string name="dynamic_mode_notification_title" msgid="1388718452788985481">"Économiseur de batterie activé"</string>
<string name="dynamic_mode_notification_summary" msgid="1639031262484979689">"Réduction de l\'utilisation de la batterie pour prolonger son autonomie"</string>
@@ -2273,6 +2273,7 @@
<string name="accessibility_autoclick_double_click" msgid="2103826849116176478">"Double-cliquer"</string>
<string name="accessibility_autoclick_drag" msgid="1499559489796843224">"Faire glisser"</string>
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Faire défiler"</string>
+ <string name="accessibility_autoclick_long_press" msgid="8757561680164116951">"Appuyer de manière prolongée"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Pause"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Position"</string>
<string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Faire défiler vers le haut"</string>
@@ -2557,11 +2558,14 @@
<string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Applications"</string>
<string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Vos empreintes ne peuvent plus être reconnues. Reconfigurez le déverrouillage par empreinte digitale."</string>
<string name="usb_apm_usb_plugged_in_when_locked_notification_title" msgid="468577168569874967">"Appareil USB branché alors que l\'appareil Android est verrouillé"</string>
- <string name="usb_apm_usb_plugged_in_when_locked_notification_text" msgid="6695268246267993166">"L\'appareil USB est branché alors que l\'appareil Android est verrouillé. Pour utiliser l\'appareil, veuillez d\'abord déverrouiller Android, puis brancher à nouveau l\'appareil USB."</string>
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_notification_text (5561957202444135984) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_no_replug_notification_text (3844834116914726455) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_low_power_charge_notification_text (1264294871764444170) -->
+ <skip />
<string name="usb_apm_usb_suspicious_activity_notification_title" msgid="3461195995882871461">"Activité USB suspecte"</string>
<string name="usb_apm_usb_suspicious_activity_notification_text" msgid="6537085605929303187">"Le signal de données USB a été désactivé."</string>
- <!-- no translation found for fingerprint_frr_notification_title (3668331918920659093) -->
- <skip />
- <!-- no translation found for fingerprint_frr_notification_msg (2162966846624639252) -->
- <skip />
+ <string name="fingerprint_frr_notification_title" msgid="3668331918920659093">"Vous rencontrez des problèmes avec le déverrouillage par empreinte digitale ?"</string>
+ <string name="fingerprint_frr_notification_msg" msgid="2162966846624639252">"Appuyez pour consulter des conseils afin d\'améliorer votre expérience de déverrouillage"</string>
</resources>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index 012cda7..66d36d0 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -2272,6 +2272,7 @@
<string name="accessibility_autoclick_double_click" msgid="2103826849116176478">"Facer dobre clic"</string>
<string name="accessibility_autoclick_drag" msgid="1499559489796843224">"Arrastrar"</string>
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Desprazar"</string>
+ <string name="accessibility_autoclick_long_press" msgid="8757561680164116951">"Manter premido"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Pausa"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Posición"</string>
<string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Desprazar cara arriba"</string>
@@ -2556,11 +2557,14 @@
<string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Aplicacións"</string>
<string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Xa non se recoñecen as túas impresións dixitais. Configura de novo o desbloqueo dactilar."</string>
<string name="usb_apm_usb_plugged_in_when_locked_notification_title" msgid="468577168569874967">"Dispositivo USB conectado cando Android está bloqueado"</string>
- <string name="usb_apm_usb_plugged_in_when_locked_notification_text" msgid="6695268246267993166">"O dispositivo USB está conectado cando Android está bloqueado. Para usalo, primeiro desbloquea Android e despois volve inserir o dispositivo USB."</string>
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_notification_text (5561957202444135984) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_no_replug_notification_text (3844834116914726455) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_low_power_charge_notification_text (1264294871764444170) -->
+ <skip />
<string name="usb_apm_usb_suspicious_activity_notification_title" msgid="3461195995882871461">"Actividade USB sospeitosa"</string>
<string name="usb_apm_usb_suspicious_activity_notification_text" msgid="6537085605929303187">"Desactivouse o indicador de datos USB."</string>
- <!-- no translation found for fingerprint_frr_notification_title (3668331918920659093) -->
- <skip />
- <!-- no translation found for fingerprint_frr_notification_msg (2162966846624639252) -->
- <skip />
+ <string name="fingerprint_frr_notification_title" msgid="3668331918920659093">"Tes problemas co desbloqueo dactilar?"</string>
+ <string name="fingerprint_frr_notification_msg" msgid="2162966846624639252">"Toca para consultar os consellos sobre como mellorar o proceso de desbloqueo"</string>
</resources>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index a042053..b391927 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -262,15 +262,14 @@
<string name="global_actions" product="tv" msgid="3871763739487450369">"Android TVના વિકલ્પો"</string>
<string name="global_actions" product="default" msgid="6410072189971495460">"ફોન વિકલ્પો"</string>
<string name="global_action_lock" msgid="6949357274257655383">"સ્ક્રીન લૉક"</string>
- <string name="global_action_power_off" msgid="4404936470711393203">"પાવર બંધ"</string>
+ <string name="global_action_power_off" msgid="4404936470711393203">"પાવર બંધ કરો"</string>
<string name="global_action_power_options" msgid="1185286119330160073">"પાવર"</string>
<string name="global_action_restart" msgid="4678451019561687074">"ફરી શરૂ કરો"</string>
<string name="global_action_emergency" msgid="1387617624177105088">"ઇમર્જન્સી"</string>
<string name="global_action_bug_report" msgid="5127867163044170003">"બગ રિપોર્ટ"</string>
<string name="global_action_logout" msgid="6093581310002476511">"સત્ર સમાપ્ત કરો"</string>
<string name="global_action_screenshot" msgid="2610053466156478564">"સ્ક્રીનશૉટ"</string>
- <!-- no translation found for global_action_standby (6786920589970257642) -->
- <skip />
+ <string name="global_action_standby" msgid="6786920589970257642">"સ્ટૅન્ડબાય"</string>
<string name="bugreport_title" msgid="8549990811777373050">"બગ રિપોર્ટ"</string>
<string name="bugreport_message" msgid="5212529146119624326">"આ, એક ઇ-મેઇલ મેસેજ તરીકે મોકલવા માટે, તમારા વર્તમાન ડિવાઇસના સ્ટેટસ વિશે માહિતી એકત્રિત કરશે. એક બગ રિપોર્ટના શરુ થવાથી લઈને મોકલવા માટે તૈયાર થવા સુધીની પ્રક્રિયામાં થોડો સમય લાગશે; કૃપા કરીને ધીરજ રાખો."</string>
<string name="bugreport_option_interactive_title" msgid="7968287837902871289">"ક્રિયાપ્રતિક્રિયાત્મક રિપોર્ટ"</string>
@@ -1594,7 +1593,7 @@
<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>
+ <string name="reset" msgid="3865826612628171429">"રીસેટ કરો"</string>
<string name="submit" msgid="862795280643405865">"સબમિટ કરો"</string>
<string name="car_mode_disable_notification_title" msgid="8450693275833142896">"ડ્રાઇવિંગ ઍપ ચાલી રહી છે"</string>
<string name="car_mode_disable_notification_message" msgid="8954550232288567515">"ડ્રાઇવિંગ ઍપથી બહાર નીકળવા માટે ટૅપ કરો."</string>
@@ -2212,7 +2211,7 @@
<string name="nas_upgrade_notification_enable_action" msgid="3046406808378726874">"ઓકે"</string>
<string name="nas_upgrade_notification_disable_action" msgid="3794833210043497982">"બંધ કરો"</string>
<string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"વધુ જાણો"</string>
- <string name="nas_upgrade_notification_learn_more_content" msgid="3735480566983530650">"Android 12માં Android માટે અનુકૂળ નોટિફિકેશનને બદલે વધુ સારા નોટિફિકેશન છે. આ સુવિધા સૂચિત ક્રિયાઓ અને જવાબો બતાવે છે તેમજ તમારા નોટિફિકેશનની યોગ્ય ગોઠવણી કરે છે.\n\nવધુ સારા નોટિફિકેશન સંપર્કોના નામ અને મેસેજ જેવી વ્યક્તિગત માહિતી સહિત નોટિફિકેશનનું બધું કન્ટેન્ટ ઍક્સેસ કરી શકે છે. આ સુવિધા ફોન કૉલના જવાબ આપવા કે \'ખલેલ પાડશો નહીં\'નું નિયંત્રણ કરવા જેવા નોટિફિકેશન છોડવાની કે તેનો જવાબ આપવાની ક્રિયા પણ કરી શકે છે."</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="3735480566983530650">"Android 12માં Android માટે અનુકૂળ નોટિફિકેશનને બદલે વધુ સારા નોટિફિકેશન છે. આ સુવિધા સૂચિત ઍક્શન અને જવાબો બતાવે છે તેમજ તમારા નોટિફિકેશનની યોગ્ય ગોઠવણી કરે છે.\n\nવધુ સારા નોટિફિકેશન સંપર્કોના નામ અને મેસેજ જેવી વ્યક્તિગત માહિતી સહિત નોટિફિકેશનનું બધું કન્ટેન્ટ ઍક્સેસ કરી શકે છે. આ સુવિધા ફોન કૉલના જવાબ આપવા કે \'ખલેલ પાડશો નહીં\'નું નિયંત્રણ કરવા જેવા નોટિફિકેશન છોડવાની કે તેનો જવાબ આપવાની ક્રિયા પણ કરી શકે છે."</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"રૂટિન મોડની માહિતીનું નોટિફિકેશન"</string>
<string name="dynamic_mode_notification_title" msgid="1388718452788985481">"બૅટરી સેવરની સુવિધા ચાલુ કરી છે"</string>
<string name="dynamic_mode_notification_summary" msgid="1639031262484979689">"બૅટરીની આવરદા વધારવા માટે બૅટરીનો વપરાશ ઘટાડી રહ્યાં છીએ"</string>
@@ -2273,6 +2272,7 @@
<string name="accessibility_autoclick_double_click" msgid="2103826849116176478">"બે વાર ક્લિક કરો"</string>
<string name="accessibility_autoclick_drag" msgid="1499559489796843224">"ખેંચો"</string>
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"સ્ક્રોલ કરો"</string>
+ <string name="accessibility_autoclick_long_press" msgid="8757561680164116951">"થોડીવાર દબાવી રાખો"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"થોભાવો"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"સ્થિતિ"</string>
<string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"ઉપર સ્ક્રોલ કરો"</string>
@@ -2557,11 +2557,14 @@
<string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"ઍપ્લિકેશનો"</string>
<string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"તમારી ફિંગરપ્રિન્ટને હવેથી ઓળખી શકાશે નહીં. ફિંગરપ્રિન્ટ અનલૉક સુવિધાનું ફરી સેટઅપ કરો."</string>
<string name="usb_apm_usb_plugged_in_when_locked_notification_title" msgid="468577168569874967">"લૉક કરેલું હોય ત્યારે USB ડિવાઇસ પ્લગ-ઇન હોય છે"</string>
- <string name="usb_apm_usb_plugged_in_when_locked_notification_text" msgid="6695268246267993166">"Android લૉક હોય ત્યારે USB ડિવાઇસ પ્લગ-ઇન હોય છે. ડિવાઇસનો ઉપયોગ કરવા માટે, કૃપા કરીને પહેલા Android અનલૉક કરો અને પછી USB ડિવાઇસનો ઉપયોગ કરવા માટે તેને ફરી શામેલ કરો."</string>
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_notification_text (5561957202444135984) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_no_replug_notification_text (3844834116914726455) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_low_power_charge_notification_text (1264294871764444170) -->
+ <skip />
<string name="usb_apm_usb_suspicious_activity_notification_title" msgid="3461195995882871461">"શંકાસ્પદ USB ઍક્ટિવિટી"</string>
<string name="usb_apm_usb_suspicious_activity_notification_text" msgid="6537085605929303187">"USB ડેટા સિગ્નલ બંધ કરવામાં આવ્યું છે."</string>
- <!-- no translation found for fingerprint_frr_notification_title (3668331918920659093) -->
- <skip />
- <!-- no translation found for fingerprint_frr_notification_msg (2162966846624639252) -->
- <skip />
+ <string name="fingerprint_frr_notification_title" msgid="3668331918920659093">"ફિંગરપ્રિન્ટ અનલૉક સુવિધાનો ઉપયોગ કરવામાં સમસ્યા આવી રહી છે?"</string>
+ <string name="fingerprint_frr_notification_msg" msgid="2162966846624639252">"તમારા અનલૉકિંગના અનુભવને બહેતર બનાવવા માટેની ટિપનો રિવ્યૂ કરવા માટે ટૅપ કરો"</string>
</resources>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index f757523..830952c 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -2272,6 +2272,7 @@
<string name="accessibility_autoclick_double_click" msgid="2103826849116176478">"दो बार क्लिक करें"</string>
<string name="accessibility_autoclick_drag" msgid="1499559489796843224">"खींचें और छोड़ें"</string>
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"स्क्रोल करें"</string>
+ <string name="accessibility_autoclick_long_press" msgid="8757561680164116951">"दबाकर रखें"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"रोकें"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"पोज़िशन"</string>
<string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"ऊपर की ओर स्क्रोल करें"</string>
@@ -2556,11 +2557,14 @@
<string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"ऐप्लिकेशन"</string>
<string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"अब आपके फ़िंगरप्रिंट की पहचान नहीं की जा सकती. फ़िंगरप्रिंट अनलॉक की सुविधा को दोबारा सेट अप करें."</string>
<string name="usb_apm_usb_plugged_in_when_locked_notification_title" msgid="468577168569874967">"यूएसबी डिवाइस को, Android डिवाइस के लॉक होने के दौरान प्लग इन किया गया"</string>
- <string name="usb_apm_usb_plugged_in_when_locked_notification_text" msgid="6695268246267993166">"यूएसबी डिवाइस को, Android डिवाइस के लॉक होने के दौरान प्लग इन किया गया. यूएसबी डिवाइस का इस्तेमाल करने के लिए, कृपया पहले Android डिवाइस को अनलॉक करें. इसके बाद, यूएसबी को फिर से इंसर्ट करें."</string>
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_notification_text (5561957202444135984) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_no_replug_notification_text (3844834116914726455) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_low_power_charge_notification_text (1264294871764444170) -->
+ <skip />
<string name="usb_apm_usb_suspicious_activity_notification_title" msgid="3461195995882871461">"यूएसबी से कोई संदिग्ध गतिविधि की गई"</string>
<string name="usb_apm_usb_suspicious_activity_notification_text" msgid="6537085605929303187">"यूएसबी से डेटा सिग्नल भेजने की प्रोसेस बंद कर दी गई है."</string>
- <!-- no translation found for fingerprint_frr_notification_title (3668331918920659093) -->
- <skip />
- <!-- no translation found for fingerprint_frr_notification_msg (2162966846624639252) -->
- <skip />
+ <string name="fingerprint_frr_notification_title" msgid="3668331918920659093">"क्या आपको फ़िंगरप्रिंट अनलॉक की सुविधा का इस्तेमाल करने में समस्या आ रही है?"</string>
+ <string name="fingerprint_frr_notification_msg" msgid="2162966846624639252">"डिवाइस को अनलॉक करने का अनुभव बेहतर बनाने की सलाह देखने के लिए टैप करें"</string>
</resources>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 58e0fc7..5f0196b8 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -1803,7 +1803,7 @@
<string name="color_inversion_feature_name" msgid="2672824491933264951">"Inverzija boja"</string>
<string name="color_correction_feature_name" msgid="7975133554160979214">"Korekcija boja"</string>
<string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Način rada jednom rukom"</string>
- <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Još tamnije"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Dodatno zatamnjenje"</string>
<string name="hearing_aids_feature_name" msgid="1125892105105852542">"Slušna pomagala"</string>
<string name="autoclick_feature_name" msgid="8149248738736949630">"Automatski klik"</string>
<string name="hearing_device_status_disconnected" msgid="497547752953543832">"Nije povezano"</string>
@@ -2273,6 +2273,7 @@
<string name="accessibility_autoclick_double_click" msgid="2103826849116176478">"Dvostruki klik"</string>
<string name="accessibility_autoclick_drag" msgid="1499559489796843224">"Povuci"</string>
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Pomakni se"</string>
+ <string name="accessibility_autoclick_long_press" msgid="8757561680164116951">"Dugi pritisak"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Pauziraj"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Pozicija"</string>
<string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Pomakni prema gore"</string>
@@ -2557,11 +2558,14 @@
<string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Aplikacije"</string>
<string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Vaši se otisci prstiju više ne prepoznaju. Ponovo postavite otključavanje otiskom prsta."</string>
<string name="usb_apm_usb_plugged_in_when_locked_notification_title" msgid="468577168569874967">"USB uređaj priključen je kada je zaključan"</string>
- <string name="usb_apm_usb_plugged_in_when_locked_notification_text" msgid="6695268246267993166">"USB uređaj priključen je kad je Android zaključan. Da biste upotrebljavali uređaj, najprije otključajte Android, a zatim ponovno umetnite USB uređaj da biste ga upotrebljavali."</string>
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_notification_text (5561957202444135984) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_no_replug_notification_text (3844834116914726455) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_low_power_charge_notification_text (1264294871764444170) -->
+ <skip />
<string name="usb_apm_usb_suspicious_activity_notification_title" msgid="3461195995882871461">"Sumnjiva aktivnost USB-a"</string>
<string name="usb_apm_usb_suspicious_activity_notification_text" msgid="6537085605929303187">"USB podatkovni signal je onemogućen."</string>
- <!-- no translation found for fingerprint_frr_notification_title (3668331918920659093) -->
- <skip />
- <!-- no translation found for fingerprint_frr_notification_msg (2162966846624639252) -->
- <skip />
+ <string name="fingerprint_frr_notification_title" msgid="3668331918920659093">"Imate li problema s otključavanjem otiskom prsta?"</string>
+ <string name="fingerprint_frr_notification_msg" msgid="2162966846624639252">"Dodirnite da biste pregledali savjete za lakše otključavanje"</string>
</resources>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index dce01ec..393dff5 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -1800,7 +1800,7 @@
<string name="disable_accessibility_shortcut" msgid="5806091378745232383">"Billentyűparancs kikapcsolása"</string>
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Billentyűparancs használata"</string>
<string name="color_inversion_feature_name" msgid="2672824491933264951">"Színek invertálása"</string>
- <string name="color_correction_feature_name" msgid="7975133554160979214">"Színjavítás"</string>
+ <string name="color_correction_feature_name" msgid="7975133554160979214">"Színkorrekció"</string>
<string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Egykezes mód"</string>
<string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Extrasötét"</string>
<string name="hearing_aids_feature_name" msgid="1125892105105852542">"Hallásjavító eszközök"</string>
@@ -2272,6 +2272,7 @@
<string name="accessibility_autoclick_double_click" msgid="2103826849116176478">"Duplakattintás"</string>
<string name="accessibility_autoclick_drag" msgid="1499559489796843224">"Húzás"</string>
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Görgetés"</string>
+ <string name="accessibility_autoclick_long_press" msgid="8757561680164116951">"Hosszú megnyomás"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Szüneteltetés"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Pozíció"</string>
<string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Görgetés felfelé"</string>
@@ -2556,7 +2557,12 @@
<string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Alkalmazások"</string>
<string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Az ujjlenyomata már nem ismerhető fel. Állítsa be újra a Feloldás ujjlenyomattal funkciót."</string>
<string name="usb_apm_usb_plugged_in_when_locked_notification_title" msgid="468577168569874967">"USB-eszköz zárolt állapotban csatlakoztatva"</string>
- <string name="usb_apm_usb_plugged_in_when_locked_notification_text" msgid="6695268246267993166">"Az USB-eszköz csatlakoztatása az Android rendszer zárolt állapotában történt. Az eszköz használatához először oldja fel az Android zárolását, majd helyezze be újra az USB-eszközt."</string>
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_notification_text (5561957202444135984) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_no_replug_notification_text (3844834116914726455) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_low_power_charge_notification_text (1264294871764444170) -->
+ <skip />
<string name="usb_apm_usb_suspicious_activity_notification_title" msgid="3461195995882871461">"Gyanús USB-tevékenység"</string>
<string name="usb_apm_usb_suspicious_activity_notification_text" msgid="6537085605929303187">"Az USB-adatjel le lett tiltva."</string>
<string name="fingerprint_frr_notification_title" msgid="3668331918920659093">"Problémája adódott a Feloldás ujjlenyomattal funkcióval?"</string>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index 50a527a..925a5cb 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -2272,6 +2272,7 @@
<string name="accessibility_autoclick_double_click" msgid="2103826849116176478">"Երկու անգամ սեղմել"</string>
<string name="accessibility_autoclick_drag" msgid="1499559489796843224">"Քաշել"</string>
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Ոլորել"</string>
+ <string name="accessibility_autoclick_long_press" msgid="8757561680164116951">"Երկար սեղմել"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Դադարեցնել"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Դիրքը"</string>
<string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Թերթել վերև"</string>
@@ -2556,7 +2557,12 @@
<string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Հավելվածներ"</string>
<string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Ձեր մատնահետքերն այլևս չեն կարող ճանաչվել։ Նորից կարգավորեք մատնահետքով ապակողպումը։"</string>
<string name="usb_apm_usb_plugged_in_when_locked_notification_title" msgid="468577168569874967">"Ապակողպեք Android սարքը"</string>
- <string name="usb_apm_usb_plugged_in_when_locked_notification_text" msgid="6695268246267993166">"Android սարքը կողպված է։ USB կրիչն օգտագործելու համար նախ ապակողպեք Android սարքը, այնուհետև նորից տեղադրեք կրիչը։"</string>
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_notification_text (5561957202444135984) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_no_replug_notification_text (3844834116914726455) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_low_power_charge_notification_text (1264294871764444170) -->
+ <skip />
<string name="usb_apm_usb_suspicious_activity_notification_title" msgid="3461195995882871461">"Հայտնաբերվել է կասկածելի USB կրիչ"</string>
<string name="usb_apm_usb_suspicious_activity_notification_text" msgid="6537085605929303187">"USB-ով տվյալների փոխանցումն անջատված է։"</string>
<string name="fingerprint_frr_notification_title" msgid="3668331918920659093">"Մատնահետքով ապակողպման խնդիր ունե՞ք"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index c1502d0..8cd0d62 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -1453,7 +1453,7 @@
<string name="usb_unsupported_audio_accessory_title" msgid="2335775548086533065">"Aksesori audio analog terdeteksi"</string>
<string name="usb_unsupported_audio_accessory_message" msgid="1300168007129796621">"Perangkat yang terpasang tidak kompatibel dengan ponsel ini. Ketuk untuk mempelajari lebih lanjut."</string>
<string name="adb_active_notification_title" msgid="408390247354560331">"Proses debug USB terhubung"</string>
- <string name="adb_active_notification_message" msgid="5617264033476778211">"Ketuk untuk nonaktifkan proses debug USB"</string>
+ <string name="adb_active_notification_message" msgid="5617264033476778211">"Ketuk untuk menonaktifkan proses debug USB"</string>
<string name="adb_active_notification_message" product="tv" msgid="6624498401272780855">"Pilih untuk menonaktifkan debugging USB."</string>
<string name="adbwifi_active_notification_title" msgid="6147343659168302473">"Proses debug nirkabel terhubung"</string>
<string name="adbwifi_active_notification_message" msgid="930987922852867972">"Ketuk untuk menonaktifkan proses debug nirkabel."</string>
@@ -1485,7 +1485,7 @@
<string name="select_keyboard_layout_notification_message" msgid="8835158247369158154">"Ketuk untuk memilih bahasa dan tata letak"</string>
<string name="fast_scroll_alphabet" msgid="8854435958703888376">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="2529539945421557329">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
- <string name="alert_windows_notification_channel_group_name" msgid="6063891141815714246">"Tampilkan di atas apl lain"</string>
+ <string name="alert_windows_notification_channel_group_name" msgid="6063891141815714246">"Tampilkan menindih aplikasi lain"</string>
<string name="alert_windows_notification_channel_name" msgid="3437528564303192620">"<xliff:g id="NAME">%s</xliff:g> ditampilkan di atas aplikasi lain"</string>
<string name="alert_windows_notification_title" msgid="6331662751095228536">"<xliff:g id="NAME">%s</xliff:g> ditampilkan di atas aplikasi lain"</string>
<string name="alert_windows_notification_message" msgid="6538171456970725333">"Jika Anda tidak ingin <xliff:g id="NAME">%s</xliff:g> menggunakan fitur ini, ketuk untuk membuka setelan dan menonaktifkannya."</string>
@@ -2272,6 +2272,7 @@
<string name="accessibility_autoclick_double_click" msgid="2103826849116176478">"Klik dua kali"</string>
<string name="accessibility_autoclick_drag" msgid="1499559489796843224">"Tarik"</string>
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Scroll"</string>
+ <string name="accessibility_autoclick_long_press" msgid="8757561680164116951">"Tekan lama"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Jeda"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Posisi"</string>
<string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Scroll ke Atas"</string>
@@ -2556,7 +2557,12 @@
<string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Aplikasi"</string>
<string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Sidik jari Anda tidak dapat dikenali lagi. Siapkan Buka dengan Sidik Jari lagi."</string>
<string name="usb_apm_usb_plugged_in_when_locked_notification_title" msgid="468577168569874967">"Perangkat USB dicolokkan saat terkunci"</string>
- <string name="usb_apm_usb_plugged_in_when_locked_notification_text" msgid="6695268246267993166">"Perangkat USB dicolokkan saat Android terkunci. Untuk menggunakan perangkat, buka kunci Android terlebih dahulu, lalu pasang kembali perangkat USB untuk menggunakannya."</string>
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_notification_text (5561957202444135984) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_no_replug_notification_text (3844834116914726455) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_low_power_charge_notification_text (1264294871764444170) -->
+ <skip />
<string name="usb_apm_usb_suspicious_activity_notification_title" msgid="3461195995882871461">"Aktivitas USB yang mencurigakan"</string>
<string name="usb_apm_usb_suspicious_activity_notification_text" msgid="6537085605929303187">"Sinyal data USB telah dinonaktifkan."</string>
<string name="fingerprint_frr_notification_title" msgid="3668331918920659093">"Mengalami masalah terkait fitur Buka dengan Sidik Jari?"</string>
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index 29fda71..aeaa483 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -2272,6 +2272,7 @@
<string name="accessibility_autoclick_double_click" msgid="2103826849116176478">"Tvísmella"</string>
<string name="accessibility_autoclick_drag" msgid="1499559489796843224">"Draga"</string>
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Fletta"</string>
+ <string name="accessibility_autoclick_long_press" msgid="8757561680164116951">"Halda inni"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Hlé"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Staðsetning"</string>
<string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Fletta upp"</string>
@@ -2556,11 +2557,14 @@
<string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Forrit"</string>
<string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Ekki er lengur hægt að bera kennsl á fingraförin þín. Settu fingrafarskenni upp aftur."</string>
<string name="usb_apm_usb_plugged_in_when_locked_notification_title" msgid="468577168569874967">"USB-tæki stungið í samband á meðan tæki var læst"</string>
- <string name="usb_apm_usb_plugged_in_when_locked_notification_text" msgid="6695268246267993166">"USB-tæki stungið í samband á meðan Android-tækið var læst. Byrjaðu á því að taka Android-tækið úr lás og stingdu síðan USB-tækinu í samband til að nota það."</string>
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_notification_text (5561957202444135984) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_no_replug_notification_text (3844834116914726455) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_low_power_charge_notification_text (1264294871764444170) -->
+ <skip />
<string name="usb_apm_usb_suspicious_activity_notification_title" msgid="3461195995882871461">"Grunsamleg USB-virkni"</string>
<string name="usb_apm_usb_suspicious_activity_notification_text" msgid="6537085605929303187">"Slökkt var á USB-gagnamerki."</string>
- <!-- no translation found for fingerprint_frr_notification_title (3668331918920659093) -->
- <skip />
- <!-- no translation found for fingerprint_frr_notification_msg (2162966846624639252) -->
- <skip />
+ <string name="fingerprint_frr_notification_title" msgid="3668331918920659093">"Er fingrafarskenni að valda þér vandræðum?"</string>
+ <string name="fingerprint_frr_notification_msg" msgid="2162966846624639252">"Ýttu til að sjá ábendingar um hvernig þú getur bætt ferlið við að taka tækið úr lás"</string>
</resources>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index fce090a..a2a9ee2 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -2273,6 +2273,7 @@
<string name="accessibility_autoclick_double_click" msgid="2103826849116176478">"Doppio clic"</string>
<string name="accessibility_autoclick_drag" msgid="1499559489796843224">"Trascina"</string>
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Scorri"</string>
+ <string name="accessibility_autoclick_long_press" msgid="8757561680164116951">"Tieni premuto"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Metti in pausa"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Posizione"</string>
<string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Scorri verso l\'alto"</string>
@@ -2557,7 +2558,9 @@
<string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Applicazioni"</string>
<string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Non è più possibile riconoscere le tue impronte. Riconfigura lo Sblocco con l\'Impronta."</string>
<string name="usb_apm_usb_plugged_in_when_locked_notification_title" msgid="468577168569874967">"Dispositivo USB collegato quando bloccato"</string>
- <string name="usb_apm_usb_plugged_in_when_locked_notification_text" msgid="6695268246267993166">"Il dispositivo USB è collegato quando Android è bloccato. Per usare il dispositivo, sblocca prima Android e poi reinserisci il dispositivo USB."</string>
+ <string name="usb_apm_usb_plugged_in_when_locked_notification_text" msgid="5561957202444135984">"Il dispositivo USB è collegato quando Android è bloccato. Per usare il dispositivo, sblocca prima Android."</string>
+ <string name="usb_apm_usb_plugged_in_when_locked_no_replug_notification_text" msgid="3844834116914726455">"Il dispositivo USB è collegato quando Android è bloccato. Per usare il dispositivo, sblocca prima Android e poi reinserisci il dispositivo USB."</string>
+ <string name="usb_apm_usb_plugged_in_when_locked_low_power_charge_notification_text" msgid="1264294871764444170">"La funzionalità di ricarica potrebbe essere ridotta o non disponibile quando il dispositivo è bloccato."</string>
<string name="usb_apm_usb_suspicious_activity_notification_title" msgid="3461195995882871461">"Attività USB sospetta"</string>
<string name="usb_apm_usb_suspicious_activity_notification_text" msgid="6537085605929303187">"L\'indicatore di dati USB è stato disattivato."</string>
<string name="fingerprint_frr_notification_title" msgid="3668331918920659093">"Hai problemi con lo Sblocco con l\'Impronta?"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 275405f..fca90df 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -270,8 +270,7 @@
<string name="global_action_bug_report" msgid="5127867163044170003">"דוח איתור באגים"</string>
<string name="global_action_logout" msgid="6093581310002476511">"סיום הפעלה"</string>
<string name="global_action_screenshot" msgid="2610053466156478564">"צילום מסך"</string>
- <!-- no translation found for global_action_standby (6786920589970257642) -->
- <skip />
+ <string name="global_action_standby" msgid="6786920589970257642">"המתנה"</string>
<string name="bugreport_title" msgid="8549990811777373050">"דיווח על באג"</string>
<string name="bugreport_message" msgid="5212529146119624326">"הפעולה הזו תאסוף מידע על מצב המכשיר הנוכחי שלך כדי לשלוח אותו כהודעת אימייל. היא תימשך זמן קצר מרגע פתיחת הדיווח על הבאג ועד לשליחת ההודעה בפועל. יש להמתין בסבלנות."</string>
<string name="bugreport_option_interactive_title" msgid="7968287837902871289">"דוח אינטראקטיבי"</string>
@@ -726,7 +725,7 @@
</string-array>
<string name="fingerprint_error_vendor_unknown" msgid="4170002184907291065">"משהו השתבש. עליך לנסות שוב."</string>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"סמל טביעת אצבע"</string>
- <string name="device_unlock_notification_name" msgid="2632928999862915709">"ביטול הנעילה של המכשיר"</string>
+ <string name="device_unlock_notification_name" msgid="2632928999862915709">"פתיחת הנעילה של המכשיר"</string>
<string name="alternative_unlock_setup_notification_title" msgid="6241508547901933544">"כדאי לנסות דרך אחרת לביטול הנעילה"</string>
<string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"אפשר להשתמש בפתיחה ע\"י זיהוי הפנים כשטביעת האצבע שלך לא מזוהה, למשל כשהאצבעות שלך רטובות"</string>
<string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"אפשר להשתמש בביטול הנעילה בטביעת אצבע כשהפנים שלך לא מזוהות, למשל כשאין מספיק אור"</string>
@@ -1455,7 +1454,7 @@
<string name="usb_unsupported_audio_accessory_title" msgid="2335775548086533065">"המכשיר זיהה התקן אודיו אנלוגי"</string>
<string name="usb_unsupported_audio_accessory_message" msgid="1300168007129796621">"ההתקן שחיברת לא תואם לטלפון הזה. יש ללחוץ לקבלת מידע נוסף."</string>
<string name="adb_active_notification_title" msgid="408390247354560331">"ניפוי באגים ב-USB מחובר"</string>
- <string name="adb_active_notification_message" msgid="5617264033476778211">"לוחצים להשבתת ניפוי הבאגים ב-USB"</string>
+ <string name="adb_active_notification_message" msgid="5617264033476778211">"כדי להשבית את ניפוי הבאגים ב-USB, צריך ללחוץ כאן"</string>
<string name="adb_active_notification_message" product="tv" msgid="6624498401272780855">"יש ללחוץ על ההתראה כדי להשבית ניפוי באגים ב-USB."</string>
<string name="adbwifi_active_notification_title" msgid="6147343659168302473">"ניפוי הבאגים האלחוטי מחובר"</string>
<string name="adbwifi_active_notification_message" msgid="930987922852867972">"יש ללחוץ כדי להשבית ניפוי באגים אלחוטי"</string>
@@ -1731,7 +1730,7 @@
<string name="display_manager_overlay_display_title" msgid="1480158037150469170">"<xliff:g id="NAME">%1$s</xliff:g>: <xliff:g id="WIDTH">%2$d</xliff:g>x<xliff:g id="HEIGHT">%3$d</xliff:g>, <xliff:g id="DPI">%4$d</xliff:g> dpi"</string>
<string name="display_manager_overlay_display_secure_suffix" msgid="2810034719482834679">", מאובטח"</string>
<string name="kg_forgot_pattern_button_text" msgid="406145459223122537">"שכחת את קו ביטול הנעילה?"</string>
- <string name="kg_wrong_pattern" msgid="1342812634464179931">"קו ביטול נעילה שגוי"</string>
+ <string name="kg_wrong_pattern" msgid="1342812634464179931">"קו פתיחת נעילה שגוי"</string>
<string name="kg_wrong_password" msgid="2384677900494439426">"סיסמה שגויה"</string>
<string name="kg_wrong_pin" msgid="3680925703673166482">"קוד אימות שגוי"</string>
<string name="kg_pattern_instructions" msgid="8366024510502517748">"צריך לשרטט את קו ביטול הנעילה"</string>
@@ -1821,9 +1820,9 @@
<string name="accessibility_gesture_instructional_text" msgid="4133877896011098550">"התכונה תיפתח בפעם הבאה שייעשה שימוש במקש הקיצור הזה. צריך להחליק למעלה עם 2 אצבעות מהחלק התחתון של המסך ולשחרר במהירות."</string>
<string name="accessibility_gesture_3finger_instructional_text" msgid="1124458279366968154">"התכונה תיפתח בפעם הבאה שייעשה שימוש במקש הקיצור הזה. צריך להחליק למעלה עם 3 אצבעות מהחלק התחתון של המסך ולשחרר במהירות."</string>
<string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"הגדלה"</string>
- <string name="hearing_device_switch_phone_mic_notification_title" msgid="6645178038359708836">"להחליף למיקרופון של הטלפון?"</string>
+ <string name="hearing_device_switch_phone_mic_notification_title" msgid="6645178038359708836">"רוצה לעבור למיקרופון של הטלפון?"</string>
<string name="hearing_device_switch_hearing_mic_notification_title" msgid="4612074852145289569">"להחליף למיקרופון של מכשיר השמיעה?"</string>
- <string name="hearing_device_switch_phone_mic_notification_text" msgid="1332426273666077412">"כדי לשמוע טוב יותר או אם הסוללה של מכשיר השמיעה נחלשת. במצב הזה המיקרופון מוחלף רק במהלך השיחה."</string>
+ <string name="hearing_device_switch_phone_mic_notification_text" msgid="1332426273666077412">"זה יכול לשפר את הסאונד או לעזור אם הסוללה של מכשיר השמיעה עומדת להיגמר. אחרי השיחה תוחזר למצב הקודם."</string>
<string name="hearing_device_switch_hearing_mic_notification_text" msgid="8288368365767284208">"אפשר לשוחח במצב דיבורית באמצעות המיקרופון של מכשיר השמיעה. במצב הזה המיקרופון מוחלף רק במהלך השיחה."</string>
<string name="hearing_device_notification_switch_button" msgid="3619524619430941300">"החלפה"</string>
<string name="hearing_device_notification_settings_button" msgid="6673651052880279178">"הגדרות"</string>
@@ -2274,6 +2273,7 @@
<string name="accessibility_autoclick_double_click" msgid="2103826849116176478">"לחיצה כפולה"</string>
<string name="accessibility_autoclick_drag" msgid="1499559489796843224">"גרירה"</string>
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"גלילה"</string>
+ <string name="accessibility_autoclick_long_press" msgid="8757561680164116951">"לחיצה ארוכה"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"השהיה"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"מיקום"</string>
<string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"גלילה למעלה"</string>
@@ -2558,11 +2558,14 @@
<string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"אפליקציות"</string>
<string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"טביעות האצבע שלך נשחקו ואי אפשר לזהות אותן. צריך להגדיר \'פתיחה בטביעת אצבע\' מחדש."</string>
<string name="usb_apm_usb_plugged_in_when_locked_notification_title" msgid="468577168569874967">"התקן ה-USB מחובר כשהמכשיר נעול"</string>
- <string name="usb_apm_usb_plugged_in_when_locked_notification_text" msgid="6695268246267993166">"התקן ה-USB מחובר כשמערכת Android נעולה. כדי להשתמש בהתקן ה-USB, קודם צריך לבטל את הנעילה של Android ואז לחבר את ההתקן מחדש."</string>
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_notification_text (5561957202444135984) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_no_replug_notification_text (3844834116914726455) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_low_power_charge_notification_text (1264294871764444170) -->
+ <skip />
<string name="usb_apm_usb_suspicious_activity_notification_title" msgid="3461195995882871461">"זוהתה פעילות חשודה בהתקן USB"</string>
<string name="usb_apm_usb_suspicious_activity_notification_text" msgid="6537085605929303187">"האות עם הנתונים מהתקן ה-USB הושבת."</string>
- <!-- no translation found for fingerprint_frr_notification_title (3668331918920659093) -->
- <skip />
- <!-- no translation found for fingerprint_frr_notification_msg (2162966846624639252) -->
- <skip />
+ <string name="fingerprint_frr_notification_title" msgid="3668331918920659093">"נתקלת בבעיה בפתיחה בטביעת אצבע?"</string>
+ <string name="fingerprint_frr_notification_msg" msgid="2162966846624639252">"אפשר ללחוץ כאן כדי לקבל טיפים לפתיחה נוחה יותר של המכשיר"</string>
</resources>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 4dadcc5..5810ea6 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -2272,6 +2272,7 @@
<string name="accessibility_autoclick_double_click" msgid="2103826849116176478">"ダブルクリック"</string>
<string name="accessibility_autoclick_drag" msgid="1499559489796843224">"ドラッグ"</string>
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"スクロール"</string>
+ <string name="accessibility_autoclick_long_press" msgid="8757561680164116951">"長押し"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"一時停止"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"位置"</string>
<string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"上にスクロール"</string>
@@ -2556,7 +2557,9 @@
<string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"アプリ"</string>
<string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"指紋を認識できなくなりました。指紋認証をもう一度設定してください。"</string>
<string name="usb_apm_usb_plugged_in_when_locked_notification_title" msgid="468577168569874967">"ロック状態で USB デバイスが接続されました"</string>
- <string name="usb_apm_usb_plugged_in_when_locked_notification_text" msgid="6695268246267993166">"Android がロックされている状態で USB デバイスが接続されています。デバイスを使用するには、まず Android のロックを解除してから、USB デバイスを再挿入してください。"</string>
+ <string name="usb_apm_usb_plugged_in_when_locked_notification_text" msgid="5561957202444135984">"Android がロックされている状態で USB デバイスが接続されています。デバイスを使用するには、まず Android のロックを解除してください。"</string>
+ <string name="usb_apm_usb_plugged_in_when_locked_no_replug_notification_text" msgid="3844834116914726455">"Android がロックされている状態で USB デバイスが接続されています。デバイスを使用するには、まず Android のロックを解除してから、USB デバイスを挿し直してください。"</string>
+ <string name="usb_apm_usb_plugged_in_when_locked_low_power_charge_notification_text" msgid="1264294871764444170">"ロック中は、充電の機能が低下したり、充電できなくなったりすることがあります。"</string>
<string name="usb_apm_usb_suspicious_activity_notification_title" msgid="3461195995882871461">"不審な USB アクティビティ"</string>
<string name="usb_apm_usb_suspicious_activity_notification_text" msgid="6537085605929303187">"USB データシグナルが無効になっています。"</string>
<string name="fingerprint_frr_notification_title" msgid="3668331918920659093">"指紋認証で問題が発生している場合"</string>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index 4d9e06c..c028f4e 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -2272,6 +2272,7 @@
<string name="accessibility_autoclick_double_click" msgid="2103826849116176478">"ორმაგი დაწკაპუნება"</string>
<string name="accessibility_autoclick_drag" msgid="1499559489796843224">"ჩავლება"</string>
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"გადაადგილება"</string>
+ <string name="accessibility_autoclick_long_press" msgid="8757561680164116951">"ხანგრძლივი დაჭერა"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"პაუზა"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"პოზიცია"</string>
<string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"ზემოთ გადაადგილება"</string>
@@ -2556,7 +2557,12 @@
<string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"აპლიკაციები"</string>
<string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"თქვენი თითის ანაბეჭდის ამოცნობა ვეღარ ხერხდება. ხელახლა დააყენეთ ანაბეჭდით განბლოკვა."</string>
<string name="usb_apm_usb_plugged_in_when_locked_notification_title" msgid="468577168569874967">"USB მოწყობილობა ჩართულია, როდესაც ჩაკეტილია"</string>
- <string name="usb_apm_usb_plugged_in_when_locked_notification_text" msgid="6695268246267993166">"USB მოწყობილობა ჩართულია, როდესაც Android ჩაკეტილია. მოწყობილობის გამოსაყენებლად ჯერ განბლოკეთ Android და შემდეგ ხელახლა ჩადეთ USB მოწყობილობა მის გამოსაყენებლად."</string>
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_notification_text (5561957202444135984) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_no_replug_notification_text (3844834116914726455) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_low_power_charge_notification_text (1264294871764444170) -->
+ <skip />
<string name="usb_apm_usb_suspicious_activity_notification_title" msgid="3461195995882871461">"საეჭვო USB აქტივობა"</string>
<string name="usb_apm_usb_suspicious_activity_notification_text" msgid="6537085605929303187">"USB მონაცემთა სიგნალი გამორთულია."</string>
<string name="fingerprint_frr_notification_title" msgid="3668331918920659093">"ანაბეჭდით განბლოკვასთან დაკავშირებული პრობლემა გაქვთ?"</string>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index af3727f..7a79153 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -2004,7 +2004,7 @@
<string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> – <xliff:g id="END">%2$s</xliff:g>"</string>
<string name="zen_mode_trigger_summary_combined" msgid="6492381546327807669">"<xliff:g id="DAYS">%1$s</xliff:g> <xliff:g id="TIMES">%2$s</xliff:g>"</string>
<string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Кез келген күнтізбе"</string>
- <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> кейбір дыбыстарды өшіруде"</string>
+ <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> кейбір дыбыстарды өшіріп жатыр."</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"There\'s an internal problem with your device, and it may be unstable until you factory data reset."</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"There\'s an internal problem with your device. Contact your manufacturer for details."</string>
<string name="stk_cc_ussd_to_dial" msgid="3139884150741157610">"USSD сұрауы кәдімгі қоңырауға өзгертілді"</string>
@@ -2039,7 +2039,7 @@
<string name="call_notification_incoming_text" msgid="6143109825406638201">"Кіріс қоңырау"</string>
<string name="call_notification_ongoing_text" msgid="3880832933933020875">"Қоңырау"</string>
<string name="call_notification_screening_text" msgid="8396931408268940208">"Келген қоңырауды сүзу"</string>
- <string name="default_notification_channel_label" msgid="3697928973567217330">"Санатқа жатқызылмаған"</string>
+ <string name="default_notification_channel_label" msgid="3697928973567217330">"Санаты көрсетілмеген"</string>
<string name="promotional_notification_channel_label" msgid="7414844730492860233">"Промонауқандар"</string>
<string name="social_notification_channel_label" msgid="106520267132019945">"Әлеуметтік желі"</string>
<string name="news_notification_channel_label" msgid="4299937455247883311">"Жаңалықтар"</string>
@@ -2062,7 +2062,7 @@
<string name="locale_search_menu" msgid="6258090710176422934">"Іздеу"</string>
<string name="app_suspended_title" msgid="888873445010322650">"Қолданба қолжетімді емес"</string>
<string name="app_suspended_default_message" msgid="6451215678552004172">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> дәл қазір қолжетімді емес. Ол <xliff:g id="APP_NAME_1">%2$s</xliff:g> арқылы басқарылады."</string>
- <string name="app_suspended_more_details" msgid="211260942831587014">"Толығырақ"</string>
+ <string name="app_suspended_more_details" msgid="211260942831587014">"Толық ақпарат"</string>
<string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"Қолданбаны қайта қосу"</string>
<string name="work_mode_off_title" msgid="6367463960165135829">"Жұмыс қолданбаларын қайта қосасыз ба?"</string>
<string name="work_mode_turn_on" msgid="5316648862401307800">"Қайта қосу"</string>
@@ -2210,7 +2210,7 @@
<string name="nas_upgrade_notification_content" msgid="5157550369837103337">"Ұсынылған әрекеттер мен жауаптар енді кеңейтілген хабарландырулар функциясы арқылы қамтамасыз етіледі. Android-тың \"Бейімделетін хабарландырулар\" функциясына бұдан былай қолдау көрсетілмейді."</string>
<string name="nas_upgrade_notification_enable_action" msgid="3046406808378726874">"Жарайды"</string>
<string name="nas_upgrade_notification_disable_action" msgid="3794833210043497982">"Өшіру"</string>
- <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"Толығырақ"</string>
+ <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"Толық ақпарат"</string>
<string name="nas_upgrade_notification_learn_more_content" msgid="3735480566983530650">"Android 12 жүйесінде \"Бейімделетін хабарландырулар\" функциясын \"Кеңейтілген хабарландырулар\" алмастырды. Бұл функция ұсынылған әрекеттер мен жауаптарды көрсетіп, хабарландыруларыңызды ретке келтіреді.\n\nОл хабарландыру контентін, соның ішінде жеке ақпаратыңызды (мысалы, контакт аттары мен хабарларды) пайдалана алады. Сондай-ақ бұл функция арқылы хабарландыруларды жабуға немесе оларға жауап беруге (мысалы, телефон қоңырауларына жауап беруге және Мазаламау режимін басқаруға) болады."</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Режим туралы хабарландыру"</string>
<string name="dynamic_mode_notification_title" msgid="1388718452788985481">"Батареяны үнемдеу режимі қосулы"</string>
@@ -2272,6 +2272,7 @@
<string name="accessibility_autoclick_double_click" msgid="2103826849116176478">"Екі рет басу"</string>
<string name="accessibility_autoclick_drag" msgid="1499559489796843224">"Сүйреу"</string>
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Айналдыру"</string>
+ <string name="accessibility_autoclick_long_press" msgid="8757561680164116951">"Басып тұру"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Кідірту"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Орналастыру"</string>
<string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Жоғары айналдыру"</string>
@@ -2556,11 +2557,14 @@
<string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Қолданбалар"</string>
<string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Саусағыңыздың іздері бұдан былай танылмайды. Саусақ ізімен ашу функциясын қайта реттеу"</string>
<string name="usb_apm_usb_plugged_in_when_locked_notification_title" msgid="468577168569874967">"USB құрылғысы құлыптаулы кезде жалғанған"</string>
- <string name="usb_apm_usb_plugged_in_when_locked_notification_text" msgid="6695268246267993166">"USB құрылғысы Android құрылғысы құлыптаулы кезде жалғанған. Құрылғыны пайдалану үшін алдымен Android құрылғысының құлпын ашып, содан кейін USB құрылғысын қайта енгізіңіз."</string>
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_notification_text (5561957202444135984) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_no_replug_notification_text (3844834116914726455) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_low_power_charge_notification_text (1264294871764444170) -->
+ <skip />
<string name="usb_apm_usb_suspicious_activity_notification_title" msgid="3461195995882871461">"USB-ге қатысты күдікті әрекет"</string>
<string name="usb_apm_usb_suspicious_activity_notification_text" msgid="6537085605929303187">"USB дерек сигналы өшірілді."</string>
- <!-- no translation found for fingerprint_frr_notification_title (3668331918920659093) -->
- <skip />
- <!-- no translation found for fingerprint_frr_notification_msg (2162966846624639252) -->
- <skip />
+ <string name="fingerprint_frr_notification_title" msgid="3668331918920659093">"Саусақ ізімен ашу функциясына қатысты мәселе шықты ма?"</string>
+ <string name="fingerprint_frr_notification_msg" msgid="2162966846624639252">"Экранның құлпын ашу бойынша кеңестерді көру үшін түртіңіз."</string>
</resources>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index c03c046..c77582a 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -2272,6 +2272,7 @@
<string name="accessibility_autoclick_double_click" msgid="2103826849116176478">"ចុចពីរដង"</string>
<string name="accessibility_autoclick_drag" msgid="1499559489796843224">"អូស"</string>
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"រំកិល"</string>
+ <string name="accessibility_autoclick_long_press" msgid="8757561680164116951">"ចុចឱ្យយូរ"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"ផ្អាក"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"ទីតាំង"</string>
<string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"រំកិលឡើងលើ"</string>
@@ -2556,7 +2557,12 @@
<string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"កម្មវិធី"</string>
<string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"លែងអាចសម្គាល់ស្នាមម្រាមដៃរបស់អ្នកបានទៀតហើយ។ សូមរៀបចំការដោះសោដោយស្កេនស្នាមម្រាមដៃម្ដងទៀត។"</string>
<string name="usb_apm_usb_plugged_in_when_locked_notification_title" msgid="468577168569874967">"បានដោតឧបករណ៍ USB នៅពេលជាប់សោ"</string>
- <string name="usb_apm_usb_plugged_in_when_locked_notification_text" msgid="6695268246267993166">"ឧបករណ៍ USB ត្រូវបានដោត នៅពេល Android ត្រូវបានចាក់សោ។ ដើម្បីប្រើឧបករណ៍ សូមដោះសោ Android ជាមុនសិន បន្ទាប់មកដោតឧបករណ៍ USB ឡើងវិញ ដើម្បីប្រើប្រាស់វា។"</string>
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_notification_text (5561957202444135984) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_no_replug_notification_text (3844834116914726455) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_low_power_charge_notification_text (1264294871764444170) -->
+ <skip />
<string name="usb_apm_usb_suspicious_activity_notification_title" msgid="3461195995882871461">"សកម្មភាព USB ដែលគួរឱ្យសង្ស័យ"</string>
<string name="usb_apm_usb_suspicious_activity_notification_text" msgid="6537085605929303187">"សញ្ញាទិន្នន័យ USB ត្រូវបានបិទ។"</string>
<string name="fingerprint_frr_notification_title" msgid="3668331918920659093">"មានបញ្ហាពាក់ព័ន្ធនឹងការដោះសោដោយស្កេនស្នាមម្រាមដៃឬ?"</string>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index 42e515c..2e933e3 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -1339,7 +1339,7 @@
<string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"ನೀವು ಪವರ್ ಬಟನ್ ಒತ್ತಿದ್ದೀರಿ — ಇದು ಸಾಮಾನ್ಯವಾಗಿ ಸ್ಕ್ರೀನ್ ಅನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸುತ್ತದೆ.\n\nನಿಮ್ಮ ಫಿಂಗರ್ಪ್ರಿಂಟ್ ಅನ್ನು ಹೊಂದಿಸುವಾಗ ಲಘುವಾಗಿ ಟ್ಯಾಪ್ ಮಾಡಲು ಪ್ರಯತ್ನಿಸಿ."</string>
<string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"ಸೆಟಪ್ ಅಂತ್ಯಗೊಳಿಸಲು, ಸ್ಕ್ರೀನ್ ಆಫ್ ಮಾಡಿ"</string>
<string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"ಆಫ್ ಮಾಡಿ"</string>
- <string name="fp_power_button_bp_title" msgid="5585506104526820067">"ಫಿಂಗರ್ಪ್ರಿಂಟ್ ಪರಿಶೀಲನೆ ಮುಂದುವರಿಸುವುದೇ?"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"ಫಿಂಗರ್ಪ್ರಿಂಟ್ ಪರಿಶೀಲನೆ ಮುಂದುವರಿಸಬೇಕೇ?"</string>
<string name="fp_power_button_bp_message" msgid="2983163038168903393">"ನೀವು ಪವರ್ ಬಟನ್ ಒತ್ತಿದ್ದೀರಿ — ಇದು ಸಾಮಾನ್ಯವಾಗಿ ಸ್ಕ್ರೀನ್ ಅನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸುತ್ತದೆ.\n\nನಿಮ್ಮ ಫಿಂಗರ್ಪ್ರಿಂಟ್ ಅನ್ನು ಪರಿಶೀಲಿಸಲು ಲಘುವಾಗಿ ಟ್ಯಾಪ್ ಮಾಡಲು ಪ್ರಯತ್ನಿಸಿ."</string>
<string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"ಸ್ಕ್ರೀನ್ ಆಫ್ ಮಾಡಿ"</string>
<string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"ಮುಂದುವರಿಸಿ"</string>
@@ -2004,7 +2004,7 @@
<string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> ನಿಂದ <xliff:g id="END">%2$s</xliff:g> ವರೆಗೆ"</string>
<string name="zen_mode_trigger_summary_combined" msgid="6492381546327807669">"<xliff:g id="DAYS">%1$s</xliff:g>, <xliff:g id="TIMES">%2$s</xliff:g>"</string>
<string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"ಯಾವುದೇ ಕ್ಯಾಲೆಂಡರ್"</string>
- <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> ಧ್ವನಿ ಮ್ಯೂಟ್ ಮಾಡುತ್ತಿದ್ದಾರೆ"</string>
+ <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> ಕೆಲವು ಧ್ವನಿ ಮ್ಯೂಟ್ ಮಾಡುತ್ತಿದೆ"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"ನಿಮ್ಮ ಸಾಧನದಲ್ಲಿ ಆಂತರಿಕ ಸಮಸ್ಯೆಯಿದೆ ಹಾಗೂ ನೀವು ಫ್ಯಾಕ್ಟರಿ ಡೇಟಾವನ್ನು ರೀಸೆಟ್ ಮಾಡುವವರೆಗೂ ಅದು ಅಸ್ಥಿರವಾಗಬಹುದು."</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"ನಿಮ್ಮ ಸಾಧನದಲ್ಲಿ ಆಂತರಿಕ ಸಮಸ್ಯೆಯಿದೆ. ವಿವರಗಳಿಗಾಗಿ ನಿಮ್ಮ ತಯಾರಕರನ್ನು ಸಂಪರ್ಕಿಸಿ."</string>
<string name="stk_cc_ussd_to_dial" msgid="3139884150741157610">"USSD ವಿನಂತಿಯನ್ನು ಸಾಮಾನ್ಯ ಕರೆಗೆ ಬದಲಾಯಿಸಲಾಗಿದೆ"</string>
@@ -2272,6 +2272,7 @@
<string name="accessibility_autoclick_double_click" msgid="2103826849116176478">"ಡಬಲ್ ಕ್ಲಿಕ್"</string>
<string name="accessibility_autoclick_drag" msgid="1499559489796843224">"ಡ್ರ್ಯಾಗ್ ಮಾಡಿ"</string>
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"ಸ್ಕ್ರಾಲ್ ಮಾಡಿ"</string>
+ <string name="accessibility_autoclick_long_press" msgid="8757561680164116951">"ಲಾಂಗ್ ಪ್ರೆಸ್"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"ವಿರಾಮಗೊಳಿಸಿ"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"ಸ್ಥಾನ"</string>
<string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"ಮೇಲಕ್ಕೆ ಸ್ಕ್ರಾಲ್ ಮಾಡಿ"</string>
@@ -2556,7 +2557,12 @@
<string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"ಆ್ಯಪ್ಗಳು"</string>
<string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"ನಿಮ್ಮ ಫಿಂಗರ್ಪ್ರಿಂಟ್ಗಳನ್ನು ಇನ್ನು ಮುಂದೆ ಗುರುತಿಸಲಾಗುವುದಿಲ್ಲ. ಫಿಂಗರ್ಪ್ರಿಂಟ್ ಅನ್ಲಾಕ್ ಅನ್ನು ಮತ್ತೊಮ್ಮೆ ಸೆಟಪ್ ಮಾಡಿ."</string>
<string name="usb_apm_usb_plugged_in_when_locked_notification_title" msgid="468577168569874967">"ಲಾಕ್ ಆಗಿರುವಾಗ USB ಸಾಧನವನ್ನು ಪ್ಲಗ್-ಇನ್ ಮಾಡಲಾಗುತ್ತದೆ"</string>
- <string name="usb_apm_usb_plugged_in_when_locked_notification_text" msgid="6695268246267993166">"Android ಲಾಕ್ ಆಗಿರುವಾಗ USB ಸಾಧನವನ್ನು ಪ್ಲಗ್-ಇನ್ ಮಾಡಲಾಗುತ್ತದೆ. ಸಾಧನವನ್ನು ಬಳಸಲು, ಮೊದಲು Android ಅನ್ನು ಅನ್ಲಾಕ್ ಮಾಡಿ ಮತ್ತು ನಂತರ ಅದನ್ನು ಬಳಸಲು USB ಸಾಧನವನ್ನು ಪುನಃ ಸೇರಿಸಿ."</string>
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_notification_text (5561957202444135984) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_no_replug_notification_text (3844834116914726455) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_low_power_charge_notification_text (1264294871764444170) -->
+ <skip />
<string name="usb_apm_usb_suspicious_activity_notification_title" msgid="3461195995882871461">"ಅನುಮಾನಾಸ್ಪದ USB ಚಟುವಟಿಕೆ"</string>
<string name="usb_apm_usb_suspicious_activity_notification_text" msgid="6537085605929303187">"USB ಡೇಟಾ ಸಿಗ್ನಲ್ ಅನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ."</string>
<string name="fingerprint_frr_notification_title" msgid="3668331918920659093">"ಫಿಂಗರ್ಪ್ರಿಂಟ್ ಅನ್ಲಾಕ್ನಲ್ಲಿ ತೊಂದರೆ ಇದೆಯೇ?"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index e79dc40..3bac4f5 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -2272,6 +2272,7 @@
<string name="accessibility_autoclick_double_click" msgid="2103826849116176478">"더블클릭"</string>
<string name="accessibility_autoclick_drag" msgid="1499559489796843224">"드래그"</string>
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"스크롤"</string>
+ <string name="accessibility_autoclick_long_press" msgid="8757561680164116951">"길게 누르기"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"일시중지"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"위치"</string>
<string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"위로 스크롤"</string>
@@ -2556,11 +2557,14 @@
<string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"애플리케이션"</string>
<string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"지문을 더 이상 인식할 수 없습니다. 지문 잠금 해제를 다시 설정하세요."</string>
<string name="usb_apm_usb_plugged_in_when_locked_notification_title" msgid="468577168569874967">"잠금 상태에서 USB 기기가 연결됨"</string>
- <string name="usb_apm_usb_plugged_in_when_locked_notification_text" msgid="6695268246267993166">"Android가 잠겨 있는 상태에서 USB 기기가 연결되었습니다. 기기를 사용하려면 먼저 Android를 잠금 해제한 다음 USB 기기를 다시 삽입하세요."</string>
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_notification_text (5561957202444135984) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_no_replug_notification_text (3844834116914726455) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_low_power_charge_notification_text (1264294871764444170) -->
+ <skip />
<string name="usb_apm_usb_suspicious_activity_notification_title" msgid="3461195995882871461">"의심스러운 USB 활동"</string>
<string name="usb_apm_usb_suspicious_activity_notification_text" msgid="6537085605929303187">"USB 데이터 신호가 사용 중지됨"</string>
- <!-- no translation found for fingerprint_frr_notification_title (3668331918920659093) -->
- <skip />
- <!-- no translation found for fingerprint_frr_notification_msg (2162966846624639252) -->
- <skip />
+ <string name="fingerprint_frr_notification_title" msgid="3668331918920659093">"지문 잠금 해제에 문제가 있나요?"</string>
+ <string name="fingerprint_frr_notification_msg" msgid="2162966846624639252">"잠금 해제 환경을 개선하기 위한 도움말을 검토하려면 탭하세요."</string>
</resources>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index f0b99b6..e21d2dd 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -2272,6 +2272,7 @@
<string name="accessibility_autoclick_double_click" msgid="2103826849116176478">"Эки жолу чыкылдатуу"</string>
<string name="accessibility_autoclick_drag" msgid="1499559489796843224">"Сүйрөө"</string>
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Сыдыруу"</string>
+ <string name="accessibility_autoclick_long_press" msgid="8757561680164116951">"Кое бербей басып туруу"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Тындыруу"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Орду"</string>
<string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Жогору сыдыруу"</string>
@@ -2556,11 +2557,14 @@
<string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Колдонмолор"</string>
<string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Манжаңыздын изи мындан ары таанылбайт. Манжа изи менен ачуу функциясын кайрадан тууралаңыз."</string>
<string name="usb_apm_usb_plugged_in_when_locked_notification_title" msgid="468577168569874967">"USB түзмөк Android кулпуланып турганда туташтырылды"</string>
- <string name="usb_apm_usb_plugged_in_when_locked_notification_text" msgid="6695268246267993166">"USB түзмөк Android кулпуланганда туташтырылды. Түзмөктү колдонуу үчүн алгач Android\'дин кулпусун ачып, USB түзмөктү кайрадан сайыңыз."</string>
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_notification_text (5561957202444135984) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_no_replug_notification_text (3844834116914726455) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_low_power_charge_notification_text (1264294871764444170) -->
+ <skip />
<string name="usb_apm_usb_suspicious_activity_notification_title" msgid="3461195995882871461">"USB\'ге байланыштуу шектүү аракет"</string>
<string name="usb_apm_usb_suspicious_activity_notification_text" msgid="6537085605929303187">"USB сигналы өчүрүлдү."</string>
- <!-- no translation found for fingerprint_frr_notification_title (3668331918920659093) -->
- <skip />
- <!-- no translation found for fingerprint_frr_notification_msg (2162966846624639252) -->
- <skip />
+ <string name="fingerprint_frr_notification_title" msgid="3668331918920659093">"Манжа изи менен ачылбай жатабы?"</string>
+ <string name="fingerprint_frr_notification_msg" msgid="2162966846624639252">"Кулпуну ачуу боюнча кеңештерди көрүү үчүн таптап коюңуз"</string>
</resources>
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index a84b7c5..ffd700a 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -2272,6 +2272,7 @@
<string name="accessibility_autoclick_double_click" msgid="2103826849116176478">"ຄລິກສອງເທື່ອ"</string>
<string name="accessibility_autoclick_drag" msgid="1499559489796843224">"ລາກ"</string>
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"ເລື່ອນ"</string>
+ <string name="accessibility_autoclick_long_press" msgid="8757561680164116951">"ກົດຄ້າງໄວ້"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"ຢຸດຊົ່ວຄາວ"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"ຕຳແໜ່ງ"</string>
<string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"ເລື່ອນຂຶ້ນ"</string>
@@ -2556,7 +2557,9 @@
<string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"ແອັບພລິເຄຊັນ"</string>
<string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"ລະບົບບໍ່ສາມາດຈຳແນກລາຍນິ້ວມືຂອງທ່ານໄດ້ອີກຕໍ່ໄປ. ກະລຸນາຕັ້ງຄ່າການປົດລັອກດ້ວຍລາຍນິ້ວມືອີກຄັ້ງ."</string>
<string name="usb_apm_usb_plugged_in_when_locked_notification_title" msgid="468577168569874967">"ທ່ານສຽບອຸປະກອນ USB ໃນຂະນະທີ່ລັອກຢູ່"</string>
- <string name="usb_apm_usb_plugged_in_when_locked_notification_text" msgid="6695268246267993166">"ທ່ານສຽບອຸປະກອນ USB ໃນຂະນະທີ່ Android ລັອກຢູ່. ເພື່ອໃຊ້ອຸປະກອນ, ກະລຸນາປົດລັອກ Android ກ່ອນແລ້ວຈຶ່ງສຽບອຸປະກອນ USB ອີກຄັ້ງເພື່ອນຳໃຊ້."</string>
+ <string name="usb_apm_usb_plugged_in_when_locked_notification_text" msgid="5561957202444135984">"ທ່ານສຽບອຸປະກອນ USB ໃນຂະນະທີ່ Android ລັອກຢູ່. ເພື່ອໃຊ້ອຸປະກອນ, ກະລຸນາປົດລັອກ Android ກ່ອນ."</string>
+ <string name="usb_apm_usb_plugged_in_when_locked_no_replug_notification_text" msgid="3844834116914726455">"ທ່ານສຽບອຸປະກອນ USB ໃນຂະນະທີ່ Android ລັອກຢູ່. ເພື່ອໃຊ້ອຸປະກອນ, ກະລຸນາປົດລັອກ Android ກ່ອນແລ້ວຈຶ່ງສຽບອຸປະກອນ USB ອີກຄັ້ງເພື່ອນຳໃຊ້."</string>
+ <string name="usb_apm_usb_plugged_in_when_locked_low_power_charge_notification_text" msgid="1264294871764444170">"ຄວາມສາມາດໃນການສາກໄຟອາດຈະຫຼຸດລົງ ຫຼື ໃຊ້ບໍ່ໄດ້ເມື່ອລັອກໄວ້."</string>
<string name="usb_apm_usb_suspicious_activity_notification_title" msgid="3461195995882871461">"ການເຄື່ອນໄຫວຂອງ USB ທີ່ໜ້າສົງໄສ"</string>
<string name="usb_apm_usb_suspicious_activity_notification_text" msgid="6537085605929303187">"ສັນຍານຂໍ້ມູນຂອງ USB ຖືກປິດການນຳໃຊ້."</string>
<string name="fingerprint_frr_notification_title" msgid="3668331918920659093">"ພົບບັນຫາໃນການປົດລັອກດ້ວຍລາຍນິ້ວມືບໍ?"</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index 1d54ba4..84aac82 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -2274,6 +2274,7 @@
<string name="accessibility_autoclick_double_click" msgid="2103826849116176478">"Dukart spustelėti"</string>
<string name="accessibility_autoclick_drag" msgid="1499559489796843224">"Vilkti"</string>
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Slinkti"</string>
+ <string name="accessibility_autoclick_long_press" msgid="8757561680164116951">"Ilgai paspausti"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Pristabdyti"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Pozicija"</string>
<string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Slinkti aukštyn"</string>
@@ -2558,11 +2559,11 @@
<string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Programos"</string>
<string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Nebegalima atpažinti jūsų piršto atspaudų. Dar kartą nustatykite atrakinimą piršto atspaudu."</string>
<string name="usb_apm_usb_plugged_in_when_locked_notification_title" msgid="468577168569874967">"USB įrenginys prijungtas, kai užrakintas"</string>
- <string name="usb_apm_usb_plugged_in_when_locked_notification_text" msgid="6695268246267993166">"USB įrenginys prijungtas, kai „Android“ užrakintas. Jei norite naudoti įrenginį, pirmiausia atrakinkite „Android“, tada vėl įkiškite USB įrenginį, kad galėtumėte jį naudoti."</string>
+ <string name="usb_apm_usb_plugged_in_when_locked_notification_text" msgid="5561957202444135984">"USB įrenginys prijungtas, kai „Android“ užrakintas. Jei norite naudoti įrenginį, pirmiausia atrakinkite „Android“."</string>
+ <string name="usb_apm_usb_plugged_in_when_locked_no_replug_notification_text" msgid="3844834116914726455">"USB įrenginys prijungtas, kai „Android“ užrakintas. Jei norite naudoti įrenginį, pirmiausia atrakinkite „Android“, tada vėl įkiškite USB įrenginį, kad galėtumėte jį naudoti."</string>
+ <string name="usb_apm_usb_plugged_in_when_locked_low_power_charge_notification_text" msgid="1264294871764444170">"Įkrovimo galimybės gali būti ribotos arba nepasiekiamos, kai įrenginys užrakintas."</string>
<string name="usb_apm_usb_suspicious_activity_notification_title" msgid="3461195995882871461">"Įtartina USB veikla"</string>
<string name="usb_apm_usb_suspicious_activity_notification_text" msgid="6537085605929303187">"USB duomenų signalas išjungtas."</string>
- <!-- no translation found for fingerprint_frr_notification_title (3668331918920659093) -->
- <skip />
- <!-- no translation found for fingerprint_frr_notification_msg (2162966846624639252) -->
- <skip />
+ <string name="fingerprint_frr_notification_title" msgid="3668331918920659093">"Kyla problemų dėl atrakinimo piršto atspaudu?"</string>
+ <string name="fingerprint_frr_notification_msg" msgid="2162966846624639252">"Palieskite, kad peržiūrėtumėte patarimus, kaip pagerinti atrakinimo funkciją"</string>
</resources>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index 2e3b82c..42b09948 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -2273,6 +2273,7 @@
<string name="accessibility_autoclick_double_click" msgid="2103826849116176478">"Veikt dubultklikšķi"</string>
<string name="accessibility_autoclick_drag" msgid="1499559489796843224">"Vilkt"</string>
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Ritināt"</string>
+ <string name="accessibility_autoclick_long_press" msgid="8757561680164116951">"Nospiest un turēt"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Pārtraukt"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Pozīcija"</string>
<string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Ritināt augšup"</string>
@@ -2557,11 +2558,14 @@
<string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Lietojumprogrammas"</string>
<string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Jūsu pirkstu nospiedumus vairs nevar atpazīt. Vēlreiz iestatiet autorizāciju ar pirksta nospiedumu."</string>
<string name="usb_apm_usb_plugged_in_when_locked_notification_title" msgid="468577168569874967">"USB ierīce tika pievienota, kad Android ierīce bija bloķēta"</string>
- <string name="usb_apm_usb_plugged_in_when_locked_notification_text" msgid="6695268246267993166">"USB ierīce tika pievienota, kad Android ierīce bija bloķēta. Lai izmantotu ierīci, vispirms atbloķējiet Android ierīci un pēc tam atkārtoti pievienojiet USB ierīci."</string>
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_notification_text (5561957202444135984) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_no_replug_notification_text (3844834116914726455) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_low_power_charge_notification_text (1264294871764444170) -->
+ <skip />
<string name="usb_apm_usb_suspicious_activity_notification_title" msgid="3461195995882871461">"Aizdomīgas darbības ar USB"</string>
<string name="usb_apm_usb_suspicious_activity_notification_text" msgid="6537085605929303187">"USB datu signāls ir atspējots."</string>
- <!-- no translation found for fingerprint_frr_notification_title (3668331918920659093) -->
- <skip />
- <!-- no translation found for fingerprint_frr_notification_msg (2162966846624639252) -->
- <skip />
+ <string name="fingerprint_frr_notification_title" msgid="3668331918920659093">"Vai radušās problēmas saistībā ar autorizāciju ar pirksta nospiedumu?"</string>
+ <string name="fingerprint_frr_notification_msg" msgid="2162966846624639252">"Pieskarieties, lai skatītu padomus par to, kā uzlabot atbloķēšanas pieredzi."</string>
</resources>
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index b42c024..c01f073 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -1803,7 +1803,7 @@
<string name="color_correction_feature_name" msgid="7975133554160979214">"Корекција на боите"</string>
<string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Режим со една рака"</string>
<string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Дополнително затемнување"</string>
- <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Слушни помагала"</string>
+ <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Слушни апарати"</string>
<string name="autoclick_feature_name" msgid="8149248738736949630">"Автоматско кликнување"</string>
<string name="hearing_device_status_disconnected" msgid="497547752953543832">"Не е поврзано"</string>
<string name="hearing_device_status_connected" msgid="2149385149669918764">"Поврзано"</string>
@@ -2211,7 +2211,7 @@
<string name="nas_upgrade_notification_enable_action" msgid="3046406808378726874">"Во ред"</string>
<string name="nas_upgrade_notification_disable_action" msgid="3794833210043497982">"Исклучи"</string>
<string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"Дознајте повеќе"</string>
- <string name="nas_upgrade_notification_learn_more_content" msgid="3735480566983530650">"„Подобрените известувања“ ги заменија „Адаптивните известувања на Android“ во Android 12. Оваа функција прикажува предложени дејства и одговори и ги организира вашите известувања. \n\n„Подобрените известувања“ може да пристапуваат до содржините од известувањата, вклучително и личните податоци, како што се имињата на контактите и пораките. Функцијава може и да ги отфрла или да одговара на известувањата, како на пример, да одговара на телефонски повици и да го контролира режимот „Не вознемирувај“."</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="3735480566983530650">"„Подобрени известувања“ ја замени „Адаптивни известувања на Android“ во Android 12. Функцијава прикажува предложени дејства и одговори и ви ги организира известувањата. \n\n„Подобрени известувања“ може да пристапува до содржините на известувањата, вклучително и до личните податоци како имињата на контактите и пораките. Функцијава може и да ги отфрла или да одговара на известувањата, на пр. да одговара на телефонски повици и да го контролира режимот „Не вознемирувај“."</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Известување за информации за режимот за рутини"</string>
<string name="dynamic_mode_notification_title" msgid="1388718452788985481">"„Штедачот на батерија“ е вклучен"</string>
<string name="dynamic_mode_notification_summary" msgid="1639031262484979689">"Се намалува користењето на батеријата за нејзино подолго траење"</string>
@@ -2272,6 +2272,7 @@
<string name="accessibility_autoclick_double_click" msgid="2103826849116176478">"Кликнување двапати"</string>
<string name="accessibility_autoclick_drag" msgid="1499559489796843224">"Повлекување"</string>
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Лизгање"</string>
+ <string name="accessibility_autoclick_long_press" msgid="8757561680164116951">"Долго притиснете"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Паузирај"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Позиционирај"</string>
<string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Лизгај нагоре"</string>
@@ -2556,11 +2557,14 @@
<string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Апликации"</string>
<string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Вашите отпечатоци веќе не може да се препознаат. Поставете „Отклучување со отпечаток“ повторно."</string>
<string name="usb_apm_usb_plugged_in_when_locked_notification_title" msgid="468577168569874967">"USB-уредот е приклучен кога е заклучен"</string>
- <string name="usb_apm_usb_plugged_in_when_locked_notification_text" msgid="6695268246267993166">"USB-уредот е приклучен кога Android е заклучен. За да го користите уредот, прво отклучете го Android, па вметнете го USB-уредот повторно за да го користите."</string>
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_notification_text (5561957202444135984) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_no_replug_notification_text (3844834116914726455) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_low_power_charge_notification_text (1264294871764444170) -->
+ <skip />
<string name="usb_apm_usb_suspicious_activity_notification_title" msgid="3461195995882871461">"Сомнителна активност на USB-уредот"</string>
<string name="usb_apm_usb_suspicious_activity_notification_text" msgid="6537085605929303187">"Сигналот со податоци преку USB е оневозможен."</string>
- <!-- no translation found for fingerprint_frr_notification_title (3668331918920659093) -->
- <skip />
- <!-- no translation found for fingerprint_frr_notification_msg (2162966846624639252) -->
- <skip />
+ <string name="fingerprint_frr_notification_title" msgid="3668331918920659093">"Имате проблем со „Отклучување со отпечаток“?"</string>
+ <string name="fingerprint_frr_notification_msg" msgid="2162966846624639252">"Допрете за да прегледате совети за подобрување на доживувањето со отклучување"</string>
</resources>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index 73332af..dd3930a 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -2272,6 +2272,7 @@
<string name="accessibility_autoclick_double_click" msgid="2103826849116176478">"ഡബിൾ ക്ലിക്ക്"</string>
<string name="accessibility_autoclick_drag" msgid="1499559489796843224">"വലിച്ചിടുക"</string>
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"സ്ക്രോൾ ചെയ്യുക"</string>
+ <string name="accessibility_autoclick_long_press" msgid="8757561680164116951">"ദീർഘനേരം അമർത്തുക"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"താൽക്കാലികമായി നിർത്തുക"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"സ്ഥാനം"</string>
<string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"മുകളിലേക്ക് സ്ക്രോൾ ചെയ്യുക"</string>
@@ -2556,7 +2557,12 @@
<string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"ആപ്പുകൾ"</string>
<string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"നിങ്ങളുടെ ഫിംഗർപ്രിന്റുകൾ ഇനി തിരിച്ചറിയാനാകില്ല. ഫിംഗർപ്രിന്റ് അൺലോക്ക് വീണ്ടും സജ്ജീകരിക്കുക."</string>
<string name="usb_apm_usb_plugged_in_when_locked_notification_title" msgid="468577168569874967">"ലോക്കായിരിക്കുമ്പോഴാണ് USB ഉപകരണം പ്ലഗ്-ഇൻ ചെയ്തത്"</string>
- <string name="usb_apm_usb_plugged_in_when_locked_notification_text" msgid="6695268246267993166">"Android ലോക്കായിരിക്കുമ്പോഴാണ് USB ഉപകരണം പ്ലഗ്-ഇൻ ചെയ്തത്. ഉപകരണം ഉപയോഗിക്കാൻ ആദ്യം Android അൺലോക്ക് ചെയ്യുക, ശേഷം USB ഉപകരണം വീണ്ടും ഇൻസേർട്ട് ചെയ്ത് ഉപയോഗിക്കുക."</string>
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_notification_text (5561957202444135984) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_no_replug_notification_text (3844834116914726455) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_low_power_charge_notification_text (1264294871764444170) -->
+ <skip />
<string name="usb_apm_usb_suspicious_activity_notification_title" msgid="3461195995882871461">"സംശയാസ്പദമായ USB ആക്റ്റിവിറ്റി"</string>
<string name="usb_apm_usb_suspicious_activity_notification_text" msgid="6537085605929303187">"USB ഡാറ്റാ സിഗ്നൽ പ്രവർത്തനരഹിതമാക്കി."</string>
<string name="fingerprint_frr_notification_title" msgid="3668331918920659093">"ഫിംഗർപ്രിന്റ് അൺലോക്കുമായി ബന്ധപ്പെട്ട് പ്രശ്നം നേരിടുന്നുണ്ടോ?"</string>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index 10d650b..75b1f61 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -2272,6 +2272,7 @@
<string name="accessibility_autoclick_double_click" msgid="2103826849116176478">"2 товших"</string>
<string name="accessibility_autoclick_drag" msgid="1499559489796843224">"Чирэх"</string>
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Гүйлгэх"</string>
+ <string name="accessibility_autoclick_long_press" msgid="8757561680164116951">"Удаан дарах"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Түр зогсоох"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Байрлал"</string>
<string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Дээш гүйлгэх"</string>
@@ -2556,11 +2557,14 @@
<string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Аппликэйшн"</string>
<string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Таны хурууны хээг цаашид таних боломжгүй. Хурууны хээгээр түгжээ тайлахыг дахин тохируулна уу."</string>
<string name="usb_apm_usb_plugged_in_when_locked_notification_title" msgid="468577168569874967">"Түгжээтэй үед USB төхөөрөмж залгаатай байна"</string>
- <string name="usb_apm_usb_plugged_in_when_locked_notification_text" msgid="6695268246267993166">"Android-г түгжсэн үед USB төхөөрөмж залгаатай байна. Төхөөрөмжийг ашиглахын тулд эхлээд Android-н түгжээг тайлж, дараа нь USB төхөөрөмжийг ашиглахын тулд дахин оруулна уу."</string>
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_notification_text (5561957202444135984) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_no_replug_notification_text (3844834116914726455) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_low_power_charge_notification_text (1264294871764444170) -->
+ <skip />
<string name="usb_apm_usb_suspicious_activity_notification_title" msgid="3461195995882871461">"USB-н сэжигтэй үйл ажиллагаа"</string>
<string name="usb_apm_usb_suspicious_activity_notification_text" msgid="6537085605929303187">"USB-н өгөгдлийн дохиог идэвхгүй болгосон."</string>
- <!-- no translation found for fingerprint_frr_notification_title (3668331918920659093) -->
- <skip />
- <!-- no translation found for fingerprint_frr_notification_msg (2162966846624639252) -->
- <skip />
+ <string name="fingerprint_frr_notification_title" msgid="3668331918920659093">"Хурууны хээгээр түгжээ тайлахтай холбоотой асуудал гарсан уу?"</string>
+ <string name="fingerprint_frr_notification_msg" msgid="2162966846624639252">"Түгжээг тайлах хэрэглээгээ сайжруулах зөвлөгөөг хянахын тулд товшино уу"</string>
</resources>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index 7ef7761..42665bf 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -353,7 +353,7 @@
<string name="permgrouplab_sensors" msgid="9134046949784064495">"शरीर सेन्सर"</string>
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"आपल्या महत्त्वाच्या मापनांविषयी सेन्सर डेटा अॅक्सेस करा"</string>
<string name="permgrouplab_notifications" msgid="5472972361980668884">"सूचना"</string>
- <string name="permgroupdesc_notifications" msgid="4608679556801506580">"सूचना दाखवा"</string>
+ <string name="permgroupdesc_notifications" msgid="4608679556801506580">"नोटिफिकेशन दाखवा"</string>
<string name="permgrouplab_xr_tracking" msgid="7418994009794287471">"XR ट्रॅकिंग डेटा"</string>
<string name="permgroupdesc_xr_tracking" msgid="6777198859446500821">"तुमच्याबद्दल आणि तुमच्या सभोवतालच्या वातावरणाबद्दल XR डेटा अॅक्सेस करा"</string>
<string name="permgrouplab_xr_tracking_sensitive" msgid="1194833982988144536">"संवेदनशील XR ट्रॅकिंग डेटा"</string>
@@ -2004,7 +2004,7 @@
<string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> ते <xliff:g id="END">%2$s</xliff:g>"</string>
<string name="zen_mode_trigger_summary_combined" msgid="6492381546327807669">"<xliff:g id="DAYS">%1$s</xliff:g>, <xliff:g id="TIMES">%2$s</xliff:g>"</string>
<string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"कोणतेही कॅलेंडर"</string>
- <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> काही ध्वनी म्यूट करत आहे"</string>
+ <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> काही आवाज म्यूट करत आहे"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"आपल्या डिव्हाइसमध्ये अंतर्गत समस्या आहे आणि तुमचा फॅक्टरी डेटा रीसेट होईपर्यंत ती अस्थिर असू शकते."</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"आपल्या डिव्हाइसमध्ये अंतर्गत समस्या आहे. तपशीलांसाठी आपल्या निर्मात्याशी संपर्क साधा."</string>
<string name="stk_cc_ussd_to_dial" msgid="3139884150741157610">"USSD विनंती नियमित कॉलवर बदलली"</string>
@@ -2272,6 +2272,7 @@
<string name="accessibility_autoclick_double_click" msgid="2103826849116176478">"डबल क्लिक करा"</string>
<string name="accessibility_autoclick_drag" msgid="1499559489796843224">"ड्रॅग करा"</string>
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"स्क्रोल करा"</string>
+ <string name="accessibility_autoclick_long_press" msgid="8757561680164116951">"प्रेस करून ठेवा"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"थांबवा"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"स्थिती"</string>
<string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"वर स्क्रोल करा"</string>
@@ -2556,7 +2557,12 @@
<string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"अॅप्लिकेशन"</string>
<string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"तुमची फिंगरप्रिंट यापुढे ओळखता येणार नाहीत. फिंगरप्रिंट अनलॉक पुन्हा सेट करा."</string>
<string name="usb_apm_usb_plugged_in_when_locked_notification_title" msgid="468577168569874967">"लॉक केलेले असताना USB डिव्हाइस प्लग इन केले आहे"</string>
- <string name="usb_apm_usb_plugged_in_when_locked_notification_text" msgid="6695268246267993166">"Android लॉक केलेले असताना USB डिव्हाइस प्लग इन केले आहे. डिव्हाइस वापरण्यासाठी, कृपया सर्वप्रथम Android अनलॉक करा आणि त्यानंतर USB डिव्हाइस वापरण्यासाठी ते पुन्हा घाला."</string>
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_notification_text (5561957202444135984) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_no_replug_notification_text (3844834116914726455) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_low_power_charge_notification_text (1264294871764444170) -->
+ <skip />
<string name="usb_apm_usb_suspicious_activity_notification_title" msgid="3461195995882871461">"संशयास्पद USB ॲक्टिव्हिटी"</string>
<string name="usb_apm_usb_suspicious_activity_notification_text" msgid="6537085605929303187">"USB डेटा सिग्नल बंद करण्यात आला आहे."</string>
<string name="fingerprint_frr_notification_title" msgid="3668331918920659093">"फिंगरप्रिंट अनलॉक यासंबंधित समस्या येत आहे का?"</string>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index 65cdba9..dde74a3 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -2272,6 +2272,7 @@
<string name="accessibility_autoclick_double_click" msgid="2103826849116176478">"Klik dua kali"</string>
<string name="accessibility_autoclick_drag" msgid="1499559489796843224">"Seret"</string>
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Tatal"</string>
+ <string name="accessibility_autoclick_long_press" msgid="8757561680164116951">"Tekan lama"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Jeda"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Kedudukan"</string>
<string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Tatal Ke Atas"</string>
@@ -2556,7 +2557,9 @@
<string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Aplikasi"</string>
<string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Cap jari anda tidak dapat dicam lagi. Sediakan semula Buka Kunci Cap Jari."</string>
<string name="usb_apm_usb_plugged_in_when_locked_notification_title" msgid="468577168569874967">"Peranti USB dipalamkan apabila dikunci"</string>
- <string name="usb_apm_usb_plugged_in_when_locked_notification_text" msgid="6695268246267993166">"Peranti USB dipalamkan apabila Android dikunci. Untuk menggunakan peranti, sila buka kunci Android dahulu, kemudian masukkan semula peranti USB untuk menggunakan peranti itu."</string>
+ <string name="usb_apm_usb_plugged_in_when_locked_notification_text" msgid="5561957202444135984">"Peranti USB dipalamkan apabila Android dikunci. Untuk menggunakan peranti, sila buka kunci Android dahulu."</string>
+ <string name="usb_apm_usb_plugged_in_when_locked_no_replug_notification_text" msgid="3844834116914726455">"Peranti USB dipalamkan apabila Android dikunci. Untuk menggunakan peranti, sila buka kunci Android dahulu, kemudian masukkan semula peranti USB untuk menggunakan peranti itu."</string>
+ <string name="usb_apm_usb_plugged_in_when_locked_low_power_charge_notification_text" msgid="1264294871764444170">"Keupayaan pengecasan mungkin berkurang atau tidak tersedia apabila dikunci."</string>
<string name="usb_apm_usb_suspicious_activity_notification_title" msgid="3461195995882871461">"Aktiviti USB yang mencurigakan"</string>
<string name="usb_apm_usb_suspicious_activity_notification_text" msgid="6537085605929303187">"Isyarat data USB telah dilumpuhkan."</string>
<string name="fingerprint_frr_notification_title" msgid="3668331918920659093">"Menghadapi masalah dengan ciri Buka Kunci Cap Jari?"</string>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index 5b37b25..fe332b2 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -168,7 +168,7 @@
<string name="scNullCipherIssueNonEncryptedSummaryNotification" msgid="7386936934128110388">"သင့် <xliff:g id="NETWORK_NAME">%1$s</xliff:g> ဆင်းမ် သုံးချိန်တွင် ဖုန်း၊ မက်ဆေ့ဂျ်၊ ဒေတာ လုံခြုံမှုအားနည်းသည်"</string>
<string name="scNullCipherIssueNonEncryptedSummary" msgid="5093428974513703253">"သင့် <xliff:g id="NETWORK_NAME">%1$s</xliff:g> ဆင်းမ် သုံးချိန်တွင် ဖုန်း၊ မက်ဆေ့ဂျ်၊ ဒေတာ လုံခြုံမှုအားနည်းသည်။\n\nသင့်ချိတ်ဆက်မှုကို ထပ်မံအသွင်ဝှက်ပါက အကြောင်းကြားချက်နောက်တစ်ခု ရရှိပါမည်။"</string>
<string name="scNullCipherIssueActionSettings" msgid="5888857706424639946">"မိုဘိုင်းကွန်ရက် လုံခြုံရေး ဆက်တင်များ"</string>
- <string name="scNullCipherIssueActionLearnMore" msgid="7896642417214757769">"ပိုမိုလေ့လာရန်"</string>
+ <string name="scNullCipherIssueActionLearnMore" msgid="7896642417214757769">"ပိုလေ့လာရန်"</string>
<string name="scNullCipherIssueActionGotIt" msgid="8747796640866585787">"နားလည်ပြီ"</string>
<string name="fcComplete" msgid="1080909484660507044">"ပုံစံကုတ်ပြီးဆုံးသည်"</string>
<string name="fcError" msgid="5325116502080221346">"ဆက်သွယ်မှုဆိုင်ရာပြသနာ သို့မဟုတ် တရားမဝင်သောပုံစံကုတ်"</string>
@@ -1729,7 +1729,7 @@
<string name="display_manager_overlay_display_title" msgid="1480158037150469170">"<xliff:g id="NAME">%1$s</xliff:g>: <xliff:g id="WIDTH">%2$d</xliff:g>x<xliff:g id="HEIGHT">%3$d</xliff:g>, <xliff:g id="DPI">%4$d</xliff:g> dpi"</string>
<string name="display_manager_overlay_display_secure_suffix" msgid="2810034719482834679">", လုံခြုံသော"</string>
<string name="kg_forgot_pattern_button_text" msgid="406145459223122537">"ပုံဖော်မှုအား မေ့လျော့ခြင်း"</string>
- <string name="kg_wrong_pattern" msgid="1342812634464179931">"ပုံဆွဲအမှား"</string>
+ <string name="kg_wrong_pattern" msgid="1342812634464179931">"ပုံစံ မှားနေသည်"</string>
<string name="kg_wrong_password" msgid="2384677900494439426">"စကားဝှက်အမှား"</string>
<string name="kg_wrong_pin" msgid="3680925703673166482">"ပင် နံပါတ်အမှား"</string>
<string name="kg_pattern_instructions" msgid="8366024510502517748">"သင့်ရဲ့ သော့ဖွင့်သော ပုံစံကို ဆွဲပါ"</string>
@@ -2062,7 +2062,7 @@
<string name="locale_search_menu" msgid="6258090710176422934">"ရှာဖွေရန်"</string>
<string name="app_suspended_title" msgid="888873445010322650">"အက်ပ်ကို မရရှိနိုင်ပါ"</string>
<string name="app_suspended_default_message" msgid="6451215678552004172">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> ကို လောလောဆယ် မရနိုင်ပါ။ ၎င်းကို <xliff:g id="APP_NAME_1">%2$s</xliff:g> က စီမံထားပါသည်။"</string>
- <string name="app_suspended_more_details" msgid="211260942831587014">"ပိုမိုလေ့လာရန်"</string>
+ <string name="app_suspended_more_details" msgid="211260942831587014">"ပိုလေ့လာရန်"</string>
<string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"အက်ပ်ကို ခဏမရပ်တော့ရန်"</string>
<string name="work_mode_off_title" msgid="6367463960165135829">"အလုပ်သုံးအက်ပ် ပြန်ဖွင့်မလား။"</string>
<string name="work_mode_turn_on" msgid="5316648862401307800">"ပြန်ဖွင့်ရန်"</string>
@@ -2210,7 +2210,7 @@
<string name="nas_upgrade_notification_content" msgid="5157550369837103337">"အကြံပြုထားသော လုပ်ဆောင်ချက်နှင့် ပြန်စာများကို ယခုအခါ အဆင့်မြင့် အကြောင်းကြားချက်များဖြင့် ပံ့ပိုးပေးနေပါသည်။ ‘Android အလိုက်သင့် အကြောင်းကြားချက်များ’ ကို ပံ့ပိုးမပေးတော့ပါ။"</string>
<string name="nas_upgrade_notification_enable_action" msgid="3046406808378726874">"OK"</string>
<string name="nas_upgrade_notification_disable_action" msgid="3794833210043497982">"ပိတ်ရန်"</string>
- <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"ပိုမိုလေ့လာရန်"</string>
+ <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"ပိုလေ့လာရန်"</string>
<string name="nas_upgrade_notification_learn_more_content" msgid="3735480566983530650">"Android 12 တွင် ‘Android အလိုက်သင့် အကြောင်းကြားချက်များ’ ကို အဆင့်မြင့် အကြောင်းကြားချက်များဖြင့် အစားထိုးထားသည်။ ဤဝန်ဆောင်မှုက အကြံပြုထားသော လုပ်ဆောင်ချက်နှင့် ပြန်စာများကို ပြပေးပြီး သင်၏အကြောင်းကြားချက်များကို စီစဉ်ပေးသည်။\n\nအဆင့်မြင့် အကြောင်းကြားချက်များက အဆက်အသွယ်အမည်နှင့် မက်ဆေ့ဂျ်များကဲ့သို့ ကိုယ်ရေးကိုယ်တာအချက်လက်များ အပါအဝင် အကြောင်းကြားချက် အကြောင်းအရာကို သုံးနိုင်သည်။ ဤဝန်ဆောင်မှုက ဖုန်းခေါ်ဆိုမှုများ ဖြေခြင်းနှင့် ‘မနှောင့်ယှက်ရ’ ကို ထိန်းချုပ်ခြင်းကဲ့သို့ အကြောင်းကြားချက်များကို ပယ်နိုင်သည် (သို့) တုံ့ပြန်နိုင်သည်။"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"ပုံမှန်မုဒ်အတွက် အချက်အလက်ပြသည့် အကြောင်းကြားချက်"</string>
<string name="dynamic_mode_notification_title" msgid="1388718452788985481">"ဘက်ထရီ အားထိန်း ဖွင့်ထားသည်"</string>
@@ -2272,6 +2272,7 @@
<string name="accessibility_autoclick_double_click" msgid="2103826849116176478">"နှစ်ချက်နှိပ်ရန်"</string>
<string name="accessibility_autoclick_drag" msgid="1499559489796843224">"ဖိဆွဲရန်"</string>
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"လှိမ့်ရန်"</string>
+ <string name="accessibility_autoclick_long_press" msgid="8757561680164116951">"ကြာကြာနှိပ်ရန်"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"ခဏရပ်ရန်"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"နေရာ"</string>
<string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"အပေါ်သို့ လှိမ့်ရန်"</string>
@@ -2556,7 +2557,12 @@
<string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"အပလီကေးရှင်းများ"</string>
<string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"သင့်လက်ဗွေများကို မသိရှိနိုင်တော့ပါ။ ‘လက်ဗွေသုံး လော့ခ်ဖွင့်ခြင်း’ ထပ်မံစနစ်ထည့်သွင်းပါ။"</string>
<string name="usb_apm_usb_plugged_in_when_locked_notification_title" msgid="468577168569874967">"လော့ခ်ချထားချိန်တွင် USB စက်ကို ပလတ်တပ်ထားခြင်း"</string>
- <string name="usb_apm_usb_plugged_in_when_locked_notification_text" msgid="6695268246267993166">"Android လော့ခ်ချထားချိန်တွင် USB စက်ကို ပလတ်တပ်ထားသည်။ စက်သုံးရန်အတွက် Android ကို အရင်ဖွင့်ပြီး USB စက်ပြန်ထည့်ကာ သုံးပါ။"</string>
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_notification_text (5561957202444135984) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_no_replug_notification_text (3844834116914726455) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_low_power_charge_notification_text (1264294871764444170) -->
+ <skip />
<string name="usb_apm_usb_suspicious_activity_notification_title" msgid="3461195995882871461">"သံသယဖြစ်ဖွယ် USB လုပ်ဆောင်ချက်"</string>
<string name="usb_apm_usb_suspicious_activity_notification_text" msgid="6537085605929303187">"USB ဒေတာအချက်ပြမှုကို ပိတ်လိုက်ပါပြီ။"</string>
<string name="fingerprint_frr_notification_title" msgid="3668331918920659093">"‘လက်ဗွေသုံး လော့ခ်ဖွင့်ခြင်း’ တွင် ပြဿနာရှိနေသလား။"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 09fdb0f..6a58a87 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -1799,7 +1799,7 @@
<string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Ferdig"</string>
<string name="disable_accessibility_shortcut" msgid="5806091378745232383">"Slå av snarveien"</string>
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Bruk snarveien"</string>
- <string name="color_inversion_feature_name" msgid="2672824491933264951">"Fargeinvertering"</string>
+ <string name="color_inversion_feature_name" msgid="2672824491933264951">"Farge&#173;invertering"</string>
<string name="color_correction_feature_name" msgid="7975133554160979214">"Fargekorrigering"</string>
<string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Enhåndsmodus"</string>
<string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Ekstra dimmet"</string>
@@ -2272,6 +2272,7 @@
<string name="accessibility_autoclick_double_click" msgid="2103826849116176478">"Dobbeltklikk"</string>
<string name="accessibility_autoclick_drag" msgid="1499559489796843224">"Dra"</string>
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Rull"</string>
+ <string name="accessibility_autoclick_long_press" msgid="8757561680164116951">"Langt trykk"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Sett på pause"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Plassér"</string>
<string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Rull opp"</string>
@@ -2556,11 +2557,14 @@
<string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Apper"</string>
<string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Fingeravtrykkene dine kan ikke gjenkjennes lenger. Konfigurer opplåsing med fingeravtrykk på nytt."</string>
<string name="usb_apm_usb_plugged_in_when_locked_notification_title" msgid="468577168569874967">"USB-enheten er koblet til når den er låst"</string>
- <string name="usb_apm_usb_plugged_in_when_locked_notification_text" msgid="6695268246267993166">"USB-enheten er koblet til når Android er låst. Før du kan bruke enheten, må du låse opp Android og så sette inn USB-enheten på nytt."</string>
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_notification_text (5561957202444135984) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_no_replug_notification_text (3844834116914726455) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_low_power_charge_notification_text (1264294871764444170) -->
+ <skip />
<string name="usb_apm_usb_suspicious_activity_notification_title" msgid="3461195995882871461">"Mistenkelig USB-aktivitet"</string>
<string name="usb_apm_usb_suspicious_activity_notification_text" msgid="6537085605929303187">"USB-datasignalet er slått av."</string>
- <!-- no translation found for fingerprint_frr_notification_title (3668331918920659093) -->
- <skip />
- <!-- no translation found for fingerprint_frr_notification_msg (2162966846624639252) -->
- <skip />
+ <string name="fingerprint_frr_notification_title" msgid="3668331918920659093">"Har du problemer med Opplåsing med fingeravtrykk?"</string>
+ <string name="fingerprint_frr_notification_msg" msgid="2162966846624639252">"Trykk for å se tips om hvordan du kan forbedre opplåsingsopplevelsen"</string>
</resources>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index 9c3d8f8..8896298 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -2004,7 +2004,7 @@
<string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> देखि <xliff:g id="END">%2$s</xliff:g> सम्म"</string>
<string name="zen_mode_trigger_summary_combined" msgid="6492381546327807669">"<xliff:g id="DAYS">%1$s</xliff:g>, <xliff:g id="TIMES">%2$s</xliff:g>"</string>
<string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"कुनै पनि पात्रो"</string>
- <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> ले केही ध्वनिहरू म्युट गर्दै छ"</string>
+ <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> ले केही साउन्ड म्युट गर्दै छ"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"तपाईंको यन्त्रसँग आन्तरिक समस्या छ, र तपाईंले फ्याक्ट्री डाटा रिसेट नगर्दासम्म यो अस्थिर रहन्छ।"</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"तपाईंको यन्त्रसँग आन्तरिक समस्या छ। विवरणहरूको लागि आफ्नो निर्मातासँग सम्पर्क गर्नुहोस्।"</string>
<string name="stk_cc_ussd_to_dial" msgid="3139884150741157610">"USSD अनुरोधलाई नियमित कलमा परिवर्तन गरियो"</string>
@@ -2211,7 +2211,7 @@
<string name="nas_upgrade_notification_enable_action" msgid="3046406808378726874">"ठिक छ"</string>
<string name="nas_upgrade_notification_disable_action" msgid="3794833210043497982">"अफ गर्नुहोस्"</string>
<string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"थप जान्नुहोस्"</string>
- <string name="nas_upgrade_notification_learn_more_content" msgid="3735480566983530650">"Android १२ मा Android को एड्याप्टिभ नोटिफिकेसनहरू नामक सुविधालाई परिष्कृत नोटिफिकेसनहरू नामक सुविधाले प्रतिस्थापन गरेको छ। यो सुविधाले कारबाही तथा जवाफसम्बन्धी सुझाव देखाउँछ र तपाईंका नोटिफिकेसनहरू व्यवस्थित गर्छ।\n\nपरिष्कृत नोटिफिकेसनहरू नामक सुविधाले नोटिफिकेसनमा उल्लिखित सम्पर्क व्यक्तिको नाम र म्यासेज जस्ता व्यक्तिगत जानकारीलगायतका सामग्री हेर्न तथा प्रयोग गर्न सक्छ। यो सुविधाले फोन उठाउने तथा \'बाधा नपुऱ्याउनुहोस्\' मोड नियन्त्रण गर्ने कार्यसहित नोटिफिकेसनहरू हटाउने वा नोटिफिकेसनहरूको जवाफ दिने कार्य पनि गर्न सक्छ।"</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="3735480566983530650">"Android १२ मा Android को एड्याप्टिभ नोटिफिकेसनहरू नामक सुविधालाई परिष्कृत नोटिफिकेसनहरू नामक सुविधाले प्रतिस्थापन गरेको छ। यो सुविधाले कारबाही तथा जवाफसम्बन्धी सुझाव देखाउँछ र तपाईंका नोटिफिकेसनहरू व्यवस्थित गर्छ।\n\nपरिष्कृत नोटिफिकेसनहरू नामक सुविधाले नोटिफिकेसनमा उल्लिखित सम्पर्क व्यक्तिको नाम र म्यासेज जस्ता व्यक्तिगत जानकारीलगायतका सामग्री एक्सेस गर्न सक्छ। यो सुविधाले फोन उठाउने तथा \'बाधा नपुऱ्याउनुहोस्\' मोड नियन्त्रण गर्ने कार्यसहित नोटिफिकेसनहरू हटाउने वा नोटिफिकेसनहरूको जवाफ दिने कार्य पनि गर्न सक्छ।"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"दिनचर्या मोडको जानकारीमूलक सूचना"</string>
<string name="dynamic_mode_notification_title" msgid="1388718452788985481">"ब्याट्री सेभर अन गरिएको छ"</string>
<string name="dynamic_mode_notification_summary" msgid="1639031262484979689">"ब्याट्रीको आयु बढाउन ब्याट्रीको खपत कम गरिँदै छ"</string>
@@ -2272,6 +2272,7 @@
<string name="accessibility_autoclick_double_click" msgid="2103826849116176478">"डबल क्लिक गर्नुहोस्"</string>
<string name="accessibility_autoclick_drag" msgid="1499559489796843224">"ड्र्याग गर्नुहोस्"</string>
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"स्क्रोल गर्नुहोस्"</string>
+ <string name="accessibility_autoclick_long_press" msgid="8757561680164116951">"केही बेरसम्म थिच्नुहोस्"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"पज गर्नुहोस्"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"स्थिति"</string>
<string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"माथितिर स्क्रोल गर्नुहोस्"</string>
@@ -2447,7 +2448,7 @@
<string name="notification_action_check_bg_apps" msgid="4758877443365362532">"कुन कुन एप सक्रिय छ भन्ने कुरा जाँच्नुहोस्"</string>
<string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"तपाईंको <xliff:g id="DEVICE">%1$s</xliff:g> मार्फत फोनको क्यामेरा प्रयोग गर्न मिल्दैन"</string>
<string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"तपाईंको <xliff:g id="DEVICE">%1$s</xliff:g> मार्फत ट्याब्लेटको क्यामेरा प्रयोग गर्न मिल्दैन"</string>
- <string name="vdm_secure_window" msgid="161700398158812314">"स्ट्रिम गरिरहेका बेला यो सामग्री हेर्न तथा प्रयोग गर्न मिल्दैन। बरु आफ्नो फोनमार्फत सो सामग्री हेर्ने तथा प्रयोग गर्ने प्रयास गर्नुहोस्।"</string>
+ <string name="vdm_secure_window" msgid="161700398158812314">"स्ट्रिम गरिरहेका बेला यो सामग्री एक्सेस गर्न मिल्दैन। बरु आफ्नो फोनमार्फत सो सामग्री हेर्ने तथा प्रयोग गर्ने प्रयास गर्नुहोस्।"</string>
<string name="vdm_pip_blocked" msgid="4036107522497281397">"स्ट्रिम गरिरहेका बेला picture-in-picture मोड प्रयोग गर्न मिल्दैन"</string>
<string name="system_locale_title" msgid="711882686834677268">"सिस्टम डिफल्ट"</string>
<string name="default_card_name" msgid="9198284935962911468">"कार्ड <xliff:g id="CARDNUMBER">%d</xliff:g>"</string>
@@ -2556,7 +2557,12 @@
<string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"एपहरू"</string>
<string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"तपाईंको फिंगरप्रिन्ट अब पहिचान गर्न सकिँदैन। फिंगरप्रिन्ट अनलक फेरि सेटअप गर्नुहोस्।"</string>
<string name="usb_apm_usb_plugged_in_when_locked_notification_title" msgid="468577168569874967">"लक भएपछि USB डिभाइस घुसाइएको छ"</string>
- <string name="usb_apm_usb_plugged_in_when_locked_notification_text" msgid="6695268246267993166">"Android लक भएपछि USB डिभाइस घुसाइएको छ। USB डिभाइस प्रयोग गर्न कृपया सुरुमा Android अनलक गर्नुहोस् र त्यसपछि USB डिभाइस फेरि घुसाउनुहोस्।"</string>
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_notification_text (5561957202444135984) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_no_replug_notification_text (3844834116914726455) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_low_power_charge_notification_text (1264294871764444170) -->
+ <skip />
<string name="usb_apm_usb_suspicious_activity_notification_title" msgid="3461195995882871461">"USB सम्बन्धी शङ्कास्पद गतिविधि"</string>
<string name="usb_apm_usb_suspicious_activity_notification_text" msgid="6537085605929303187">"USB डेटा सिग्नल अफ गरिएको छ।"</string>
<string name="fingerprint_frr_notification_title" msgid="3668331918920659093">"फिंगरप्रिन्ट अनलक प्रयोग गर्न समस्या भइरहेको छ?"</string>
diff --git a/core/res/res/values-night/colors.xml b/core/res/res/values-night/colors.xml
index e19ee23..f4dfd8d 100644
--- a/core/res/res/values-night/colors.xml
+++ b/core/res/res/values-night/colors.xml
@@ -48,6 +48,8 @@
<color name="language_picker_item_selected_stroke">#185ABC</color>
<!-- Color for various surfaces related to system-wide blur -->
+ <color name="shade_panel_fg">@color/shade_panel_fg_color</color>
+ <color name="shade_panel_bg">@color/shade_panel_bg_color</color>
<color name="surface_effect_0">@color/surface_effect_0_color</color>
<color name="surface_effect_1">@color/surface_effect_1_color</color>
<color name="surface_effect_2">@color/surface_effect_2_color</color>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index 48eacdc..56584e2 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -2272,6 +2272,7 @@
<string name="accessibility_autoclick_double_click" msgid="2103826849116176478">"Dubbelklikken"</string>
<string name="accessibility_autoclick_drag" msgid="1499559489796843224">"Slepen"</string>
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Scrollen"</string>
+ <string name="accessibility_autoclick_long_press" msgid="8757561680164116951">"Lang indrukken"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Pauzeren"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Positie"</string>
<string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Omhoog scrollen"</string>
@@ -2556,7 +2557,12 @@
<string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Apps"</string>
<string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Je vingerafdrukken worden niet meer herkend. Stel Ontgrendelen met vingerafdruk opnieuw in."</string>
<string name="usb_apm_usb_plugged_in_when_locked_notification_title" msgid="468577168569874967">"USB-apparaat aangesloten terwijl vergrendeld"</string>
- <string name="usb_apm_usb_plugged_in_when_locked_notification_text" msgid="6695268246267993166">"Het USB-apparaat is aangesloten terwijl Android is vergrendeld. Als je het apparaat wilt gebruiken, ontgrendel je eerst Android en plaats je het USB-apparaat opnieuw om het te gebruiken."</string>
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_notification_text (5561957202444135984) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_no_replug_notification_text (3844834116914726455) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_low_power_charge_notification_text (1264294871764444170) -->
+ <skip />
<string name="usb_apm_usb_suspicious_activity_notification_title" msgid="3461195995882871461">"Verdachte USB-activiteit"</string>
<string name="usb_apm_usb_suspicious_activity_notification_text" msgid="6537085605929303187">"USB-gegevenssignaal is uitgezet."</string>
<string name="fingerprint_frr_notification_title" msgid="3668331918920659093">"Heb je problemen met Ontgrendelen met vingerafdruk?"</string>
diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml
index d21312f..a80b61e 100644
--- a/core/res/res/values-or/strings.xml
+++ b/core/res/res/values-or/strings.xml
@@ -2272,6 +2272,7 @@
<string name="accessibility_autoclick_double_click" msgid="2103826849116176478">"ଦୁଇ ଥର କ୍ଲିକ କରନ୍ତୁ"</string>
<string name="accessibility_autoclick_drag" msgid="1499559489796843224">"ଡ୍ରାଗ କରନ୍ତୁ"</string>
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"ସ୍କ୍ରୋଲ କରନ୍ତୁ"</string>
+ <string name="accessibility_autoclick_long_press" msgid="8757561680164116951">"ଅଧିକ ସମୟ ଦବାନ୍ତୁ"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"ବିରତ କରନ୍ତୁ"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"ସ୍ଥିତି"</string>
<string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"ଉପରକୁ ସ୍କ୍ରୋଲ କରନ୍ତୁ"</string>
@@ -2556,11 +2557,14 @@
<string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"ଆପ୍ଲିକେସନ"</string>
<string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"ଆପଣଙ୍କ ଟିପଚିହ୍ନକୁ ଆଉ ଚିହ୍ନଟ କରାଯାଇପାରିବ ନାହିଁ। ଫିଙ୍ଗରପ୍ରିଣ୍ଟ ଅନଲକ ପୁଣି ସେଟ ଅପ କରନ୍ତୁ।"</string>
<string name="usb_apm_usb_plugged_in_when_locked_notification_title" msgid="468577168569874967">"ଲକ ଥିବା ସମୟରେ USB ଡିଭାଇସ ପ୍ଲଗ ଇନ ହୋଇଛି"</string>
- <string name="usb_apm_usb_plugged_in_when_locked_notification_text" msgid="6695268246267993166">"Android ଲକ ଥିବାବେଳେ USB ଡିଭାଇସ ପ୍ଲଗ ଇନ ହୋଇଥାଏ। ଡିଭାଇସ ବ୍ୟବହାର କରିବାକୁ, ଦୟାକରି ପ୍ରଥମେ Android ଅନଲକ କରନ୍ତୁ ଏବଂ ତାପରେ ଏହାକୁ ବ୍ୟବହାର କରିବାକୁ USB ଡିଭାଇସ ପୁଣି ଭର୍ତ୍ତି କରନ୍ତୁ।"</string>
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_notification_text (5561957202444135984) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_no_replug_notification_text (3844834116914726455) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_low_power_charge_notification_text (1264294871764444170) -->
+ <skip />
<string name="usb_apm_usb_suspicious_activity_notification_title" msgid="3461195995882871461">"ସନ୍ଦେହଜନକ USB କାର୍ଯ୍ୟକଳାପ"</string>
<string name="usb_apm_usb_suspicious_activity_notification_text" msgid="6537085605929303187">"USB ଡାଟା ସିଗନାଲକୁ ଅକ୍ଷମ କରାଯାଇଛି।"</string>
- <!-- no translation found for fingerprint_frr_notification_title (3668331918920659093) -->
- <skip />
- <!-- no translation found for fingerprint_frr_notification_msg (2162966846624639252) -->
- <skip />
+ <string name="fingerprint_frr_notification_title" msgid="3668331918920659093">"ଫିଙ୍ଗରପ୍ରିଣ୍ଟ ଅନଲକରେ ଅସୁବିଧାର ସମ୍ମୁଖୀନ ହେଉଛନ୍ତି?"</string>
+ <string name="fingerprint_frr_notification_msg" msgid="2162966846624639252">"ଆପଣଙ୍କ ଅନଲକ କରିବାର ଅନୁଭୂତିକୁ ଉନ୍ନତ କରିବା ପାଇଁ ଟିପ୍ସର ସମୀକ୍ଷା କରିବାକୁ ଟାପ କରନ୍ତୁ"</string>
</resources>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index 90853e6..febff55b 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -2272,6 +2272,7 @@
<string name="accessibility_autoclick_double_click" msgid="2103826849116176478">"ਡਬਲ ਕਲਿੱਕ ਕਰੋ"</string>
<string name="accessibility_autoclick_drag" msgid="1499559489796843224">"ਘਸੀਟੋ"</string>
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"ਸਕ੍ਰੋਲ ਕਰੋ"</string>
+ <string name="accessibility_autoclick_long_press" msgid="8757561680164116951">"ਦਬਾਈ ਰੱਖੋ"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"ਰੋਕੋ"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"ਸਥਿਤੀ"</string>
<string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"ਉੱਪਰ ਵੱਲ ਸਕ੍ਰੋਲ ਕਰੋ"</string>
@@ -2556,7 +2557,12 @@
<string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"ਐਪਲੀਕੇਸ਼ਨਾਂ"</string>
<string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"ਤੁਹਾਡੇ ਫਿੰਗਰਪ੍ਰਿੰਟਾਂ ਦੀ ਹੁਣ ਪਛਾਣ ਨਹੀਂ ਕੀਤੀ ਜਾ ਸਕਦੀ। ਫਿੰਗਰਪ੍ਰਿੰਟ ਅਣਲਾਕ ਦਾ ਦੁਬਾਰਾ ਸੈੱਟਅੱਪ ਕਰੋ।"</string>
<string name="usb_apm_usb_plugged_in_when_locked_notification_title" msgid="468577168569874967">"ਲਾਕ ਹੋਣ \'ਤੇ USB ਡੀਵਾਈਸ ਨੂੰ ਪਲੱਗ-ਇਨ ਕੀਤਾ ਗਿਆ"</string>
- <string name="usb_apm_usb_plugged_in_when_locked_notification_text" msgid="6695268246267993166">"Android ਲਾਕ ਹੋਣ \'ਤੇ USB ਡੀਵਾਈਸ ਨੂੰ ਪਲੱਗ-ਇਨ ਕੀਤਾ ਗਿਆ। ਡੀਵਾਈਸ ਦੀ ਵਰਤੋਂ ਕਰਨ ਲਈ, ਕਿਰਪਾ ਕਰਕੇ ਪਹਿਲਾਂ Android ਨੂੰ ਅਣਲਾਕ ਕਰੋ ਅਤੇ ਫਿਰ USB ਡੀਵਾਈਸ ਦੀ ਵਰਤੋਂ ਕਰਨ ਲਈ ਉਸਨੂੰ ਦੁਬਾਰਾ ਲਗਾਓ।"</string>
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_notification_text (5561957202444135984) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_no_replug_notification_text (3844834116914726455) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_low_power_charge_notification_text (1264294871764444170) -->
+ <skip />
<string name="usb_apm_usb_suspicious_activity_notification_title" msgid="3461195995882871461">"ਸ਼ੱਕੀ USB ਸਰਗਰਮੀ"</string>
<string name="usb_apm_usb_suspicious_activity_notification_text" msgid="6537085605929303187">"USB ਡਾਟਾ ਸਿਗਨਲ ਬੰਦ ਕਰ ਦਿੱਤਾ ਗਿਆ ਹੈ।"</string>
<string name="fingerprint_frr_notification_title" msgid="3668331918920659093">"ਕੀ ਫਿੰਗਰਪ੍ਰਿੰਟ ਅਣਲਾਕ ਸੰਬੰਧੀ ਕੋਈ ਸਮੱਸਿਆ ਆ ਰਹੀ ਹੈ?"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index b557219..07c5c91 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -271,8 +271,7 @@
<string name="global_action_bug_report" msgid="5127867163044170003">"Zgłoś błąd"</string>
<string name="global_action_logout" msgid="6093581310002476511">"Zakończ sesję"</string>
<string name="global_action_screenshot" msgid="2610053466156478564">"Zrzut ekranu"</string>
- <!-- no translation found for global_action_standby (6786920589970257642) -->
- <skip />
+ <string name="global_action_standby" msgid="6786920589970257642">"Tryb gotowości"</string>
<string name="bugreport_title" msgid="8549990811777373050">"Zgłoś błąd"</string>
<string name="bugreport_message" msgid="5212529146119624326">"Informacje o bieżącym stanie urządzenia zostaną zebrane i wysłane e-mailem. Przygotowanie zgłoszenia błędu do wysłania chwilę potrwa, więc zachowaj cierpliwość."</string>
<string name="bugreport_option_interactive_title" msgid="7968287837902871289">"Raport interaktywny"</string>
@@ -2275,6 +2274,7 @@
<string name="accessibility_autoclick_double_click" msgid="2103826849116176478">"Dwukrotne kliknięcie"</string>
<string name="accessibility_autoclick_drag" msgid="1499559489796843224">"Przeciąganie"</string>
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Przewijanie"</string>
+ <string name="accessibility_autoclick_long_press" msgid="8757561680164116951">"Przytrzymaj"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Wstrzymaj"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Pozycja"</string>
<string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Przewiń w górę"</string>
@@ -2559,11 +2559,14 @@
<string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Aplikacje"</string>
<string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Nie można już rozpoznać Twoich odcisków palców. Skonfiguruj ponownie odblokowywanie odciskiem palca."</string>
<string name="usb_apm_usb_plugged_in_when_locked_notification_title" msgid="468577168569874967">"Podłączono urządzenie USB przy zablokowanym urządzeniu"</string>
- <string name="usb_apm_usb_plugged_in_when_locked_notification_text" msgid="6695268246267993166">"Podłączono urządzenie USB, gdy Android był zablokowany. Aby używać urządzenia USB, najpierw odblokuj Androida, a potem podłącz je ponownie."</string>
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_notification_text (5561957202444135984) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_no_replug_notification_text (3844834116914726455) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_low_power_charge_notification_text (1264294871764444170) -->
+ <skip />
<string name="usb_apm_usb_suspicious_activity_notification_title" msgid="3461195995882871461">"Podejrzana aktywność przez USB"</string>
<string name="usb_apm_usb_suspicious_activity_notification_text" msgid="6537085605929303187">"Sygnał danych z urządzenia USB został wyłączony."</string>
- <!-- no translation found for fingerprint_frr_notification_title (3668331918920659093) -->
- <skip />
- <!-- no translation found for fingerprint_frr_notification_msg (2162966846624639252) -->
- <skip />
+ <string name="fingerprint_frr_notification_title" msgid="3668331918920659093">"Masz problem z odblokowywaniem odciskiem palca?"</string>
+ <string name="fingerprint_frr_notification_msg" msgid="2162966846624639252">"Kliknij, aby poznać wskazówki, które ułatwią odblokowywanie"</string>
</resources>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index 197abc2..4c7ffd1 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -2273,6 +2273,7 @@
<string name="accessibility_autoclick_double_click" msgid="2103826849116176478">"Clicar duas vezes"</string>
<string name="accessibility_autoclick_drag" msgid="1499559489796843224">"Arrastar"</string>
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Rolar"</string>
+ <string name="accessibility_autoclick_long_press" msgid="8757561680164116951">"Tocar e manter pressionado"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Pausar"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Posição"</string>
<string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Rolar para cima"</string>
@@ -2557,11 +2558,14 @@
<string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Apps"</string>
<string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"As impressões digitais não são mais reconhecidas. Configure o Desbloqueio por impressão digital de novo."</string>
<string name="usb_apm_usb_plugged_in_when_locked_notification_title" msgid="468577168569874967">"O dispositivo USB foi conectado com o Android bloqueado"</string>
- <string name="usb_apm_usb_plugged_in_when_locked_notification_text" msgid="6695268246267993166">"O dispositivo USB foi conectado com o Android bloqueado. Para usá-lo, primeiro desbloqueie o Android e, em seguida, reconecte o dispositivo USB."</string>
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_notification_text (5561957202444135984) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_no_replug_notification_text (3844834116914726455) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_low_power_charge_notification_text (1264294871764444170) -->
+ <skip />
<string name="usb_apm_usb_suspicious_activity_notification_title" msgid="3461195995882871461">"Atividade suspeita do USB"</string>
<string name="usb_apm_usb_suspicious_activity_notification_text" msgid="6537085605929303187">"O indicador de dados USB foi desativado."</string>
- <!-- no translation found for fingerprint_frr_notification_title (3668331918920659093) -->
- <skip />
- <!-- no translation found for fingerprint_frr_notification_msg (2162966846624639252) -->
- <skip />
+ <string name="fingerprint_frr_notification_title" msgid="3668331918920659093">"Está tendo problemas com o Desbloqueio por Impressão Digital?"</string>
+ <string name="fingerprint_frr_notification_msg" msgid="2162966846624639252">"Toque para ver dicas sobre como melhorar a experiência de desbloqueio"</string>
</resources>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index eabe39e..5e9ed96 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -2273,6 +2273,7 @@
<string name="accessibility_autoclick_double_click" msgid="2103826849116176478">"Clicar duas vezes"</string>
<string name="accessibility_autoclick_drag" msgid="1499559489796843224">"Arrastar"</string>
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Deslocar"</string>
+ <string name="accessibility_autoclick_long_press" msgid="8757561680164116951">"Manter premido"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Pausar"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Posição"</string>
<string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Deslocar página para cima"</string>
@@ -2557,7 +2558,12 @@
<string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Aplicações"</string>
<string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Já não é possível reconhecer as suas impressões digitais. Configure o Desbloqueio por impressão digital novamente."</string>
<string name="usb_apm_usb_plugged_in_when_locked_notification_title" msgid="468577168569874967">"Dispositivo USB ligado quando bloqueado"</string>
- <string name="usb_apm_usb_plugged_in_when_locked_notification_text" msgid="6695268246267993166">"O dispositivo USB está ligado quando o Android está bloqueado. Para usar o dispositivo, desbloqueie primeiro o Android e, de seguida, volte a inserir o dispositivo USB para o usar."</string>
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_notification_text (5561957202444135984) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_no_replug_notification_text (3844834116914726455) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_low_power_charge_notification_text (1264294871764444170) -->
+ <skip />
<string name="usb_apm_usb_suspicious_activity_notification_title" msgid="3461195995882871461">"Atividade USB suspeita"</string>
<string name="usb_apm_usb_suspicious_activity_notification_text" msgid="6537085605929303187">"O sinal de dados USB foi desativado."</string>
<string name="fingerprint_frr_notification_title" msgid="3668331918920659093">"Está a ter problemas com o Desbloqueio por impressão digital?"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 197abc2..4c7ffd1 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -2273,6 +2273,7 @@
<string name="accessibility_autoclick_double_click" msgid="2103826849116176478">"Clicar duas vezes"</string>
<string name="accessibility_autoclick_drag" msgid="1499559489796843224">"Arrastar"</string>
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Rolar"</string>
+ <string name="accessibility_autoclick_long_press" msgid="8757561680164116951">"Tocar e manter pressionado"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Pausar"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Posição"</string>
<string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Rolar para cima"</string>
@@ -2557,11 +2558,14 @@
<string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Apps"</string>
<string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"As impressões digitais não são mais reconhecidas. Configure o Desbloqueio por impressão digital de novo."</string>
<string name="usb_apm_usb_plugged_in_when_locked_notification_title" msgid="468577168569874967">"O dispositivo USB foi conectado com o Android bloqueado"</string>
- <string name="usb_apm_usb_plugged_in_when_locked_notification_text" msgid="6695268246267993166">"O dispositivo USB foi conectado com o Android bloqueado. Para usá-lo, primeiro desbloqueie o Android e, em seguida, reconecte o dispositivo USB."</string>
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_notification_text (5561957202444135984) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_no_replug_notification_text (3844834116914726455) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_low_power_charge_notification_text (1264294871764444170) -->
+ <skip />
<string name="usb_apm_usb_suspicious_activity_notification_title" msgid="3461195995882871461">"Atividade suspeita do USB"</string>
<string name="usb_apm_usb_suspicious_activity_notification_text" msgid="6537085605929303187">"O indicador de dados USB foi desativado."</string>
- <!-- no translation found for fingerprint_frr_notification_title (3668331918920659093) -->
- <skip />
- <!-- no translation found for fingerprint_frr_notification_msg (2162966846624639252) -->
- <skip />
+ <string name="fingerprint_frr_notification_title" msgid="3668331918920659093">"Está tendo problemas com o Desbloqueio por Impressão Digital?"</string>
+ <string name="fingerprint_frr_notification_msg" msgid="2162966846624639252">"Toque para ver dicas sobre como melhorar a experiência de desbloqueio"</string>
</resources>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index 5891e06..4035d73 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -2208,11 +2208,11 @@
<string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"Notificarea a fost mutată la un nivel superior. Atinge pentru a oferi feedback."</string>
<string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"Notificarea a fost mutată la un nivel inferior. Atinge pentru a oferi feedback."</string>
<string name="nas_upgrade_notification_title" msgid="8436359459300146555">"Notificări optimizate"</string>
- <string name="nas_upgrade_notification_content" msgid="5157550369837103337">"Acțiunile și răspunsurile sugerate sunt acum trimise prin notificări optimizate. Notificările adaptive Android nu mai sunt acceptate."</string>
+ <string name="nas_upgrade_notification_content" msgid="5157550369837103337">"Acțiunile și răspunsurile sugerate sunt acum trimise prin notificări optimizate. Notificările adaptate Android nu mai sunt acceptate."</string>
<string name="nas_upgrade_notification_enable_action" msgid="3046406808378726874">"OK"</string>
<string name="nas_upgrade_notification_disable_action" msgid="3794833210043497982">"Dezactivează"</string>
<string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"Află mai multe"</string>
- <string name="nas_upgrade_notification_learn_more_content" msgid="3735480566983530650">"Notificările optimizate au înlocuit Notificările adaptive Android de pe Android 12. Această funcție afișează acțiuni și răspunsuri sugerate și organizează notificările.\n\nNotificările optimizate pot accesa conținutul notificărilor, inclusiv informații cu caracter personal, precum mesajele și numele persoanelor de contact. În plus, funcția poate să închidă sau să răspundă la notificări, de exemplu, să răspundă la apeluri telefonice și să gestioneze opțiunea Nu deranja."</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="3735480566983530650">"Notificările optimizate au înlocuit Notificările adaptate Android de pe Android 12. Această funcție afișează acțiuni și răspunsuri sugerate și organizează notificările.\n\nNotificările optimizate pot accesa conținutul notificărilor, inclusiv informații cu caracter personal, precum mesajele și numele persoanelor de contact. În plus, funcția poate să închidă sau să răspundă la notificări, de exemplu, să răspundă la apeluri telefonice și să gestioneze opțiunea Nu deranja."</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Notificare pentru informații despre modul Rutină"</string>
<string name="dynamic_mode_notification_title" msgid="1388718452788985481">"Economisirea bateriei este activată"</string>
<string name="dynamic_mode_notification_summary" msgid="1639031262484979689">"Se reduce utilizarea bateriei pentru a-i extinde autonomia"</string>
@@ -2273,6 +2273,7 @@
<string name="accessibility_autoclick_double_click" msgid="2103826849116176478">"Dublu clic"</string>
<string name="accessibility_autoclick_drag" msgid="1499559489796843224">"Trage"</string>
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Derulează"</string>
+ <string name="accessibility_autoclick_long_press" msgid="8757561680164116951">"Apasă lung"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Întrerupe"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Poziție"</string>
<string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Derulează în sus"</string>
@@ -2557,11 +2558,14 @@
<string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Aplicații"</string>
<string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"E posibil ca amprentele tale să nu mai fie recunoscute. Configurează din nou Deblocarea cu amprenta."</string>
<string name="usb_apm_usb_plugged_in_when_locked_notification_title" msgid="468577168569874967">"Dispozitiv USB conectat când Android este blocat"</string>
- <string name="usb_apm_usb_plugged_in_when_locked_notification_text" msgid="6695268246267993166">"Dispozitivul USB este conectat când dispozitivul Android este blocat. Pentru a folosi dispozitivul, deblochează mai întâi dispozitivul Android, apoi reconectează dispozitivul USB pentru a-l folosi."</string>
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_notification_text (5561957202444135984) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_no_replug_notification_text (3844834116914726455) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_low_power_charge_notification_text (1264294871764444170) -->
+ <skip />
<string name="usb_apm_usb_suspicious_activity_notification_title" msgid="3461195995882871461">"Activitate suspectă a dispozitivului USB"</string>
<string name="usb_apm_usb_suspicious_activity_notification_text" msgid="6537085605929303187">"Semnalul de date USB a fost dezactivat."</string>
- <!-- no translation found for fingerprint_frr_notification_title (3668331918920659093) -->
- <skip />
- <!-- no translation found for fingerprint_frr_notification_msg (2162966846624639252) -->
- <skip />
+ <string name="fingerprint_frr_notification_title" msgid="3668331918920659093">"Ai probleme cu Deblocarea cu amprenta?"</string>
+ <string name="fingerprint_frr_notification_msg" msgid="2162966846624639252">"Atinge pentru a vedea sfaturi pentru îmbunătățirea experienței de deblocare"</string>
</resources>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 105d60c..b94e065 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -2006,7 +2006,7 @@
<string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> – <xliff:g id="END">%2$s</xliff:g>"</string>
<string name="zen_mode_trigger_summary_combined" msgid="6492381546327807669">"<xliff:g id="DAYS">%1$s</xliff:g>, <xliff:g id="TIMES">%2$s</xliff:g>"</string>
<string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Любой календарь"</string>
- <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> приглушает некоторые звуки."</string>
+ <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> отключает некоторые звуки"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"Произошла внутренняя ошибка, и устройство может работать нестабильно, пока вы не выполните сброс настроек."</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"Произошла внутренняя ошибка. Обратитесь к производителю устройства за подробными сведениями."</string>
<string name="stk_cc_ussd_to_dial" msgid="3139884150741157610">"USSD-запрос преобразован в обычный вызов"</string>
@@ -2274,6 +2274,7 @@
<string name="accessibility_autoclick_double_click" msgid="2103826849116176478">"Нажать дважды"</string>
<string name="accessibility_autoclick_drag" msgid="1499559489796843224">"Перетащить"</string>
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Прокрутить"</string>
+ <string name="accessibility_autoclick_long_press" msgid="8757561680164116951">"Долгое нажатие"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Приостановить"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Положение"</string>
<string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Прокрутка вверх"</string>
@@ -2558,11 +2559,14 @@
<string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Приложения"</string>
<string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Ваши отпечатки больше не распознаются. Настройте разблокировку по отпечатку пальца снова."</string>
<string name="usb_apm_usb_plugged_in_when_locked_notification_title" msgid="468577168569874967">"Разблокируйте экран"</string>
- <string name="usb_apm_usb_plugged_in_when_locked_notification_text" msgid="6695268246267993166">"Устройство Android заблокировано. Чтобы использовать USB-устройство, переподключите его после разблокировки экрана."</string>
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_notification_text (5561957202444135984) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_no_replug_notification_text (3844834116914726455) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_low_power_charge_notification_text (1264294871764444170) -->
+ <skip />
<string name="usb_apm_usb_suspicious_activity_notification_title" msgid="3461195995882871461">"Обнаружено подозрительное USB-устройство"</string>
<string name="usb_apm_usb_suspicious_activity_notification_text" msgid="6537085605929303187">"Передача данных по USB отключена."</string>
- <!-- no translation found for fingerprint_frr_notification_title (3668331918920659093) -->
- <skip />
- <!-- no translation found for fingerprint_frr_notification_msg (2162966846624639252) -->
- <skip />
+ <string name="fingerprint_frr_notification_title" msgid="3668331918920659093">"Проблемы с разблокировкой по отпечатку пальца?"</string>
+ <string name="fingerprint_frr_notification_msg" msgid="2162966846624639252">"Нажмите, чтобы посмотреть советы о том, как разблокировать экран."</string>
</resources>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index a91ad9c..466ed88 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -2272,6 +2272,7 @@
<string name="accessibility_autoclick_double_click" msgid="2103826849116176478">"දෙවරක් ක්ලික් කරන්න"</string>
<string name="accessibility_autoclick_drag" msgid="1499559489796843224">"අදින්න"</string>
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"අනුචලනය කරන්න"</string>
+ <string name="accessibility_autoclick_long_press" msgid="8757561680164116951">"දිගු එබීම"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"විරාම කරන්න"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"ස්ථානය"</string>
<string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"ඉහළට අනුචලනය කරන්න"</string>
@@ -2556,7 +2557,12 @@
<string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"යෙදුම්"</string>
<string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"ඔබේ ඇඟිලි සලකුණු තවදුරටත් හඳුනාගත නොහැක. ඇඟිලි සලකුණු අගුළු හැරීම නැවත පිහිටුවන්න."</string>
<string name="usb_apm_usb_plugged_in_when_locked_notification_title" msgid="468577168569874967">"අගුළු දමා ඇති විට USB උපාංගය පේනුගත කර ඇත"</string>
- <string name="usb_apm_usb_plugged_in_when_locked_notification_text" msgid="6695268246267993166">"Android අගුළු දමා ඇති විට USB උපාංගය පේනුගත කර ඇත. උපාංගය භාවිතා කිරීමට, පළමුව Android අගුළු හැර, පසුව එය භාවිතා කිරීමට USB උපාංගය නැවත ඇතුළු කරන්න."</string>
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_notification_text (5561957202444135984) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_no_replug_notification_text (3844834116914726455) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_low_power_charge_notification_text (1264294871764444170) -->
+ <skip />
<string name="usb_apm_usb_suspicious_activity_notification_title" msgid="3461195995882871461">"සැක සහිත USB ක්රියාකාරකම"</string>
<string name="usb_apm_usb_suspicious_activity_notification_text" msgid="6537085605929303187">"USB දත්ත සංඥාව අබල කර ඇත."</string>
<string name="fingerprint_frr_notification_title" msgid="3668331918920659093">"ඇඟිලි සලකුණු අගුළු හැරීමේ ගැටලුවක් තිබේ ද?"</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index 9555c62..dd2ce73 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -2274,6 +2274,7 @@
<string name="accessibility_autoclick_double_click" msgid="2103826849116176478">"Dvakrát kliknúť"</string>
<string name="accessibility_autoclick_drag" msgid="1499559489796843224">"Presunúť"</string>
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Posúvať"</string>
+ <string name="accessibility_autoclick_long_press" msgid="8757561680164116951">"Dlho stlačiť"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Pozastaviť"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Pozícia"</string>
<string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Posunúť nahor"</string>
@@ -2558,7 +2559,12 @@
<string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Aplikácie"</string>
<string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Vaše odtlačky prstov sa už nedajú rozpoznať. Znova nastavte odomknutie odtlačkom prsta."</string>
<string name="usb_apm_usb_plugged_in_when_locked_notification_title" msgid="468577168569874967">"Zariadenie USB bolo pripojené počas uzamknutia"</string>
- <string name="usb_apm_usb_plugged_in_when_locked_notification_text" msgid="6695268246267993166">"Zariadenie USB bolo pripojené, keď bolo zariadenie s Androidom uzamknuté. Ak chcete dané zariadenie používať, najprv odomknite zariadenie s Androidom a potom znova vložte zariadenie USB."</string>
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_notification_text (5561957202444135984) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_no_replug_notification_text (3844834116914726455) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_low_power_charge_notification_text (1264294871764444170) -->
+ <skip />
<string name="usb_apm_usb_suspicious_activity_notification_title" msgid="3461195995882871461">"Podozrivá aktivita cez USB"</string>
<string name="usb_apm_usb_suspicious_activity_notification_text" msgid="6537085605929303187">"Dátový signál cez USB bol deaktivovaný."</string>
<string name="fingerprint_frr_notification_title" msgid="3668331918920659093">"Máte problémy s odomknutím odtlačkom prsta?"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 56336d7..0875359 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -2274,6 +2274,7 @@
<string name="accessibility_autoclick_double_click" msgid="2103826849116176478">"Dvoklik"</string>
<string name="accessibility_autoclick_drag" msgid="1499559489796843224">"Vlečenje"</string>
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Drsenje"</string>
+ <string name="accessibility_autoclick_long_press" msgid="8757561680164116951">"Dolg pritisk"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Začasna zaustavitev"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Položaj"</string>
<string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Pomik gor"</string>
@@ -2558,7 +2559,9 @@
<string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Aplikacije"</string>
<string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Vaših prstnih odtisov ni več mogoče prepoznati. Znova nastavite odklepanje s prstnim odtisom."</string>
<string name="usb_apm_usb_plugged_in_when_locked_notification_title" msgid="468577168569874967">"Naprava USB je bila priključena pri zaklenjenem Androidu"</string>
- <string name="usb_apm_usb_plugged_in_when_locked_notification_text" msgid="6695268246267993166">"Naprava USB je bila priključena pri zaklenjenem Androidu. Če želite uporabljati napravo, najprej odklenite Android in nato znova vstavite napravo USB, da jo boste lahko uporabljali."</string>
+ <string name="usb_apm_usb_plugged_in_when_locked_notification_text" msgid="5561957202444135984">"Naprava USB je bila priključena pri zaklenjenem Androidu. Če želite uporabljati napravo, najprej odklenite Android."</string>
+ <string name="usb_apm_usb_plugged_in_when_locked_no_replug_notification_text" msgid="3844834116914726455">"Naprava USB je bila priključena pri zaklenjenem Androidu. Če želite uporabljati napravo, najprej odklenite Android in nato znova vstavite napravo USB, da jo boste lahko uporabljali."</string>
+ <string name="usb_apm_usb_plugged_in_when_locked_low_power_charge_notification_text" msgid="1264294871764444170">"Zmogljivost polnjenja je morda zmanjšana ali ni na voljo, ko je naprava zaklenjena."</string>
<string name="usb_apm_usb_suspicious_activity_notification_title" msgid="3461195995882871461">"Sumljiva dejavnost USB"</string>
<string name="usb_apm_usb_suspicious_activity_notification_text" msgid="6537085605929303187">"Podatkovni signal USB je bil onemogočen."</string>
<string name="fingerprint_frr_notification_title" msgid="3668331918920659093">"Imate težave z odklepanjem s prstnim odtisom?"</string>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index aeda58f..ab19862 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -2272,6 +2272,7 @@
<string name="accessibility_autoclick_double_click" msgid="2103826849116176478">"Kliko dy herë"</string>
<string name="accessibility_autoclick_drag" msgid="1499559489796843224">"Zvarrit"</string>
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Lëviz"</string>
+ <string name="accessibility_autoclick_long_press" msgid="8757561680164116951">"Mbaj shtypur gjatë"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Vendos në pauzë"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Pozicioni"</string>
<string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Lëviz lart"</string>
@@ -2556,11 +2557,14 @@
<string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Aplikacionet"</string>
<string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Gjurmët e tua të gishtave nuk mund të njihen më. Konfiguro përsëri \"Shkyçjen me gjurmën e gishtit\"."</string>
<string name="usb_apm_usb_plugged_in_when_locked_notification_title" msgid="468577168569874967">"Pajisja USB është e futur kur është e kyçur"</string>
- <string name="usb_apm_usb_plugged_in_when_locked_notification_text" msgid="6695268246267993166">"Pajisja USB është e futur kur Android është i kyçur. Për ta përdorur pajisjen, shkyç fillimisht Android dhe më pas fut përsëri pajisjen USB për ta përdorur."</string>
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_notification_text (5561957202444135984) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_no_replug_notification_text (3844834116914726455) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_low_power_charge_notification_text (1264294871764444170) -->
+ <skip />
<string name="usb_apm_usb_suspicious_activity_notification_title" msgid="3461195995882871461">"Aktivitet i dyshimtë i USB-së"</string>
<string name="usb_apm_usb_suspicious_activity_notification_text" msgid="6537085605929303187">"Sinjali i të dhënave të USB-së është çaktivizuar."</string>
- <!-- no translation found for fingerprint_frr_notification_title (3668331918920659093) -->
- <skip />
- <!-- no translation found for fingerprint_frr_notification_msg (2162966846624639252) -->
- <skip />
+ <string name="fingerprint_frr_notification_title" msgid="3668331918920659093">"Ke probleme me \"Shkyçjen me gjurmën e gishtit\"?"</string>
+ <string name="fingerprint_frr_notification_msg" msgid="2162966846624639252">"Trokit për të shikuar këshilla për të përmirësuar përvojën tënde me shkyçjen"</string>
</resources>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index ee335ab..10c556b 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -2273,6 +2273,7 @@
<string name="accessibility_autoclick_double_click" msgid="2103826849116176478">"Двапут кликните"</string>
<string name="accessibility_autoclick_drag" msgid="1499559489796843224">"Превуците"</string>
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Скролујте"</string>
+ <string name="accessibility_autoclick_long_press" msgid="8757561680164116951">"Дуг притисак"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Паузирај"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Позиција"</string>
<string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Скролуј нагоре"</string>
@@ -2557,7 +2558,12 @@
<string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Апликације"</string>
<string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Отисци прстију више не могу да се препознају. Поново подесите откључавање отиском прста."</string>
<string name="usb_apm_usb_plugged_in_when_locked_notification_title" msgid="468577168569874967">"USB уређај је прикључен када је Android закључан"</string>
- <string name="usb_apm_usb_plugged_in_when_locked_notification_text" msgid="6695268246267993166">"USB уређај је прикључен када је Android закључан. Да бисте користили уређај, прво откључајте Android, па поново убаците USB уређај да бисте га користили."</string>
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_notification_text (5561957202444135984) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_no_replug_notification_text (3844834116914726455) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_low_power_charge_notification_text (1264294871764444170) -->
+ <skip />
<string name="usb_apm_usb_suspicious_activity_notification_title" msgid="3461195995882871461">"Сумњива USB активност"</string>
<string name="usb_apm_usb_suspicious_activity_notification_text" msgid="6537085605929303187">"Сигнал за пренос података са USB-а је онемогућен."</string>
<string name="fingerprint_frr_notification_title" msgid="3668331918920659093">"Имате проблема са откључавањем отиском прста?"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index c5cd5bd..3c52e98 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -2272,6 +2272,7 @@
<string name="accessibility_autoclick_double_click" msgid="2103826849116176478">"Dubbelklicka"</string>
<string name="accessibility_autoclick_drag" msgid="1499559489796843224">"Trycka och dra"</string>
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Scrolla"</string>
+ <string name="accessibility_autoclick_long_press" msgid="8757561680164116951">"Tryck länge"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Pausa"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Position"</string>
<string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Scrolla uppåt"</string>
@@ -2556,11 +2557,14 @@
<string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Appar"</string>
<string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Det går inte längre att känna igen dina fingeravtryck. Ställ in fingeravtryckslås igen."</string>
<string name="usb_apm_usb_plugged_in_when_locked_notification_title" msgid="468577168569874967">"USB-enhet ansluten när enheten är låst"</string>
- <string name="usb_apm_usb_plugged_in_when_locked_notification_text" msgid="6695268246267993166">"USB-enheten är ansluten när Android är låst. Om du vill använda enheten måste du först låsa upp Android och sedan sätta i USB-enheten igen."</string>
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_notification_text (5561957202444135984) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_no_replug_notification_text (3844834116914726455) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_low_power_charge_notification_text (1264294871764444170) -->
+ <skip />
<string name="usb_apm_usb_suspicious_activity_notification_title" msgid="3461195995882871461">"Misstänkt USB-aktivitet"</string>
<string name="usb_apm_usb_suspicious_activity_notification_text" msgid="6537085605929303187">"USB-datasignalen har inaktiverats."</string>
- <!-- no translation found for fingerprint_frr_notification_title (3668331918920659093) -->
- <skip />
- <!-- no translation found for fingerprint_frr_notification_msg (2162966846624639252) -->
- <skip />
+ <string name="fingerprint_frr_notification_title" msgid="3668331918920659093">"Har du problem med fingeravtryckslåset?"</string>
+ <string name="fingerprint_frr_notification_msg" msgid="2162966846624639252">"Tryck för att se tips om hur du kan förbättra upplåsningen"</string>
</resources>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index 06ad6f6..9b8e63e8 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -270,7 +270,7 @@
<string name="global_action_logout" msgid="6093581310002476511">"Maliza kipindi"</string>
<string name="global_action_screenshot" msgid="2610053466156478564">"Picha ya skrini"</string>
<string name="global_action_standby" msgid="6786920589970257642">"Hali tuli"</string>
- <string name="bugreport_title" msgid="8549990811777373050">"Ripoti ya hitilafu"</string>
+ <string name="bugreport_title" msgid="8549990811777373050">"Ripoti hitilafu"</string>
<string name="bugreport_message" msgid="5212529146119624326">"Hii itakusanya maelezo kuhusu hali ya kifaa chako kwa sasa, na itume kama barua pepe. Itachukua muda mfupi tangu ripoti ya hitilafu ianze kuzalishwa hadi iwe tayari kutumwa; vumilia."</string>
<string name="bugreport_option_interactive_title" msgid="7968287837902871289">"Ripoti ya kushirikiana"</string>
<string name="bugreport_option_interactive_summary" msgid="8493795476325339542">"Tumia chaguo hili katika hali nyingi. Hukuruhusu kufuatilia jinsi ripoti yako inavyoendelea, kuandika maelezo zaidi kuhusu tatizo na kupiga picha za skrini. Huenda ikaacha baadhi ya sehemu ambazo hazitumiki sana na zinachukua muda mrefu kuripoti."</string>
@@ -1801,7 +1801,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Tumia Njia ya Mkato"</string>
<string name="color_inversion_feature_name" msgid="2672824491933264951">"Ugeuzaji rangi"</string>
<string name="color_correction_feature_name" msgid="7975133554160979214">"Usahihishaji wa rangi"</string>
- <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Hali ya kutumia kwa mkono mmoja"</string>
+ <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Kutumia kwa mkono mmoja"</string>
<string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Kipunguza mwangaza zaidi"</string>
<string name="hearing_aids_feature_name" msgid="1125892105105852542">"Vifaa vya kusaidia kusikia"</string>
<string name="autoclick_feature_name" msgid="8149248738736949630">"Kubofya kiotomatiki"</string>
@@ -2125,7 +2125,7 @@
<string name="app_category_social" msgid="2278269325488344054">"Mitandao Jamii na Mawasiliano"</string>
<string name="app_category_news" msgid="1172762719574964544">"Habari na Magazeti"</string>
<string name="app_category_maps" msgid="6395725487922533156">"Ramani na Maelekezo"</string>
- <string name="app_category_productivity" msgid="1844422703029557883">"Uzalishaji"</string>
+ <string name="app_category_productivity" msgid="1844422703029557883">"Tija"</string>
<string name="app_category_accessibility" msgid="6643521607848547683">"Ufikivu"</string>
<string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"Hifadhi ya kifaa"</string>
<string name="adb_debugging_notification_channel_tv" msgid="4764046459631031496">"Utatuzi wa USB"</string>
@@ -2272,6 +2272,7 @@
<string name="accessibility_autoclick_double_click" msgid="2103826849116176478">"Bofya mara mbili"</string>
<string name="accessibility_autoclick_drag" msgid="1499559489796843224">"Buruta"</string>
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Sogeza"</string>
+ <string name="accessibility_autoclick_long_press" msgid="8757561680164116951">"Bonyeza kwa muda mrefu"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Sitisha"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Nafasi"</string>
<string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Sogeza Juu"</string>
@@ -2556,11 +2557,14 @@
<string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Programu"</string>
<string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Alama zako za vidole hazitambuliki tena. Weka tena mipangilio ya Kufungua kwa Alama ya Kidole."</string>
<string name="usb_apm_usb_plugged_in_when_locked_notification_title" msgid="468577168569874967">"Kifaa cha USB kimechomekwa wakati Android imefungwa"</string>
- <string name="usb_apm_usb_plugged_in_when_locked_notification_text" msgid="6695268246267993166">"Kifaa cha USB huchomekwa wakati Android imefungwa. Tafadhali fungua kwanza Android kisha uweke upya kifaa cha USB ili ukitumie."</string>
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_notification_text (5561957202444135984) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_no_replug_notification_text (3844834116914726455) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_low_power_charge_notification_text (1264294871764444170) -->
+ <skip />
<string name="usb_apm_usb_suspicious_activity_notification_title" msgid="3461195995882871461">"Shughuli za USB za kutilia shaka"</string>
<string name="usb_apm_usb_suspicious_activity_notification_text" msgid="6537085605929303187">"Kiashiria cha data cha USB kimezimwa."</string>
- <!-- no translation found for fingerprint_frr_notification_title (3668331918920659093) -->
- <skip />
- <!-- no translation found for fingerprint_frr_notification_msg (2162966846624639252) -->
- <skip />
+ <string name="fingerprint_frr_notification_title" msgid="3668331918920659093">"Je, unatatizwa na kipengele cha Kufungua kwa Alama ya Kidole?"</string>
+ <string name="fingerprint_frr_notification_msg" msgid="2162966846624639252">"Gusa ili uangalie vidokezo vya kuboresha matumizi yako ya kufungua"</string>
</resources>
diff --git a/core/res/res/values-sw600dp/bools.xml b/core/res/res/values-sw600dp/bools.xml
index b339717..067d0d4 100644
--- a/core/res/res/values-sw600dp/bools.xml
+++ b/core/res/res/values-sw600dp/bools.xml
@@ -15,7 +15,6 @@
-->
<resources>
- <bool name="show_ongoing_ime_switcher">true</bool>
<bool name="kg_share_status_area">false</bool>
<bool name="kg_sim_puk_account_full_screen">false</bool>
<!-- No camera for you, tablet user -->
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index f7a471e..65dbec5 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -2211,7 +2211,7 @@
<string name="nas_upgrade_notification_enable_action" msgid="3046406808378726874">"சரி"</string>
<string name="nas_upgrade_notification_disable_action" msgid="3794833210043497982">"ஆஃப் செய்"</string>
<string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"மேலும் அறிக"</string>
- <string name="nas_upgrade_notification_learn_more_content" msgid="3735480566983530650">"Android 12 பதிப்பில் \'Android சூழலுக்கேற்ற அறிவிப்புகள்\' அம்சத்திற்குப் பதிலாக \'மேம்பட்ட அறிவிப்புகள்\' மாற்றப்பட்டுள்ளது. இந்த அம்சம், பரிந்துரைக்கப்படும் செயல்களையும் பதில்களையும் காட்டுவதுடன் உங்கள் அறிவிப்புகளையும் ஒழுங்கமைக்கும்.\n\nதொடர்புகளின் பெயர்கள், மெசேஜ்கள் போன்ற தனிப்பட்ட தகவல்கள் உட்பட அனைத்து அறிவிப்பு உள்ளடக்கத்தையும் \'மேம்பட்ட அறிவிப்புகள்\' அணுக முடியும். மேலும் இந்த அம்சத்தால் அறிவிப்புகளை நிராகரிக்கவும் அவற்றுக்குப் பதிலளிக்கவும் முடியும் (அழைப்புகளுக்குப் பதிலளிப்பது, \'தொந்தரவு செய்ய வேண்டாம்\' அம்சத்தைக் கட்டுப்படுத்துவது போன்றவை)."</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="3735480566983530650">"Android 12 பதிப்பில் \'Android சூழலுக்கேற்ற அறிவிப்புகள்\' அம்சத்திற்குப் பதிலாக \'மேம்பட்ட அறிவிப்புகள்\' மாற்றப்பட்டுள்ளது. இந்த அம்சம், பரிந்துரைக்கப்படும் செயல்களையும் பதில்களையும் காட்டுவதுடன் உங்கள் அறிவிப்புகளையும் ஒழுங்கமைக்கும்.\n\nதொடர்புகளின் பெயர்கள், மெசேஜ்கள் போன்ற தனிப்பட்ட தகவல்கள் உட்பட அனைத்து அறிவிப்பு உள்ளடக்கத்தையும் \'மேம்பட்ட அறிவிப்புகள்\' அணுக முடியும். மேலும் இந்த அம்சத்தால் அறிவிப்புகளை நிராகரிக்கவும் அவற்றுக்குப் பதிலளிக்கவும் முடியும் (எ.கா. அழைப்புகளுக்குப் பதிலளிப்பது) மற்றும் \'தொந்தரவு செய்ய வேண்டாம்\' அம்சத்தைக் கட்டுப்படுத்தவும் முடியும்."</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"வழக்கமான பேட்டரி சேமிப்பானுக்கான விவர அறிவிப்பு"</string>
<string name="dynamic_mode_notification_title" msgid="1388718452788985481">"பேட்டரி சேமிப்பு இயக்கப்பட்டுள்ளது"</string>
<string name="dynamic_mode_notification_summary" msgid="1639031262484979689">"பேட்டரி ஆயுளை நீட்டிக்க, பேட்டரி உபயோகத்தைக் குறைக்கிறது"</string>
@@ -2272,6 +2272,7 @@
<string name="accessibility_autoclick_double_click" msgid="2103826849116176478">"இரு கிளிக் செய்யும்"</string>
<string name="accessibility_autoclick_drag" msgid="1499559489796843224">"இழுக்கும்"</string>
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"நகர்த்தும்"</string>
+ <string name="accessibility_autoclick_long_press" msgid="8757561680164116951">"அழுத்திப் பிடியுங்கள்"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"இடைநிறுத்து"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"நிலை"</string>
<string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"மேலே நகர்த்து"</string>
@@ -2556,11 +2557,14 @@
<string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"ஆப்ஸ்"</string>
<string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"உங்கள் கைரேகைகளை இனி அடையாளம் காண முடியாது. கைரேகை அன்லாக் அம்சத்தை மீண்டும் அமையுங்கள்."</string>
<string name="usb_apm_usb_plugged_in_when_locked_notification_title" msgid="468577168569874967">"பூட்டப்பட்டிருக்கும்போது USB சாதனம் செருகப்பட்டது"</string>
- <string name="usb_apm_usb_plugged_in_when_locked_notification_text" msgid="6695268246267993166">"Android பூட்டப்பட்டிருக்கும்போது USB சாதனம் செருகப்பட்டது. சாதனத்தைப் பயன்படுத்த, முதலில் Androidடைத் திறக்கவும், பின்னர் USB சாதனத்தைப் பயன்படுத்த அதை மீண்டும் செருகவும்."</string>
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_notification_text (5561957202444135984) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_no_replug_notification_text (3844834116914726455) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_low_power_charge_notification_text (1264294871764444170) -->
+ <skip />
<string name="usb_apm_usb_suspicious_activity_notification_title" msgid="3461195995882871461">"சந்தேகத்திற்குரிய USB செயல்பாடு"</string>
<string name="usb_apm_usb_suspicious_activity_notification_text" msgid="6537085605929303187">"USB தரவு சிக்னல் முடக்கப்பட்டுள்ளது."</string>
- <!-- no translation found for fingerprint_frr_notification_title (3668331918920659093) -->
- <skip />
- <!-- no translation found for fingerprint_frr_notification_msg (2162966846624639252) -->
- <skip />
+ <string name="fingerprint_frr_notification_title" msgid="3668331918920659093">"கைரேகை அன்லாக் அம்சத்தில் ஏதேனும் சிக்கலை எதிர்கொள்கிறீர்களா?"</string>
+ <string name="fingerprint_frr_notification_msg" msgid="2162966846624639252">"அன்லாக் செய்யும் அனுபவத்தை மேம்படுத்தும் உதவிக்குறிப்புகளைப் பார்க்க தட்டவும்"</string>
</resources>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index f85686b..f33d70f 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -2004,7 +2004,7 @@
<string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> నుండి <xliff:g id="END">%2$s</xliff:g> వరకు"</string>
<string name="zen_mode_trigger_summary_combined" msgid="6492381546327807669">"<xliff:g id="DAYS">%1$s</xliff:g>, <xliff:g id="TIMES">%2$s</xliff:g>"</string>
<string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"ఏదైనా క్యాలెండర్"</string>
- <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> కొన్ని ధ్వనులను మ్యూట్ చేస్తోంది"</string>
+ <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> కొన్ని సౌండ్స్ మ్యూట్ చేస్తోంది"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"మీ పరికరంతో అంతర్గత సమస్య ఏర్పడింది మరియు మీరు ఫ్యాక్టరీ డేటా రీసెట్ చేసే వరకు అస్థిరంగా ఉంటుంది."</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"మీ పరికరంతో అంతర్గత సమస్య ఏర్పడింది. వివరాల కోసం మీ తయారీదారుని సంప్రదించండి."</string>
<string name="stk_cc_ussd_to_dial" msgid="3139884150741157610">"USSD రిక్వెస్ట్ సాధారణ కాల్కు మార్చబడింది"</string>
@@ -2272,6 +2272,7 @@
<string name="accessibility_autoclick_double_click" msgid="2103826849116176478">"డబుల్ క్లిక్"</string>
<string name="accessibility_autoclick_drag" msgid="1499559489796843224">"లాగండి"</string>
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"స్క్రోల్ చేయండి"</string>
+ <string name="accessibility_autoclick_long_press" msgid="8757561680164116951">"ఎక్కువసేపు నొక్కి, ఉంచండి"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"పాజ్ చేయండి"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"స్థానం"</string>
<string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"పైకి స్క్రోల్ చేయండి"</string>
@@ -2556,7 +2557,12 @@
<string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"అప్లికేషన్లు"</string>
<string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"మీ వేలిముద్రలను ఇకపై గుర్తించడం సాధ్యం కాదు. వేలిముద్ర అన్లాక్ను మళ్లీ సెటప్ చేయండి."</string>
<string name="usb_apm_usb_plugged_in_when_locked_notification_title" msgid="468577168569874967">"లాక్ చేసి ఉన్నప్పుడు USB డివైజ్ ప్లగ్-ఇన్ అయి ఉంది"</string>
- <string name="usb_apm_usb_plugged_in_when_locked_notification_text" msgid="6695268246267993166">"Android లాక్ అయినప్పుడు USB డివైజ్ ప్లగ్-ఇన్ చేయబడింది. డివైజ్ను ఉపయోగించడానికి, దయచేసి ముందుగా Androidను అన్లాక్ చేసి, ఆ తర్వాత, USB డివైజ్ను ఉపయోగించడానికి దాన్ని మళ్లీ ఇన్సర్ట్ చేయండి."</string>
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_notification_text (5561957202444135984) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_no_replug_notification_text (3844834116914726455) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_low_power_charge_notification_text (1264294871764444170) -->
+ <skip />
<string name="usb_apm_usb_suspicious_activity_notification_title" msgid="3461195995882871461">"USB యాక్టివిటీ అనుమానాస్పదంగా ఉంది"</string>
<string name="usb_apm_usb_suspicious_activity_notification_text" msgid="6537085605929303187">"USB డేటా సిగ్నల్ డిజేబుల్ అయింది."</string>
<string name="fingerprint_frr_notification_title" msgid="3668331918920659093">"వేలిముద్ర అన్లాక్తో సమస్య ఉందా?"</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 1145d9d..c078c2d 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -2272,6 +2272,7 @@
<string name="accessibility_autoclick_double_click" msgid="2103826849116176478">"ดับเบิลคลิก"</string>
<string name="accessibility_autoclick_drag" msgid="1499559489796843224">"ลาก"</string>
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"เลื่อน"</string>
+ <string name="accessibility_autoclick_long_press" msgid="8757561680164116951">"กดค้าง"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"หยุดชั่วคราว"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"วางตำแหน่ง"</string>
<string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"เลื่อนขึ้น"</string>
@@ -2556,7 +2557,12 @@
<string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"แอปพลิเคชัน"</string>
<string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"ระบบจะไม่จดจำลายนิ้วมือของคุณอีกต่อไป ตั้งค่าการปลดล็อกด้วยลายนิ้วมืออีกครั้ง"</string>
<string name="usb_apm_usb_plugged_in_when_locked_notification_title" msgid="468577168569874967">"เสียบอุปกรณ์ USB ขณะล็อกอยู่"</string>
- <string name="usb_apm_usb_plugged_in_when_locked_notification_text" msgid="6695268246267993166">"คุณเสียบอุปกรณ์ USB ขณะ Android ล็อกอยู่ หากต้องการใช้อุปกรณ์ โปรดปลดล็อก Android ก่อนแล้วจึงเสียบอุปกรณ์ USB อีกครั้งเพื่อใช้งาน"</string>
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_notification_text (5561957202444135984) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_no_replug_notification_text (3844834116914726455) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_low_power_charge_notification_text (1264294871764444170) -->
+ <skip />
<string name="usb_apm_usb_suspicious_activity_notification_title" msgid="3461195995882871461">"กิจกรรม USB ที่น่าสงสัย"</string>
<string name="usb_apm_usb_suspicious_activity_notification_text" msgid="6537085605929303187">"สัญญาณข้อมูล USB ปิดอยู่"</string>
<string name="fingerprint_frr_notification_title" msgid="3668331918920659093">"หากพบปัญหาในการปลดล็อกด้วยลายนิ้วมือ"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 55a06d7..2bec27d 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -2272,6 +2272,7 @@
<string name="accessibility_autoclick_double_click" msgid="2103826849116176478">"Mag-double click"</string>
<string name="accessibility_autoclick_drag" msgid="1499559489796843224">"I-drag"</string>
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Mag-scroll"</string>
+ <string name="accessibility_autoclick_long_press" msgid="8757561680164116951">"Pindutin nang matagal"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"I-pause"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Posisyon"</string>
<string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Mag-scroll Pataas"</string>
@@ -2556,7 +2557,9 @@
<string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Mga Application"</string>
<string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Hindi na makikilala ang iyong mga fingerprint. I-set up ulit ang Pag-unlock Gamit ang Fingerprint."</string>
<string name="usb_apm_usb_plugged_in_when_locked_notification_title" msgid="468577168569874967">"Nakasaksak ang USB device kapag naka-lock"</string>
- <string name="usb_apm_usb_plugged_in_when_locked_notification_text" msgid="6695268246267993166">"Nakasaksak ang USB device kapag naka-lock ang Android. Para magamit ang device, paki-unlock muna ang Android at pagkatapos ay isaksak ulit ang USB device para magamit ito."</string>
+ <string name="usb_apm_usb_plugged_in_when_locked_notification_text" msgid="5561957202444135984">"Isinaksak ang USB device nang naka-lock ang Android. Para magamit ang device, paki-unlock muna ang Android."</string>
+ <string name="usb_apm_usb_plugged_in_when_locked_no_replug_notification_text" msgid="3844834116914726455">"Isinaksak ang USB device nang naka-lock ang Android. Para magamit ang device, paki-unlock muna ang Android at pagkatapos ay isaksak ulit ang USB device para magamit ito."</string>
+ <string name="usb_apm_usb_plugged_in_when_locked_low_power_charge_notification_text" msgid="1264294871764444170">"Posibleng mabawasan o hindi available ang kakayahan sa pag-charge kapag naka-lock."</string>
<string name="usb_apm_usb_suspicious_activity_notification_title" msgid="3461195995882871461">"Kahina-hinalang aktibidad sa USB"</string>
<string name="usb_apm_usb_suspicious_activity_notification_text" msgid="6537085605929303187">"Na-disable ang data signal ng USB."</string>
<string name="fingerprint_frr_notification_title" msgid="3668331918920659093">"Nagkakaproblema ka ba sa Pag-unlock Gamit ang Fingerprint?"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index d01cdb9..5ca7038 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -2272,6 +2272,7 @@
<string name="accessibility_autoclick_double_click" msgid="2103826849116176478">"Çift tıklama"</string>
<string name="accessibility_autoclick_drag" msgid="1499559489796843224">"Sürükleme"</string>
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Kaydırma"</string>
+ <string name="accessibility_autoclick_long_press" msgid="8757561680164116951">"Uzun bas"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Duraklatma"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Konum"</string>
<string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Yukarı Kaydır"</string>
@@ -2556,11 +2557,14 @@
<string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Uygulamalar"</string>
<string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Parmak izleriniz artık tanınamıyor. Parmak İzi Kilidi\'ni tekrar kurun."</string>
<string name="usb_apm_usb_plugged_in_when_locked_notification_title" msgid="468577168569874967">"USB cihazı, Android kilitliyken takıldı"</string>
- <string name="usb_apm_usb_plugged_in_when_locked_notification_text" msgid="6695268246267993166">"USB cihazı, Android kilitliyken takıldı. Cihazı kullanmak için lütfen önce Android\'in kilidini açın, ardından USB cihazını yeniden takın."</string>
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_notification_text (5561957202444135984) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_no_replug_notification_text (3844834116914726455) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_low_power_charge_notification_text (1264294871764444170) -->
+ <skip />
<string name="usb_apm_usb_suspicious_activity_notification_title" msgid="3461195995882871461">"Şüpheli USB etkinliği"</string>
<string name="usb_apm_usb_suspicious_activity_notification_text" msgid="6537085605929303187">"USB veri sinyali devre dışı bırakıldı."</string>
- <!-- no translation found for fingerprint_frr_notification_title (3668331918920659093) -->
- <skip />
- <!-- no translation found for fingerprint_frr_notification_msg (2162966846624639252) -->
- <skip />
+ <string name="fingerprint_frr_notification_title" msgid="3668331918920659093">"Parmak İzi Kilidi ile ilgili sorun mu yaşıyorsunuz?"</string>
+ <string name="fingerprint_frr_notification_msg" msgid="2162966846624639252">"Kilit açma deneyiminizi daha iyi hale getirmeyle ilgili ipuçlarına göz atmak için dokunun"</string>
</resources>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 3e302f1..9d35aad 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -2274,6 +2274,7 @@
<string name="accessibility_autoclick_double_click" msgid="2103826849116176478">"Двічі натиснути"</string>
<string name="accessibility_autoclick_drag" msgid="1499559489796843224">"Перетягнути"</string>
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Прокрутити"</string>
+ <string name="accessibility_autoclick_long_press" msgid="8757561680164116951">"Утримувати"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Призупинити"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Змінити позицію"</string>
<string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Прокрутити вгору"</string>
@@ -2558,11 +2559,14 @@
<string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Додатки"</string>
<string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Ваші відбитки пальців більше не розпізнаються. Налаштуйте розблокування відбитком пальця повторно."</string>
<string name="usb_apm_usb_plugged_in_when_locked_notification_title" msgid="468577168569874967">"USB-пристрій підключено до заблокованого пристрою"</string>
- <string name="usb_apm_usb_plugged_in_when_locked_notification_text" msgid="6695268246267993166">"USB-пристрій підключено, коли пристрій Android заблоковано. Щоб використовувати USB-пристрій, вийміть і знову вставте його після того, як розблокуєте пристрій Android."</string>
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_notification_text (5561957202444135984) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_no_replug_notification_text (3844834116914726455) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_low_power_charge_notification_text (1264294871764444170) -->
+ <skip />
<string name="usb_apm_usb_suspicious_activity_notification_title" msgid="3461195995882871461">"Підозрілі дії з USB-пристроєм"</string>
<string name="usb_apm_usb_suspicious_activity_notification_text" msgid="6537085605929303187">"Передавання даних через USB вимкнено."</string>
- <!-- no translation found for fingerprint_frr_notification_title (3668331918920659093) -->
- <skip />
- <!-- no translation found for fingerprint_frr_notification_msg (2162966846624639252) -->
- <skip />
+ <string name="fingerprint_frr_notification_title" msgid="3668331918920659093">"Виникли проблеми з розблокуванням відбитком пальця?"</string>
+ <string name="fingerprint_frr_notification_msg" msgid="2162966846624639252">"Натисніть, щоб переглянути поради щодо зручнішого розблокування екрана"</string>
</resources>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index 3148768..c48d91f 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -2004,7 +2004,7 @@
<string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> تا <xliff:g id="END">%2$s</xliff:g>"</string>
<string name="zen_mode_trigger_summary_combined" msgid="6492381546327807669">"<xliff:g id="DAYS">%1$s</xliff:g>، <xliff:g id="TIMES">%2$s</xliff:g>"</string>
<string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"کوئی بھی کیلنڈر"</string>
- <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> کچھ آوازوں کو خاموش کر رہا ہے"</string>
+ <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> کچھ آوازوں کو خاموش کر رہا ہے"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"آپ کے آلہ میں ایک داخلی مسئلہ ہے اور جب تک آپ فیکٹری ڈیٹا کو دوبارہ ترتیب نہیں دے دیتے ہیں، ہوسکتا ہے کہ یہ غیر مستحکم رہے۔"</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"آپ کے آلہ میں ایک داخلی مسئلہ ہے۔ تفصیلات کیلئے اپنے مینوفیکچرر سے رابطہ کریں۔"</string>
<string name="stk_cc_ussd_to_dial" msgid="3139884150741157610">"USSD درخواست کو ریگولر کال میں تبدیل کر دیا گیا"</string>
@@ -2272,6 +2272,7 @@
<string name="accessibility_autoclick_double_click" msgid="2103826849116176478">"دو بار کلک کریں"</string>
<string name="accessibility_autoclick_drag" msgid="1499559489796843224">"گھسیٹیں"</string>
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"اسکرول کریں"</string>
+ <string name="accessibility_autoclick_long_press" msgid="8757561680164116951">"دیر تک دبائے رکھیں"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"موقوف کریں"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"پوزیشن"</string>
<string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"اوپر اسکرول کریں"</string>
@@ -2556,7 +2557,12 @@
<string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"ایپلیکیشنز"</string>
<string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"آپ کے فنگر پرنٹس کو مزید پہچانا نہیں جا سکتا۔ فنگر پرنٹ اَن لاک کو دوبارہ سیٹ اپ کریں۔"</string>
<string name="usb_apm_usb_plugged_in_when_locked_notification_title" msgid="468577168569874967">"مقفل ہونے پر USB آلہ پلگ ان ہوتا ہے"</string>
- <string name="usb_apm_usb_plugged_in_when_locked_notification_text" msgid="6695268246267993166">"Android کے مقفل ہونے پر USB آلہ پلگ ان ہوتا ہے۔ آلہ استعمال کرنے کے لیے، براہ کرم پہلے Android کو غیر مقفل کریں اور پھر اسے استعمال کرنے کے لیے USB آلہ کو دوبارہ داخل کریں۔"</string>
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_notification_text (5561957202444135984) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_no_replug_notification_text (3844834116914726455) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_low_power_charge_notification_text (1264294871764444170) -->
+ <skip />
<string name="usb_apm_usb_suspicious_activity_notification_title" msgid="3461195995882871461">"USB کی مشکوک سرگرمی"</string>
<string name="usb_apm_usb_suspicious_activity_notification_text" msgid="6537085605929303187">"USB ڈیٹا سگنل کو غیر فعال کر دیا گیا ہے۔"</string>
<string name="fingerprint_frr_notification_title" msgid="3668331918920659093">"فنگر پرنٹ اَن لاک کے ضمن میں دشواری کا سامنا ہے؟"</string>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index e04227b..bb2d233 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -2272,6 +2272,7 @@
<string name="accessibility_autoclick_double_click" msgid="2103826849116176478">"Ikki marta bosish"</string>
<string name="accessibility_autoclick_drag" msgid="1499559489796843224">"Tortish"</string>
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Aylantirish"</string>
+ <string name="accessibility_autoclick_long_press" msgid="8757561680164116951">"Bosib turish"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Pauza"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Joylashuvi"</string>
<string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Tepaga varaqlash"</string>
@@ -2556,11 +2557,11 @@
<string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Ilovalar"</string>
<string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Barmoq izlaringiz endi tanilmaydi. Barmoq izi bilan ochishni qayta sozlang."</string>
<string name="usb_apm_usb_plugged_in_when_locked_notification_title" msgid="468577168569874967">"Qulflanganida USB qurilma quvvat manbasiga ulandi"</string>
- <string name="usb_apm_usb_plugged_in_when_locked_notification_text" msgid="6695268246267993166">"Android qulflanganda USB qurilma quvvat manbasiga ulandi. Qurilmani ishlatish uchun avval Android qulfini oching va keyin uni ishlatish uchun USB qurilmani qayta kiriting."</string>
+ <string name="usb_apm_usb_plugged_in_when_locked_notification_text" msgid="5561957202444135984">"Android qulflanganda USB qurilma quvvat manbasiga ulandi. Qurilmani ishlatish uchun avval Android qulfini oching."</string>
+ <string name="usb_apm_usb_plugged_in_when_locked_no_replug_notification_text" msgid="3844834116914726455">"Android qulflanganda USB qurilma quvvat manbasiga ulandi. Qurilmani ishlatish uchun avval Android qulfini oching va keyin uni ishlatish uchun USB qurilmani qayta kiriting."</string>
+ <string name="usb_apm_usb_plugged_in_when_locked_low_power_charge_notification_text" msgid="1264294871764444170">"Qulflanganda quvvat olish imkoniyati kamayishi yoki ishlamasligi mumkin."</string>
<string name="usb_apm_usb_suspicious_activity_notification_title" msgid="3461195995882871461">"Shubhali USB faoliyati"</string>
<string name="usb_apm_usb_suspicious_activity_notification_text" msgid="6537085605929303187">"USB maʼlumotlar signali faolsizlantirilgan."</string>
- <!-- no translation found for fingerprint_frr_notification_title (3668331918920659093) -->
- <skip />
- <!-- no translation found for fingerprint_frr_notification_msg (2162966846624639252) -->
- <skip />
+ <string name="fingerprint_frr_notification_title" msgid="3668331918920659093">"Barmoq izi bilan ochishda muammo bormi?"</string>
+ <string name="fingerprint_frr_notification_msg" msgid="2162966846624639252">"Qulfdan chiqarish tajribangizni yaxshilash borasida maslahatlar bilan tanishish uchun bosing"</string>
</resources>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 12ad030..f0a353a 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -2272,6 +2272,7 @@
<string name="accessibility_autoclick_double_click" msgid="2103826849116176478">"Nhấp đúp"</string>
<string name="accessibility_autoclick_drag" msgid="1499559489796843224">"Kéo"</string>
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Cuộn"</string>
+ <string name="accessibility_autoclick_long_press" msgid="8757561680164116951">"Nhấn và giữ"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Tạm dừng"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Vị trí"</string>
<string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Cuộn lên"</string>
@@ -2556,11 +2557,14 @@
<string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Ứng dụng"</string>
<string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Hệ thống không nhận dạng được vân tay của bạn. Hãy thiết lập lại tính năng Mở khoá bằng vân tay."</string>
<string name="usb_apm_usb_plugged_in_when_locked_notification_title" msgid="468577168569874967">"Thiết bị USB được cắm khi thiết bị Android đang khoá"</string>
- <string name="usb_apm_usb_plugged_in_when_locked_notification_text" msgid="6695268246267993166">"Thiết bị USB đã được cắm khi thiết bị Android đang khoá. Để sử dụng thiết bị USB, trước tiên, hãy mở khoá thiết bị Android rồi cắm lại thiết bị USB."</string>
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_notification_text (5561957202444135984) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_no_replug_notification_text (3844834116914726455) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_low_power_charge_notification_text (1264294871764444170) -->
+ <skip />
<string name="usb_apm_usb_suspicious_activity_notification_title" msgid="3461195995882871461">"Hoạt động đáng ngờ liên quan đến USB"</string>
<string name="usb_apm_usb_suspicious_activity_notification_text" msgid="6537085605929303187">"Tín hiệu dữ liệu qua USB đã bị tắt."</string>
- <!-- no translation found for fingerprint_frr_notification_title (3668331918920659093) -->
- <skip />
- <!-- no translation found for fingerprint_frr_notification_msg (2162966846624639252) -->
- <skip />
+ <string name="fingerprint_frr_notification_title" msgid="3668331918920659093">"Bạn gặp sự cố khi sử dụng tính năng Mở khoá bằng vân tay?"</string>
+ <string name="fingerprint_frr_notification_msg" msgid="2162966846624639252">"Nhấn để xem các mẹo giúp cải thiện trải nghiệm mở khoá"</string>
</resources>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index df66ffa..76c943d 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -1729,7 +1729,7 @@
<string name="display_manager_overlay_display_title" msgid="1480158037150469170">"<xliff:g id="NAME">%1$s</xliff:g>:<xliff:g id="WIDTH">%2$d</xliff:g>x<xliff:g id="HEIGHT">%3$d</xliff:g>,<xliff:g id="DPI">%4$d</xliff:g> dpi"</string>
<string name="display_manager_overlay_display_secure_suffix" msgid="2810034719482834679">",安全"</string>
<string name="kg_forgot_pattern_button_text" msgid="406145459223122537">"忘记了图案"</string>
- <string name="kg_wrong_pattern" msgid="1342812634464179931">"图案错误"</string>
+ <string name="kg_wrong_pattern" msgid="1342812634464179931">"解锁图案错误"</string>
<string name="kg_wrong_password" msgid="2384677900494439426">"密码错误"</string>
<string name="kg_wrong_pin" msgid="3680925703673166482">"PIN码有误"</string>
<string name="kg_pattern_instructions" msgid="8366024510502517748">"绘制您的图案"</string>
@@ -2004,7 +2004,7 @@
<string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g>到<xliff:g id="END">%2$s</xliff:g>"</string>
<string name="zen_mode_trigger_summary_combined" msgid="6492381546327807669">"<xliff:g id="DAYS">%1$s</xliff:g><xliff:g id="TIMES">%2$s</xliff:g>"</string>
<string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"所有日历"</string>
- <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g>正在将某些音效设为静音"</string>
+ <string name="muted_by" msgid="91464083490094950">"“<xliff:g id="THIRD_PARTY">%1$s</xliff:g>”已将某些音效设为静音"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"您的设备内部出现了问题。如果不将设备恢复出厂设置,设备运行可能会不稳定。"</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"您的设备内部出现了问题。请联系您的设备制造商了解详情。"</string>
<string name="stk_cc_ussd_to_dial" msgid="3139884150741157610">"USSD 请求已更改为普通通话"</string>
@@ -2272,6 +2272,7 @@
<string name="accessibility_autoclick_double_click" msgid="2103826849116176478">"双击"</string>
<string name="accessibility_autoclick_drag" msgid="1499559489796843224">"拖动"</string>
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"滚动"</string>
+ <string name="accessibility_autoclick_long_press" msgid="8757561680164116951">"长按"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"暂停"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"位置"</string>
<string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"向上滚动"</string>
@@ -2556,11 +2557,14 @@
<string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"应用"</string>
<string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"系统无法再识别您的指纹。请重新设置“指纹解锁”功能。"</string>
<string name="usb_apm_usb_plugged_in_when_locked_notification_title" msgid="468577168569874967">"锁定时插入了 USB 设备"</string>
- <string name="usb_apm_usb_plugged_in_when_locked_notification_text" msgid="6695268246267993166">"在 Android 设备锁定时插入了 USB 设备。如需使用 USB 设备,请先解锁 Android 设备,然后重新插入 USB 设备才能使用 USB 设备。"</string>
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_notification_text (5561957202444135984) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_no_replug_notification_text (3844834116914726455) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_low_power_charge_notification_text (1264294871764444170) -->
+ <skip />
<string name="usb_apm_usb_suspicious_activity_notification_title" msgid="3461195995882871461">"可疑的 USB 活动"</string>
<string name="usb_apm_usb_suspicious_activity_notification_text" msgid="6537085605929303187">"USB 数据信号已停用。"</string>
- <!-- no translation found for fingerprint_frr_notification_title (3668331918920659093) -->
- <skip />
- <!-- no translation found for fingerprint_frr_notification_msg (2162966846624639252) -->
- <skip />
+ <string name="fingerprint_frr_notification_title" msgid="3668331918920659093">"使用指纹解锁时遇到问题了吗?"</string>
+ <string name="fingerprint_frr_notification_msg" msgid="2162966846624639252">"点按即可查看提升解锁体验的小贴士"</string>
</resources>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index 933f8de..b9d950f 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -2004,7 +2004,7 @@
<string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g>至<xliff:g id="END">%2$s</xliff:g>"</string>
<string name="zen_mode_trigger_summary_combined" msgid="6492381546327807669">"<xliff:g id="DAYS">%1$s</xliff:g>,<xliff:g id="TIMES">%2$s</xliff:g>"</string>
<string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"任何日曆"</string>
- <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g>正將某些音效設為靜音"</string>
+ <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g>目前將部分音效設為靜音"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"你裝置的系統發生問題,回復原廠設定後即可解決該問題。"</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"你裝置的系統發生問題,請聯絡你的製造商瞭解詳情。"</string>
<string name="stk_cc_ussd_to_dial" msgid="3139884150741157610">"USSD 要求已變更為一般通話"</string>
@@ -2272,6 +2272,7 @@
<string name="accessibility_autoclick_double_click" msgid="2103826849116176478">"連按兩下"</string>
<string name="accessibility_autoclick_drag" msgid="1499559489796843224">"拖曳"</string>
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"捲動"</string>
+ <string name="accessibility_autoclick_long_press" msgid="8757561680164116951">"長按"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"暫停"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"位置"</string>
<string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"向上捲動"</string>
@@ -2556,11 +2557,14 @@
<string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"應用程式"</string>
<string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"無法再辨識你的指紋。請重新設定「指紋解鎖」功能。"</string>
<string name="usb_apm_usb_plugged_in_when_locked_notification_title" msgid="468577168569874967">"USB 裝置插入時已鎖定"</string>
- <string name="usb_apm_usb_plugged_in_when_locked_notification_text" msgid="6695268246267993166">"USB 裝置插入時,Android 已鎖定。如要使用裝置,請先解鎖 Android,然後重新插入 USB 裝置,才能使用。"</string>
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_notification_text (5561957202444135984) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_no_replug_notification_text (3844834116914726455) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_low_power_charge_notification_text (1264294871764444170) -->
+ <skip />
<string name="usb_apm_usb_suspicious_activity_notification_title" msgid="3461195995882871461">"可疑的 USB 活動"</string>
<string name="usb_apm_usb_suspicious_activity_notification_text" msgid="6537085605929303187">"USB 資料訊號已停用。"</string>
- <!-- no translation found for fingerprint_frr_notification_title (3668331918920659093) -->
- <skip />
- <!-- no translation found for fingerprint_frr_notification_msg (2162966846624639252) -->
- <skip />
+ <string name="fingerprint_frr_notification_title" msgid="3668331918920659093">"使用「指紋解鎖」時遇到問題嗎?"</string>
+ <string name="fingerprint_frr_notification_msg" msgid="2162966846624639252">"輕按即可查看關於改善解鎖體驗的貼士"</string>
</resources>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 75b21b4..773b473 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -2004,7 +2004,7 @@
<string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g>到<xliff:g id="END">%2$s</xliff:g>"</string>
<string name="zen_mode_trigger_summary_combined" msgid="6492381546327807669">"<xliff:g id="DAYS">%1$s</xliff:g>、<xliff:g id="TIMES">%2$s</xliff:g>"</string>
<string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"任何日曆"</string>
- <string name="muted_by" msgid="91464083490094950">"「<xliff:g id="THIRD_PARTY">%1$s</xliff:g>」正在關閉部分音效"</string>
+ <string name="muted_by" msgid="91464083490094950">"「<xliff:g id="THIRD_PARTY">%1$s</xliff:g>」關閉了部分音效"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"你的裝置發生內部問題,必須將裝置恢復原廠設定才能解除不穩定狀態。"</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"你的裝置發生內部問題,詳情請洽裝置製造商。"</string>
<string name="stk_cc_ussd_to_dial" msgid="3139884150741157610">"USSD 要求已變更為一般通話"</string>
@@ -2211,7 +2211,7 @@
<string name="nas_upgrade_notification_enable_action" msgid="3046406808378726874">"確定"</string>
<string name="nas_upgrade_notification_disable_action" msgid="3794833210043497982">"關閉"</string>
<string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"瞭解詳情"</string>
- <string name="nas_upgrade_notification_learn_more_content" msgid="3735480566983530650">"在 Android 12 中,加強型通知功能已取代 Android 自動調整通知。這項功能可以顯示建議的操作和回覆內容,也可以幫你整理通知訊息。\n\n加強型通知功能可存取通知內容,包括聯絡人名稱和訊息內文等個人資訊。此外,這項功能還能關閉或回覆通知,例如接聽來電及控管「零打擾」功能。"</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="3735480566983530650">"在 Android 12 中,加強型通知功能已取代 Android 自動調整通知,會顯示建議的操作和回覆內容,也可以幫你整理通知訊息。\n\n加強型通知功能可存取通知內容,包括聯絡人名稱和訊息內文等個人資訊;此外,還能關閉或回覆通知,例如接聽來電及控管「零打擾」功能。"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"日常安排模式資訊通知"</string>
<string name="dynamic_mode_notification_title" msgid="1388718452788985481">"已開啟省電模式"</string>
<string name="dynamic_mode_notification_summary" msgid="1639031262484979689">"降低電池用量,以便延長電池續航力"</string>
@@ -2272,6 +2272,7 @@
<string name="accessibility_autoclick_double_click" msgid="2103826849116176478">"按兩下"</string>
<string name="accessibility_autoclick_drag" msgid="1499559489796843224">"拖曳"</string>
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"捲動"</string>
+ <string name="accessibility_autoclick_long_press" msgid="8757561680164116951">"長按"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"暫停"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"位置"</string>
<string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"向上捲動"</string>
@@ -2556,11 +2557,14 @@
<string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"應用程式"</string>
<string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"系統無法再辨識你的指紋,請重新設定「指紋解鎖」。"</string>
<string name="usb_apm_usb_plugged_in_when_locked_notification_title" msgid="468577168569874967">"在鎖定時插入 USB 裝置"</string>
- <string name="usb_apm_usb_plugged_in_when_locked_notification_text" msgid="6695268246267993166">"在 Android 裝置鎖定時插入 USB 裝置。如要使用 USB 裝置,請先解鎖 Android 裝置再重新插入。"</string>
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_notification_text (5561957202444135984) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_no_replug_notification_text (3844834116914726455) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_low_power_charge_notification_text (1264294871764444170) -->
+ <skip />
<string name="usb_apm_usb_suspicious_activity_notification_title" msgid="3461195995882871461">"可疑的 USB 活動"</string>
<string name="usb_apm_usb_suspicious_activity_notification_text" msgid="6537085605929303187">"USB 資料信號已停用。"</string>
- <!-- no translation found for fingerprint_frr_notification_title (3668331918920659093) -->
- <skip />
- <!-- no translation found for fingerprint_frr_notification_msg (2162966846624639252) -->
- <skip />
+ <string name="fingerprint_frr_notification_title" msgid="3668331918920659093">"使用指紋解鎖時發生問題嗎?"</string>
+ <string name="fingerprint_frr_notification_msg" msgid="2162966846624639252">"輕觸即可查看提示,瞭解如何提升解鎖體驗"</string>
</resources>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index e97bb88..4184c6e 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -2272,6 +2272,7 @@
<string name="accessibility_autoclick_double_click" msgid="2103826849116176478">"Chofoza kabili"</string>
<string name="accessibility_autoclick_drag" msgid="1499559489796843224">"Hudula"</string>
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Skrola"</string>
+ <string name="accessibility_autoclick_long_press" msgid="8757561680164116951">"Cindezela isikhathi eside"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Misa"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Indawo"</string>
<string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Skrolela Phezulu"</string>
@@ -2556,11 +2557,14 @@
<string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Ama-application"</string>
<string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Isigxivizo somunwe wakho ngeke zisakwazi ukubonwa. Setha Ukuvula Ngesigxivizo Somunwe futhi."</string>
<string name="usb_apm_usb_plugged_in_when_locked_notification_title" msgid="468577168569874967">"Idivayisi ye-USB iyapulagwa uma i-Android ikhiyiwe."</string>
- <string name="usb_apm_usb_plugged_in_when_locked_notification_text" msgid="6695268246267993166">"Idivayisi ye-USB iyapulagwa uma i-Android ikhiyiwe. Ukuze usebenzisa idivayisi, sicela uvule i-Android kuqala bese uphinde ufake idivayisi ye-USB ukuze uyisebenzise."</string>
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_notification_text (5561957202444135984) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_no_replug_notification_text (3844834116914726455) -->
+ <skip />
+ <!-- no translation found for usb_apm_usb_plugged_in_when_locked_low_power_charge_notification_text (1264294871764444170) -->
+ <skip />
<string name="usb_apm_usb_suspicious_activity_notification_title" msgid="3461195995882871461">"Umsebenzi we-USB osolisayo"</string>
<string name="usb_apm_usb_suspicious_activity_notification_text" msgid="6537085605929303187">"Isignali yedatha ye-USB ivaliwe."</string>
- <!-- no translation found for fingerprint_frr_notification_title (3668331918920659093) -->
- <skip />
- <!-- no translation found for fingerprint_frr_notification_msg (2162966846624639252) -->
- <skip />
+ <string name="fingerprint_frr_notification_title" msgid="3668331918920659093">"Unenkinga Ngokuvula Ngesigxivizo Somunwe?"</string>
+ <string name="fingerprint_frr_notification_msg" msgid="2162966846624639252">"Thepha ukuze ubone amacebiso okuthi ungayithuthukisa kanjani indlela ovula ngayo"</string>
</resources>
diff --git a/core/res/res/values/bools.xml b/core/res/res/values/bools.xml
index b097a61..3653845 100644
--- a/core/res/res/values/bools.xml
+++ b/core/res/res/values/bools.xml
@@ -21,7 +21,6 @@
<bool name="action_bar_embed_tabs">true</bool>
<bool name="split_action_bar_is_narrow">true</bool>
<bool name="preferences_prefer_dual_pane">false</bool>
- <bool name="show_ongoing_ime_switcher">true</bool>
<bool name="action_bar_expanded_action_views_exclusive">true</bool>
<!-- Whether or not to use the drawable/lockscreen_notselected and
drawable/lockscreen_selected instead of the generic dots when displaying
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index bf1423b..b98e293 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -584,6 +584,9 @@
<color name="side_fps_button_color">#00677E</color>
<!-- Color for various surfaces related to system-wide blur -->
+ <color name="shade_panel_fg">@color/shade_panel_fg_color</color>
+ <color name="shade_panel_bg">@color/shade_panel_bg_color</color>
+ <color name="shade_panel_scrim">@color/shade_panel_scrim_color</color>
<color name="surface_effect_0">@color/surface_effect_0_color</color>
<color name="surface_effect_1">@color/surface_effect_1_color</color>
<color name="surface_effect_2">@color/surface_effect_2_color</color>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 021eebe..9f3846a 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2659,10 +2659,15 @@
If false, Content-disposition fragments are ignored -->
<bool name="config_mms_content_disposition_support">true</bool>
- <!-- MMS user agent string -->
+ <!-- MMS user agent string, for example OemNameDeviceName
+ If the string contains a '%s' format string then that substring will be replaced
+ with the value of Build.MODEL , ie
+ <string name="config_mms_user_agent">OemName%s</string> -->
<string name="config_mms_user_agent" translatable="false"></string>
- <!-- MMS user agent prolfile url -->
+ <!-- MMS user agent prolfile url, for example https://uaprof.oem_name.com/DeviceName.xml
+ if the string contains a '%s' format string then that substring will be replaced
+ with the value of Build.MODEL , ie https://uaprof.oem_name.com/%s.xml -->
<string name="config_mms_user_agent_profile_url" translatable="false"></string>
<!-- The default list of possible CMF Names|Style|ColorSource. This array can be
@@ -6194,6 +6199,11 @@
<!-- If true, multiuser switcher would be automatically enabled when second user is created on the device. -->
<bool name="config_enableUserSwitcherUponUserCreation">true</bool>
+ <!--
+ Whether the user switching can only happen by logging out and going through the system user (login screen).
+ -->
+ <bool name="config_userSwitchingMustGoThroughLoginScreen">false</bool>
+
<!-- Set to true to make assistant show in front of the dream/screensaver. -->
<bool name="config_assistantOnTopOfDream">false</bool>
@@ -6466,6 +6476,10 @@
<bool name="config_enableBackSound">false</bool>
<!-- Chooser image editing activity. Must handle ACTION_EDIT image/png intents.
+ This name is in the ComponentName flattened format (package/class) [DO NOT TRANSLATE] -->
+ <string name="config_preferredSystemImageEditor" translatable="false"></string>
+ <!-- Chooser fallback image editing activity, used if the preferred one is disabled.
+ Must handle ACTION_EDIT image/png intents.
If omitted, image editing will not be offered via Chooser.
This name is in the ComponentName flattened format (package/class) [DO NOT TRANSLATE] -->
<string name="config_systemImageEditor" translatable="false"></string>
@@ -7383,6 +7397,9 @@
screen. -->
<bool name="config_dragToMaximizeInDesktopMode">false</bool>
+ <!-- Whether the home screen should be shown behind freeform tasks in desktop mode. -->
+ <bool name="config_showHomeBehindDesktop">false</bool>
+
<!-- Frame rate compatibility value for Wallpaper
FRAME_RATE_COMPATIBILITY_MIN (102) is used by default for lower power consumption -->
<integer name="config_wallpaperFrameRateCompatibility">102</integer>
@@ -7485,4 +7502,7 @@
<!-- Biometrics fingerprint frr notification target activity component name, it shall be customized by OEMs -->
<string translatable="false" name="config_fingerprintFrrTargetComponent"></string>
+ <!-- Default name for all newly configured tv profiles. -->
+ <string name="config_configured_tv_profile_name">configured_user</string>
+
</resources>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index d4be25f..b37a925 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -268,9 +268,6 @@
72dp (content margin) - 12dp (action padding) - 4dp (button inset) -->
<dimen name="notification_2025_actions_margin_start">56dp</dimen>
- <!-- Notification action button text size -->
- <dimen name="notification_2025_action_text_size">16sp</dimen>
-
<!-- The margin on the end of most content views (ignores the expander) -->
<dimen name="notification_content_margin_end">16dp</dimen>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 212df49..40ff7f7 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -6866,7 +6866,9 @@
<!-- AdvancedProtectionService USB notifications -->
<string name="usb_apm_usb_plugged_in_when_locked_notification_title">USB device plugged in when locked</string>
- <string name="usb_apm_usb_plugged_in_when_locked_notification_text">USB device is plugged in when Android is locked. To use device, please unlock Android first and then reinsert USB device to use it.</string>
+ <string name="usb_apm_usb_plugged_in_when_locked_notification_text">USB device is plugged in when Android is locked. To use device, please unlock Android first.</string>
+ <string name="usb_apm_usb_plugged_in_when_locked_no_replug_notification_text">USB device is plugged in when Android is locked. To use device, please unlock Android first and then reinsert USB device to use it.</string>
+ <string name="usb_apm_usb_plugged_in_when_locked_low_power_charge_notification_text">Charging capability may be reduced or unavailable when locked.</string>
<string name="usb_apm_usb_suspicious_activity_notification_title">Suspicious USB activity</string>
<string name="usb_apm_usb_suspicious_activity_notification_text">USB data signal has been disabled.</string>
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 1c3529f..3f75fd9 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -28,7 +28,7 @@
PLEASE READ
===============================================================
-->
-<resources>
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Global Theme Styles -->
<eat-comment/>
@@ -990,7 +990,9 @@
</style>
<style name="TextAppearance.Toast">
- <item name="fontFamily">@*android:string/config_bodyFontFamily</item>
+ <item name="fontFamily" android:featureFlag="android.app.notifications_redesign_fonts">variable-body-medium</item>
+ <item name="fontFamily" android:featureFlag="!android.app.notifications_redesign_fonts">@*android:string/config_bodyFontFamily</item>
+
<item name="textSize">14sp</item>
<item name="lineHeight">20sp</item>
<item name="textColor">?android:attr/textColorPrimary</item>
@@ -1638,12 +1640,17 @@
<!-- The style for normal action button on notification -->
<style name="NotificationAction" parent="Widget.Material.Light.Button.Borderless.Small">
- <item name="textColor">@color/notification_action_button_text_color</item>
+ <item name="fontFamily" android:featureFlag="android.app.notifications_redesign_fonts">variable-title-small-emphasized</item>
+
+ <item name="textColor" android:featureFlag="android.app.notifications_redesign_fonts">@color/materialColorPrimary</item>
+ <item name="textColor" android:featureFlag="!android.app.notifications_redesign_fonts">@color/notification_action_button_text_color</item>
+
<item name="background">@drawable/notification_material_action_background</item>
</style>
- <!-- The style for emphasized action button on notification: Colored bordered ink button -->
+ <!-- The style for emphasized action button on notification (used e.g. for calls): Colored bordered ink button -->
<style name="NotificationEmphasizedAction" parent="Widget.Material.Button">
+ <item name="fontFamily" android:featureFlag="android.app.notifications_redesign_fonts">variable-title-medium-emphasized</item>
<item name="background">@drawable/btn_notification_emphasized</item>
<item name="stateListAnimator">@anim/flat_button_state_list_anim_material</item>
</style>
diff --git a/core/res/res/values/styles_device_defaults.xml b/core/res/res/values/styles_device_defaults.xml
index 3b2f244..4dfa099 100644
--- a/core/res/res/values/styles_device_defaults.xml
+++ b/core/res/res/values/styles_device_defaults.xml
@@ -30,7 +30,7 @@
PLEASE READ
===============================================================
-->
-<resources>
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Widget Styles -->
<style name="Widget.DeviceDefault" parent="Widget.Material"/>
<style name="Widget.DeviceDefault.Button" parent="Widget.Material.Button"/>
@@ -285,22 +285,29 @@
<item name="fontFamily">@string/config_bodyFontFamily</item>
</style>
<style name="TextAppearance.DeviceDefault.Notification" parent="TextAppearance.Material.Notification">
- <item name="fontFamily">@string/config_bodyFontFamily</item>
+ <item name="fontFamily" android:featureFlag="android.app.notifications_redesign_fonts">variable-body-medium</item>
+ <item name="fontFamily" android:featureFlag="!android.app.notifications_redesign_fonts">@string/config_bodyFontFamily</item>
</style>
<style name="TextAppearance.DeviceDefault.Notification.Title" parent="TextAppearance.Material.Notification.Title">
- <item name="fontFamily">@string/config_headlineFontFamilyMedium</item>
+ <item name="fontFamily" android:featureFlag="android.app.notifications_redesign_fonts">variable-title-small-emphasized</item>
+ <item name="fontFamily" android:featureFlag="!android.app.notifications_redesign_fonts">@string/config_headlineFontFamilyMedium</item>
</style>
+ <!-- unused as of the 2025 redesign -->
<style name="TextAppearance.DeviceDefault.Notification.BigTitle" parent="TextAppearance.Material.Notification.BigTitle">
- <item name="fontFamily">@string/config_headlineFontFamilyMedium</item>
+ <item name="fontFamily" android:featureFlag="android.app.notifications_redesign_fonts">variable-title-small-emphasized</item>
+ <item name="fontFamily" android:featureFlag="!android.app.notifications_redesign_fonts">@string/config_headlineFontFamilyMedium</item>
</style>
<style name="TextAppearance.DeviceDefault.Notification.Reply" parent="TextAppearance.Material.Notification.Reply">
- <item name="fontFamily">@string/config_bodyFontFamily</item>
+ <item name="fontFamily" android:featureFlag="android.app.notifications_redesign_fonts">variable-body-medium</item>
+ <item name="fontFamily" android:featureFlag="!android.app.notifications_redesign_fonts">@string/config_bodyFontFamily</item>
</style>
<style name="TextAppearance.DeviceDefault.Notification.Info" parent="TextAppearance.Material.Notification.Info">
- <item name="fontFamily">@string/config_bodyFontFamily</item>
+ <item name="fontFamily" android:featureFlag="android.app.notifications_redesign_fonts">variable-body-small</item>
+ <item name="fontFamily" android:featureFlag="!android.app.notifications_redesign_fonts">@string/config_bodyFontFamily</item>
</style>
<style name="TextAppearance.DeviceDefault.Notification.Time" parent="TextAppearance.Material.Notification.Time">
- <item name="fontFamily">@string/config_bodyFontFamily</item>
+ <item name="fontFamily" android:featureFlag="android.app.notifications_redesign_fonts">variable-body-small</item>
+ <item name="fontFamily" android:featureFlag="!android.app.notifications_redesign_fonts">@string/config_bodyFontFamily</item>
</style>
<style name="TextAppearance.DeviceDefault.Widget" parent="TextAppearance.Material.Widget">
<item name="fontFamily">@string/config_bodyFontFamily</item>
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index b614259..4f714af 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -28,7 +28,7 @@
PLEASE READ
===============================================================
-->
-<resources>
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Preference styles -->
<eat-comment/>
@@ -474,13 +474,14 @@
<style name="TextAppearance.Material.Notification.Title">
<item name="textColor">@color/notification_primary_text_color_current</item>
- <item name="fontFamily">sans-serif-medium</item>
+ <item name="fontFamily" android:featureFlag="!android.app.notifications_redesign_fonts">sans-serif-medium</item>
<item name="textSize">@dimen/notification_title_text_size</item>
</style>
+ <!-- unused as of the 2025 redesign -->
<style name="TextAppearance.Material.Notification.BigTitle">
<item name="textColor">@color/notification_primary_text_color_current</item>
- <item name="fontFamily">sans-serif-medium</item>
+ <item name="fontFamily" android:featureFlag="!android.app.notifications_redesign_fonts">sans-serif-medium</item>
<item name="textSize">@dimen/notification_big_title_text_size</item>
</style>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 80e5721..762a1f9 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -533,6 +533,9 @@
<java-symbol type="bool" name="config_enableUdcSysfsUsbStateUpdate"/>
<java-symbol type="bool" name="config_enableSearchTileHideIllustrationInPrivateSpace"/>
+ <java-symbol type="color" name="shade_panel_fg" />
+ <java-symbol type="color" name="shade_panel_bg" />
+ <java-symbol type="color" name="shade_panel_scrim" />
<java-symbol type="color" name="surface_effect_0" />
<java-symbol type="color" name="surface_effect_1" />
<java-symbol type="color" name="surface_effect_2" />
@@ -2085,7 +2088,6 @@
<java-symbol type="bool" name="config_supportLongPressPowerWhenNonInteractive" />
<java-symbol type="bool" name="config_supportShortPressPowerWhenDefaultDisplayOn" />
<java-symbol type="bool" name="config_wimaxEnabled" />
- <java-symbol type="bool" name="show_ongoing_ime_switcher" />
<java-symbol type="color" name="config_defaultNotificationColor" />
<java-symbol type="color" name="decor_view_status_guard" />
<java-symbol type="color" name="decor_view_status_guard_light" />
@@ -2235,6 +2237,7 @@
<java-symbol type="string" name="car_mode_disable_notification_message" />
<java-symbol type="string" name="car_mode_disable_notification_title" />
<java-symbol type="string" name="chooser_wallpaper" />
+ <java-symbol type="string" name="config_preferredSystemImageEditor" />
<java-symbol type="string" name="config_systemImageEditor" />
<java-symbol type="string" name="config_datause_iface" />
<java-symbol type="string" name="config_activityRecognitionHardwarePackageName" />
@@ -4828,6 +4831,7 @@
<java-symbol type="bool" name="config_showUserSwitcherByDefault" />
<java-symbol type="bool" name="config_allowChangeUserSwitcherEnabled" />
<java-symbol type="bool" name="config_enableUserSwitcherUponUserCreation" />
+ <java-symbol type="bool" name="config_userSwitchingMustGoThroughLoginScreen" />
<!-- Set to true to make assistant show in front of the dream/screensaver. -->
<java-symbol type="bool" name="config_assistantOnTopOfDream"/>
@@ -5846,6 +5850,9 @@
screen. -->
<java-symbol type="bool" name="config_dragToMaximizeInDesktopMode" />
+ <!-- Whether the home screen should be shown behind freeform tasks in desktop mode. -->
+ <java-symbol type="bool" name="config_showHomeBehindDesktop" />
+
<!-- Frame rate compatibility value for Wallpaper -->
<java-symbol type="integer" name="config_wallpaperFrameRateCompatibility" />
@@ -5989,6 +5996,8 @@
<!-- Advanced Protection Service USB feature -->
<java-symbol type="string" name="usb_apm_usb_plugged_in_when_locked_notification_title" />
<java-symbol type="string" name="usb_apm_usb_plugged_in_when_locked_notification_text" />
+<java-symbol type="string" name="usb_apm_usb_plugged_in_when_locked_no_replug_notification_text" />
+ <java-symbol type="string" name="usb_apm_usb_plugged_in_when_locked_low_power_charge_notification_text" />
<java-symbol type="string" name="usb_apm_usb_suspicious_activity_notification_title" />
<java-symbol type="string" name="usb_apm_usb_suspicious_activity_notification_text" />
@@ -6009,4 +6018,7 @@
<java-symbol type="string" name="fingerprint_frr_notification_title" />
<java-symbol type="string" name="fingerprint_frr_notification_msg" />
+ <!-- Default name for newly configured tv profile. -->
+ <java-symbol type="string" name="config_configured_tv_profile_name" />
+
</resources>
diff --git a/core/sysprop/FoldLockBehaviorProperties.sysprop b/core/sysprop/FoldLockBehaviorProperties.sysprop
index 120e4bb..d337954 100644
--- a/core/sysprop/FoldLockBehaviorProperties.sysprop
+++ b/core/sysprop/FoldLockBehaviorProperties.sysprop
@@ -22,11 +22,3 @@
scope: Internal
access: Readonly
}
-
-prop {
- api_name: "fold_grace_period_enabled"
- type: Boolean
- prop_name: "persist.fold_grace_period_enabled"
- scope: Internal
- access: Readonly
-}
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index 339242d..b4b7781 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -1420,6 +1420,16 @@
android:resource="@xml/accessibility_shortcut_test_activity"/>
</activity>
+ <activity android:name="com.android.internal.widget.ActionBarContextViewActivity"
+ android:label="ActionBarContextViewActivity"
+ android:exported="true"
+ android:theme="@style/ActionBarContextViewTheme">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
+ </intent-filter>
+ </activity>
+
<!-- Activity-level metadata -->
<meta-data android:name="com.android.frameworks.coretests.isApp" android:value="true" />
<meta-data android:name="com.android.frameworks.coretests.string" android:value="foo" />
diff --git a/core/tests/coretests/res/values/styles.xml b/core/tests/coretests/res/values/styles.xml
index e7009d14..c53add6 100644
--- a/core/tests/coretests/res/values/styles.xml
+++ b/core/tests/coretests/res/values/styles.xml
@@ -84,5 +84,15 @@
<item name="android:fontFeatureSettings">\"smcp\"</item>
<item name="android:fontVariationSettings">\'wdth\' 150</item>
</style>
+ <style name="ActionBarContextViewTheme" parent="android:Theme.NoTitleBar">
+ <item name="android:actionModeStyle">@style/ActionBarContextViewActionModeTheme</item>
+ </style>
+ <style name="ActionBarContextViewActionModeTheme">
+ <item name="android:paddingLeft">10px</item>
+ <item name="android:paddingTop">20px</item>
+ <item name="android:paddingRight">30px</item>
+ <item name="android:paddingBottom">40px</item>
+ <item name="android:height">200px</item>
+ </style>
</resources>
diff --git a/core/tests/coretests/src/android/app/NotificationChannelTest.java b/core/tests/coretests/src/android/app/NotificationChannelTest.java
index b1d995a..32d508f 100644
--- a/core/tests/coretests/src/android/app/NotificationChannelTest.java
+++ b/core/tests/coretests/src/android/app/NotificationChannelTest.java
@@ -704,8 +704,6 @@
}
@Test
- @EnableFlags({Flags.FLAG_RESTRICT_AUDIO_ATTRIBUTES_MEDIA,
- Flags.FLAG_RESTRICT_AUDIO_ATTRIBUTES_CALL, Flags.FLAG_RESTRICT_AUDIO_ATTRIBUTES_ALARM})
public void testCopy() {
NotificationChannel original = new NotificationChannel("id", "name", 2);
original.setDescription("desc");
diff --git a/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java b/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java
index 21ab8fc..322098d 100644
--- a/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java
+++ b/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java
@@ -22,7 +22,6 @@
import static android.app.PropertyInvalidatedCache.MODULE_SYSTEM;
import static android.app.PropertyInvalidatedCache.MODULE_TEST;
import static android.app.PropertyInvalidatedCache.NonceStore.INVALID_NONCE_INDEX;
-import static com.android.internal.os.Flags.FLAG_APPLICATION_SHARED_MEMORY_ENABLED;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -561,7 +560,6 @@
// Verify the behavior of shared memory nonce storage. This does not directly test the cache
// storing nonces in shared memory.
- @RequiresFlagsEnabled(FLAG_APPLICATION_SHARED_MEMORY_ENABLED)
@Test
@DisabledOnRavenwood(reason = "PIC doesn't use SharedMemory on Ravenwood")
public void testSharedMemoryStorage() {
@@ -611,7 +609,6 @@
// Verify that the configured number of nonce slots is actually available. This test
// hard-codes the configured number of slots, which means that this test must be changed
// whenever the shared memory configuration changes.
- @RequiresFlagsEnabled(FLAG_APPLICATION_SHARED_MEMORY_ENABLED)
@Test
@DisabledOnRavenwood(reason = "PIC doesn't use SharedMemory on Ravenwood")
public void testSharedMemoryNonceConfig() {
diff --git a/core/tests/coretests/src/android/content/ContextTest.java b/core/tests/coretests/src/android/content/ContextTest.java
index 2505500..4ead370 100644
--- a/core/tests/coretests/src/android/content/ContextTest.java
+++ b/core/tests/coretests/src/android/content/ContextTest.java
@@ -29,6 +29,13 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
import android.app.ActivityThread;
import android.content.res.Configuration;
@@ -46,18 +53,22 @@
import android.platform.test.ravenwood.RavenwoodRule;
import android.view.Display;
import android.window.WindowTokenClient;
+import android.window.WindowTokenClientController;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
+import com.android.internal.util.GcUtils;
import com.android.window.flags.Flags;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.lang.ref.WeakReference;
+
/**
* Build/Install/Run:
* atest FrameworksCoreTests:ContextTest
@@ -331,4 +342,27 @@
assertWithMessage("ComponentCallbacks should delegate to SystemUiContext "
+ "if the flag is enabled.").that(callbacks.mConfiguration).isEqualTo(config);
}
+
+ @Test
+ @DisabledOnRavenwood(blockedBy = Context.class)
+ public void testSystemUiContextCleanUp() {
+ final WindowTokenClientController origController =
+ WindowTokenClientController.getInstance();
+ final WindowTokenClientController mockController = mock(WindowTokenClientController.class);
+ doReturn(true).when(mockController).attachToDisplayContent(any(), anyInt());
+ doNothing().when(mockController).detachIfNeeded(any());
+ WindowTokenClientController.overrideForTesting(mockController);
+
+ WeakReference<Context> windowContextRef = new WeakReference<>(
+ ActivityThread.currentActivityThread()
+ .createSystemUiContextForTesting(DEFAULT_DISPLAY));
+ final WindowTokenClient token =
+ (WindowTokenClient) windowContextRef.get().getWindowContextToken();
+
+ GcUtils.runGcAndFinalizersSync();
+
+ verify(mockController).detachIfNeeded(eq(token));
+
+ WindowTokenClientController.overrideForTesting(origController);
+ }
}
diff --git a/core/tests/coretests/src/android/os/IpcDataCacheTest.java b/core/tests/coretests/src/android/os/IpcDataCacheTest.java
index 8c97786..5559aed 100644
--- a/core/tests/coretests/src/android/os/IpcDataCacheTest.java
+++ b/core/tests/coretests/src/android/os/IpcDataCacheTest.java
@@ -25,7 +25,6 @@
import android.app.PropertyInvalidatedCache;
import android.app.PropertyInvalidatedCache.Args;
-import android.multiuser.Flags;
import android.platform.test.annotations.DisabledOnRavenwood;
import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
@@ -205,7 +204,6 @@
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_CACHING_DEVELOPMENT_IMPROVEMENTS)
public void testRemoteCallBypass() {
// A stand-in for the binder. The test verifies that calls are passed through to
diff --git a/core/tests/coretests/src/android/view/ContentRecordingSessionTest.java b/core/tests/coretests/src/android/view/ContentRecordingSessionTest.java
index f8800cb..4aa974ee 100644
--- a/core/tests/coretests/src/android/view/ContentRecordingSessionTest.java
+++ b/core/tests/coretests/src/android/view/ContentRecordingSessionTest.java
@@ -27,6 +27,7 @@
import android.os.Binder;
import android.os.IBinder;
import android.os.Parcel;
+import android.os.Process;
import android.platform.test.annotations.Presubmit;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -47,6 +48,7 @@
public class ContentRecordingSessionTest {
private static final int DISPLAY_ID = 1;
private static final int TASK_ID = 123;
+ private static final int UID = 1234;
private static final IBinder WINDOW_TOKEN = new Binder("DisplayContentWindowToken");
@Test
@@ -147,6 +149,51 @@
}
@Test
+ public void testIsValid_overlaySession() {
+ // Canonical task session.
+ ContentRecordingSession overlaySession = ContentRecordingSession.createOverlaySession(
+ DISPLAY_ID, UID);
+ overlaySession.setVirtualDisplayId(DEFAULT_DISPLAY);
+ assertThat(ContentRecordingSession.isValid(overlaySession)).isTrue();
+
+ // Different display values.
+ ContentRecordingSession overlaySession0 = ContentRecordingSession.createOverlaySession(
+ INVALID_DISPLAY, UID);
+ overlaySession0.setVirtualDisplayId(DEFAULT_DISPLAY);
+ assertThat(ContentRecordingSession.isValid(overlaySession0)).isFalse();
+
+ ContentRecordingSession overlaySession1 = ContentRecordingSession.createOverlaySession(
+ DISPLAY_ID, UID);
+ overlaySession1.setVirtualDisplayId(DEFAULT_DISPLAY);
+ overlaySession1.setDisplayToRecord(INVALID_DISPLAY);
+ assertThat(ContentRecordingSession.isValid(overlaySession1)).isFalse();
+
+ ContentRecordingSession overlaySession2 = ContentRecordingSession.createOverlaySession(
+ INVALID_DISPLAY, UID);
+ overlaySession2.setVirtualDisplayId(DEFAULT_DISPLAY);
+ overlaySession2.setDisplayToRecord(DISPLAY_ID);
+ assertThat(ContentRecordingSession.isValid(overlaySession2)).isTrue();
+
+ // UID values.
+ ContentRecordingSession overlaySession3 = ContentRecordingSession.createOverlaySession(
+ DISPLAY_ID, Process.INVALID_UID);
+ overlaySession3.setVirtualDisplayId(DEFAULT_DISPLAY);
+ assertThat(ContentRecordingSession.isValid(overlaySession3)).isFalse();
+
+ ContentRecordingSession overlaySession4 = ContentRecordingSession.createOverlaySession(
+ DISPLAY_ID, UID);
+ overlaySession4.setVirtualDisplayId(DEFAULT_DISPLAY);
+ overlaySession4.setRecordingOwnerUid(Process.INVALID_UID);
+ assertThat(ContentRecordingSession.isValid(overlaySession4)).isFalse();
+
+ ContentRecordingSession overlaySession5 = ContentRecordingSession.createOverlaySession(
+ DISPLAY_ID, Process.INVALID_UID);
+ overlaySession5.setVirtualDisplayId(DEFAULT_DISPLAY);
+ overlaySession5.setRecordingOwnerUid(UID);
+ assertThat(ContentRecordingSession.isValid(overlaySession5)).isTrue();
+ }
+
+ @Test
public void testIsProjectionOnSameDisplay() {
assertThat(ContentRecordingSession.isProjectionOnSameDisplay(null, null)).isFalse();
ContentRecordingSession session = ContentRecordingSession.createDisplaySession(
diff --git a/core/tests/coretests/src/android/view/InsetsStateTest.java b/core/tests/coretests/src/android/view/InsetsStateTest.java
index 1144ee1..2fc91b8 100644
--- a/core/tests/coretests/src/android/view/InsetsStateTest.java
+++ b/core/tests/coretests/src/android/view/InsetsStateTest.java
@@ -20,6 +20,7 @@
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.view.InsetsSource.FLAG_FORCE_CONSUMING;
import static android.view.InsetsSource.FLAG_FORCE_CONSUMING_OPAQUE_CAPTION_BAR;
+import static android.view.InsetsSource.FLAG_INVALID;
import static android.view.InsetsSource.ID_IME;
import static android.view.InsetsSource.SIDE_BOTTOM;
import static android.view.InsetsSource.SIDE_TOP;
@@ -354,22 +355,40 @@
state1.addSource(new InsetsSource(ID_CAPTION_BAR, captionBar()).setFrame(0, 0, 0, 5));
assertFalse(state1.equals(
- state2, false /* excludesCaptionBar */, false /* excludesInvisibleIme */));
+ state2,
+ false /* excludesCaptionBar */,
+ false /* excludesInvisibleIme */,
+ false /* excludesInvalidSource */));
assertTrue(state1.equals(
- state2, true /* excludesCaptionBar */, false /* excludesInvisibleIme */));
+ state2,
+ true /* excludesCaptionBar */,
+ false /* excludesInvisibleIme */,
+ false /* excludesInvalidSource */));
state2.addSource(new InsetsSource(ID_CAPTION_BAR, captionBar()).setFrame(0, 0, 0, 10));
assertFalse(state1.equals(
- state2, false /* excludesCaptionBar */, false /* excludesInvisibleIme */));
+ state2,
+ false /* excludesCaptionBar */,
+ false /* excludesInvisibleIme */,
+ false /* excludesInvalidSource */));
assertTrue(state1.equals(
- state2, true /* excludesCaptionBar */, false /* excludesInvisibleIme */));
+ state2,
+ true /* excludesCaptionBar */,
+ false /* excludesInvisibleIme */,
+ false /* excludesInvalidSource */));
state1.addSource(new InsetsSource(ID_STATUS_BAR, statusBars()));
state2.addSource(new InsetsSource(ID_STATUS_BAR, statusBars()));
assertFalse(state1.equals(
- state2, false /* excludesCaptionBar */, false /* excludesInvisibleIme */));
+ state2,
+ false /* excludesCaptionBar */,
+ false /* excludesInvisibleIme */,
+ false /* excludesInvalidSource */));
assertTrue(state1.equals(
- state2, true /* excludesCaptionBar */, false /* excludesInvisibleIme */));
+ state2,
+ true /* excludesCaptionBar */,
+ false /* excludesInvisibleIme */,
+ false /* excludesInvalidSource */));
}
@Test
@@ -380,22 +399,86 @@
final InsetsSource imeSource1 = new InsetsSource(ID_IME, ime()).setVisible(true);
state1.addSource(imeSource1);
assertFalse(state1.equals(
- state2, false /* excludesCaptionBar */, false /* excludesInvisibleIme */));
+ state2,
+ false /* excludesCaptionBar */,
+ false /* excludesInvisibleIme */,
+ false /* excludesInvalidSource */));
assertFalse(state1.equals(
- state2, false /* excludesCaptionBar */, true /* excludesInvisibleIme */));
+ state2,
+ false /* excludesCaptionBar */,
+ true /* excludesInvisibleIme */,
+ false /* excludesInvalidSource */));
imeSource1.setVisible(false);
assertFalse(state1.equals(
- state2, false /* excludesCaptionBar */, false /* excludesInvisibleIme */));
+ state2,
+ false /* excludesCaptionBar */,
+ false /* excludesInvisibleIme */,
+ false /* excludesInvalidSource */));
assertTrue(state1.equals(
- state2, false /* excludesCaptionBar */, true /* excludesInvisibleIme */));
+ state2,
+ false /* excludesCaptionBar */,
+ true /* excludesInvisibleIme */,
+ false /* excludesInvalidSource */));
final InsetsSource imeSource2 = new InsetsSource(ID_IME, ime()).setFrame(0, 0, 0, 10);
state2.addSource(imeSource2);
assertFalse(state1.equals(
- state2, false /* excludesCaptionBar */, false /* excludesInvisibleIme */));
+ state2,
+ false /* excludesCaptionBar */,
+ false /* excludesInvisibleIme */,
+ false /* excludesInvalidSource */));
assertTrue(state1.equals(
- state2, false /* excludesCaptionBar */, true /* excludesInvisibleIme */));
+ state2,
+ false /* excludesCaptionBar */,
+ true /* excludesInvisibleIme */,
+ false /* excludesInvalidSource */));
+ }
+
+ @Test
+ public void testEquals_excludesInvalidSource() {
+ final InsetsState state1 = new InsetsState();
+ final InsetsState state2 = new InsetsState();
+
+ final InsetsSource imeSource1 = new InsetsSource(ID_IME, ime());
+ state1.addSource(imeSource1);
+ assertFalse(state1.equals(
+ state2,
+ false /* excludesCaptionBar */,
+ false /* excludesInvisibleIme */,
+ false /* excludesInvalidSource */));
+ assertFalse(state1.equals(
+ state2,
+ false /* excludesCaptionBar */,
+ false /* excludesInvisibleIme */,
+ true /* excludesInvalidSource */));
+
+ imeSource1.setFlags(FLAG_INVALID);
+ assertFalse(state1.equals(
+ state2,
+ false /* excludesCaptionBar */,
+ false /* excludesInvisibleIme */,
+ false /* excludesInvalidSource */));
+ assertTrue(state1.equals(
+ state2,
+ false /* excludesCaptionBar */,
+ false /* excludesInvisibleIme */,
+ true /* excludesInvalidSource */));
+
+ final InsetsSource imeSource2 = new InsetsSource(ID_IME, ime())
+ .setFrame(0, 0, 0, 10)
+ .setFlags(FLAG_INVALID);
+ state2.addSource(imeSource2);
+ assertFalse(state1.equals(
+ state2,
+ false /* excludesCaptionBar */,
+ false /* excludesInvisibleIme */,
+ false /* excludesInvalidSource */));
+ assertTrue(state1.equals(
+ state2,
+ false /* excludesCaptionBar */,
+ false /* excludesInvisibleIme */,
+ true /* excludesInvalidSource */));
}
@Test
diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java
index 1b7805c..5d178ac 100644
--- a/core/tests/coretests/src/android/view/ViewRootImplTest.java
+++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java
@@ -1548,7 +1548,7 @@
@Test
@EnableFlags(FLAG_FORCE_INVERT_COLOR)
- public void determineForceDarkType_isLightThemeAndNotLightBackground_returnsNone()
+ public void determineForceDarkType_isLightThemeAndNotLightBackground_returnsForceInvertColorDark()
throws Exception {
// Set up configurations for force invert color
waitForSystemNightModeActivated(true);
@@ -1557,7 +1557,8 @@
setUpViewAttributes(/* isLightTheme= */ true, /* isLightBackground = */ false);
TestUtils.waitUntil("Waiting for ForceDarkType to be ready",
- () -> (mViewRootImpl.determineForceDarkType() == ForceDarkType.NONE));
+ () -> (mViewRootImpl.determineForceDarkType()
+ == ForceDarkType.FORCE_INVERT_COLOR_DARK));
}
@Test
diff --git a/core/tests/coretests/src/android/view/WindowManagerWrapperTest.kt b/core/tests/coretests/src/android/view/WindowManagerWrapperTest.kt
new file mode 100644
index 0000000..92d0111
--- /dev/null
+++ b/core/tests/coretests/src/android/view/WindowManagerWrapperTest.kt
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.view
+
+import android.platform.test.annotations.Presubmit
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Tests for the [WindowManagerWrapper].
+ *
+ * Build/Install/Run:
+ * atest FrameworksCoreTests:WindowManagerWrapperTest
+ */
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+@Presubmit
+class WindowManagerWrapperTest {
+
+ /**
+ * Tests that all default methods from [WindowManager] are implemented.
+ */
+ @Test
+ fun testWindowManagerWrapperImplementation() {
+ val windowManagerInterface = WindowManager::class.java
+ val wmInterfaceMethods = windowManagerInterface.methods
+ val windowManagerWrapperClass = WindowManagerWrapper::class.java
+ val wrapperMethodsFromWm = windowManagerWrapperClass.declaredMethods
+ .filter { m -> windowManagerInterface.isAssignableFrom(m.declaringClass) }
+ .map { m -> m.name }
+ .toSet()
+
+ // Only checks the default methods in WM interface. Missing implementation of non-default
+ // methods should be caught at compile time.
+ val wmDefaultMethods = wmInterfaceMethods
+ .filter { m -> m.isDefault }
+ .map { m -> m.name }
+ .toList()
+
+ for (defaultMethod in wmDefaultMethods) {
+ assertThat(wrapperMethodsFromWm).contains(defaultMethod)
+ }
+ }
+}
diff --git a/core/tests/coretests/src/android/view/input/LetterboxScrollProcessorTest.java b/core/tests/coretests/src/android/view/input/LetterboxScrollProcessorTest.java
index 7b10b26..aa0d72f 100644
--- a/core/tests/coretests/src/android/view/input/LetterboxScrollProcessorTest.java
+++ b/core/tests/coretests/src/android/view/input/LetterboxScrollProcessorTest.java
@@ -30,6 +30,7 @@
import android.os.Handler;
import android.os.Looper;
import android.platform.test.annotations.Presubmit;
+import android.view.InputEvent;
import android.view.MotionEvent;
import android.view.ViewConfiguration;
@@ -81,7 +82,7 @@
/* startX= */ 0f, /* startY= */ 0f);
// Get processed events from Letterbox Scroll Processor.
- final List<MotionEvent> processedEvents = processMotionEvents(tapGestureEvents);
+ final List<InputEvent> processedEvents = processMotionEvents(tapGestureEvents);
// Ensure no changes are made to events after processing - event locations should not be
// adjusted because the gesture started in the app's bounds (for all gestures).
@@ -99,7 +100,7 @@
dialogBounds);
// Get processed events from Letterbox Scroll Processor.
- List<MotionEvent> processedEvents = processMotionEvents(tapGestureEvents);
+ List<InputEvent> processedEvents = processMotionEvents(tapGestureEvents);
// Ensure no changes are made to events after processing - the event should be forwarded as
// normal.
@@ -115,7 +116,7 @@
/* startX= */ -100f, /* startY= */ -100f);
// Get processed events from Letterbox Scroll Processor.
- final List<MotionEvent> processedEvents = processMotionEvents(tapGestureEvents);
+ final List<InputEvent> processedEvents = processMotionEvents(tapGestureEvents);
// All events should be ignored since it was a non-scroll gesture and out of bounds.
assertEquals(0, processedEvents.size());
@@ -128,7 +129,7 @@
/* startX= */ 0f, /* startY= */ 0f);
// Get processed events from Letterbox Scroll Processor.
- final List<MotionEvent> processedEvents = processMotionEvents(scrollGestureEvents);
+ final List<InputEvent> processedEvents = processMotionEvents(scrollGestureEvents);
// Ensure no changes are made to events after processing - event locations should not be
// adjusted because the gesture started in the app's bounds (for all gestures).
@@ -144,7 +145,7 @@
/* startX= */ 390f, /* startY= */ 790f);
// Get processed events from Letterbox Scroll Processor.
- final List<MotionEvent> processedEvents = processMotionEvents(scrollGestureEvents);
+ final List<InputEvent> processedEvents = processMotionEvents(scrollGestureEvents);
// Ensure no changes are made to events after processing - event locations should not be
// adjusted because the gesture started in the app's bounds (for all gestures), even if it
@@ -161,18 +162,18 @@
/* startX= */ -100f, /* startY= */ 0f);
// Get processed events from Letterbox Scroll Processor.
- List<MotionEvent> processedEvents = processMotionEvents(scrollGestureEvents);
+ List<InputEvent> processedEvents = processMotionEvents(scrollGestureEvents);
// When a scroll occurs outside bounds: once detected as a scroll, the ACTION_DOWN is
// expected to be received again but with an offset so it is over the app's bounds.
// Ensure offset ACTION_DOWN is first event received.
- MotionEvent firstProcessedEvent = processedEvents.getFirst();
+ MotionEvent firstProcessedEvent = (MotionEvent) processedEvents.getFirst();
assertEquals(ACTION_DOWN, firstProcessedEvent.getAction());
assertEquals(0, firstProcessedEvent.getX(), EPSILON);
assertEquals(0, firstProcessedEvent.getY(), EPSILON);
// Ensure this event is not finished (because it was generated by LetterboxScrollProcessor).
- assertNull(mLetterboxScrollProcessor.processMotionEventBeforeFinish(firstProcessedEvent));
+ assertNull(mLetterboxScrollProcessor.processInputEventBeforeFinish(firstProcessedEvent));
}
@Test
@@ -182,7 +183,7 @@
/* startX= */ -100f, /* startY= */ 0f);
// Get processed events from Letterbox Scroll Processor.
- final List<MotionEvent> processedEvents = processMotionEvents(scrollGestureEvents);
+ final List<InputEvent> processedEvents = processMotionEvents(scrollGestureEvents);
// When a scroll occurs outside bounds: once detected as a scroll, an offset ACTION_DOWN is
// placed and then the rest of the gesture is offset also. Some ACTION_MOVE events may be
@@ -198,12 +199,12 @@
}
@NonNull
- private List<MotionEvent> processMotionEvents(@NonNull List<MotionEvent> motionEvents) {
- final List<MotionEvent> processedEvents = new ArrayList<>();
+ private List<InputEvent> processMotionEvents(@NonNull List<MotionEvent> motionEvents) {
+ final List<InputEvent> processedEvents = new ArrayList<>();
for (MotionEvent motionEvent : motionEvents) {
MotionEvent clonedEvent = MotionEvent.obtain(motionEvent);
- List<MotionEvent> letterboxScrollCompatEvents =
- mLetterboxScrollProcessor.processMotionEvent(clonedEvent);
+ List<InputEvent> letterboxScrollCompatEvents =
+ mLetterboxScrollProcessor.processInputEventForCompatibility(clonedEvent);
if (letterboxScrollCompatEvents == null) {
// Use original event if null returned (no adjustments made).
processedEvents.add(clonedEvent);
@@ -284,35 +285,38 @@
private void assertEventLocationsAreNotAdjusted(
@NonNull List<MotionEvent> originalEvents,
- @NonNull List<MotionEvent> processedEvents) {
+ @NonNull List<InputEvent> processedEvents) {
assertEquals("MotionEvent arrays are not the same size",
originalEvents.size(), processedEvents.size());
for (int i = 0; i < originalEvents.size(); i++) {
assertEquals("X coordinates was unexpectedly adjusted at index " + i,
- originalEvents.get(i).getX(), processedEvents.get(i).getX(), EPSILON);
+ originalEvents.get(i).getX(), ((MotionEvent) processedEvents.get(i)).getX(),
+ EPSILON);
assertEquals("Y coordinates was unexpectedly adjusted at index " + i,
- originalEvents.get(i).getY(), processedEvents.get(i).getY(), EPSILON);
+ originalEvents.get(i).getY(), ((MotionEvent) processedEvents.get(i)).getY(),
+ EPSILON);
}
}
private void assertXCoordinatesAdjustedToZero(
@NonNull List<MotionEvent> originalEvents,
- @NonNull List<MotionEvent> processedEvents) {
+ @NonNull List<InputEvent> processedEvents) {
assertEquals("MotionEvent arrays are not the same size",
originalEvents.size(), processedEvents.size());
for (int i = 0; i < originalEvents.size(); i++) {
assertEquals("X coordinate was not adjusted to 0 at index " + i,
- 0, processedEvents.get(i).getX(), EPSILON);
+ 0, ((MotionEvent) processedEvents.get(i)).getX(), EPSILON);
assertEquals("Y coordinate was unexpectedly adjusted at index " + i,
- originalEvents.get(i).getY(), processedEvents.get(i).getY(), EPSILON);
+ originalEvents.get(i).getY(), ((MotionEvent) processedEvents.get(i)).getY(),
+ EPSILON);
}
}
- private void assertMotionEventsShouldBeFinished(@NonNull List<MotionEvent> processedEvents) {
- for (MotionEvent processedEvent : processedEvents) {
- assertNotNull(mLetterboxScrollProcessor.processMotionEventBeforeFinish(processedEvent));
+ private void assertMotionEventsShouldBeFinished(@NonNull List<InputEvent> processedEvents) {
+ for (InputEvent processedEvent : processedEvents) {
+ assertNotNull(mLetterboxScrollProcessor.processInputEventBeforeFinish(processedEvent));
}
}
}
diff --git a/core/tests/coretests/src/android/view/input/StylusButtonCompatibilityTest.kt b/core/tests/coretests/src/android/view/input/StylusButtonCompatibilityTest.kt
new file mode 100644
index 0000000..d7ee5cf
--- /dev/null
+++ b/core/tests/coretests/src/android/view/input/StylusButtonCompatibilityTest.kt
@@ -0,0 +1,98 @@
+/*
+* Copyright 2025 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package android.view.input
+
+import android.content.Context
+import android.os.Build
+import android.os.SystemClock
+import android.platform.test.annotations.Presubmit
+import android.view.MotionEvent
+import androidx.test.filters.SmallTest
+import androidx.test.platform.app.InstrumentationRegistry
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertNotNull
+import org.junit.Assert.assertTrue
+import org.junit.Before
+import org.junit.Test
+
+/**
+ * Tests for [StylusButtonCompatibility].
+ *
+ * Build/Install/Run:
+ * atest FrameworksCoreTests:StylusButtonCompatibilityTest
+ */
+@SmallTest
+@Presubmit
+class StylusButtonCompatibilityTest {
+ private lateinit var stylusButtonCompatibility: StylusButtonCompatibility
+ private lateinit var context: Context
+
+ @Before
+ fun setUp() {
+ context = InstrumentationRegistry.getInstrumentation().targetContext
+ stylusButtonCompatibility = StylusButtonCompatibility()
+ }
+
+ @Test
+ fun targetSdkMCompatibilityNotNeeded() {
+ context.getApplicationInfo().targetSdkVersion = Build.VERSION_CODES.M
+
+ assertFalse(StylusButtonCompatibility.isCompatibilityNeeded(context))
+ }
+
+ @Test
+ fun targetSdkLCompatibilityNotNeeded() {
+ context.getApplicationInfo().targetSdkVersion = Build.VERSION_CODES.LOLLIPOP_MR1
+
+ assertTrue(StylusButtonCompatibility.isCompatibilityNeeded(context))
+ }
+
+ @Test
+ fun primaryStylusButtonAddsSecondaryButton() {
+ val event = MotionEvent.obtain(
+ SystemClock.uptimeMillis(), SystemClock.uptimeMillis(),
+ MotionEvent.ACTION_BUTTON_PRESS, /* x= */ 100f, /* y= */ 200f, /* metaState= */ 0
+ )
+ event.buttonState = MotionEvent.BUTTON_STYLUS_PRIMARY
+
+ val result = stylusButtonCompatibility.processInputEventForCompatibility(event)
+
+ assertNotNull(result)
+ assertEquals(
+ MotionEvent.BUTTON_SECONDARY or MotionEvent.BUTTON_STYLUS_PRIMARY,
+ (result as MotionEvent).buttonState
+ )
+ }
+
+ @Test
+ fun secondaryStylusButtonAddsTertiaryButton() {
+ val event = MotionEvent.obtain(
+ SystemClock.uptimeMillis(), SystemClock.uptimeMillis(),
+ MotionEvent.ACTION_BUTTON_PRESS, /* x= */ 100f, /* y= */ 200f, /* metaState= */ 0
+ )
+ event.buttonState = MotionEvent.BUTTON_STYLUS_SECONDARY
+
+ val result = stylusButtonCompatibility.processInputEventForCompatibility(event)
+
+ assertNotNull(result)
+ assertEquals(
+ MotionEvent.BUTTON_TERTIARY or MotionEvent.BUTTON_STYLUS_SECONDARY,
+ (result as MotionEvent).buttonState
+ )
+ }
+}
diff --git a/core/tests/coretests/src/android/widget/focus/RequestFocus.java b/core/tests/coretests/src/android/widget/focus/RequestFocus.java
index 5042efd..fc496a7 100644
--- a/core/tests/coretests/src/android/widget/focus/RequestFocus.java
+++ b/core/tests/coretests/src/android/widget/focus/RequestFocus.java
@@ -35,6 +35,7 @@
// bottom right button starts with the focus.
final Button bottomRightButton = findViewById(R.id.bottomRightButton);
+ bottomRightButton.setFocusableInTouchMode(true);
bottomRightButton.requestFocus();
bottomRightButton.setText("I should have focus");
}
diff --git a/core/tests/coretests/src/android/widget/focus/RequestFocusTest.java b/core/tests/coretests/src/android/widget/focus/RequestFocusTest.java
index bc770c5..d022812 100644
--- a/core/tests/coretests/src/android/widget/focus/RequestFocusTest.java
+++ b/core/tests/coretests/src/android/widget/focus/RequestFocusTest.java
@@ -16,72 +16,80 @@
package android.widget.focus;
+import static org.junit.Assert.*;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import android.os.Handler;
-import android.test.ActivityInstrumentationTestCase2;
import android.util.AndroidRuntimeException;
+import android.view.KeyEvent;
import android.view.View;
import android.view.View.OnFocusChangeListener;
-import android.view.ViewTreeObserver.OnGlobalFocusChangeListener;
import android.widget.Button;
-import androidx.test.annotation.UiThreadTest;
+import androidx.test.ext.junit.rules.ActivityScenarioRule;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.LargeTest;
import androidx.test.filters.MediumTest;
+import androidx.test.platform.app.InstrumentationRegistry;
import com.android.frameworks.coretests.R;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
import org.mockito.InOrder;
/**
* {@link RequestFocusTest} is set up to exercise cases where the views that
* have focus become invisible or GONE.
*/
-public class RequestFocusTest extends ActivityInstrumentationTestCase2<RequestFocus> {
-
+@RunWith(AndroidJUnit4.class)
+public class RequestFocusTest {
private Button mTopLeftButton;
private Button mBottomLeftButton;
private Button mTopRightButton;
private Button mBottomRightButton;
private Handler mHandler;
- public RequestFocusTest() {
- super(RequestFocus.class);
- }
+ @Rule
+ public ActivityScenarioRule<RequestFocus> activityRule =
+ new ActivityScenarioRule<>(RequestFocus.class);
- @Override
- public void setUp() throws Exception {
- super.setUp();
+ @Before
+ public void setViewFieldsAndForceNonTouchMode() throws Exception {
+ activityRule.getScenario().onActivity(a -> {
+ mHandler = a.getHandler();
+ mTopLeftButton = (Button) a.findViewById(R.id.topLeftButton);
+ mBottomLeftButton = (Button) a.findViewById(R.id.bottomLeftButton);
+ mTopRightButton = (Button) a.findViewById(R.id.topRightButton);
+ mBottomRightButton = (Button) a.findViewById(R.id.bottomRightButton);
- final RequestFocus a = getActivity();
- mHandler = a.getHandler();
- mTopLeftButton = (Button) a.findViewById(R.id.topLeftButton);
- mBottomLeftButton = (Button) a.findViewById(R.id.bottomLeftButton);
- mTopRightButton = (Button) a.findViewById(R.id.topRightButton);
- mBottomRightButton = (Button) a.findViewById(R.id.bottomRightButton);
- }
+ assertNotNull(mHandler);
+ assertNotNull(mTopLeftButton);
+ assertNotNull(mTopRightButton);
+ assertNotNull(mBottomLeftButton);
+ assertNotNull(mBottomRightButton);
+ assertTrue("requestFocus() should work from onCreate.", mBottomRightButton.hasFocus());
+ });
- // Test that setUp did what we expect it to do. These asserts
- // can't go in SetUp, or the test will hang.
- @MediumTest
- public void testSetUpConditions() throws Exception {
- assertNotNull(mHandler);
- assertNotNull(mTopLeftButton);
- assertNotNull(mTopRightButton);
- assertNotNull(mBottomLeftButton);
- assertNotNull(mBottomRightButton);
- assertTrue("requestFocus() should work from onCreate.", mBottomRightButton.hasFocus());
+ // Force non-touch mode by sending a key.
+ android.app.Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+ instrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_DPAD_DOWN);
+ instrumentation.waitForIdleSync(); // Wait for UI thread to be idle
}
// Test that a posted requestFocus works.
+ @Test
@LargeTest
public void testPostedRequestFocus() throws Exception {
- mHandler.post(new Runnable() { public void run() {
- mBottomLeftButton.requestFocus();
- }});
- synchronized(this) {
+ mHandler.post(new Runnable() {
+ public void run() {
+ mBottomLeftButton.requestFocus();
+ }
+ });
+ synchronized (this) {
try {
wait(500);
} catch (InterruptedException e) {
@@ -92,6 +100,7 @@
}
// Test that a requestFocus from the wrong thread fails.
+ @Test
@MediumTest
public void testWrongThreadRequestFocusFails() throws Exception {
try {
@@ -100,7 +109,7 @@
} catch (AndroidRuntimeException e) {
// Expected. The actual exception is not public, so we can't catch it.
assertEquals("android.view.ViewRootImpl$CalledFromWrongThreadException",
- e.getClass().getName());
+ e.getClass().getName());
}
}
@@ -112,43 +121,41 @@
*
* @throws Exception If an error occurs.
*/
- @UiThreadTest
- public void testOnFocusChangeCallbackOrderWhenClearingFocusOfFirstFocusable()
- throws Exception {
- // Get the first focusable.
- Button clearingFocusButton = mTopLeftButton;
- Button gainingFocusButton = mTopLeftButton;
+ @Test
+ @LargeTest
+ public void testOnFocusChangeCallbackOrderWhenClearingFocusOfFirstFocusable() throws Exception {
+ activityRule.getScenario().onActivity(a -> {
+ // Get the first focusable.
+ Button clearingFocusButton = mTopLeftButton;
+ Button gainingFocusButton = mTopLeftButton;
- // Make sure that the clearing focus View is the first focusable.
- View focusCandidate = clearingFocusButton.getRootView().getParent().focusSearch(null,
- View.FOCUS_FORWARD);
- assertSame("The clearing focus button is the first focusable.",
- clearingFocusButton, focusCandidate);
- assertSame("The gaining focus button is the first focusable.",
- gainingFocusButton, focusCandidate);
+ // Make sure that the clearing focus View is the first focusable.
+ View focusCandidate = clearingFocusButton.getRootView().getParent().focusSearch(
+ null, View.FOCUS_FORWARD);
+ assertSame("The clearing focus button is the first focusable.", clearingFocusButton,
+ focusCandidate);
+ assertSame("The gaining focus button is the first focusable.", gainingFocusButton,
+ focusCandidate);
- // Focus the clearing focus button.
- clearingFocusButton.requestFocus();
- assertTrue(clearingFocusButton.hasFocus());
+ // Focus the clearing focus button.
+ clearingFocusButton.requestFocus();
+ assertTrue(clearingFocusButton.hasFocus());
- // Register the invocation order checker.
- CombinedListeners mock = mock(CombinedListeners.class);
- clearingFocusButton.setOnFocusChangeListener(mock);
- gainingFocusButton.setOnFocusChangeListener(mock);
- clearingFocusButton.getViewTreeObserver().addOnGlobalFocusChangeListener(mock);
+ // Register the invocation order checker.
+ OnFocusChangeListener mock = mock(OnFocusChangeListener.class);
+ clearingFocusButton.setOnFocusChangeListener(mock);
+ gainingFocusButton.setOnFocusChangeListener(mock);
- // Try to clear focus.
- clearingFocusButton.clearFocus();
+ // Try to clear focus.
+ clearingFocusButton.clearFocus();
- // Check that no callback was invoked since focus did not move.
- InOrder inOrder = inOrder(mock);
- inOrder.verify(mock).onFocusChange(clearingFocusButton, false);
- inOrder.verify(mock).onGlobalFocusChanged(clearingFocusButton, gainingFocusButton);
- inOrder.verify(mock).onFocusChange(gainingFocusButton, true);
+ // Check that no callback was invoked since focus did not move.
+ InOrder inOrder = inOrder(mock);
+ inOrder.verify(mock).onFocusChange(clearingFocusButton, false);
+ inOrder.verify(mock).onFocusChange(gainingFocusButton, true);
+ });
}
- public interface CombinedListeners extends OnFocusChangeListener, OnGlobalFocusChangeListener {}
-
/**
* This tests check whether the on focus change callbacks are invoked in
* the proper order when a View loses focus and the framework gives it to
@@ -156,37 +163,40 @@
*
* @throws Exception
*/
- @UiThreadTest
+ @Test
+ @LargeTest
public void testOnFocusChangeCallbackOrderWhenClearingFocusOfNotFirstFocusable()
throws Exception {
- Button clearingFocusButton = mTopRightButton;
- Button gainingFocusButton = mTopLeftButton;
+ activityRule.getScenario().onActivity(a -> {
+ Button clearingFocusButton = mTopRightButton;
+ Button gainingFocusButton = mTopLeftButton;
- // Make sure that the clearing focus View is not the first focusable.
- View focusCandidate = clearingFocusButton.getRootView().getParent().focusSearch(null,
- View.FOCUS_FORWARD);
- assertNotSame("The clearing focus button is not the first focusable.",
- clearingFocusButton, focusCandidate);
- assertSame("The gaining focus button is the first focusable.",
- gainingFocusButton, focusCandidate);
+ // Make sure that the clearing focus View is not the first focusable.
+ View focusCandidate = clearingFocusButton.getRootView().getParent().focusSearch(
+ null, View.FOCUS_FORWARD);
- // Focus the clearing focus button.
- clearingFocusButton.requestFocus();
- assertTrue(clearingFocusButton.hasFocus());
+ assertNotSame("The clearing focus button is not the first focusable.",
+ clearingFocusButton, focusCandidate);
+ assertSame("The gaining focus button is the first focusable.", gainingFocusButton,
+ focusCandidate);
- // Register the invocation order checker.
- CombinedListeners mock = mock(CombinedListeners.class);
- clearingFocusButton.setOnFocusChangeListener(mock);
- gainingFocusButton.setOnFocusChangeListener(mock);
- clearingFocusButton.getViewTreeObserver().addOnGlobalFocusChangeListener(mock);
+ // Focus the clearing focus button.
+ clearingFocusButton.requestFocus();
+ assertTrue(clearingFocusButton.hasFocus());
- // Try to clear focus.
- clearingFocusButton.clearFocus();
+ // Register the invocation order checker.
+ OnFocusChangeListener mock = mock(OnFocusChangeListener.class);
+ clearingFocusButton.setOnFocusChangeListener(mock);
+ gainingFocusButton.setOnFocusChangeListener(mock);
- // Check that no callback was invoked since focus did not move.
- InOrder inOrder = inOrder(mock);
- inOrder.verify(mock).onFocusChange(clearingFocusButton, false);
- inOrder.verify(mock).onGlobalFocusChanged(clearingFocusButton, gainingFocusButton);
- inOrder.verify(mock).onFocusChange(gainingFocusButton, true);
+ // Try to clear focus.
+ clearingFocusButton.clearFocus();
+
+ // Check that no callback was invoked since focus did not move.
+ InOrder inOrder = inOrder(mock);
+ inOrder.verify(mock).onFocusChange(clearingFocusButton, false);
+
+ inOrder.verify(mock).onFocusChange(gainingFocusButton, true);
+ });
}
}
diff --git a/core/tests/coretests/src/android/window/ConfigurationChangeSettingTest.kt b/core/tests/coretests/src/android/window/ConfigurationChangeSettingTest.kt
index 1234a820..346e850 100644
--- a/core/tests/coretests/src/android/window/ConfigurationChangeSettingTest.kt
+++ b/core/tests/coretests/src/android/window/ConfigurationChangeSettingTest.kt
@@ -18,10 +18,7 @@
import android.os.Parcel
import android.os.Parcelable
-import android.platform.test.annotations.DisableFlags
-import android.platform.test.annotations.EnableFlags
import android.platform.test.annotations.Presubmit
-import android.platform.test.flag.junit.SetFlagsRule
import android.view.Display.DEFAULT_DISPLAY
import android.window.ConfigurationChangeSetting.SETTING_TYPE_UNKNOWN
import android.window.ConfigurationChangeSetting.SETTING_TYPE_DISPLAY_DENSITY
@@ -30,12 +27,10 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.server.LocalServices
-import com.android.window.flags.Flags
import com.google.common.truth.Truth.assertThat
import kotlin.test.AfterTest
import kotlin.test.BeforeTest
import org.junit.Assert.assertThrows
-import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.any
@@ -54,9 +49,6 @@
@Presubmit
@RunWith(AndroidJUnit4::class)
class ConfigurationChangeSettingTest {
- @get:Rule
- val setFlagsRule: SetFlagsRule = SetFlagsRule()
-
private val mMockConfigurationChangeSettingInternal = mock<ConfigurationChangeSettingInternal>()
@BeforeTest
@@ -73,12 +65,6 @@
tearDownLocalService()
}
- @Test(expected = IllegalStateException::class)
- @DisableFlags(Flags.FLAG_CONDENSE_CONFIGURATION_CHANGE_FOR_SIMPLE_MODE)
- fun settingCreation_whenFlagDisabled_throwsException() {
- ConfigurationChangeSetting.DensitySetting(DEFAULT_DISPLAY, TEST_DENSITY)
- }
-
@Test
fun invalidSettingType_appClient_throwsException() {
val parcel = Parcel.obtain()
@@ -95,7 +81,6 @@
}
@Test
- @EnableFlags(Flags.FLAG_CONDENSE_CONFIGURATION_CHANGE_FOR_SIMPLE_MODE)
fun densitySettingParcelable_appClient_recreatesSucceeds() {
val setting = ConfigurationChangeSetting.DensitySetting(DEFAULT_DISPLAY, TEST_DENSITY)
@@ -106,7 +91,6 @@
}
@Test
- @EnableFlags(Flags.FLAG_CONDENSE_CONFIGURATION_CHANGE_FOR_SIMPLE_MODE)
fun densitySettingParcelable_systemServer_createsImplFromInternal() {
val setting = ConfigurationChangeSetting.DensitySetting(DEFAULT_DISPLAY, TEST_DENSITY)
val mockDensitySetting = mock<ConfigurationChangeSetting.DensitySetting>()
@@ -124,7 +108,6 @@
}
@Test
- @EnableFlags(Flags.FLAG_CONDENSE_CONFIGURATION_CHANGE_FOR_SIMPLE_MODE)
fun fontScaleSettingParcelable_appClient_recreatesSucceeds() {
val setting = ConfigurationChangeSetting.FontScaleSetting(TEST_FONT_SCALE)
@@ -135,7 +118,6 @@
}
@Test
- @EnableFlags(Flags.FLAG_CONDENSE_CONFIGURATION_CHANGE_FOR_SIMPLE_MODE)
fun fontScaleSettingParcelable_systemServer_createsImplFromInternal() {
val setting = ConfigurationChangeSetting.FontScaleSetting(TEST_FONT_SCALE)
val mockFontScaleSetting = mock<ConfigurationChangeSetting.FontScaleSetting>()
diff --git a/core/tests/coretests/src/android/window/DesktopExperienceFlagsTest.java b/core/tests/coretests/src/android/window/DesktopExperienceFlagsTest.java
index e840d6d..b9926ba 100644
--- a/core/tests/coretests/src/android/window/DesktopExperienceFlagsTest.java
+++ b/core/tests/coretests/src/android/window/DesktopExperienceFlagsTest.java
@@ -142,80 +142,6 @@
assertThat(mNotOverriddenLocalFlag.isTrue()).isFalse();
}
- @Test
- public void isTrue_flagHasDotAndNotOverridden_withSysPropOverride_returnAsDevOptionEnabled()
- throws Exception {
- setSysProp(DesktopExperienceFlags.SYSTEM_PROPERTY_NAME, OVERRIDE_ON_SETTING);
- setSysProp(DesktopExperienceFlags.SYSTEM_PROPERTY_OVERRIDE_PREFIX + "baseName", "true");
- DesktopExperienceFlag flag = new DesktopExperienceFlag(() -> false, false,
- "test.flag.baseName");
-
- if (Flags.showDesktopExperienceDevOption()) {
- assertThat(flag.isTrue()).isTrue();
- } else {
- assertThat(flag.isTrue()).isFalse();
- }
- }
-
- @Test
- public void isTrue_flagHasNoDotAndNotOverridden_withSysPropOverride_returnAsDevOptionEnabled()
- throws Exception {
- setSysProp(DesktopExperienceFlags.SYSTEM_PROPERTY_NAME, OVERRIDE_ON_SETTING);
- setSysProp(DesktopExperienceFlags.SYSTEM_PROPERTY_OVERRIDE_PREFIX + "fullName", "true");
- DesktopExperienceFlag flag = new DesktopExperienceFlag(() -> false, false, "fullName");
-
- if (Flags.showDesktopExperienceDevOption()) {
- assertThat(flag.isTrue()).isTrue();
- } else {
- assertThat(flag.isTrue()).isFalse();
- }
- }
-
- @Test
- public void isTrue_flagHasNoNameAndNotOverridden_withSysPropOverride_returnAsDevOptionEnabled()
- throws Exception {
- setSysProp(DesktopExperienceFlags.SYSTEM_PROPERTY_NAME, OVERRIDE_ON_SETTING);
- setSysProp(DesktopExperienceFlags.SYSTEM_PROPERTY_OVERRIDE_PREFIX, "true");
- DesktopExperienceFlag flag = new DesktopExperienceFlag(() -> false, false, "");
-
- assertThat(flag.isTrue()).isFalse();
- }
-
- @Test
- public void isTrue_flagHasDot_devOptionEnabled_flagOverridden_withSysPropOverride_returnFalse()
- throws Exception {
- setSysProp(DesktopExperienceFlags.SYSTEM_PROPERTY_NAME, OVERRIDE_ON_SETTING);
- setSysProp(DesktopExperienceFlags.SYSTEM_PROPERTY_OVERRIDE_PREFIX + "baseName", "false");
- DesktopExperienceFlag flag = new DesktopExperienceFlag(() -> false, true,
- "test.flag.baseName");
-
- assertThat(flag.isTrue()).isFalse();
- }
-
- @Test
- public void isTrue_flagHasNoDot_devOptionEnabled_flagOverridden_withOverride_returnFalse()
- throws Exception {
- setSysProp(DesktopExperienceFlags.SYSTEM_PROPERTY_NAME, OVERRIDE_ON_SETTING);
- setSysProp(DesktopExperienceFlags.SYSTEM_PROPERTY_OVERRIDE_PREFIX + "fullName", "false");
- DesktopExperienceFlag flag = new DesktopExperienceFlag(() -> false, true, "fullName");
-
- assertThat(flag.isTrue()).isFalse();
- }
-
- @Test
- public void isTrue_flagHasNoName_devOptionEnabled_flagOverridden_withOverride()
- throws Exception {
- setSysProp(DesktopExperienceFlags.SYSTEM_PROPERTY_NAME, OVERRIDE_ON_SETTING);
- setSysProp(DesktopExperienceFlags.SYSTEM_PROPERTY_OVERRIDE_PREFIX, "false");
- DesktopExperienceFlag flag = new DesktopExperienceFlag(() -> false, true, "");
-
- if (Flags.showDesktopExperienceDevOption()) {
- assertThat(flag.isTrue()).isTrue();
- } else {
- assertThat(flag.isTrue()).isFalse();
- }
- }
-
private void setSysProp(String name, String value) throws Exception {
if (!mInitialSyspropValues.containsKey(name)) {
String initialValue = mUiDevice.executeShellCommand("getprop " + name).trim();
diff --git a/core/tests/coretests/src/android/window/WindowContextTest.java b/core/tests/coretests/src/android/window/WindowContextTest.java
index def7d9a..5f35bf3 100644
--- a/core/tests/coretests/src/android/window/WindowContextTest.java
+++ b/core/tests/coretests/src/android/window/WindowContextTest.java
@@ -35,6 +35,8 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
@@ -53,9 +55,11 @@
import android.hardware.display.DisplayManager;
import android.os.Binder;
import android.os.IBinder;
+import android.os.RemoteException;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
import android.platform.test.flag.junit.SetFlagsRule;
+import android.util.PollingCheck;
import android.view.Display;
import android.view.IWindowManager;
import android.view.View;
@@ -71,6 +75,7 @@
import androidx.test.rule.ActivityTestRule;
import com.android.frameworks.coretests.R;
+import com.android.internal.util.GcUtils;
import com.android.window.flags.Flags;
import org.junit.After;
@@ -79,6 +84,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.lang.ref.WeakReference;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -182,7 +188,14 @@
mWindowContext.release();
// After the window context's release, the window token is also removed.
- assertFalse("Token must be removed after release.", mWms.isWindowToken(token));
+ PollingCheck.waitFor(() -> {
+ try {
+ return !mWms.isWindowToken(token);
+ } catch (RemoteException e) {
+ fail("Fail to call isWindowToken:" + e);
+ return false;
+ }
+ });
}
/**
@@ -515,6 +528,23 @@
return (WindowContext) instContext.createWindowContext(display, type, null /* options */);
}
+ @Test
+ public void testWindowContextCleanup() {
+ final WindowTokenClientController mockController = mock(WindowTokenClientController.class);
+ doReturn(true).when(mockController).attachToDisplayArea(
+ any(), anyInt(), anyInt(), any());
+ doNothing().when(mockController).detachIfNeeded(any());
+ WindowTokenClientController.overrideForTesting(mockController);
+
+ WeakReference<WindowContext> windowContextRef = new WeakReference<>(createWindowContext());
+ final WindowTokenClient token =
+ (WindowTokenClient) windowContextRef.get().getWindowContextToken();
+
+ GcUtils.runGcAndFinalizersSync();
+
+ verify(mockController).detachIfNeeded(eq(token));
+ }
+
private static class ConfigurationListener implements ComponentCallbacks {
private Configuration mConfiguration;
private CountDownLatch mLatch = new CountDownLatch(1);
diff --git a/core/tests/coretests/src/com/android/internal/os/BackgroundThreadTest.java b/core/tests/coretests/src/com/android/internal/os/BackgroundThreadTest.java
index 402ba0d..fb70a3d 100644
--- a/core/tests/coretests/src/com/android/internal/os/BackgroundThreadTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BackgroundThreadTest.java
@@ -51,4 +51,22 @@
boolean success = done.block(5000);
assertThat(success).isTrue();
}
+
+ @Test
+ public void test_startIfNeeded() {
+ BackgroundThread.startIfNeeded();
+
+ BackgroundThread thread = BackgroundThread.get();
+ assertThat(thread.getLooper()).isNotEqualTo(Looper.getMainLooper());
+ }
+
+ @Test
+ public void test_startIfNeededMultipleTimes() {
+ BackgroundThread.startIfNeeded();
+ BackgroundThread.startIfNeeded();
+ BackgroundThread.startIfNeeded();
+
+ BackgroundThread thread = BackgroundThread.get();
+ assertThat(thread.getLooper()).isNotEqualTo(Looper.getMainLooper());
+ }
}
diff --git a/core/tests/coretests/src/com/android/internal/util/RateLimitingCacheTest.java b/core/tests/coretests/src/com/android/internal/util/RateLimitingCacheTest.java
index 82d90fc..d09c91d 100644
--- a/core/tests/coretests/src/com/android/internal/util/RateLimitingCacheTest.java
+++ b/core/tests/coretests/src/com/android/internal/util/RateLimitingCacheTest.java
@@ -39,7 +39,7 @@
@RunWith(AndroidJUnit4.class)
public class RateLimitingCacheTest {
- private int mCounter = 0;
+ private volatile int mCounter = 0;
@Before
public void before() {
@@ -59,12 +59,12 @@
TestRateLimitingCache<Integer> s = new TestRateLimitingCache<>(0);
int first = s.get(mFetcher);
- assertEquals(first, 0);
+ assertEquals(0, first);
int second = s.get(mFetcher);
- assertEquals(second, 1);
+ assertEquals(1, second);
s.advanceTime(20);
int third = s.get(mFetcher);
- assertEquals(third, 2);
+ assertEquals(2, third);
}
/**
@@ -76,17 +76,17 @@
TestRateLimitingCache<Integer> s = new TestRateLimitingCache<>(100);
int first = s.get(mFetcher);
- assertEquals(first, 0);
+ assertEquals(0, first);
int second = s.get(mFetcher);
// Too early to change
- assertEquals(second, 0);
+ assertEquals(0, second);
s.advanceTime(150);
int third = s.get(mFetcher);
// Changed by now
- assertEquals(third, 1);
+ assertEquals(1, third);
int fourth = s.get(mFetcher);
// Too early to change again
- assertEquals(fourth, 1);
+ assertEquals(1, fourth);
}
/**
@@ -98,11 +98,11 @@
TestRateLimitingCache<Integer> s = new TestRateLimitingCache<>(-1);
int first = s.get(mFetcher);
- assertEquals(first, 0);
+ assertEquals(0, first);
s.advanceTime(200);
// Should return the original value every time
int second = s.get(mFetcher);
- assertEquals(second, 0);
+ assertEquals(0, second);
}
/**
@@ -157,7 +157,7 @@
final long periodsElapsed = 1 + ((endTimeMillis - startTimeMillis) / periodMillis);
final long expected = Math.min(100 + 100, periodsElapsed * maxCountPerPeriod) - 1;
- assertEquals(mCounter, expected);
+ assertEquals(expected, mCounter);
}
/**
@@ -209,7 +209,7 @@
t1.join();
t2.join();
- assertEquals(counter.get(), 2);
+ assertEquals(2, counter.get());
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/topwindoweffects/data/entity/SqueezeEffectCornerResourceId.kt b/core/tests/coretests/src/com/android/internal/widget/ActionBarContextViewActivity.java
similarity index 81%
copy from packages/SystemUI/src/com/android/systemui/topwindoweffects/data/entity/SqueezeEffectCornerResourceId.kt
copy to core/tests/coretests/src/com/android/internal/widget/ActionBarContextViewActivity.java
index a55301e..520bb21 100644
--- a/packages/SystemUI/src/com/android/systemui/topwindoweffects/data/entity/SqueezeEffectCornerResourceId.kt
+++ b/core/tests/coretests/src/com/android/internal/widget/ActionBarContextViewActivity.java
@@ -14,6 +14,9 @@
* limitations under the License.
*/
-package com.android.systemui.topwindoweffects.data.entity
+package com.android.internal.widget;
-data class SqueezeEffectCornerResourceId(val top: Int, val bottom: Int)
+import android.app.Activity;
+
+public class ActionBarContextViewActivity extends Activity {
+}
diff --git a/core/tests/coretests/src/com/android/internal/widget/ActionBarContextViewTest.java b/core/tests/coretests/src/com/android/internal/widget/ActionBarContextViewTest.java
new file mode 100644
index 0000000..444de95
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/widget/ActionBarContextViewTest.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.widget;
+
+import static org.junit.Assert.assertEquals;
+
+import android.view.View;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+import androidx.test.rule.ActivityTestRule;
+
+import com.android.window.flags.Flags;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests for {@link ActionBarContextView}.
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class ActionBarContextViewTest {
+
+ @Rule
+ public ActivityTestRule<ActionBarContextViewActivity> mActivityRule =
+ new ActivityTestRule<>(ActionBarContextViewActivity.class);
+
+ @Test
+ public void testOnConfigurationChanged_contentHeight() {
+ final ActionBarContextView view = new ActionBarContextView(mActivityRule.getActivity());
+
+ // The value is specified in actionModeStyle
+ assertEquals(200, view.getContentHeight());
+
+ view.dispatchConfigurationChanged(view.getResources().getConfiguration());
+
+ // The value must be the same after calling onConfigurationChanged
+ assertEquals(200, view.getContentHeight());
+ }
+
+ @Test
+ public void testOnMeasure_externalVerticalPadding() {
+ final ActionBarContextView view = new ActionBarContextView(mActivityRule.getActivity());
+
+ // The values are specified in actionModeStyle
+ assertEquals(10, view.getPaddingLeft());
+ assertEquals(20, view.getPaddingTop());
+ assertEquals(30, view.getPaddingRight());
+ assertEquals(40, view.getPaddingBottom());
+
+ view.setPadding(15, 25, 35, 45);
+
+ view.measure(
+ View.MeasureSpec.makeMeasureSpec(1000, View.MeasureSpec.EXACTLY),
+ View.MeasureSpec.makeMeasureSpec(2000, View.MeasureSpec.AT_MOST));
+
+ // Flags.actionModeEdgeToEdge() enabled:
+ // 210 = 200 (content height) + 5 (external top padding) + 5 (external bottom padding)
+ // Flags.actionModeEdgeToEdge() disabled:
+ // If content height is greater than 0, the measured height should be content height (200).
+ assertEquals(Flags.actionModeEdgeToEdge() ? 210 : 200, view.getMeasuredHeight());
+ }
+}
diff --git a/core/tests/mockingcoretests/src/android/widget/ToastTest.java b/core/tests/mockingcoretests/src/android/widget/ToastTest.java
index 79bc81d..8eea70b 100644
--- a/core/tests/mockingcoretests/src/android/widget/ToastTest.java
+++ b/core/tests/mockingcoretests/src/android/widget/ToastTest.java
@@ -94,7 +94,6 @@
}
@Test
- @EnableFlags(Flags.FLAG_TOAST_NO_WEAKREF)
public void enqueueFail_nullifiesNextView() throws RemoteException {
Looper.prepare();
@@ -116,28 +115,4 @@
tn = t.getTn();
assertThat(tn.getNextView()).isNull();
}
-
- @Test
- @DisableFlags(Flags.FLAG_TOAST_NO_WEAKREF)
- public void enqueueFail_doesNotNullifyNextView() throws RemoteException {
- Looper.prepare();
-
- // allow 1st toast and fail on the 2nd
- when(sMockNMS.enqueueToast(anyString(), any(), any(), anyInt(), anyBoolean(),
- anyInt())).thenReturn(true, false);
-
- // first toast is enqueued
- Toast t = Toast.makeText(mContext, "Toast1", Toast.LENGTH_SHORT);
- t.setView(mock(View.class));
- t.show();
- Toast.TN tn = t.getTn();
- assertThat(tn.getNextView()).isNotNull();
-
- // second toast is not enqueued
- t = Toast.makeText(mContext, "Toast2", Toast.LENGTH_SHORT);
- t.setView(mock(View.class));
- t.show();
- tn = t.getTn();
- assertThat(tn.getNextView()).isNotNull();
- }
}
diff --git a/core/tests/utiltests/src/android/util/ListenerGroupTest.java b/core/tests/utiltests/src/android/util/ListenerGroupTest.java
new file mode 100644
index 0000000..87fe03c
--- /dev/null
+++ b/core/tests/utiltests/src/android/util/ListenerGroupTest.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.util;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertTrue;
+
+import android.os.Handler;
+import android.os.Looper;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.annotations.GuardedBy;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
+
+
+@SmallTest
+public class ListenerGroupTest {
+
+ private Object mInitialObject = new Object();
+
+ private ListenerGroup<Object> mListenerGroup;
+
+ @Before
+ public void setUp() throws InterruptedException {
+ mListenerGroup = new ListenerGroup<>(mInitialObject, new Handler(Looper.getMainLooper()));
+ }
+
+ @Test
+ public void test_added_listener_gets_initial_value() {
+ final int valueCount = 1;
+ final ValueListener valueListener = new ValueListener(valueCount);
+
+ mListenerGroup.addListener(Runnable::run, valueListener);
+
+ final boolean waitCompleted = valueListener.waitForValues();
+ List<Object> values = valueListener.getValues();
+
+ assertTrue("waitForValues did not complete.", waitCompleted);
+ assertEquals("Value count does not match.", valueCount, values.size());
+ assertEquals("First value does not match initial value", mInitialObject,
+ valueListener.getValues().getFirst());
+ }
+
+ @Test
+ public void test_added_listener_gets_receives_updates() {
+ int valueCount = 2;
+ Object nextValue = new Object();
+ ValueListener valueListener = new ValueListener(2);
+
+ mListenerGroup.addListener(Runnable::run, valueListener);
+ mListenerGroup.accept(nextValue);
+
+ boolean waitCompleted = valueListener.waitForValues();
+ List<Object> values = valueListener.getValues();
+
+ assertTrue("waitForValues did not complete.", waitCompleted);
+ assertEquals("Value count does not match.", valueCount, values.size());
+ assertEquals("Next value not received", nextValue,
+ valueListener.getValues().getLast());
+ }
+
+ @Test
+ public void test_removed_listener_stops_receiving_updates() {
+ final int valueCount = 1;
+ Object nextValue = new Object();
+ ValueListener valueListener = new ValueListener(valueCount);
+ ValueListener stopListener = new ValueListener(valueCount);
+
+ mListenerGroup.addListener(Runnable::run, valueListener);
+ mListenerGroup.removeListener(valueListener);
+
+ mListenerGroup.accept(nextValue);
+ mListenerGroup.addListener(Runnable::run, stopListener);
+
+ boolean waitCompleted = valueListener.waitForValues() && stopListener.waitForValues();
+ List<Object> values = valueListener.getValues();
+
+ assertTrue("waitForValues did not complete.", waitCompleted);
+ assertEquals("Value count does not match.", valueCount, values.size());
+ assertEquals("Incorrect values received.", mInitialObject,
+ values.getFirst());
+ assertEquals("stopListener must receive next value", nextValue,
+ stopListener.getValues().getFirst());
+ }
+
+ private static final class ValueListener implements Consumer<Object> {
+
+ private final Object mLock = new Object();
+
+ @GuardedBy("mLock")
+ private final List<Object> mValues = new ArrayList<>();
+
+ private final CountDownLatch mCountDownLatch;
+
+ ValueListener(int valueCount) {
+ mCountDownLatch = new CountDownLatch(valueCount);
+ }
+
+ @Override
+ public void accept(Object o) {
+ synchronized (mLock) {
+ mValues.add(Objects.requireNonNull(o));
+ mCountDownLatch.countDown();
+ }
+ }
+
+ public boolean waitForValues() {
+ try {
+ return mCountDownLatch.await(16, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException e) {
+ return false;
+ }
+
+ }
+
+ public List<Object> getValues() {
+ synchronized (mLock) {
+ return new ArrayList<>(mValues);
+ }
+ }
+ }
+}
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 333cf1a..4f8e2b7 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -266,6 +266,7 @@
<permission name="android.permission.ACCESS_LOWPAN_STATE"/>
<permission name="android.permission.BACKUP"/>
<permission name="android.permission.ENTER_TRADE_IN_MODE"/>
+ <permission name="android.permission.GET_DEVICE_LOCK_ENROLLMENT_TYPE"/>
<!-- Needed for GMSCore Location API test only -->
<permission name="android.permission.LOCATION_BYPASS"/>
<!-- Needed for test only -->
diff --git a/data/keyboards/Generic.kl b/data/keyboards/Generic.kl
index 2b0802b..02e0268 100644
--- a/data/keyboards/Generic.kl
+++ b/data/keyboards/Generic.kl
@@ -426,14 +426,13 @@
key 583 ASSIST
key 585 EMOJI_PICKER
key 586 DICTATE
+key 591 DO_NOT_DISTURB
key 656 MACRO_1
key 657 MACRO_2
key 658 MACRO_3
key 659 MACRO_4
# Keys defined by HID usages
-key usage 0x010082 LOCK FALLBACK_USAGE_MAPPING
-key usage 0x01009B DO_NOT_DISTURB FALLBACK_USAGE_MAPPING
key usage 0x0c0065 SCREENSHOT FALLBACK_USAGE_MAPPING
key usage 0x0c0067 WINDOW FALLBACK_USAGE_MAPPING
key usage 0x0c006F BRIGHTNESS_UP FALLBACK_USAGE_MAPPING
diff --git a/graphics/java/android/graphics/drawable/BitmapDrawable.java b/graphics/java/android/graphics/drawable/BitmapDrawable.java
index 579ac60..2d0375e 100644
--- a/graphics/java/android/graphics/drawable/BitmapDrawable.java
+++ b/graphics/java/android/graphics/drawable/BitmapDrawable.java
@@ -77,6 +77,8 @@
* @attr ref android.R.styleable#BitmapDrawable_mipMap
* @attr ref android.R.styleable#BitmapDrawable_tileMode
*/
+@android.ravenwood.annotation.RavenwoodPartiallyAllowlisted
+@android.ravenwood.annotation.RavenwoodKeepPartialClass
public class BitmapDrawable extends Drawable {
private static final int DEFAULT_PAINT_FLAGS =
Paint.FILTER_BITMAP_FLAG | Paint.DITHER_FLAG;
@@ -149,6 +151,7 @@
* Create drawable from a bitmap, setting initial target density based on
* the display metrics of the resources.
*/
+ @android.ravenwood.annotation.RavenwoodKeep
public BitmapDrawable(Resources res, Bitmap bitmap) {
if (bitmap == null) {
Log.w(TAG, "BitmapDrawable created with null Bitmap");
@@ -232,10 +235,12 @@
/**
* Returns the bitmap used by this drawable to render. May be null.
*/
+ @android.ravenwood.annotation.RavenwoodKeep
public final Bitmap getBitmap() {
return mBitmapState.mBitmap;
}
+ @android.ravenwood.annotation.RavenwoodKeep
private void computeBitmapSize() {
final Bitmap bitmap = mBitmapState.mBitmap;
if (bitmap != null) {
@@ -956,11 +961,13 @@
}
@Override
+ @android.ravenwood.annotation.RavenwoodKeep
public int getIntrinsicWidth() {
return mBitmapWidth;
}
@Override
+ @android.ravenwood.annotation.RavenwoodKeep
public int getIntrinsicHeight() {
return mBitmapHeight;
}
@@ -982,6 +989,7 @@
return mBitmapState;
}
+ @android.ravenwood.annotation.RavenwoodKeepWholeClass
final static class BitmapState extends ConstantState {
final Paint mPaint;
@@ -1060,6 +1068,7 @@
* The one helper to rule them all. This is called by all public & private
* constructors to set the state and initialize local properties.
*/
+ @android.ravenwood.annotation.RavenwoodKeep
private void init(BitmapState state, Resources res) {
mBitmapState = state;
updateLocalState(res);
@@ -1074,6 +1083,7 @@
* after significant state changes, e.g. from the One True Constructor and
* after inflating or applying a theme.
*/
+ @android.ravenwood.annotation.RavenwoodKeep
private void updateLocalState(Resources res) {
mTargetDensity = resolveDensity(res, mBitmapState.mTargetDensity);
mBlendModeFilter = updateBlendModeFilter(mBlendModeFilter, mBitmapState.mTint,
diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java
index 7f2feac..213a5a9 100644
--- a/graphics/java/android/graphics/drawable/Drawable.java
+++ b/graphics/java/android/graphics/drawable/Drawable.java
@@ -180,6 +180,8 @@
* <a href="{@docRoot}guide/topics/resources/drawable-resource.html">Drawable Resources</a>
* document.</p></div>
*/
+@android.ravenwood.annotation.RavenwoodPartiallyAllowlisted
+@android.ravenwood.annotation.RavenwoodKeepPartialClass
public abstract class Drawable {
private static final Rect ZERO_BOUNDS_RECT = new Rect();
@@ -1482,6 +1484,7 @@
* {@link Drawable#mutate()} on a Drawable should typically create a new ConstantState for that
* Drawable.
*/
+ @android.ravenwood.annotation.RavenwoodKeepWholeClass
public static abstract class ConstantState {
/**
* Creates a new Drawable instance from its constant state.
@@ -1587,6 +1590,7 @@
return tintFilter;
}
+ @android.ravenwood.annotation.RavenwoodIgnore
@Nullable BlendModeColorFilter updateBlendModeFilter(@Nullable BlendModeColorFilter blendFilter,
@Nullable ColorStateList tint, @Nullable BlendMode blendMode) {
if (tint == null || blendMode == null) {
@@ -1666,6 +1670,7 @@
}
}
+ @android.ravenwood.annotation.RavenwoodKeep
static int resolveDensity(@Nullable Resources r, int parentDensity) {
final int densityDpi = r == null ? parentDensity : r.getDisplayMetrics().densityDpi;
return densityDpi == 0 ? DisplayMetrics.DENSITY_DEFAULT : densityDpi;
@@ -1725,4 +1730,3 @@
}
}
}
-
diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp
index 033c934..fc52e33 100644
--- a/libs/WindowManager/Shell/Android.bp
+++ b/libs/WindowManager/Shell/Android.bp
@@ -175,6 +175,7 @@
"//frameworks/base/packages/SystemUI/aconfig:com_android_systemui_flags_lib",
"//frameworks/libs/systemui:com_android_systemui_shared_flags_lib",
"//frameworks/libs/systemui:iconloader_base",
+ "//frameworks/libs/systemui:view_capture",
"PlatformAnimationLib",
"WindowManager-Shell-lite-proto",
"WindowManager-Shell-proto",
@@ -193,6 +194,7 @@
"jsr330",
"kotlinx-coroutines-android",
"kotlinx-coroutines-core",
+ "mechanics",
"perfetto_trace_java_protos",
],
libs: [
diff --git a/libs/WindowManager/Shell/aconfig/multitasking.aconfig b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
index 67b1788..f608843 100644
--- a/libs/WindowManager/Shell/aconfig/multitasking.aconfig
+++ b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
@@ -245,3 +245,23 @@
description: "Refactor TaskViewTransitions.startAnimation"
bug: "402454136"
}
+
+flag {
+ name: "enable_pip_box_shadows"
+ namespace: "multitasking"
+ description: "Enables box shadows for PiP"
+ bug: "367464660"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
+ name: "enable_bubble_swipe_up_cleanup"
+ namespace: "multitasking"
+ description: "Enable clean up of bubbles swipe gesture handling"
+ bug: "391909607"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleExpandedViewTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleExpandedViewTest.kt
index c02fe41..5cd0007 100644
--- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleExpandedViewTest.kt
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleExpandedViewTest.kt
@@ -29,6 +29,7 @@
import androidx.test.filters.SmallTest
import com.android.internal.protolog.ProtoLog
import com.android.window.flags.Flags.FLAG_EXCLUDE_TASK_FROM_RECENTS
+import com.android.wm.shell.Flags.FLAG_ENABLE_BUBBLE_ANYTHING
import com.android.wm.shell.Flags.FLAG_ENABLE_BUBBLE_TASK_VIEW_LISTENER
import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.taskview.TaskView
@@ -36,6 +37,7 @@
import com.android.wm.shell.taskview.TaskViewTaskController
import com.google.common.truth.Truth.assertThat
import com.google.common.util.concurrent.MoreExecutors.directExecutor
+import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Rule
import org.junit.Test
@@ -107,10 +109,24 @@
val wctCaptor = argumentCaptor<WindowContainerTransaction>()
verify(taskOrganizer).applyTransaction(wctCaptor.capture())
val wct = wctCaptor.lastValue
- assertThat(wct.changes).hasSize(1)
- val chg = wct.changes.get(taskViewTaskToken.asBinder())
- assertThat(chg).isNotNull()
- assertThat(chg!!.forceExcludedFromRecents).isTrue()
+ val hasForceExcludedFromRecents = wct.changes.entries.stream()
+ .filter { it.key.equals(taskViewTaskToken.asBinder()) }
+ .anyMatch { it.value.forceExcludedFromRecents }
+ assertTrue(hasForceExcludedFromRecents)
+ }
+
+ @Test
+ @EnableFlags(FLAG_ENABLE_BUBBLE_ANYTHING)
+ fun onTaskCreated_disallowFlagLaunchAdjacent() {
+ bubbleTaskView.listener.onTaskCreated(123 /* taskId */, componentName)
+
+ val wctCaptor = argumentCaptor<WindowContainerTransaction>()
+ verify(taskOrganizer).applyTransaction(wctCaptor.capture())
+ val wct = wctCaptor.lastValue
+ val hasDisableLaunchAdjacent = wct.hierarchyOps.stream()
+ .filter { it.container.equals(taskViewTaskToken.asBinder()) }
+ .anyMatch { it.isLaunchAdjacentDisabled }
+ assertTrue(hasDisableLaunchAdjacent)
}
companion object {
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleTaskViewListenerTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleTaskViewListenerTest.kt
index 7a3cae8..b9de750 100644
--- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleTaskViewListenerTest.kt
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleTaskViewListenerTest.kt
@@ -50,6 +50,7 @@
import com.android.wm.shell.taskview.TaskViewController
import com.android.wm.shell.taskview.TaskViewTaskController
import com.google.common.truth.Truth.assertThat
+import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Rule
import org.junit.Test
@@ -432,10 +433,34 @@
val wctCaptor = argumentCaptor<WindowContainerTransaction>()
verify(taskOrganizer).applyTransaction(wctCaptor.capture())
val wct = wctCaptor.lastValue
- assertThat(wct.changes).hasSize(1)
- val chg = wct.changes.get(taskViewTaskToken.asBinder())
- assertThat(chg).isNotNull()
- assertThat(chg!!.forceExcludedFromRecents).isTrue()
+ val hasForceExcludedFromRecents = wct.changes.entries.stream()
+ .filter { it.key.equals(taskViewTaskToken.asBinder()) }
+ .anyMatch { it.value.forceExcludedFromRecents }
+ assertTrue(hasForceExcludedFromRecents)
+ }
+
+ @Test
+ @EnableFlags(FLAG_ENABLE_BUBBLE_ANYTHING)
+ fun onTaskCreated_disallowFlagLaunchAdjacent() {
+ val b = createAppBubble()
+ bubbleTaskViewListener.setBubble(b)
+ getInstrumentation().runOnMainSync {
+ bubbleTaskViewListener.onInitialized()
+ }
+ getInstrumentation().waitForIdleSync()
+
+ getInstrumentation().runOnMainSync {
+ bubbleTaskViewListener.onTaskCreated(123 /* taskId */, mock<ComponentName>())
+ }
+ getInstrumentation().waitForIdleSync()
+
+ val wctCaptor = argumentCaptor<WindowContainerTransaction>()
+ verify(taskOrganizer).applyTransaction(wctCaptor.capture())
+ val wct = wctCaptor.lastValue
+ val hasDisableLaunchAdjacent = wct.hierarchyOps.stream()
+ .filter { it.container.equals(taskViewTaskToken.asBinder()) }
+ .anyMatch { it.isLaunchAdjacentDisabled }
+ assertTrue(hasDisableLaunchAdjacent)
}
@Test
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml
index a2da3ae..9279246 100644
--- a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml
@@ -81,6 +81,7 @@
style="@style/DesktopModeHandleMenuWindowingButton"/>
<Space
+ android:id="@+id/split_screen_button_space"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_weight="1"/>
diff --git a/libs/WindowManager/Shell/res/values-af/strings.xml b/libs/WindowManager/Shell/res/values-af/strings.xml
index 0eee541..d21083f 100644
--- a/libs/WindowManager/Shell/res/values-af/strings.xml
+++ b/libs/WindowManager/Shell/res/values-af/strings.xml
@@ -133,6 +133,7 @@
<string name="manage_windows_text" msgid="5567366688493093920">"Bestuur vensters"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Verander aspekverhouding"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"Optimeer aansig"</string>
+ <string name="handle_menu_error_icon_text" msgid="7200375184879808684">"Fout-ikoon"</string>
<string name="close_text" msgid="4986518933445178928">"Maak toe"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Maak kieslys toe"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (werkskermvensters)"</string>
diff --git a/libs/WindowManager/Shell/res/values-am/strings.xml b/libs/WindowManager/Shell/res/values-am/strings.xml
index 97aa47f..8c96d8d 100644
--- a/libs/WindowManager/Shell/res/values-am/strings.xml
+++ b/libs/WindowManager/Shell/res/values-am/strings.xml
@@ -133,6 +133,7 @@
<string name="manage_windows_text" msgid="5567366688493093920">"መስኮቶችን አስተዳድር"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"ምጥጥነ ገፅታ ለውጥ"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"ዕይታን አትባ"</string>
+ <string name="handle_menu_error_icon_text" msgid="7200375184879808684">"የስህተት አዶ"</string>
<string name="close_text" msgid="4986518933445178928">"ዝጋ"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"ምናሌ ዝጋ"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ዴስክቶፕ መስኮት)"</string>
diff --git a/libs/WindowManager/Shell/res/values-ar/strings.xml b/libs/WindowManager/Shell/res/values-ar/strings.xml
index 1de9ce0..9ef979c36 100644
--- a/libs/WindowManager/Shell/res/values-ar/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ar/strings.xml
@@ -133,6 +133,7 @@
<string name="manage_windows_text" msgid="5567366688493093920">"إدارة النوافذ"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"تغيير نسبة العرض إلى الارتفاع"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"تحسين طريقة العرض"</string>
+ <string name="handle_menu_error_icon_text" msgid="7200375184879808684">"رمز الخطأ"</string>
<string name="close_text" msgid="4986518933445178928">"إغلاق"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"إغلاق القائمة"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (عرض المحتوى في النافذة الحالية على سطح المكتب)"</string>
diff --git a/libs/WindowManager/Shell/res/values-as/strings.xml b/libs/WindowManager/Shell/res/values-as/strings.xml
index 96f2aaa..56dc26f 100644
--- a/libs/WindowManager/Shell/res/values-as/strings.xml
+++ b/libs/WindowManager/Shell/res/values-as/strings.xml
@@ -133,6 +133,7 @@
<string name="manage_windows_text" msgid="5567366688493093920">"ৱিণ্ড’ পৰিচালনা কৰক"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"আকাৰৰ অনুপাত সলনি কৰক"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"ভিউ অপ্টিমাইজ কৰক"</string>
+ <string name="handle_menu_error_icon_text" msgid="7200375184879808684">"আসোঁৱাহৰ চিহ্ন"</string>
<string name="close_text" msgid="4986518933445178928">"বন্ধ কৰক"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"মেনু বন্ধ কৰক"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ডেস্কটপ ৱিণ্ড’ৱিং)"</string>
diff --git a/libs/WindowManager/Shell/res/values-az/strings.xml b/libs/WindowManager/Shell/res/values-az/strings.xml
index 72736ed..c277df3 100644
--- a/libs/WindowManager/Shell/res/values-az/strings.xml
+++ b/libs/WindowManager/Shell/res/values-az/strings.xml
@@ -133,6 +133,7 @@
<string name="manage_windows_text" msgid="5567366688493093920">"Pəncərələri idarə edin"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Tərəflər nisbətini dəyişin"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"Görünüşü optimallaşdırın"</string>
+ <string name="handle_menu_error_icon_text" msgid="7200375184879808684">"Xəta ikonası"</string>
<string name="close_text" msgid="4986518933445178928">"Bağlayın"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Menyunu bağlayın"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Masaüstü pəncərə rejimi)"</string>
diff --git a/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml
index ac1be35..8b2ae49 100644
--- a/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml
@@ -133,6 +133,7 @@
<string name="manage_windows_text" msgid="5567366688493093920">"Upravljajte prozorima"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Promeni razmeru"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"Optimizujte prikaz"</string>
+ <string name="handle_menu_error_icon_text" msgid="7200375184879808684">"Ikona greške"</string>
<string name="close_text" msgid="4986518933445178928">"Zatvorite"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Zatvorite meni"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (prikaz računarskih prozora)"</string>
diff --git a/libs/WindowManager/Shell/res/values-be/strings.xml b/libs/WindowManager/Shell/res/values-be/strings.xml
index 2a2bd96..6492645 100644
--- a/libs/WindowManager/Shell/res/values-be/strings.xml
+++ b/libs/WindowManager/Shell/res/values-be/strings.xml
@@ -133,6 +133,7 @@
<string name="manage_windows_text" msgid="5567366688493093920">"Кіраваць вокнамі"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Змяніць суадносіны бакоў"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"Аптымізаваць прагляд"</string>
+ <string name="handle_menu_error_icon_text" msgid="7200375184879808684">"Значок памылкі"</string>
<string name="close_text" msgid="4986518933445178928">"Закрыць"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Закрыць меню"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (рэжым вокнаў працоўнага стала)"</string>
diff --git a/libs/WindowManager/Shell/res/values-bg/strings.xml b/libs/WindowManager/Shell/res/values-bg/strings.xml
index f5a93d0..b0fb279 100644
--- a/libs/WindowManager/Shell/res/values-bg/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bg/strings.xml
@@ -133,6 +133,7 @@
<string name="manage_windows_text" msgid="5567366688493093920">"Управление на прозорците"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Промяна на съотношението"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"Оптимизиране на изгледа"</string>
+ <string name="handle_menu_error_icon_text" msgid="7200375184879808684">"Икона за грешка"</string>
<string name="close_text" msgid="4986518933445178928">"Затваряне"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Затваряне на менюто"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (режим за настолни компютри)"</string>
diff --git a/libs/WindowManager/Shell/res/values-bn/strings.xml b/libs/WindowManager/Shell/res/values-bn/strings.xml
index 2cc109c..384dd79 100644
--- a/libs/WindowManager/Shell/res/values-bn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bn/strings.xml
@@ -133,6 +133,7 @@
<string name="manage_windows_text" msgid="5567366688493093920">"উইন্ডো ম্যানেজ করুন"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"অ্যাস্পেক্ট রেশিও পরিবর্তন করুন"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"ভিউ অপ্টিমাইজ করুন"</string>
+ <string name="handle_menu_error_icon_text" msgid="7200375184879808684">"সমস্যার আইকন"</string>
<string name="close_text" msgid="4986518933445178928">"বন্ধ করুন"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"\'মেনু\' বন্ধ করুন"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ডেস্কটপ উইন্ডোইং)"</string>
diff --git a/libs/WindowManager/Shell/res/values-bs/strings.xml b/libs/WindowManager/Shell/res/values-bs/strings.xml
index 34b23ba..aff72f93 100644
--- a/libs/WindowManager/Shell/res/values-bs/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bs/strings.xml
@@ -133,6 +133,7 @@
<string name="manage_windows_text" msgid="5567366688493093920">"Upravljanje prozorima"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Promjena formata slike"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"Optimiziranje prikaza"</string>
+ <string name="handle_menu_error_icon_text" msgid="7200375184879808684">"Ikona greške"</string>
<string name="close_text" msgid="4986518933445178928">"Zatvaranje"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Zatvaranje menija"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (računarski prikaz prozora)"</string>
diff --git a/libs/WindowManager/Shell/res/values-ca/strings.xml b/libs/WindowManager/Shell/res/values-ca/strings.xml
index ccb12b3..2897063 100644
--- a/libs/WindowManager/Shell/res/values-ca/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ca/strings.xml
@@ -133,6 +133,7 @@
<string name="manage_windows_text" msgid="5567366688493093920">"Gestiona les finestres"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Canvia la relació d\'aspecte"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"Visualització d\'Optimize"</string>
+ <string name="handle_menu_error_icon_text" msgid="7200375184879808684">"Icona d\'error"</string>
<string name="close_text" msgid="4986518933445178928">"Tanca"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Tanca el menú"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (sistema de finestres de tipus escriptori)"</string>
diff --git a/libs/WindowManager/Shell/res/values-cs/strings.xml b/libs/WindowManager/Shell/res/values-cs/strings.xml
index 9d9d127..cc8e75f 100644
--- a/libs/WindowManager/Shell/res/values-cs/strings.xml
+++ b/libs/WindowManager/Shell/res/values-cs/strings.xml
@@ -133,6 +133,7 @@
<string name="manage_windows_text" msgid="5567366688493093920">"Spravovat okna"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Změnit poměr stran"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"Optimalizovat zobrazení"</string>
+ <string name="handle_menu_error_icon_text" msgid="7200375184879808684">"Ikona chyby"</string>
<string name="close_text" msgid="4986518933445178928">"Zavřít"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Zavřít nabídku"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (okna na ploše)"</string>
diff --git a/libs/WindowManager/Shell/res/values-da/strings.xml b/libs/WindowManager/Shell/res/values-da/strings.xml
index 17cb246..e5be1b1 100644
--- a/libs/WindowManager/Shell/res/values-da/strings.xml
+++ b/libs/WindowManager/Shell/res/values-da/strings.xml
@@ -133,6 +133,7 @@
<string name="manage_windows_text" msgid="5567366688493093920">"Administrer vinduer"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Skift billedformat"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"Optimer visning"</string>
+ <string name="handle_menu_error_icon_text" msgid="7200375184879808684">"Fejlikon"</string>
<string name="close_text" msgid="4986518933445178928">"Luk"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Luk menu"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (vinduer på skrivebordet)"</string>
diff --git a/libs/WindowManager/Shell/res/values-de/strings.xml b/libs/WindowManager/Shell/res/values-de/strings.xml
index c84d014..97588c5 100644
--- a/libs/WindowManager/Shell/res/values-de/strings.xml
+++ b/libs/WindowManager/Shell/res/values-de/strings.xml
@@ -133,6 +133,7 @@
<string name="manage_windows_text" msgid="5567366688493093920">"Fenster verwalten"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Seitenverhältnis ändern"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"Ansicht optimieren"</string>
+ <string name="handle_menu_error_icon_text" msgid="7200375184879808684">"Fehlersymbol"</string>
<string name="close_text" msgid="4986518933445178928">"Schließen"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Menü schließen"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Desktop-Freiform-Fenster)"</string>
diff --git a/libs/WindowManager/Shell/res/values-el/strings.xml b/libs/WindowManager/Shell/res/values-el/strings.xml
index 9faf44b..50250cf 100644
--- a/libs/WindowManager/Shell/res/values-el/strings.xml
+++ b/libs/WindowManager/Shell/res/values-el/strings.xml
@@ -133,6 +133,7 @@
<string name="manage_windows_text" msgid="5567366688493093920">"Διαχείριση παραθύρων"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Αλλαγή λόγου διαστάσεων"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"Βελτιστοποίηση προβολής"</string>
+ <string name="handle_menu_error_icon_text" msgid="7200375184879808684">"Εικονίδιο σφάλματος"</string>
<string name="close_text" msgid="4986518933445178928">"Κλείσιμο"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Κλείσιμο μενού"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Προσαρμογή σε παράθυρο στην επιφάνεια εργασίας)"</string>
diff --git a/libs/WindowManager/Shell/res/values-en-rAU/strings.xml b/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
index dcc0554..ddf6dff 100644
--- a/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
@@ -133,6 +133,7 @@
<string name="manage_windows_text" msgid="5567366688493093920">"Manage windows"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Change aspect ratio"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"Optimise view"</string>
+ <string name="handle_menu_error_icon_text" msgid="7200375184879808684">"Error icon"</string>
<string name="close_text" msgid="4986518933445178928">"Close"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Close menu"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Desktop windowing)"</string>
diff --git a/libs/WindowManager/Shell/res/values-en-rCA/strings.xml b/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
index 3e2de7c..654f7c1 100644
--- a/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
@@ -133,6 +133,7 @@
<string name="manage_windows_text" msgid="5567366688493093920">"Manage Windows"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Change aspect ratio"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"Optimize View"</string>
+ <string name="handle_menu_error_icon_text" msgid="7200375184879808684">"Error Icon"</string>
<string name="close_text" msgid="4986518933445178928">"Close"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Close Menu"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Desktop windowing)"</string>
diff --git a/libs/WindowManager/Shell/res/values-en-rGB/strings.xml b/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
index dcc0554..ddf6dff 100644
--- a/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
@@ -133,6 +133,7 @@
<string name="manage_windows_text" msgid="5567366688493093920">"Manage windows"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Change aspect ratio"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"Optimise view"</string>
+ <string name="handle_menu_error_icon_text" msgid="7200375184879808684">"Error icon"</string>
<string name="close_text" msgid="4986518933445178928">"Close"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Close menu"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Desktop windowing)"</string>
diff --git a/libs/WindowManager/Shell/res/values-en-rIN/strings.xml b/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
index dcc0554..ddf6dff 100644
--- a/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
@@ -133,6 +133,7 @@
<string name="manage_windows_text" msgid="5567366688493093920">"Manage windows"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Change aspect ratio"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"Optimise view"</string>
+ <string name="handle_menu_error_icon_text" msgid="7200375184879808684">"Error icon"</string>
<string name="close_text" msgid="4986518933445178928">"Close"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Close menu"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Desktop windowing)"</string>
diff --git a/libs/WindowManager/Shell/res/values-es-rUS/strings.xml b/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
index 0f048a5..9d1d059 100644
--- a/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
+++ b/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
@@ -133,6 +133,7 @@
<string name="manage_windows_text" msgid="5567366688493093920">"Administrar ventanas"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Cambiar relación de aspecto"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"Optimizar vista"</string>
+ <string name="handle_menu_error_icon_text" msgid="7200375184879808684">"Ícono de error"</string>
<string name="close_text" msgid="4986518933445178928">"Cerrar"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Cerrar menú"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (renderización en ventanas de escritorio)"</string>
diff --git a/libs/WindowManager/Shell/res/values-es/strings.xml b/libs/WindowManager/Shell/res/values-es/strings.xml
index ab2b70e..d6fbd0d 100644
--- a/libs/WindowManager/Shell/res/values-es/strings.xml
+++ b/libs/WindowManager/Shell/res/values-es/strings.xml
@@ -133,6 +133,7 @@
<string name="manage_windows_text" msgid="5567366688493093920">"Gestionar ventanas"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Cambiar relación de aspecto"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"Optimizar vista"</string>
+ <string name="handle_menu_error_icon_text" msgid="7200375184879808684">"Icono de error"</string>
<string name="close_text" msgid="4986518933445178928">"Cerrar"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Cerrar menú"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (modo Ventanas)"</string>
diff --git a/libs/WindowManager/Shell/res/values-et/strings.xml b/libs/WindowManager/Shell/res/values-et/strings.xml
index 3560439..7de5466 100644
--- a/libs/WindowManager/Shell/res/values-et/strings.xml
+++ b/libs/WindowManager/Shell/res/values-et/strings.xml
@@ -133,6 +133,7 @@
<string name="manage_windows_text" msgid="5567366688493093920">"Akende haldamine"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Kuvasuhte muutmine"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"Vaate optimeerimine"</string>
+ <string name="handle_menu_error_icon_text" msgid="7200375184879808684">"Veaikoon"</string>
<string name="close_text" msgid="4986518933445178928">"Sule"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Sule menüü"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (töölaua aknad)"</string>
diff --git a/libs/WindowManager/Shell/res/values-eu/strings.xml b/libs/WindowManager/Shell/res/values-eu/strings.xml
index 7a2e11a6..e38015b 100644
--- a/libs/WindowManager/Shell/res/values-eu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-eu/strings.xml
@@ -133,6 +133,7 @@
<string name="manage_windows_text" msgid="5567366688493093920">"Kudeatu leihoak"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Aldatu aspektu-erlazioa"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"Optimizatu ikuspegia"</string>
+ <string name="handle_menu_error_icon_text" msgid="7200375184879808684">"Errore-ikonoa"</string>
<string name="close_text" msgid="4986518933445178928">"Itxi"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Itxi menua"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ordenagailuan leihoak erabiltzeko modua)"</string>
diff --git a/libs/WindowManager/Shell/res/values-fa/strings.xml b/libs/WindowManager/Shell/res/values-fa/strings.xml
index 0b11c8d..a70776e 100644
--- a/libs/WindowManager/Shell/res/values-fa/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fa/strings.xml
@@ -133,6 +133,7 @@
<string name="manage_windows_text" msgid="5567366688493093920">"مدیریت کردن پنجرهها"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"تغییر نسبت ابعادی"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"بهینهسازی نما"</string>
+ <string name="handle_menu_error_icon_text" msgid="7200375184879808684">"نماد خطا"</string>
<string name="close_text" msgid="4986518933445178928">"بستن"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"بستن منو"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (پردازش پنجرهای رایانه)"</string>
diff --git a/libs/WindowManager/Shell/res/values-fi/strings.xml b/libs/WindowManager/Shell/res/values-fi/strings.xml
index d50d693..6641c0f 100644
--- a/libs/WindowManager/Shell/res/values-fi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fi/strings.xml
@@ -133,6 +133,7 @@
<string name="manage_windows_text" msgid="5567366688493093920">"Hallinnoi ikkunoita"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Vaihda kuvasuhdetta"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"Optimoi näkymä"</string>
+ <string name="handle_menu_error_icon_text" msgid="7200375184879808684">"Virhekuvake"</string>
<string name="close_text" msgid="4986518933445178928">"Sulje"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Sulje valikko"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (työpöydän ikkunointi)"</string>
diff --git a/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml b/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
index 5a81df2..75b3a33 100644
--- a/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
@@ -133,6 +133,7 @@
<string name="manage_windows_text" msgid="5567366688493093920">"Gérer les fenêtres"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Modifier les proportions"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"Optimiser l\'affichage"</string>
+ <string name="handle_menu_error_icon_text" msgid="7200375184879808684">"Icône d\'erreur"</string>
<string name="close_text" msgid="4986518933445178928">"Fermer"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Fermer le menu"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (fenêtrage du bureau)"</string>
diff --git a/libs/WindowManager/Shell/res/values-fr/strings.xml b/libs/WindowManager/Shell/res/values-fr/strings.xml
index 6c260d1..1f29c38 100644
--- a/libs/WindowManager/Shell/res/values-fr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fr/strings.xml
@@ -133,6 +133,7 @@
<string name="manage_windows_text" msgid="5567366688493093920">"Gérer les fenêtres"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Modifier le format"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"Optimiser l\'affichage"</string>
+ <string name="handle_menu_error_icon_text" msgid="7200375184879808684">"Icône Erreur"</string>
<string name="close_text" msgid="4986518933445178928">"Fermer"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Fermer le menu"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (fenêtrage de bureau)"</string>
diff --git a/libs/WindowManager/Shell/res/values-gl/strings.xml b/libs/WindowManager/Shell/res/values-gl/strings.xml
index b9ccf76..79ce543 100644
--- a/libs/WindowManager/Shell/res/values-gl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-gl/strings.xml
@@ -133,6 +133,7 @@
<string name="manage_windows_text" msgid="5567366688493093920">"Xestionar as ventás"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Cambiar a proporción"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"Optimizar a vista"</string>
+ <string name="handle_menu_error_icon_text" msgid="7200375184879808684">"Icona de erro"</string>
<string name="close_text" msgid="4986518933445178928">"Pechar"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Pechar o menú"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (modo con ventás tipo ordenador)"</string>
diff --git a/libs/WindowManager/Shell/res/values-gu/strings.xml b/libs/WindowManager/Shell/res/values-gu/strings.xml
index 50bf7b5f..9ff958f9 100644
--- a/libs/WindowManager/Shell/res/values-gu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-gu/strings.xml
@@ -133,6 +133,7 @@
<string name="manage_windows_text" msgid="5567366688493093920">"વિન્ડો મેનેજ કરો"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"સાપેક્ષ ગુણોત્તર બદલો"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"વ્યૂ ઑપ્ટિમાઇઝ કરો"</string>
+ <string name="handle_menu_error_icon_text" msgid="7200375184879808684">"ભૂલનું આઇકન"</string>
<string name="close_text" msgid="4986518933445178928">"બંધ કરો"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"મેનૂ બંધ કરો"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ડેસ્કટૉપ વિન્ડોઇંગ)"</string>
diff --git a/libs/WindowManager/Shell/res/values-hi/strings.xml b/libs/WindowManager/Shell/res/values-hi/strings.xml
index a0dd30b..fdfd44eba 100644
--- a/libs/WindowManager/Shell/res/values-hi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hi/strings.xml
@@ -133,6 +133,7 @@
<string name="manage_windows_text" msgid="5567366688493093920">"विंडो मैनेज करें"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"आसपेक्ट रेशियो (लंबाई-चौड़ाई का अनुपात) बदलें"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"व्यू को ऑप्टिमाइज़ करें"</string>
+ <string name="handle_menu_error_icon_text" msgid="7200375184879808684">"गड़बड़ी का आइकॉन"</string>
<string name="close_text" msgid="4986518933445178928">"बंद करें"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"मेन्यू बंद करें"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (डेस्कटॉप विंडोविंग)"</string>
diff --git a/libs/WindowManager/Shell/res/values-hr/strings.xml b/libs/WindowManager/Shell/res/values-hr/strings.xml
index c9e4d13..6b8e48b 100644
--- a/libs/WindowManager/Shell/res/values-hr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hr/strings.xml
@@ -133,6 +133,7 @@
<string name="manage_windows_text" msgid="5567366688493093920">"Upravljanje prozorima"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Promijeni omjer slike"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"Optimiziraj prikaz"</string>
+ <string name="handle_menu_error_icon_text" msgid="7200375184879808684">"Ikona pogreške"</string>
<string name="close_text" msgid="4986518933445178928">"Zatvorite"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Zatvorite izbornik"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (prikaz u prozorima na računalu)"</string>
diff --git a/libs/WindowManager/Shell/res/values-hu/strings.xml b/libs/WindowManager/Shell/res/values-hu/strings.xml
index 6dae47f..3119d3b 100644
--- a/libs/WindowManager/Shell/res/values-hu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hu/strings.xml
@@ -133,6 +133,7 @@
<string name="manage_windows_text" msgid="5567366688493093920">"Ablakok kezelése"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Méretarány módosítása"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"Nézet optimalizálása"</string>
+ <string name="handle_menu_error_icon_text" msgid="7200375184879808684">"Hiba ikon"</string>
<string name="close_text" msgid="4986518933445178928">"Bezárás"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Menü bezárása"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Asztali ablakkezelési mód)"</string>
diff --git a/libs/WindowManager/Shell/res/values-hy/strings.xml b/libs/WindowManager/Shell/res/values-hy/strings.xml
index b32bb94..db8490a 100644
--- a/libs/WindowManager/Shell/res/values-hy/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hy/strings.xml
@@ -133,6 +133,7 @@
<string name="manage_windows_text" msgid="5567366688493093920">"Կառավարել պատուհանները"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Փոխել կողմերի հարաբերակցությունը"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"Օպտիմալացնել"</string>
+ <string name="handle_menu_error_icon_text" msgid="7200375184879808684">"Սխալի պատկերակ"</string>
<string name="close_text" msgid="4986518933445178928">"Փակել"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Փակել ընտրացանկը"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (համակարգչային պատուհաններ)"</string>
diff --git a/libs/WindowManager/Shell/res/values-in/strings.xml b/libs/WindowManager/Shell/res/values-in/strings.xml
index 14c6113..592f6f4 100644
--- a/libs/WindowManager/Shell/res/values-in/strings.xml
+++ b/libs/WindowManager/Shell/res/values-in/strings.xml
@@ -133,6 +133,7 @@
<string name="manage_windows_text" msgid="5567366688493093920">"Kelola Jendela"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Ubah rasio aspek"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"Optimalkan Tampilan"</string>
+ <string name="handle_menu_error_icon_text" msgid="7200375184879808684">"Ikon Error"</string>
<string name="close_text" msgid="4986518933445178928">"Tutup"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Tutup Menu"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Mode jendela desktop)"</string>
diff --git a/libs/WindowManager/Shell/res/values-is/strings.xml b/libs/WindowManager/Shell/res/values-is/strings.xml
index 9e61839..c6ab9d3 100644
--- a/libs/WindowManager/Shell/res/values-is/strings.xml
+++ b/libs/WindowManager/Shell/res/values-is/strings.xml
@@ -133,6 +133,7 @@
<string name="manage_windows_text" msgid="5567366688493093920">"Stjórna gluggum"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Breyta myndhlutfalli"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"Fínstilla yfirlit"</string>
+ <string name="handle_menu_error_icon_text" msgid="7200375184879808684">"Villutákn"</string>
<string name="close_text" msgid="4986518933445178928">"Loka"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Loka valmynd"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (gluggastilling í tölvu)"</string>
diff --git a/libs/WindowManager/Shell/res/values-it/strings.xml b/libs/WindowManager/Shell/res/values-it/strings.xml
index 34b66b1..3ac668b 100644
--- a/libs/WindowManager/Shell/res/values-it/strings.xml
+++ b/libs/WindowManager/Shell/res/values-it/strings.xml
@@ -133,6 +133,7 @@
<string name="manage_windows_text" msgid="5567366688493093920">"Gestisci finestre"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Cambia proporzioni"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"Ottimizza visualizzazione"</string>
+ <string name="handle_menu_error_icon_text" msgid="7200375184879808684">"Icona di errore"</string>
<string name="close_text" msgid="4986518933445178928">"Chiudi"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Chiudi il menu"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (app in finestre)"</string>
diff --git a/libs/WindowManager/Shell/res/values-iw/strings.xml b/libs/WindowManager/Shell/res/values-iw/strings.xml
index e02b706..63da05b 100644
--- a/libs/WindowManager/Shell/res/values-iw/strings.xml
+++ b/libs/WindowManager/Shell/res/values-iw/strings.xml
@@ -133,6 +133,7 @@
<string name="manage_windows_text" msgid="5567366688493093920">"ניהול החלונות"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"שינוי יחס הגובה-רוחב"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"אופטימיזציה של התצוגה"</string>
+ <string name="handle_menu_error_icon_text" msgid="7200375184879808684">"סמל שגיאה"</string>
<string name="close_text" msgid="4986518933445178928">"סגירה"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"סגירת התפריט"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (שינוי דינמי של חלונות במחשב)"</string>
diff --git a/libs/WindowManager/Shell/res/values-ja/strings.xml b/libs/WindowManager/Shell/res/values-ja/strings.xml
index 19ac853..92e1c99 100644
--- a/libs/WindowManager/Shell/res/values-ja/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ja/strings.xml
@@ -133,6 +133,7 @@
<string name="manage_windows_text" msgid="5567366688493093920">"ウィンドウを管理する"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"アスペクト比を変更"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"ビューを最適化"</string>
+ <string name="handle_menu_error_icon_text" msgid="7200375184879808684">"エラーアイコン"</string>
<string name="close_text" msgid="4986518933445178928">"閉じる"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"メニューを閉じる"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g>(デスクトップ ウィンドウ)"</string>
diff --git a/libs/WindowManager/Shell/res/values-ka/strings.xml b/libs/WindowManager/Shell/res/values-ka/strings.xml
index 97e5b8b..f6796d7 100644
--- a/libs/WindowManager/Shell/res/values-ka/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ka/strings.xml
@@ -133,6 +133,7 @@
<string name="manage_windows_text" msgid="5567366688493093920">"ფანჯრების მართვა"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"თანაფარდობის შეცვლა"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"ხედის ოპტიმიზება"</string>
+ <string name="handle_menu_error_icon_text" msgid="7200375184879808684">"შეცდომის ხატულა"</string>
<string name="close_text" msgid="4986518933445178928">"დახურვა"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"მენიუს დახურვა"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (დესკტოპის ფანჯრის რეჟიმი)"</string>
diff --git a/libs/WindowManager/Shell/res/values-kk/strings.xml b/libs/WindowManager/Shell/res/values-kk/strings.xml
index 21f0496..a4d059a 100644
--- a/libs/WindowManager/Shell/res/values-kk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-kk/strings.xml
@@ -133,6 +133,7 @@
<string name="manage_windows_text" msgid="5567366688493093920">"Терезелерді басқару"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Арақатынасты өзгерту"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"Көріністі оңтайландыру"</string>
+ <string name="handle_menu_error_icon_text" msgid="7200375184879808684">"Қате белгішесі"</string>
<string name="close_text" msgid="4986518933445178928">"Жабу"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Мәзірді жабу"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (компьютерлік терезелер режимі)"</string>
diff --git a/libs/WindowManager/Shell/res/values-km/strings.xml b/libs/WindowManager/Shell/res/values-km/strings.xml
index 99817c0..516a87f 100644
--- a/libs/WindowManager/Shell/res/values-km/strings.xml
+++ b/libs/WindowManager/Shell/res/values-km/strings.xml
@@ -133,6 +133,7 @@
<string name="manage_windows_text" msgid="5567366688493093920">"គ្រប់គ្រងវិនដូ"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"ប្ដូរសមាមាត្រ"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"បង្កើនប្រសិទ្ធភាពការមើល"</string>
+ <string name="handle_menu_error_icon_text" msgid="7200375184879808684">"រូបបញ្ហា"</string>
<string name="close_text" msgid="4986518933445178928">"បិទ"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"បិទម៉ឺនុយ"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (មុខងារវិនដូកុំព្យូទ័រ)"</string>
diff --git a/libs/WindowManager/Shell/res/values-kn/strings.xml b/libs/WindowManager/Shell/res/values-kn/strings.xml
index 5c465bf..5982855 100644
--- a/libs/WindowManager/Shell/res/values-kn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-kn/strings.xml
@@ -133,6 +133,7 @@
<string name="manage_windows_text" msgid="5567366688493093920">"ವಿಂಡೋಗಳನ್ನು ನಿರ್ವಹಿಸಿ"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"ದೃಶ್ಯಾನುಪಾತವನ್ನು ಬದಲಾಯಿಸಿ"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"ವೀಕ್ಷಣೆಯನ್ನು ಆಪ್ಟಿಮೈಸ್ ಮಾಡಿ"</string>
+ <string name="handle_menu_error_icon_text" msgid="7200375184879808684">"ದೋಷದ ಐಕಾನ್"</string>
<string name="close_text" msgid="4986518933445178928">"ಮುಚ್ಚಿ"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"ಮೆನು ಮುಚ್ಚಿ"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ಡೆಸ್ಕ್ಟಾಪ್ ವಿಂಡೋಯಿಂಗ್)"</string>
diff --git a/libs/WindowManager/Shell/res/values-ko/strings.xml b/libs/WindowManager/Shell/res/values-ko/strings.xml
index d856438..8073071e 100644
--- a/libs/WindowManager/Shell/res/values-ko/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ko/strings.xml
@@ -133,6 +133,7 @@
<string name="manage_windows_text" msgid="5567366688493093920">"창 관리"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"가로세로 비율 변경"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"뷰 최적화"</string>
+ <string name="handle_menu_error_icon_text" msgid="7200375184879808684">"오류 아이콘"</string>
<string name="close_text" msgid="4986518933445178928">"닫기"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"메뉴 닫기"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g>(데스크톱 윈도윙)"</string>
diff --git a/libs/WindowManager/Shell/res/values-ky/strings.xml b/libs/WindowManager/Shell/res/values-ky/strings.xml
index 8c15c6a..7aa0355 100644
--- a/libs/WindowManager/Shell/res/values-ky/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ky/strings.xml
@@ -133,6 +133,7 @@
<string name="manage_windows_text" msgid="5567366688493093920">"Терезелерди тескөө"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Тараптардын катнашын өзгөртүү"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"Көрүнүштү оптималдаштыруу"</string>
+ <string name="handle_menu_error_icon_text" msgid="7200375184879808684">"Ката сүрөтчөсү"</string>
<string name="close_text" msgid="4986518933445178928">"Жабуу"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Менюну жабуу"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Иш тактанын терезелери)"</string>
diff --git a/libs/WindowManager/Shell/res/values-lo/strings.xml b/libs/WindowManager/Shell/res/values-lo/strings.xml
index f3e6f81..05e837f 100644
--- a/libs/WindowManager/Shell/res/values-lo/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lo/strings.xml
@@ -133,6 +133,7 @@
<string name="manage_windows_text" msgid="5567366688493093920">"ຈັດການໜ້າຈໍ"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"ປ່ຽນອັດຕາສ່ວນຮູບ"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"ມຸມມອງເພີ່ມປະສິດທິພາບ"</string>
+ <string name="handle_menu_error_icon_text" msgid="7200375184879808684">"ໄອຄອນຂໍ້ຜິດພາດ"</string>
<string name="close_text" msgid="4986518933445178928">"ປິດ"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"ປິດເມນູ"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ໜ້າຈໍເດັສທັອບ)"</string>
diff --git a/libs/WindowManager/Shell/res/values-lt/strings.xml b/libs/WindowManager/Shell/res/values-lt/strings.xml
index d22db2e..3e8d574 100644
--- a/libs/WindowManager/Shell/res/values-lt/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lt/strings.xml
@@ -133,6 +133,7 @@
<string name="manage_windows_text" msgid="5567366688493093920">"Tvarkyti langus"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Keisti kraštinių santykį"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"Optimizuoti rodinį"</string>
+ <string name="handle_menu_error_icon_text" msgid="7200375184879808684">"Klaidos piktograma"</string>
<string name="close_text" msgid="4986518933445178928">"Uždaryti"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Uždaryti meniu"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"„<xliff:g id="APP_NAME">%1$s</xliff:g>“ (versijos staliniams kompiuteriams rodinys)"</string>
diff --git a/libs/WindowManager/Shell/res/values-lv/strings.xml b/libs/WindowManager/Shell/res/values-lv/strings.xml
index 0e9d4e4..5a63efe8 100644
--- a/libs/WindowManager/Shell/res/values-lv/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lv/strings.xml
@@ -133,6 +133,7 @@
<string name="manage_windows_text" msgid="5567366688493093920">"Pārvaldīt logus"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Mainīt malu attiecību"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"Optimizēt skatu"</string>
+ <string name="handle_menu_error_icon_text" msgid="7200375184879808684">"Kļūdas ikona"</string>
<string name="close_text" msgid="4986518933445178928">"Aizvērt"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Aizvērt izvēlni"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (darbvirsmas logošana)"</string>
diff --git a/libs/WindowManager/Shell/res/values-mk/strings.xml b/libs/WindowManager/Shell/res/values-mk/strings.xml
index 1b7fe57..79c17be 100644
--- a/libs/WindowManager/Shell/res/values-mk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mk/strings.xml
@@ -133,6 +133,7 @@
<string name="manage_windows_text" msgid="5567366688493093920">"Управувајте со прозорците"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Промени го соодносот"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"Оптимизирај го приказот"</string>
+ <string name="handle_menu_error_icon_text" msgid="7200375184879808684">"Икона за грешка"</string>
<string name="close_text" msgid="4986518933445178928">"Затворете"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Затворете го менито"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (режим со прозорци на работната површина)"</string>
diff --git a/libs/WindowManager/Shell/res/values-ml/strings.xml b/libs/WindowManager/Shell/res/values-ml/strings.xml
index 01b9dee..43834b5 100644
--- a/libs/WindowManager/Shell/res/values-ml/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ml/strings.xml
@@ -133,6 +133,7 @@
<string name="manage_windows_text" msgid="5567366688493093920">"വിൻഡോകൾ മാനേജ് ചെയ്യുക"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"വീക്ഷണ അനുപാതം മാറ്റുക"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"കാഴ്ച ഒപ്റ്റിമൈസ് ചെയ്യുക"</string>
+ <string name="handle_menu_error_icon_text" msgid="7200375184879808684">"പിശക് ഐക്കൺ"</string>
<string name="close_text" msgid="4986518933445178928">"അടയ്ക്കുക"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"മെനു അടയ്ക്കുക"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ഡെസ്ക്ടോപ്പ് വിൻഡോയിംഗ്)"</string>
diff --git a/libs/WindowManager/Shell/res/values-mn/strings.xml b/libs/WindowManager/Shell/res/values-mn/strings.xml
index 8486c9e..5c60208 100644
--- a/libs/WindowManager/Shell/res/values-mn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mn/strings.xml
@@ -133,6 +133,7 @@
<string name="manage_windows_text" msgid="5567366688493093920">"Цонхнуудыг удирдах"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Аспектын харьцааг өөрчлөх"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"Харагдах байдлыг оновчлох"</string>
+ <string name="handle_menu_error_icon_text" msgid="7200375184879808684">"Алдааны дүрс тэмдэг"</string>
<string name="close_text" msgid="4986518933445178928">"Хаах"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Цэсийг хаах"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Дэлгэцийн цонх үүсгэх онцлог)"</string>
diff --git a/libs/WindowManager/Shell/res/values-mr/strings.xml b/libs/WindowManager/Shell/res/values-mr/strings.xml
index 2cb04ac..2d6dc28 100644
--- a/libs/WindowManager/Shell/res/values-mr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mr/strings.xml
@@ -133,6 +133,7 @@
<string name="manage_windows_text" msgid="5567366688493093920">"विंडो व्यवस्थापित करा"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"आस्पेक्ट रेशो बदला"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"दृश्य ऑप्टिमाइझ करा"</string>
+ <string name="handle_menu_error_icon_text" msgid="7200375184879808684">"एरर आयकन"</string>
<string name="close_text" msgid="4986518933445178928">"बंद करा"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"मेनू बंद करा"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (डेस्कटॉप विंडोइंग)"</string>
diff --git a/libs/WindowManager/Shell/res/values-ms/strings.xml b/libs/WindowManager/Shell/res/values-ms/strings.xml
index 1f6cb3b..b0e8187d 100644
--- a/libs/WindowManager/Shell/res/values-ms/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ms/strings.xml
@@ -133,6 +133,7 @@
<string name="manage_windows_text" msgid="5567366688493093920">"Urus Tetingkap"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Tukar nisbah bidang"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"Optimumkan Paparan"</string>
+ <string name="handle_menu_error_icon_text" msgid="7200375184879808684">"Ikon Ralat"</string>
<string name="close_text" msgid="4986518933445178928">"Tutup"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Tutup Menu"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Tetingkap desktop)"</string>
diff --git a/libs/WindowManager/Shell/res/values-my/strings.xml b/libs/WindowManager/Shell/res/values-my/strings.xml
index 684a52b..f4b45dc 100644
--- a/libs/WindowManager/Shell/res/values-my/strings.xml
+++ b/libs/WindowManager/Shell/res/values-my/strings.xml
@@ -133,6 +133,7 @@
<string name="manage_windows_text" msgid="5567366688493093920">"ဝင်းဒိုးများ စီမံရန်"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"အချိုးအစား ပြောင်းရန်"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"မြင်ကွင်းကို ပိုကောင်းအောင်လုပ်ရန်"</string>
+ <string name="handle_menu_error_icon_text" msgid="7200375184879808684">"အမှားအယွင်း သင်္ကေတ"</string>
<string name="close_text" msgid="4986518933445178928">"ပိတ်ရန်"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"မီနူး ပိတ်ရန်"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ဒက်စ်တော့ဝင်းဒိုးမုဒ်)"</string>
diff --git a/libs/WindowManager/Shell/res/values-nb/strings.xml b/libs/WindowManager/Shell/res/values-nb/strings.xml
index 0b6dd8a..d82bdcf 100644
--- a/libs/WindowManager/Shell/res/values-nb/strings.xml
+++ b/libs/WindowManager/Shell/res/values-nb/strings.xml
@@ -133,6 +133,7 @@
<string name="manage_windows_text" msgid="5567366688493093920">"Administrer vinduene"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Endre høyde/bredde-forholdet"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"Optimaliser visning"</string>
+ <string name="handle_menu_error_icon_text" msgid="7200375184879808684">"Feilikon"</string>
<string name="close_text" msgid="4986518933445178928">"Lukk"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Lukk menyen"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (datamaskin-vindusvisning)"</string>
diff --git a/libs/WindowManager/Shell/res/values-ne/strings.xml b/libs/WindowManager/Shell/res/values-ne/strings.xml
index b370a6f..be98169 100644
--- a/libs/WindowManager/Shell/res/values-ne/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ne/strings.xml
@@ -133,6 +133,7 @@
<string name="manage_windows_text" msgid="5567366688493093920">"विन्डोहरू व्यवस्थापन गर्नुहोस्"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"एस्पेक्ट रेसियो परिवर्तन गर्नुहोस्"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"भ्यू अप्टिमाइज गर्नुहोस्"</string>
+ <string name="handle_menu_error_icon_text" msgid="7200375184879808684">"त्रुटि जनाउने आइकन"</string>
<string name="close_text" msgid="4986518933445178928">"बन्द गर्नुहोस्"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"मेनु बन्द गर्नुहोस्"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (डेस्कटप विन्डोइङ)"</string>
diff --git a/libs/WindowManager/Shell/res/values-nl/strings.xml b/libs/WindowManager/Shell/res/values-nl/strings.xml
index 19420bd0..d3a02e5 100644
--- a/libs/WindowManager/Shell/res/values-nl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-nl/strings.xml
@@ -133,6 +133,7 @@
<string name="manage_windows_text" msgid="5567366688493093920">"Vensters beheren"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Beeldverhouding wijzigen"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"Weergave optimaliseren"</string>
+ <string name="handle_menu_error_icon_text" msgid="7200375184879808684">"Fouticoon"</string>
<string name="close_text" msgid="4986518933445178928">"Sluiten"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Menu sluiten"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (desktopvensterfunctie)"</string>
diff --git a/libs/WindowManager/Shell/res/values-or/strings.xml b/libs/WindowManager/Shell/res/values-or/strings.xml
index 9659dc6..8e1467b 100644
--- a/libs/WindowManager/Shell/res/values-or/strings.xml
+++ b/libs/WindowManager/Shell/res/values-or/strings.xml
@@ -133,6 +133,7 @@
<string name="manage_windows_text" msgid="5567366688493093920">"ୱିଣ୍ଡୋଗୁଡ଼ିକୁ ପରିଚାଳନା କରନ୍ତୁ"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"ଚଉଡ଼ା ଓ ଉଚ୍ଚତାର ଅନୁପାତ ପରିବର୍ତ୍ତନ କରନ୍ତୁ"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"ଭ୍ୟୁ ଅପ୍ଟିମାଇଜ କରନ୍ତୁ"</string>
+ <string name="handle_menu_error_icon_text" msgid="7200375184879808684">"ତ୍ରୁଟି ଆଇକନ"</string>
<string name="close_text" msgid="4986518933445178928">"ବନ୍ଦ କରନ୍ତୁ"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"ମେନୁ ବନ୍ଦ କରନ୍ତୁ"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ଡେସ୍କଟପ ୱିଣ୍ଡୋଇଂ)"</string>
diff --git a/libs/WindowManager/Shell/res/values-pa/strings.xml b/libs/WindowManager/Shell/res/values-pa/strings.xml
index 9e34af5..c1b659b 100644
--- a/libs/WindowManager/Shell/res/values-pa/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pa/strings.xml
@@ -133,6 +133,7 @@
<string name="manage_windows_text" msgid="5567366688493093920">"ਵਿੰਡੋਆਂ ਦਾ ਪ੍ਰਬੰਧਨ ਕਰੋ"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"ਆਕਾਰ ਅਨੁਪਾਤ ਬਦਲੋ"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"ਦ੍ਰਿਸ਼ ਨੂੰ ਸੁਯੋਗ ਬਣਾਓ"</string>
+ <string name="handle_menu_error_icon_text" msgid="7200375184879808684">"ਗੜਬੜ ਪ੍ਰਤੀਕ"</string>
<string name="close_text" msgid="4986518933445178928">"ਬੰਦ ਕਰੋ"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"ਮੀਨੂ ਬੰਦ ਕਰੋ"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ਡੈਸਕਟਾਪ ਵਿੰਡੋ)"</string>
diff --git a/libs/WindowManager/Shell/res/values-pl/strings.xml b/libs/WindowManager/Shell/res/values-pl/strings.xml
index 4c0613b..741e7ec 100644
--- a/libs/WindowManager/Shell/res/values-pl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pl/strings.xml
@@ -133,6 +133,7 @@
<string name="manage_windows_text" msgid="5567366688493093920">"Zarządzaj oknami"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Zmień format obrazu"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"Zoptymalizuj widok"</string>
+ <string name="handle_menu_error_icon_text" msgid="7200375184879808684">"Ikona błędu"</string>
<string name="close_text" msgid="4986518933445178928">"Zamknij"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Zamknij menu"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (tryb okien na pulpicie)"</string>
diff --git a/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml b/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
index 1c2a3fa..01c1ce1 100644
--- a/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
@@ -133,6 +133,7 @@
<string name="manage_windows_text" msgid="5567366688493093920">"Gerenciar janelas"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Mudar a proporção"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"Visualização otimizada"</string>
+ <string name="handle_menu_error_icon_text" msgid="7200375184879808684">"Ícone de erro"</string>
<string name="close_text" msgid="4986518933445178928">"Fechar"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Fechar menu"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (modo janela para computador)"</string>
diff --git a/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml b/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
index ca1005d..13f2b3c 100644
--- a/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
@@ -133,6 +133,7 @@
<string name="manage_windows_text" msgid="5567366688493093920">"Gerir janelas"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Alterar formato"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"Otimizar vista"</string>
+ <string name="handle_menu_error_icon_text" msgid="7200375184879808684">"Ícone de erro"</string>
<string name="close_text" msgid="4986518933445178928">"Fechar"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Fechar menu"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (janelas de computador)"</string>
diff --git a/libs/WindowManager/Shell/res/values-pt/strings.xml b/libs/WindowManager/Shell/res/values-pt/strings.xml
index 1c2a3fa..01c1ce1 100644
--- a/libs/WindowManager/Shell/res/values-pt/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt/strings.xml
@@ -133,6 +133,7 @@
<string name="manage_windows_text" msgid="5567366688493093920">"Gerenciar janelas"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Mudar a proporção"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"Visualização otimizada"</string>
+ <string name="handle_menu_error_icon_text" msgid="7200375184879808684">"Ícone de erro"</string>
<string name="close_text" msgid="4986518933445178928">"Fechar"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Fechar menu"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (modo janela para computador)"</string>
diff --git a/libs/WindowManager/Shell/res/values-ro/strings.xml b/libs/WindowManager/Shell/res/values-ro/strings.xml
index d09da5b..8d294de 100644
--- a/libs/WindowManager/Shell/res/values-ro/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ro/strings.xml
@@ -133,6 +133,7 @@
<string name="manage_windows_text" msgid="5567366688493093920">"Gestionează ferestrele"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Schimbă raportul de dimensiuni"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"Optimizează afișarea"</string>
+ <string name="handle_menu_error_icon_text" msgid="7200375184879808684">"Pictogramă de eroare"</string>
<string name="close_text" msgid="4986518933445178928">"Închide"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Închide meniul"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ferestre pe desktop)"</string>
diff --git a/libs/WindowManager/Shell/res/values-ru/strings.xml b/libs/WindowManager/Shell/res/values-ru/strings.xml
index d9ff307..74cab45 100644
--- a/libs/WindowManager/Shell/res/values-ru/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ru/strings.xml
@@ -133,6 +133,7 @@
<string name="manage_windows_text" msgid="5567366688493093920">"Управление окнами"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Изменить соотношение сторон"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"Оптимизировать"</string>
+ <string name="handle_menu_error_icon_text" msgid="7200375184879808684">"Значок ошибки"</string>
<string name="close_text" msgid="4986518933445178928">"Закрыть"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Закрыть меню"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (режим компьютерных окон)"</string>
diff --git a/libs/WindowManager/Shell/res/values-si/strings.xml b/libs/WindowManager/Shell/res/values-si/strings.xml
index 32daf89..613f6ab 100644
--- a/libs/WindowManager/Shell/res/values-si/strings.xml
+++ b/libs/WindowManager/Shell/res/values-si/strings.xml
@@ -133,6 +133,7 @@
<string name="manage_windows_text" msgid="5567366688493093920">"කවුළු කළමනාකරණය කරන්න"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"දර්ශන අනුපාතය වෙනස් කරන්න"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"දසුන ප්රශස්ත කරන්න"</string>
+ <string name="handle_menu_error_icon_text" msgid="7200375184879808684">"දෝෂ නිරූපකය"</string>
<string name="close_text" msgid="4986518933445178928">"වසන්න"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"මෙනුව වසන්න"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ඩෙස්ක්ටොප් කවුළුකරණය)"</string>
diff --git a/libs/WindowManager/Shell/res/values-sk/strings.xml b/libs/WindowManager/Shell/res/values-sk/strings.xml
index fd5c7a7..ad00b3a 100644
--- a/libs/WindowManager/Shell/res/values-sk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sk/strings.xml
@@ -133,6 +133,7 @@
<string name="manage_windows_text" msgid="5567366688493093920">"Spravovať okná"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Zmeniť pomer strán"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"Optimalizovať zobrazenie"</string>
+ <string name="handle_menu_error_icon_text" msgid="7200375184879808684">"Ikona chyby"</string>
<string name="close_text" msgid="4986518933445178928">"Zavrieť"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Zavrieť ponuku"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (windowing na pracovnej ploche)"</string>
diff --git a/libs/WindowManager/Shell/res/values-sl/strings.xml b/libs/WindowManager/Shell/res/values-sl/strings.xml
index 15a4589..290276c 100644
--- a/libs/WindowManager/Shell/res/values-sl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sl/strings.xml
@@ -133,6 +133,7 @@
<string name="manage_windows_text" msgid="5567366688493093920">"Upravljanje oken"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Sprememba razmerja stranic"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"Optimizacija pogleda"</string>
+ <string name="handle_menu_error_icon_text" msgid="7200375184879808684">"Ikona za napako"</string>
<string name="close_text" msgid="4986518933445178928">"Zapri"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Zapri meni"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (namizni način prikaza več oken hkrati)"</string>
diff --git a/libs/WindowManager/Shell/res/values-sq/strings.xml b/libs/WindowManager/Shell/res/values-sq/strings.xml
index 77316aa..b4fd8913 100644
--- a/libs/WindowManager/Shell/res/values-sq/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sq/strings.xml
@@ -133,6 +133,7 @@
<string name="manage_windows_text" msgid="5567366688493093920">"Menaxho dritaret"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Ndrysho raportin e pamjes"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"Optimizo pamjen"</string>
+ <string name="handle_menu_error_icon_text" msgid="7200375184879808684">"Ikona e gabimit"</string>
<string name="close_text" msgid="4986518933445178928">"Mbyll"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Mbyll menynë"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (modaliteti me dritare si në desktop)"</string>
diff --git a/libs/WindowManager/Shell/res/values-sr/strings.xml b/libs/WindowManager/Shell/res/values-sr/strings.xml
index 66bc2a6..7b8e8c8 100644
--- a/libs/WindowManager/Shell/res/values-sr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sr/strings.xml
@@ -133,6 +133,7 @@
<string name="manage_windows_text" msgid="5567366688493093920">"Управљајте прозорима"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Промени размеру"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"Оптимизујте приказ"</string>
+ <string name="handle_menu_error_icon_text" msgid="7200375184879808684">"Икона грешке"</string>
<string name="close_text" msgid="4986518933445178928">"Затворите"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Затворите мени"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (приказ рачунарских прозора)"</string>
diff --git a/libs/WindowManager/Shell/res/values-sv/strings.xml b/libs/WindowManager/Shell/res/values-sv/strings.xml
index 2e77f64..80311bd 100644
--- a/libs/WindowManager/Shell/res/values-sv/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sv/strings.xml
@@ -133,6 +133,7 @@
<string name="manage_windows_text" msgid="5567366688493093920">"Hantera fönster"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Ändra bildformat"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"Optimera vy"</string>
+ <string name="handle_menu_error_icon_text" msgid="7200375184879808684">"Felikon"</string>
<string name="close_text" msgid="4986518933445178928">"Stäng"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Stäng menyn"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (fönsterstapling)"</string>
diff --git a/libs/WindowManager/Shell/res/values-sw/strings.xml b/libs/WindowManager/Shell/res/values-sw/strings.xml
index 3b11337..9c004dc 100644
--- a/libs/WindowManager/Shell/res/values-sw/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sw/strings.xml
@@ -133,6 +133,7 @@
<string name="manage_windows_text" msgid="5567366688493093920">"Dhibiti Windows"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Badilisha uwiano"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"Boresha Mwonekano"</string>
+ <string name="handle_menu_error_icon_text" msgid="7200375184879808684">"Aikoni ya Hitilafu"</string>
<string name="close_text" msgid="4986518933445178928">"Funga"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Funga Menyu"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Kupanga madirisha ya kompyuta ya mezani)"</string>
diff --git a/libs/WindowManager/Shell/res/values-ta/strings.xml b/libs/WindowManager/Shell/res/values-ta/strings.xml
index 4ed3927..e9d01e4 100644
--- a/libs/WindowManager/Shell/res/values-ta/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ta/strings.xml
@@ -133,6 +133,7 @@
<string name="manage_windows_text" msgid="5567366688493093920">"சாளரங்களை நிர்வகிக்கலாம்"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"தோற்ற விகிதத்தை மாற்று"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"காட்சியை மேம்படுத்தும்"</string>
+ <string name="handle_menu_error_icon_text" msgid="7200375184879808684">"பிழை ஐகான்"</string>
<string name="close_text" msgid="4986518933445178928">"மூடும்"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"மெனுவை மூடும்"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (டெஸ்க்டாப் சாளரமாக்குதல்)"</string>
diff --git a/libs/WindowManager/Shell/res/values-te/strings.xml b/libs/WindowManager/Shell/res/values-te/strings.xml
index d7f79ca..56bbf87 100644
--- a/libs/WindowManager/Shell/res/values-te/strings.xml
+++ b/libs/WindowManager/Shell/res/values-te/strings.xml
@@ -133,6 +133,7 @@
<string name="manage_windows_text" msgid="5567366688493093920">"విండోలను మేనేజ్ చేయండి"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"ఆకార నిష్పత్తిని మార్చండి"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"వీక్షణను ఆప్టిమైజ్ చేయండి"</string>
+ <string name="handle_menu_error_icon_text" msgid="7200375184879808684">"ఎర్రర్ చిహ్నం"</string>
<string name="close_text" msgid="4986518933445178928">"మూసివేయండి"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"మెనూను మూసివేయండి"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (డెస్క్టాప్ వీక్షణ)"</string>
diff --git a/libs/WindowManager/Shell/res/values-th/strings.xml b/libs/WindowManager/Shell/res/values-th/strings.xml
index cb456cb..d3e8e3b 100644
--- a/libs/WindowManager/Shell/res/values-th/strings.xml
+++ b/libs/WindowManager/Shell/res/values-th/strings.xml
@@ -133,6 +133,7 @@
<string name="manage_windows_text" msgid="5567366688493093920">"จัดการหน้าต่าง"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"เปลี่ยนสัดส่วนการแสดงผล"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"เพิ่มประสิทธิภาพมุมมอง"</string>
+ <string name="handle_menu_error_icon_text" msgid="7200375184879808684">"ไอคอนข้อผิดพลาด"</string>
<string name="close_text" msgid="4986518933445178928">"ปิด"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"ปิดเมนู"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (หน้าต่างเดสก์ท็อป)"</string>
diff --git a/libs/WindowManager/Shell/res/values-tl/strings.xml b/libs/WindowManager/Shell/res/values-tl/strings.xml
index 83a7470..76571d5 100644
--- a/libs/WindowManager/Shell/res/values-tl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-tl/strings.xml
@@ -133,6 +133,7 @@
<string name="manage_windows_text" msgid="5567366688493093920">"Pamahalaan ang Mga Window"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Baguhin ang aspect ratio"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"I-optimize ang View"</string>
+ <string name="handle_menu_error_icon_text" msgid="7200375184879808684">"Icon ng Error"</string>
<string name="close_text" msgid="4986518933445178928">"Isara"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Isara ang Menu"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Desktop windowing)"</string>
diff --git a/libs/WindowManager/Shell/res/values-tr/strings.xml b/libs/WindowManager/Shell/res/values-tr/strings.xml
index 3d565bc..9cd3d7a 100644
--- a/libs/WindowManager/Shell/res/values-tr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-tr/strings.xml
@@ -133,6 +133,7 @@
<string name="manage_windows_text" msgid="5567366688493093920">"Pencereleri yönet"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"En boy oranını değiştir"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"Görünümü Optimize Et"</string>
+ <string name="handle_menu_error_icon_text" msgid="7200375184879808684">"Hata Simgesi"</string>
<string name="close_text" msgid="4986518933445178928">"Kapat"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Menüyü kapat"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (masaüstü pencereleme)"</string>
diff --git a/libs/WindowManager/Shell/res/values-uk/strings.xml b/libs/WindowManager/Shell/res/values-uk/strings.xml
index 6888497..3044c2b 100644
--- a/libs/WindowManager/Shell/res/values-uk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-uk/strings.xml
@@ -133,6 +133,7 @@
<string name="manage_windows_text" msgid="5567366688493093920">"Керувати вікнами"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Змінити формат"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"Оптимізувати перегляд"</string>
+ <string name="handle_menu_error_icon_text" msgid="7200375184879808684">"Значок помилки"</string>
<string name="close_text" msgid="4986518933445178928">"Закрити"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Закрити меню"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (режим вікон робочого стола)"</string>
diff --git a/libs/WindowManager/Shell/res/values-ur/strings.xml b/libs/WindowManager/Shell/res/values-ur/strings.xml
index cf16e77..da361e1 100644
--- a/libs/WindowManager/Shell/res/values-ur/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ur/strings.xml
@@ -133,6 +133,7 @@
<string name="manage_windows_text" msgid="5567366688493093920">"ونڈوز کا نظم کریں"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"تناسبی شرح کو تبدیل کریں"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"منظر کو بہتر بنائیں"</string>
+ <string name="handle_menu_error_icon_text" msgid="7200375184879808684">"خرابی کا آئیکن"</string>
<string name="close_text" msgid="4986518933445178928">"بند کریں"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"مینیو بند کریں"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ڈیسک ٹاپ ونڈوئنگ)"</string>
diff --git a/libs/WindowManager/Shell/res/values-uz/strings.xml b/libs/WindowManager/Shell/res/values-uz/strings.xml
index 9b1e30e..1971ca3 100644
--- a/libs/WindowManager/Shell/res/values-uz/strings.xml
+++ b/libs/WindowManager/Shell/res/values-uz/strings.xml
@@ -133,6 +133,7 @@
<string name="manage_windows_text" msgid="5567366688493093920">"Oynalarni boshqarish"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Tomonlar nisbatini oʻzgartirish"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"Koʻrinishni optimallashtirish"</string>
+ <string name="handle_menu_error_icon_text" msgid="7200375184879808684">"Xatolik belgisi"</string>
<string name="close_text" msgid="4986518933445178928">"Yopish"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Menyuni yopish"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Desktop rejimidagi oynalar)"</string>
diff --git a/libs/WindowManager/Shell/res/values-vi/strings.xml b/libs/WindowManager/Shell/res/values-vi/strings.xml
index 4397ccd..70aa6ec 100644
--- a/libs/WindowManager/Shell/res/values-vi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-vi/strings.xml
@@ -133,6 +133,7 @@
<string name="manage_windows_text" msgid="5567366688493093920">"Quản lý cửa sổ"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Thay đổi tỷ lệ khung hình"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"Tối ưu hoá chế độ xem"</string>
+ <string name="handle_menu_error_icon_text" msgid="7200375184879808684">"Biểu tượng lỗi"</string>
<string name="close_text" msgid="4986518933445178928">"Đóng"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Đóng trình đơn"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Chế độ cửa sổ trên máy tính)"</string>
diff --git a/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml b/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
index 6986115..bc08e34 100644
--- a/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
@@ -133,6 +133,7 @@
<string name="manage_windows_text" msgid="5567366688493093920">"管理窗口"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"更改宽高比"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"优化视图"</string>
+ <string name="handle_menu_error_icon_text" msgid="7200375184879808684">"错误图标"</string>
<string name="close_text" msgid="4986518933445178928">"关闭"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"关闭菜单"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g>(窗口化模式)"</string>
diff --git a/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml b/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
index 6252efb..cdfcf5c 100644
--- a/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
@@ -133,6 +133,7 @@
<string name="manage_windows_text" msgid="5567366688493093920">"管理視窗"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"變更長寬比"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"優化檢視模式"</string>
+ <string name="handle_menu_error_icon_text" msgid="7200375184879808684">"錯誤圖示"</string>
<string name="close_text" msgid="4986518933445178928">"關閉"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"關閉選單"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (桌面電腦視窗模式)"</string>
diff --git a/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml b/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
index 3c23034..406ac3d 100644
--- a/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
@@ -133,6 +133,7 @@
<string name="manage_windows_text" msgid="5567366688493093920">"管理視窗"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"變更顯示比例"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"最佳化檢視畫面"</string>
+ <string name="handle_menu_error_icon_text" msgid="7200375184879808684">"錯誤圖示"</string>
<string name="close_text" msgid="4986518933445178928">"關閉"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"關閉選單"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (電腦分割視窗)"</string>
diff --git a/libs/WindowManager/Shell/res/values-zu/strings.xml b/libs/WindowManager/Shell/res/values-zu/strings.xml
index 61dacd4..e67db10 100644
--- a/libs/WindowManager/Shell/res/values-zu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zu/strings.xml
@@ -133,6 +133,7 @@
<string name="manage_windows_text" msgid="5567366688493093920">"Phatha Amawindi"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Shintsha ukubukeka kwesilinganiselo"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"Lungiselela Ukubuka"</string>
+ <string name="handle_menu_error_icon_text" msgid="7200375184879808684">"Isithonjana Sephutha"</string>
<string name="close_text" msgid="4986518933445178928">"Vala"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Vala Imenyu"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Ukwenziwa kwamawindi amaningi kwedeskithophu)"</string>
diff --git a/libs/WindowManager/Shell/res/values/styles.xml b/libs/WindowManager/Shell/res/values/styles.xml
index 9318203..b7030d9 100644
--- a/libs/WindowManager/Shell/res/values/styles.xml
+++ b/libs/WindowManager/Shell/res/values/styles.xml
@@ -177,6 +177,38 @@
<item name="boxShadowSpreadRadius">0dp</item>
</style>
+ <style name="BoxShadowParamsPIPLight1">
+ <item name="boxShadowColor">#0A000000</item>
+ <item name="boxShadowOffsetX">0dp</item>
+ <item name="boxShadowOffsetY">0dp</item>
+ <item name="boxShadowBlurRadius">14dp</item>
+ <item name="boxShadowSpreadRadius">0dp</item>
+ </style>
+
+ <style name="BoxShadowParamsPIPLight2">
+ <item name="boxShadowColor">#1A000000</item>
+ <item name="boxShadowOffsetX">0dp</item>
+ <item name="boxShadowOffsetY">2dp</item>
+ <item name="boxShadowBlurRadius">8dp</item>
+ <item name="boxShadowSpreadRadius">0dp</item>
+ </style>
+
+ <style name="BoxShadowParamsPIPDark1">
+ <item name="boxShadowColor">#33000000</item>
+ <item name="boxShadowOffsetX">0dp</item>
+ <item name="boxShadowOffsetY">0dp</item>
+ <item name="boxShadowBlurRadius">14dp</item>
+ <item name="boxShadowSpreadRadius">0dp</item>
+ </style>
+
+ <style name="BoxShadowParamsPIPDark2">
+ <item name="boxShadowColor">#33000000</item>
+ <item name="boxShadowOffsetX">0dp</item>
+ <item name="boxShadowOffsetY">2dp</item>
+ <item name="boxShadowBlurRadius">8dp</item>
+ <item name="boxShadowSpreadRadius">0dp</item>
+ </style>
+
<style name="BorderSettingsUnfocusedLight">
<item name="borderStrokeWidth">1dp</item>
<item name="borderColor">#32BDC1C6</item>
@@ -197,4 +229,14 @@
<item name="borderColor">#579AA0A6</item>
</style>
+ <style name="BorderSettingsPIPLight">
+ <item name="borderStrokeWidth">1dp</item>
+ <item name="borderColor">#27BDC1C6</item>
+ </style>
+
+ <style name="BorderSettingsPIPDark">
+ <item name="borderStrokeWidth">1dp</item>
+ <item name="borderColor">#339AA0A6</item>
+ </style>
+
</resources>
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/animation/MinimizeAnimator.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/animation/MinimizeAnimator.kt
index 4ecace0..b65cc1b 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/animation/MinimizeAnimator.kt
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/animation/MinimizeAnimator.kt
@@ -26,6 +26,7 @@
import android.window.TransitionInfo.Change
import com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_MINIMIZE_WINDOW
import com.android.internal.jank.InteractionJankMonitor
+import java.time.Duration
/** Creates minimization animation */
object MinimizeAnimator {
@@ -48,6 +49,7 @@
* @param animationHandler the Handler that the animation is running on.
*/
@JvmStatic
+ @JvmOverloads
fun create(
context: Context,
change: Change,
@@ -55,6 +57,7 @@
onAnimFinish: (Animator) -> Unit,
interactionJankMonitor: InteractionJankMonitor,
animationHandler: Handler,
+ startAnimDelay: Duration = Duration.ZERO,
): Animator {
val boundsAnimator = WindowAnimator.createBoundsAnimator(
context.resources.displayMetrics,
@@ -91,6 +94,7 @@
}
}
return AnimatorSet().apply {
+ startDelay = startAnimDelay.toMillis()
playTogether(boundsAnimator, alphaAnimator)
addListener(listener)
}
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopConfig.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopConfig.kt
index 4f7a85d..d2e7d48 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopConfig.kt
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopConfig.kt
@@ -31,6 +31,7 @@
/**
* Whether a window should be maximized when it's dragged to the top edge of the screen.
*/
+ @Deprecated("Deprecated with desktop-first based drag-to-maximize")
val shouldMaximizeWhenDragToTopEdge: Boolean
/** Whether the override desktop density is enabled and valid. */
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatus.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatus.java
index 38e847f..0e4b5a8 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatus.java
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatus.java
@@ -24,7 +24,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.app.TaskInfo;
import android.content.Context;
import android.hardware.display.DisplayManager;
import android.os.SystemProperties;
@@ -37,7 +36,6 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.window.flags.Flags;
-import java.io.PrintWriter;
import java.util.Arrays;
/**
@@ -48,38 +46,10 @@
@Deprecated(forRemoval = true)
public class DesktopModeStatus {
- private static final String TAG = "DesktopModeStatus";
-
@Nullable
private static Boolean sIsLargeScreenDevice = null;
/**
- * Flag to indicate whether task resizing is veiled.
- */
- private static final boolean IS_VEILED_RESIZE_ENABLED = SystemProperties.getBoolean(
- "persist.wm.debug.desktop_veiled_resizing", true);
-
- /**
- * Flag to indicate is moving task to another display is enabled.
- */
- public static final boolean IS_DISPLAY_CHANGE_ENABLED = SystemProperties.getBoolean(
- "persist.wm.debug.desktop_change_display", false);
-
- /**
- * Flag to indicate whether to apply shadows to windows in desktop mode.
- */
- private static final boolean USE_WINDOW_SHADOWS = SystemProperties.getBoolean(
- "persist.wm.debug.desktop_use_window_shadows", true);
-
- /**
- * Flag to indicate whether to apply shadows to the focused window in desktop mode.
- *
- * Note: this flag is only relevant if USE_WINDOW_SHADOWS is false.
- */
- private static final boolean USE_WINDOW_SHADOWS_FOCUSED_WINDOW = SystemProperties.getBoolean(
- "persist.wm.debug.desktop_use_window_shadows_focused_window", false);
-
- /**
* Flag to indicate whether to use rounded corners for windows in desktop mode.
*/
private static final boolean USE_ROUNDED_CORNERS = SystemProperties.getBoolean(
@@ -91,27 +61,6 @@
private static final boolean ENFORCE_DEVICE_RESTRICTIONS = SystemProperties.getBoolean(
"persist.wm.debug.desktop_mode_enforce_device_restrictions", true);
- private static final boolean USE_APP_TO_WEB_BUILD_TIME_GENERIC_LINKS =
- SystemProperties.getBoolean(
- "persist.wm.debug.use_app_to_web_build_time_generic_links", true);
-
- /** Whether the desktop density override is enabled. */
- public static final boolean DESKTOP_DENSITY_OVERRIDE_ENABLED =
- SystemProperties.getBoolean("persist.wm.debug.desktop_mode_density_enabled", false);
-
- /** Override density for tasks when they're inside the desktop. */
- public static final int DESKTOP_DENSITY_OVERRIDE =
- SystemProperties.getInt("persist.wm.debug.desktop_mode_density", 284);
-
- /** The minimum override density allowed for tasks inside the desktop. */
- private static final int DESKTOP_DENSITY_MIN = 100;
-
- /** The maximum override density allowed for tasks inside the desktop. */
- private static final int DESKTOP_DENSITY_MAX = 1000;
-
- /** The number of [WindowDecorViewHost] instances to warm up on system start. */
- private static final int WINDOW_DECOR_PRE_WARM_SIZE = 2;
-
/**
* Sysprop declaring whether to enters desktop mode by default when the windowing mode of the
* display's root TaskDisplayArea is set to WINDOWING_MODE_FREEFORM.
@@ -123,51 +72,6 @@
"persist.wm.debug.enter_desktop_by_default_on_freeform_display";
/**
- * Sysprop declaring whether to enable drag-to-maximize for desktop windows.
- *
- * <p>If it is not defined, then {@code R.integer.config_dragToMaximizeInDesktopMode}
- * is used.
- */
- public static final String ENABLE_DRAG_TO_MAXIMIZE_SYS_PROP =
- "persist.wm.debug.enable_drag_to_maximize";
-
- /**
- * Sysprop declaring the maximum number of Tasks to show in Desktop Mode at any one time.
- *
- * <p>If it is not defined, then {@code R.integer.config_maxDesktopWindowingActiveTasks} is
- * used.
- *
- * <p>The limit does NOT affect Picture-in-Picture, Bubbles, or System Modals (like a screen
- * recording window, or Bluetooth pairing window).
- */
- private static final String MAX_TASK_LIMIT_SYS_PROP = "persist.wm.debug.desktop_max_task_limit";
-
- /**
- * Sysprop declaring the number of [WindowDecorViewHost] instances to warm up on system start.
- *
- * <p>If it is not defined, then [WINDOW_DECOR_PRE_WARM_SIZE] is used.
- */
- private static final String WINDOW_DECOR_PRE_WARM_SIZE_SYS_PROP =
- "persist.wm.debug.desktop_window_decor_pre_warm_size";
-
- /**
- * Return {@code true} if veiled resizing is active. If false, fluid resizing is used.
- */
- public static boolean isVeiledResizeEnabled() {
- return IS_VEILED_RESIZE_ENABLED;
- }
-
- /**
- * Return whether to use window shadows.
- *
- * @param isFocusedWindow whether the window to apply shadows to is focused
- */
- public static boolean useWindowShadow(boolean isFocusedWindow) {
- return USE_WINDOW_SHADOWS
- || (USE_WINDOW_SHADOWS_FOCUSED_WINDOW && isFocusedWindow);
- }
-
- /**
* Return whether to use rounded corners for windows.
*/
public static boolean useRoundedCorners() {
@@ -183,35 +87,6 @@
}
/**
- * Return the maximum limit on the number of Tasks to show in Desktop Mode at any one time.
- */
- public static int getMaxTaskLimit(@NonNull Context context) {
- return SystemProperties.getInt(MAX_TASK_LIMIT_SYS_PROP,
- context.getResources().getInteger(R.integer.config_maxDesktopWindowingActiveTasks));
- }
-
- /**
- * Return the maximum size of the window decoration surface control view host pool, or zero if
- * there should be no pooling.
- */
- public static int getWindowDecorScvhPoolSize(@NonNull Context context) {
- if (!DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_SCVH_CACHE.isTrue()) return 0;
- final int maxTaskLimit = getMaxTaskLimit(context);
- if (maxTaskLimit > 0) {
- return maxTaskLimit;
- }
- // TODO: b/368032552 - task limit equal to 0 means unlimited. Figure out what the pool
- // size should be in that case.
- return 0;
- }
-
- /** The number of [WindowDecorViewHost] instances to warm up on system start. */
- public static int getWindowDecorPreWarmSize() {
- return SystemProperties.getInt(WINDOW_DECOR_PRE_WARM_SIZE_SYS_PROP,
- WINDOW_DECOR_PRE_WARM_SIZE);
- }
-
- /**
* Return {@code true} if the current device supports desktop mode.
*/
private static boolean isDesktopModeSupported(@NonNull Context context) {
@@ -318,16 +193,6 @@
}
/**
- * @return If {@code true} we set opaque background for all freeform tasks to prevent freeform
- * tasks below from being visible if freeform task window above is translucent.
- * Otherwise if fluid resize is enabled, add a background to freeform tasks.
- */
- public static boolean shouldSetBackground(@NonNull TaskInfo taskInfo) {
- return taskInfo.isFreeform() && (!DesktopModeStatus.isVeiledResizeEnabled()
- || DesktopModeFlags.ENABLE_OPAQUE_BACKGROUND_FOR_TRANSPARENT_WINDOWS.isTrue());
- }
-
- /**
* @return {@code true} if the app handle should be shown because desktop mode is enabled or
* the device has a large screen
*/
@@ -335,36 +200,7 @@
return canEnterDesktopMode(context) || overridesShowAppHandle(context);
}
- /**
- * Return {@code true} if the override desktop density is enabled and valid.
- */
- public static boolean useDesktopOverrideDensity() {
- return isDesktopDensityOverrideEnabled() && isValidDesktopDensityOverrideSet();
- }
-
- /**
- * Returns {@code true} if the app-to-web feature is using the build-time generic links list.
- */
- public static boolean useAppToWebBuildTimeGenericLinks() {
- return USE_APP_TO_WEB_BUILD_TIME_GENERIC_LINKS;
- }
-
- /**
- * Return {@code true} if the override desktop density is enabled.
- */
- private static boolean isDesktopDensityOverrideEnabled() {
- return DESKTOP_DENSITY_OVERRIDE_ENABLED;
- }
-
- /**
- * Return {@code true} if the override desktop density is set and within a valid range.
- */
- private static boolean isValidDesktopDensityOverrideSet() {
- return DESKTOP_DENSITY_OVERRIDE >= DESKTOP_DENSITY_MIN
- && DESKTOP_DENSITY_OVERRIDE <= DESKTOP_DENSITY_MAX;
- }
-
- /**
+ /**
* Return {@code true} if desktop mode is unrestricted and is supported on the device.
*/
public static boolean isDeviceEligibleForDesktopMode(@NonNull Context context) {
@@ -425,34 +261,4 @@
context.getResources().getBoolean(
R.bool.config_enterDesktopByDefaultOnFreeformDisplay));
}
-
- /**
- * Return {@code true} if a window should be maximized when it's dragged to the top edge of the
- * screen.
- */
- public static boolean shouldMaximizeWhenDragToTopEdge(@NonNull Context context) {
- if (!DesktopExperienceFlags.ENABLE_DRAG_TO_MAXIMIZE.isTrue()) {
- return false;
- }
- return SystemProperties.getBoolean(ENABLE_DRAG_TO_MAXIMIZE_SYS_PROP,
- context.getResources().getBoolean(R.bool.config_dragToMaximizeInDesktopMode));
- }
-
- /** Dumps DesktopModeStatus flags and configs. */
- public static void dump(PrintWriter pw, String prefix, Context context) {
- String innerPrefix = prefix + " ";
- pw.print(prefix); pw.println(TAG);
- pw.print(innerPrefix); pw.print("maxTaskLimit="); pw.println(getMaxTaskLimit(context));
-
- pw.print(innerPrefix); pw.print("maxTaskLimit config override=");
- pw.println(context.getResources().getInteger(
- R.integer.config_maxDesktopWindowingActiveTasks));
-
- SystemProperties.Handle maxTaskLimitHandle = SystemProperties.find(MAX_TASK_LIMIT_SYS_PROP);
- pw.print(innerPrefix); pw.print("maxTaskLimit sysprop=");
- pw.println(maxTaskLimitHandle == null ? "null" : maxTaskLimitHandle.getInt(/* def= */ -1));
-
- pw.print(innerPrefix); pw.print("showAppHandle config override=");
- pw.println(overridesShowAppHandle(context));
- }
}
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopState.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopState.kt
index 68437fb..869135e 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopState.kt
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopState.kt
@@ -89,6 +89,11 @@
*/
val isFreeformEnabled: Boolean
+ /**
+ * Whether the home screen should be shown behind freeform tasks in the desktop.
+ */
+ val shouldShowHomeBehindDesktop: Boolean
+
companion object {
/** Creates a new [DesktopState] from a context. */
@JvmStatic
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopStateImpl.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopStateImpl.kt
index 863d5cd0..3489ff7 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopStateImpl.kt
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopStateImpl.kt
@@ -117,7 +117,7 @@
(display.type == Display.TYPE_EXTERNAL || display.type == Display.TYPE_OVERLAY) &&
enableDisplayContentModeManagement()
) {
- return windowManager?.shouldShowSystemDecors(display.displayId) ?: false
+ return windowManager?.isEligibleForDesktopMode(display.displayId) ?: false
}
return false
@@ -144,6 +144,11 @@
) != 0
override val isFreeformEnabled: Boolean = hasFreeformFeature || hasFreeformDevOption
+ override val shouldShowHomeBehindDesktop: Boolean =
+ Flags.showHomeBehindDesktop() && context.resources.getBoolean(
+ R.bool.config_showHomeBehindDesktop
+ )
+
companion object {
@VisibleForTesting
const val ENFORCE_DEVICE_RESTRICTIONS_SYS_PROP =
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/split/SplitBounds.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/split/SplitBounds.java
index 7c1faa66..b02e50e 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/split/SplitBounds.java
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/split/SplitBounds.java
@@ -47,6 +47,14 @@
* the bounds were originally in
*/
public final boolean appsStackedVertically;
+ /**
+ * If {@code true}, that means at the time of creation of this object, the phone was in
+ * seascape orientation. This is important on devices with insets, because they do not split
+ * evenly -- one of the insets must be slightly larger to account for the inset.
+ * From landscape, it is the leftTop task that expands slightly.
+ * From seascape, it is the rightBottom task that expands slightly.
+ */
+ public final boolean initiatedFromSeascape;
public final int leftTopTaskId;
public final int rightBottomTaskId;
@@ -63,11 +71,22 @@
this.visualDividerBounds = new Rect(leftTopBounds.left, leftTopBounds.bottom,
leftTopBounds.right, rightBottomBounds.top);
appsStackedVertically = true;
+ initiatedFromSeascape = false;
} else {
// horizontal apps, vertical divider
this.visualDividerBounds = new Rect(leftTopBounds.right, leftTopBounds.top,
rightBottomBounds.left, leftTopBounds.bottom);
appsStackedVertically = false;
+ // The following check is unreliable on devices without insets
+ // (initiatedFromSeascape will always be set to false.) This happens to be OK for
+ // all our current uses, but should be refactored.
+ // TODO: Create a more reliable check, or refactor how splitting works on devices
+ // with insets.
+ if (rightBottomBounds.width() > leftTopBounds.width()) {
+ initiatedFromSeascape = true;
+ } else {
+ initiatedFromSeascape = false;
+ }
}
float totalWidth = rightBottomBounds.right - leftTopBounds.left;
@@ -78,6 +97,39 @@
dividerHeightPercent = visualDividerBounds.height() / totalHeight;
}
+ /**
+ * Returns the percentage size of the left/top task (compared to the full width/height of
+ * the split pair). E.g. if the left task is 4 units wide, the divider is 2 units, and the
+ * right task is 4 units, this method will return 0.4f.
+ */
+ public float getLeftTopTaskPercent() {
+ // topTaskPercent and leftTaskPercent are defined at creation time, and are not updated
+ // on device rotate, so we have to check appsStackedVertically to return the right
+ // creation-time measurements.
+ return appsStackedVertically ? topTaskPercent : leftTaskPercent;
+ }
+
+ /**
+ * Returns the percentage size of the divider's thickness (compared to the full width/height
+ * of the split pair). E.g. if the left task is 4 units wide, the divider is 2 units, and
+ * the right task is 4 units, this method will return 0.2f.
+ */
+ public float getDividerPercent() {
+ // dividerHeightPercent and dividerWidthPercent are defined at creation time, and are
+ // not updated on device rotate, so we have to check appsStackedVertically to return
+ // the right creation-time measurements.
+ return appsStackedVertically ? dividerHeightPercent : dividerWidthPercent;
+ }
+
+ /**
+ * Returns the percentage size of the right/bottom task (compared to the full width/height
+ * of the split pair). E.g. if the left task is 4 units wide, the divider is 2 units, and
+ * the right task is 4 units, this method will return 0.4f.
+ */
+ public float getRightBottomTaskPercent() {
+ return 1 - (getLeftTopTaskPercent() + getDividerPercent());
+ }
+
public SplitBounds(Parcel parcel) {
leftTopBounds = parcel.readTypedObject(Rect.CREATOR);
rightBottomBounds = parcel.readTypedObject(Rect.CREATOR);
@@ -85,6 +137,7 @@
topTaskPercent = parcel.readFloat();
leftTaskPercent = parcel.readFloat();
appsStackedVertically = parcel.readBoolean();
+ initiatedFromSeascape = parcel.readBoolean();
leftTopTaskId = parcel.readInt();
rightBottomTaskId = parcel.readInt();
dividerWidthPercent = parcel.readFloat();
@@ -100,6 +153,7 @@
parcel.writeFloat(topTaskPercent);
parcel.writeFloat(leftTaskPercent);
parcel.writeBoolean(appsStackedVertically);
+ parcel.writeBoolean(initiatedFromSeascape);
parcel.writeInt(leftTopTaskId);
parcel.writeInt(rightBottomTaskId);
parcel.writeFloat(dividerWidthPercent);
@@ -122,7 +176,8 @@
return Objects.equals(leftTopBounds, other.leftTopBounds)
&& Objects.equals(rightBottomBounds, other.rightBottomBounds)
&& leftTopTaskId == other.leftTopTaskId
- && rightBottomTaskId == other.rightBottomTaskId;
+ && rightBottomTaskId == other.rightBottomTaskId
+ && snapPosition == other.snapPosition;
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java
index b098620..c53715c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java
@@ -102,6 +102,14 @@
}
}
+ /** Unregisters the given listener associated to the given display. */
+ public void unregisterListener(int displayId, RootTaskDisplayAreaListener listener) {
+ final ArrayList<RootTaskDisplayAreaListener> listeners = mListeners.get(displayId);
+ if (listeners != null) {
+ listeners.remove(listener);
+ }
+ }
+
public void unregisterListener(RootTaskDisplayAreaListener listener) {
for (int i = mListeners.size() - 1; i >= 0; --i) {
final List<RootTaskDisplayAreaListener> listeners = mListeners.valueAt(i);
@@ -116,6 +124,14 @@
}
/**
+ * Sets the layer of {@param sc} to be relative to the TDA on {@param displayId}.
+ */
+ public void relZToDisplayArea(int displayId, SurfaceControl sc, SurfaceControl.Transaction t,
+ int z) {
+ t.setRelativeLayer(sc, mLeashes.get(displayId), z);
+ }
+
+ /**
* Re-parents the provided surface to the leash of the provided display.
*
* @param displayId the display area to reparent to.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/AppToWebGenericLinksParser.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/AppToWebGenericLinksParser.kt
index 56447de..ea833fd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/AppToWebGenericLinksParser.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/AppToWebGenericLinksParser.kt
@@ -23,7 +23,7 @@
import com.android.wm.shell.R
import com.android.wm.shell.common.ShellExecutor
import com.android.wm.shell.shared.annotations.ShellMainThread
-import com.android.wm.shell.shared.desktopmode.DesktopModeStatus.useAppToWebBuildTimeGenericLinks
+import com.android.wm.shell.shared.desktopmode.DesktopConfig
/**
* Retrieves the build-time or server-side generic links list and parses and stores the
@@ -31,13 +31,14 @@
*/
class AppToWebGenericLinksParser(
private val context: Context,
- @ShellMainThread private val mainExecutor: ShellExecutor
+ @ShellMainThread private val mainExecutor: ShellExecutor,
+ private val desktopConfig: DesktopConfig,
) {
private val genericLinksMap: MutableMap<String, String> = mutableMapOf()
init {
// If using the server-side generic links list, register a listener
- if (!useAppToWebBuildTimeGenericLinks()) {
+ if (!desktopConfig.useAppToWebBuildTimeGenericLinks) {
DeviceConfigListener()
}
@@ -49,7 +50,7 @@
private fun updateGenericLinksMap() {
val genericLinksList =
- if (useAppToWebBuildTimeGenericLinks()) {
+ if (desktopConfig.useAppToWebBuildTimeGenericLinks) {
context.resources.getString(R.string.generic_links_list)
} else {
DeviceConfig.getString(NAMESPACE, FLAG_GENERIC_LINKS, /* defaultValue= */ "")
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/appzoomout/AppZoomOut.java b/libs/WindowManager/Shell/src/com/android/wm/shell/appzoomout/AppZoomOut.java
index 9451374..0d41a32 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/appzoomout/AppZoomOut.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/appzoomout/AppZoomOut.java
@@ -30,4 +30,23 @@
* between [0,1], 0 when fullscreen, 1 when it's at the max pushback level.
*/
void setProgress(float progress);
+
+ /**
+ * Sets the top-level scaling factor applied to all content on the screen during a zoom-out.
+ *
+ * <p>The {@code scale} parameter determines the current zoom level, ranging from {@code 0f} to
+ * {@code 1f}.
+ * <ul>
+ * <li>A value of {@code 1.0f} indicates no scaling (content is displayed at its original
+ * size).</li>
+ * <li>A value of {@code 0.0f} represents the maximum zoom-out, effectively scaling the
+ * content to zero size (though visually it might be constrained).</li>
+ * <li>Values between {@code 0.0f} and {@code 1.0f} represent intermediate zoom levels.</li>
+ * </ul>
+ *
+ * @param scale The scaling factor to apply, where {@code 1.0f} is no scale and {@code 0.0f} is
+ * maximum zoom-out.
+ */
+ void setTopLevelScale(float scale);
+
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/appzoomout/AppZoomOutController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/appzoomout/AppZoomOutController.java
index 10023c9..04ca7fc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/appzoomout/AppZoomOutController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/appzoomout/AppZoomOutController.java
@@ -18,7 +18,9 @@
import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
import static android.view.Display.DEFAULT_DISPLAY;
+
import static com.android.systemui.Flags.spatialModelAppPushback;
+import static com.android.systemui.shared.Flags.enableLppAssistInvocationEffect;
import android.app.ActivityManager;
import android.app.WindowConfiguration;
@@ -26,6 +28,7 @@
import android.content.res.Configuration;
import android.util.Slog;
import android.window.DisplayAreaInfo;
+import android.window.DisplayAreaOrganizer;
import android.window.WindowContainerTransaction;
import androidx.annotation.Nullable;
@@ -50,7 +53,8 @@
private final Context mContext;
private final ShellTaskOrganizer mTaskOrganizer;
private final DisplayController mDisplayController;
- private final AppZoomOutDisplayAreaOrganizer mDisplayAreaOrganizer;
+ private final AppZoomOutDisplayAreaOrganizer mAppDisplayAreaOrganizer;
+ private final TopLevelZoomOutDisplayAreaOrganizer mTopLevelDisplayAreaOrganizer;
private final ShellExecutor mMainExecutor;
private final AppZoomOutImpl mImpl = new AppZoomOutImpl();
@@ -77,24 +81,28 @@
public static AppZoomOutController create(Context context, ShellInit shellInit,
ShellTaskOrganizer shellTaskOrganizer, DisplayController displayController,
DisplayLayout displayLayout, @ShellMainThread ShellExecutor mainExecutor) {
- AppZoomOutDisplayAreaOrganizer displayAreaOrganizer = new AppZoomOutDisplayAreaOrganizer(
+ AppZoomOutDisplayAreaOrganizer appDisplayAreaOrganizer = new AppZoomOutDisplayAreaOrganizer(
context, displayLayout, mainExecutor);
+ TopLevelZoomOutDisplayAreaOrganizer topLevelDisplayAreaOrganizer =
+ new TopLevelZoomOutDisplayAreaOrganizer(displayLayout, mainExecutor);
return new AppZoomOutController(context, shellInit, shellTaskOrganizer, displayController,
- displayAreaOrganizer, mainExecutor);
+ appDisplayAreaOrganizer, topLevelDisplayAreaOrganizer, mainExecutor);
}
@VisibleForTesting
AppZoomOutController(Context context, ShellInit shellInit,
ShellTaskOrganizer shellTaskOrganizer, DisplayController displayController,
- AppZoomOutDisplayAreaOrganizer displayAreaOrganizer,
+ AppZoomOutDisplayAreaOrganizer appDisplayAreaOrganizer,
+ TopLevelZoomOutDisplayAreaOrganizer topLevelDisplayAreaOrganizer,
@ShellMainThread ShellExecutor mainExecutor) {
mContext = context;
mTaskOrganizer = shellTaskOrganizer;
mDisplayController = displayController;
- mDisplayAreaOrganizer = displayAreaOrganizer;
+ mAppDisplayAreaOrganizer = appDisplayAreaOrganizer;
+ mTopLevelDisplayAreaOrganizer = topLevelDisplayAreaOrganizer;
mMainExecutor = mainExecutor;
- if (spatialModelAppPushback()) {
+ if (spatialModelAppPushback() || enableLppAssistInvocationEffect()) {
shellInit.addInitCallback(this::onInit, this);
}
}
@@ -106,7 +114,12 @@
mDisplayController.addDisplayChangingController(this);
updateDisplayLayout(mContext.getDisplayId());
- mDisplayAreaOrganizer.registerOrganizer();
+ if (spatialModelAppPushback()) {
+ mAppDisplayAreaOrganizer.registerOrganizer();
+ }
+ if (enableLppAssistInvocationEffect()) {
+ mTopLevelDisplayAreaOrganizer.registerOrganizer();
+ }
}
public AppZoomOut asAppZoomOut() {
@@ -114,7 +127,19 @@
}
public void setProgress(float progress) {
- mDisplayAreaOrganizer.setProgress(progress);
+ mAppDisplayAreaOrganizer.setProgress(progress);
+ }
+
+ /**
+ * Scales all content on the screen belonging to
+ * {@link DisplayAreaOrganizer#FEATURE_WINDOWED_MAGNIFICATION}.
+ *
+ * @param scale scale factor to be applied to the surfaces.
+ */
+ private void setTopLevelScale(float scale) {
+ if (enableLppAssistInvocationEffect()) {
+ mTopLevelDisplayAreaOrganizer.setScale(scale);
+ }
}
void updateDisplayLayout(int displayId) {
@@ -123,7 +148,10 @@
Slog.w(TAG, "Failed to get new DisplayLayout.");
return;
}
- mDisplayAreaOrganizer.setDisplayLayout(newDisplayLayout);
+ mAppDisplayAreaOrganizer.setDisplayLayout(newDisplayLayout);
+ if (enableLppAssistInvocationEffect()) {
+ mTopLevelDisplayAreaOrganizer.setDisplayLayout(newDisplayLayout);
+ }
}
@Override
@@ -132,7 +160,7 @@
return;
}
if (taskInfo.getActivityType() == WindowConfiguration.ACTIVITY_TYPE_HOME) {
- mDisplayAreaOrganizer.setIsHomeTaskFocused(taskInfo.isFocused);
+ mAppDisplayAreaOrganizer.setIsHomeTaskFocused(taskInfo.isFocused);
}
}
@@ -141,7 +169,10 @@
@Nullable DisplayAreaInfo newDisplayAreaInfo, WindowContainerTransaction wct) {
// TODO: verify if there is synchronization issues.
if (toRotation != ROTATION_UNDEFINED) {
- mDisplayAreaOrganizer.onRotateDisplay(mContext, toRotation);
+ mAppDisplayAreaOrganizer.onRotateDisplay(mContext, toRotation);
+ if (enableLppAssistInvocationEffect()) {
+ mTopLevelDisplayAreaOrganizer.onRotateDisplay(mContext, toRotation);
+ }
}
}
@@ -161,5 +192,10 @@
public void setProgress(float progress) {
mMainExecutor.execute(() -> AppZoomOutController.this.setProgress(progress));
}
+
+ @Override
+ public void setTopLevelScale(float scale) {
+ mMainExecutor.execute(() -> AppZoomOutController.this.setTopLevelScale(scale));
+ }
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/appzoomout/TopLevelZoomOutDisplayAreaOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/appzoomout/TopLevelZoomOutDisplayAreaOrganizer.java
new file mode 100644
index 0000000..3e44061
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/appzoomout/TopLevelZoomOutDisplayAreaOrganizer.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.appzoomout;
+
+import android.content.Context;
+import android.util.ArrayMap;
+import android.view.Display;
+import android.view.SurfaceControl;
+import android.window.DisplayAreaAppearedInfo;
+import android.window.DisplayAreaInfo;
+import android.window.DisplayAreaOrganizer;
+import android.window.WindowContainerToken;
+
+import com.android.wm.shell.common.DisplayLayout;
+
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Executor;
+
+/** Display area organizer that manages the top level zoom out UI and states. */
+public class TopLevelZoomOutDisplayAreaOrganizer extends DisplayAreaOrganizer {
+ private final DisplayLayout mDisplayLayout = new DisplayLayout();
+ private final Map<WindowContainerToken, SurfaceControl> mDisplayAreaTokenMap =
+ new ArrayMap<>();
+
+ private float mScale = 1f;
+
+ public TopLevelZoomOutDisplayAreaOrganizer(DisplayLayout displayLayout, Executor mainExecutor) {
+ super(mainExecutor);
+ setDisplayLayout(displayLayout);
+ }
+
+ @Override
+ public void onDisplayAreaAppeared(DisplayAreaInfo displayAreaInfo, SurfaceControl leash) {
+ leash.setUnreleasedWarningCallSite(
+ "TopLevelZoomDisplayAreaOrganizer.onDisplayAreaAppeared");
+ if (displayAreaInfo.displayId == Display.DEFAULT_DISPLAY) {
+ mDisplayAreaTokenMap.put(displayAreaInfo.token, leash);
+ }
+ }
+
+ @Override
+ public void onDisplayAreaVanished(DisplayAreaInfo displayAreaInfo) {
+ final SurfaceControl leash = mDisplayAreaTokenMap.get(displayAreaInfo.token);
+ if (leash != null) {
+ leash.release();
+ }
+ mDisplayAreaTokenMap.remove(displayAreaInfo.token);
+ }
+
+ /**
+ * Registers the TopLevelZoomOutDisplayAreaOrganizer to manage the display area of
+ * {@link DisplayAreaOrganizer#FEATURE_WINDOWED_MAGNIFICATION}.
+ */
+ void registerOrganizer() {
+ final List<DisplayAreaAppearedInfo> displayAreaInfos = registerOrganizer(
+ DisplayAreaOrganizer.FEATURE_WINDOWED_MAGNIFICATION);
+ for (int i = 0; i < displayAreaInfos.size(); i++) {
+ final DisplayAreaAppearedInfo info = displayAreaInfos.get(i);
+ onDisplayAreaAppeared(info.getDisplayAreaInfo(), info.getLeash());
+ }
+ }
+
+ @Override
+ public void unregisterOrganizer() {
+ super.unregisterOrganizer();
+ reset();
+ }
+
+ void setScale(float scale) {
+ if (mScale == scale) {
+ return;
+ }
+
+ mScale = scale;
+ apply();
+ }
+
+ private void apply() {
+ SurfaceControl.Transaction tx = new SurfaceControl.Transaction();
+ mDisplayAreaTokenMap.forEach((token, leash) -> updateSurface(tx, leash, mScale));
+ tx.apply();
+ }
+
+ private void reset() {
+ setScale(1f);
+ }
+
+ private void updateSurface(SurfaceControl.Transaction tx, SurfaceControl leash, float scale) {
+ tx
+ .setScale(leash, scale, scale)
+ .setPosition(leash, (1f - scale) * mDisplayLayout.width() * 0.5f,
+ (1f - scale) * mDisplayLayout.height() * 0.5f);
+ }
+
+ void setDisplayLayout(DisplayLayout displayLayout) {
+ mDisplayLayout.set(displayLayout);
+ }
+
+ void onRotateDisplay(Context context, int toRotation) {
+ if (mDisplayLayout.rotation() == toRotation) {
+ return;
+ }
+ mDisplayLayout.rotateTo(context.getResources(), toRotation);
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimation.java
index 7c5f716..c01ad1d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimation.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimation.java
@@ -42,7 +42,8 @@
float touchX,
float touchY,
int keyAction,
- @BackEvent.SwipeEdge int swipeEdge);
+ @BackEvent.SwipeEdge int swipeEdge,
+ int displayId);
/**
* Called when the back swipe threshold is crossed.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
index d836865..e051e4f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
@@ -17,11 +17,13 @@
package com.android.wm.shell.back;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+import static android.view.Display.INVALID_DISPLAY;
import static android.view.RemoteAnimationTarget.MODE_CLOSING;
import static android.view.RemoteAnimationTarget.MODE_OPENING;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_CLOSE_PREPARE_BACK_NAVIGATION;
import static android.window.BackEvent.EDGE_NONE;
+import static android.window.DesktopExperienceFlags.ENABLE_INDEPENDENT_BACK_IN_PROJECTED;
import static android.window.TransitionInfo.FLAG_BACK_GESTURE_ANIMATED;
import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
import static android.window.TransitionInfo.FLAG_MOVED_TO_TOP;
@@ -45,6 +47,7 @@
import android.graphics.Point;
import android.graphics.Rect;
import android.hardware.input.InputManager;
+import android.hardware.input.KeyGestureEvent;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
@@ -95,6 +98,7 @@
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.List;
import java.util.function.Predicate;
/**
@@ -138,6 +142,7 @@
private final ShellExecutor mShellExecutor;
private final WindowManager mWindowManager;
private final Transitions mTransitions;
+ private final InputManager mInputManager;
@VisibleForTesting
final BackTransitionHandler mBackTransitionHandler;
@VisibleForTesting
@@ -271,6 +276,7 @@
mLatencyTracker = LatencyTracker.getInstance(mContext);
mShellCommandHandler = shellCommandHandler;
mWindowManager = context.getSystemService(WindowManager.class);
+ mInputManager = context.getSystemService(InputManager.class);
mTransitions = transitions;
mBackTransitionHandler = new BackTransitionHandler(mTransitions);
mTransitions.addHandler(mBackTransitionHandler);
@@ -325,10 +331,11 @@
float touchX,
float touchY,
int keyAction,
- @BackEvent.SwipeEdge int swipeEdge
+ @BackEvent.SwipeEdge int swipeEdge,
+ int displayId
) {
mShellExecutor.execute(
- () -> onMotionEvent(touchX, touchY, keyAction, swipeEdge));
+ () -> onMotionEvent(touchX, touchY, keyAction, swipeEdge, displayId));
}
@Override
@@ -475,7 +482,12 @@
float touchX,
float touchY,
int keyAction,
- @BackEvent.SwipeEdge int swipeEdge) {
+ @BackEvent.SwipeEdge int swipeEdge,
+ int displayId) {
+
+ if (ENABLE_INDEPENDENT_BACK_IN_PROJECTED.isTrue()) {
+ mBackAnimationAdapter.mOriginDisplayId = displayId;
+ }
BackTouchTracker activeTouchTracker = getActiveTracker();
if (activeTouchTracker != null) {
@@ -636,21 +648,23 @@
dispatchOnBackProgressed(mActiveCallback, backEvent);
}
- private void injectBackKey() {
+ private void injectBackKey(int displayId) {
ProtoLog.d(WM_SHELL_BACK_PREVIEW, "injectBackKey");
- sendBackEvent(KeyEvent.ACTION_DOWN);
- sendBackEvent(KeyEvent.ACTION_UP);
+ sendBackEvent(KeyEvent.ACTION_DOWN, displayId);
+ sendBackEvent(KeyEvent.ACTION_UP, displayId);
}
@SuppressLint("MissingPermission")
- private void sendBackEvent(int action) {
+ private void sendBackEvent(int action, int displayId) {
final long when = SystemClock.uptimeMillis();
final KeyEvent ev = new KeyEvent(when, when, action, KeyEvent.KEYCODE_BACK, 0 /* repeat */,
0 /* metaState */, KeyCharacterMap.VIRTUAL_KEYBOARD, 0 /* scancode */,
KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY,
InputDevice.SOURCE_KEYBOARD);
+ if (ENABLE_INDEPENDENT_BACK_IN_PROJECTED.isTrue()) {
+ ev.setDisplayId(displayId);
+ }
- ev.setDisplayId(mContext.getDisplay().getDisplayId());
if (!mContext.getSystemService(InputManager.class)
.injectInputEvent(ev, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC)) {
ProtoLog.e(WM_SHELL_BACK_PREVIEW, "Inject input event fail");
@@ -836,7 +850,7 @@
}
mCurrentTracker.reset();
if (triggerBack) {
- injectBackKey();
+ injectBackKey(mBackAnimationAdapter.mOriginDisplayId);
}
finishBackNavigation(triggerBack);
return;
@@ -965,7 +979,7 @@
if (mCurrentTracker.isFinished() && mCurrentTracker.getTriggerBack()) {
ProtoLog.d(WM_SHELL_BACK_PREVIEW, "resetTouchTracker -> start queued back navigation "
+ "AND post commit animation");
- injectBackKey();
+ injectBackKey(mBackAnimationAdapter.mOriginDisplayId);
finishBackNavigation(true);
mCurrentTracker.reset();
} else if (!mCurrentTracker.isFinished()) {
@@ -1153,26 +1167,22 @@
if (!Flags.delegateBackGestureToShell()) {
return;
}
- final RemoteCallback requestBackMonitor = new RemoteCallback(
- new RemoteCallback.OnResultListener() {
- @Override
- public void onResult(@Nullable Bundle result) {
- mShellExecutor.execute(() -> {
- if (mBackGestureStarted) {
- Log.w(TAG, "Back gesture is running, ignore request");
- return;
- }
- onMotionEvent(0, 0, KeyEvent.ACTION_DOWN, EDGE_NONE);
- setTriggerBack(true);
- onMotionEvent(0, 0, KeyEvent.ACTION_UP, EDGE_NONE);
- });
+ mInputManager.registerKeyGestureEventHandler(List.of(KeyGestureEvent.KEY_GESTURE_TYPE_BACK),
+ (event, focussedToken) -> {
+ if (event.getKeyGestureType() == KeyGestureEvent.KEY_GESTURE_TYPE_BACK) {
+ mShellExecutor.execute(() -> {
+ if (mBackGestureStarted) {
+ Log.w(TAG, "Back gesture is running, ignore request");
+ return;
+ }
+ onMotionEvent(0, 0, KeyEvent.ACTION_DOWN, EDGE_NONE, INVALID_DISPLAY);
+ setTriggerBack(true);
+ onMotionEvent(0, 0, KeyEvent.ACTION_UP, EDGE_NONE, INVALID_DISPLAY);
+ });
+ } else {
+ Log.w(TAG, "Unsupported gesture " + event + " received!");
}
});
- try {
- mActivityTaskManager.registerBackGestureDelegate(requestBackMonitor);
- } catch (RemoteException remoteException) {
- Log.w(TAG, "Failed register back gesture request ", remoteException);
- }
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index 7400385..a77220f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -64,6 +64,7 @@
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
+import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
@@ -376,8 +377,8 @@
mDisplayController = displayController;
final TaskViewTransitions tvTransitions;
if (TaskViewTransitions.useRepo()) {
- tvTransitions = new BubbleTaskViewTransitions(transitions, taskViewRepository,
- organizer, syncQueue);
+ tvTransitions = new TaskViewTransitions(transitions, taskViewRepository, organizer,
+ syncQueue);
} else {
tvTransitions = taskViewTransitions;
}
@@ -388,6 +389,7 @@
mSyncQueue = syncQueue;
mWmService = wmService;
mBubbleTransitions = bubbleTransitions;
+ mBubbleTransitions.setBubbleController(this);
mBubbleTaskViewFactory = new BubbleTaskViewFactory() {
@Override
public BubbleTaskView create() {
@@ -1587,17 +1589,24 @@
if (!BubbleAnythingFlagHelper.enableCreateAnyBubble()) return;
BubbleBarLocation updateLocation = isShowingAsBubbleBar() ? bubbleBarLocation : null;
if (updateLocation != null) {
+ // does not update the bubble bar location of the bubble bar, just expanded view
updateExpandedViewForBubbleBarLocation(updateLocation, source);
}
if (b.isInflated()) {
+ // mBubbleData should be updated with the new location to update the bubble bar location
mBubbleData.setSelectedBubbleAndExpandStack(b, updateLocation);
} else {
b.enable(Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE);
- ensureBubbleViewsAndWindowCreated();
- mBubbleTransitions.startLaunchIntoOrConvertToBubble(b, mExpandedViewManager,
- mBubbleTaskViewFactory, mBubblePositioner, mStackView, mLayerView,
- mBubbleIconFactory, mInflateSynchronously);
+ if (isShowingAsBubbleBar()) {
+ ensureBubbleViewsAndWindowCreated();
+ mBubbleTransitions.startLaunchIntoOrConvertToBubble(b, mExpandedViewManager,
+ mBubbleTaskViewFactory, mBubblePositioner, mStackView, mLayerView,
+ mBubbleIconFactory, mInflateSynchronously, bubbleBarLocation);
+ } else {
+ inflateAndAdd(b, /* suppressFlyout= */ true, /* showInShade= */ false,
+ updateLocation);
+ }
}
}
@@ -1633,6 +1642,39 @@
}
/**
+ * Expands and selects a bubble created from a running task in a different mode.
+ *
+ * @param taskInfo the task.
+ */
+ @Nullable
+ public Transitions.TransitionHandler expandStackAndSelectBubbleForExistingTransition(
+ @NonNull ActivityManager.RunningTaskInfo taskInfo,
+ @NonNull IBinder transition,
+ Consumer<Transitions.TransitionHandler> onInflatedCallback) {
+ if (!BubbleAnythingFlagHelper.enableBubbleToFullscreen()) return null;
+ // If there is an existing bubble then just show it
+ final String taskKey = Bubble.getAppBubbleKeyForTask(taskInfo);
+ if (mBubbleData.hasAnyBubbleWithKey(taskKey)) {
+ ProtoLog.v(WM_SHELL_BUBBLES, "expandStackAndSelectBubbleForExistingTransition(): "
+ + "skipping due to existing bubbled task=%d", taskInfo.taskId);
+ return null;
+ }
+
+ // Otherwise, create a new bubble and show it
+ Bubble b = mBubbleData.getOrCreateBubble(taskInfo); // Removes from overflow
+ ProtoLog.v(WM_SHELL_BUBBLES, "expandStackAndSelectBubbleForExistingTransition() taskId=%s",
+ taskInfo.taskId);
+ b.enable(Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE);
+
+ // Lazy init stack view when a bubble is created
+ ensureBubbleViewsAndWindowCreated();
+ return mBubbleTransitions.startLaunchNewTaskBubbleForExistingTransition(b,
+ mExpandedViewManager, mBubbleTaskViewFactory, mBubblePositioner, mStackView,
+ mLayerView, mBubbleIconFactory, mInflateSynchronously, transition,
+ onInflatedCallback);
+ }
+
+ /**
* Expands and selects a bubble based on the provided {@link BubbleEntry}. If no bubble
* exists for this entry, and it is able to bubble, a new bubble will be created.
*
@@ -2460,6 +2502,16 @@
mSysuiProxy.requestNotificationShadeTopUi(true, TAG);
}
+ if (Flags.enableBubbleSwipeUpCleanup() && !update.removedBubbles.isEmpty()
+ && !mBubbleData.hasBubbles()) {
+ // This update removed all the bubbles. Send an update to SystemUI to mark the stack
+ // collapsed. This should be sent by the UI classes (BubbleStackView or
+ // BubbleBarLayerView), but if we fail to send this, home gesture stops working.
+ // To avoid leaving the device in a bad state, add a failsafe call here to clean
+ // up the state.
+ mSysuiProxy.onStackExpandChanged(false);
+ }
+
mSysuiProxy.notifyInvalidateNotifications("BubbleData.Listener.applyUpdate");
updateBubbleViews();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
index 0777c85..07c344d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
@@ -311,7 +311,10 @@
wct.setTaskForceExcludedFromRecents(token, true /* forceExcluded */);
}
if (com.android.window.flags.Flags.disallowBubbleToEnterPip()) {
- wct.setDisablePip(tvc.getTaskToken(), true /* disablePip */);
+ wct.setDisablePip(token, true /* disablePip */);
+ }
+ if (BubbleAnythingFlagHelper.enableBubbleAnything()) {
+ wct.setDisableLaunchAdjacent(token, true);
}
tvc.getTaskOrganizer().applyTransaction(wct);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskStackListener.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskStackListener.kt
index 372891e..0608f68 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskStackListener.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskStackListener.kt
@@ -26,6 +26,7 @@
import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.common.TaskStackListenerCallback
import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_BUBBLES
+import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_BUBBLES_NOISY
import com.android.wm.shell.shared.bubbles.BubbleAnythingFlagHelper
import com.android.wm.shell.taskview.TaskViewTaskController
@@ -50,6 +51,10 @@
clearedTask: Boolean,
wasVisible: Boolean,
) {
+ ProtoLog.d(
+ WM_SHELL_BUBBLES_NOISY,
+ "BubbleTaskStackListener.onActivityRestartAttempt(): taskId=%d",
+ task.taskId)
val taskId = task.taskId
bubbleData.getBubbleInStackWithTaskId(taskId)?.let { bubble ->
if (isBubbleToFullscreen(task)) {
@@ -133,12 +138,19 @@
bubble: Bubble,
task: ActivityManager.RunningTaskInfo,
) {
+ ProtoLog.d(
+ WM_SHELL_BUBBLES_NOISY,
+ "BubbleTaskStackListener.collapsedBubbleToFullscreenInternal(): taskId=%d",
+ task.taskId)
val taskViewTaskController: TaskViewTaskController = bubble.taskView.controller
val taskOrganizer: ShellTaskOrganizer = taskViewTaskController.taskOrganizer
val wct = WindowContainerTransaction()
wct.setTaskForceExcludedFromRecents(task.token, false /* forceExcluded */)
.setLaunchNextToBubble(task.token, false /* launchNextToBubble */)
+ if (BubbleAnythingFlagHelper.enableBubbleAnything()) {
+ wct.setDisableLaunchAdjacent(task.token, false)
+ }
if (com.android.window.flags.Flags.disallowBubbleToEnterPip()) {
wct.setDisablePip(task.token, false /* disablePip */)
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewListener.java
index 47afd9a..312efd2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewListener.java
@@ -202,12 +202,16 @@
final TaskViewTaskController tvc = mTaskView.getController();
final WindowContainerToken token = tvc.getTaskToken();
final WindowContainerTransaction wct = new WindowContainerTransaction();
+ wct.setAlwaysOnTop(token, true /* alwaysOnTop */);
if (com.android.window.flags.Flags.excludeTaskFromRecents()) {
wct.setTaskForceExcludedFromRecents(token, true /* forceExcluded */);
}
if (com.android.window.flags.Flags.disallowBubbleToEnterPip()) {
wct.setDisablePip(token, true /* disablePip */);
}
+ if (BubbleAnythingFlagHelper.enableBubbleAnything()) {
+ wct.setDisableLaunchAdjacent(token, true);
+ }
tvc.getTaskOrganizer().applyTransaction(wct);
// With the task org, the taskAppeared callback will only happen once the task has
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewTransitions.java
deleted file mode 100644
index 8a97701..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewTransitions.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2025 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.bubbles;
-
-import android.app.ActivityManager;
-import android.view.SurfaceControl;
-import android.window.WindowContainerTransaction;
-
-import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.common.SyncTransactionQueue;
-import com.android.wm.shell.taskview.TaskViewRepository;
-import com.android.wm.shell.taskview.TaskViewTaskController;
-import com.android.wm.shell.taskview.TaskViewTransitions;
-import com.android.wm.shell.transition.Transitions;
-
-public class BubbleTaskViewTransitions extends TaskViewTransitions {
-
- public BubbleTaskViewTransitions(Transitions transitions, TaskViewRepository repository,
- ShellTaskOrganizer taskOrganizer, SyncTransactionQueue syncQueue) {
- super(transitions, repository, taskOrganizer, syncQueue);
- }
-
- @Override
- public void prepareOpenAnimation(TaskViewTaskController taskView, boolean newTask,
- SurfaceControl.Transaction startTransaction,
- SurfaceControl.Transaction finishTransaction, ActivityManager.RunningTaskInfo taskInfo,
- SurfaceControl leash, WindowContainerTransaction wct) {
- if (!taskInfo.getConfiguration().windowConfiguration.isAlwaysOnTop()) {
- wct.setAlwaysOnTop(taskInfo.token, true /* alwaysOnTop */);
- }
- super.prepareOpenAnimation(taskView, newTask, startTransaction, finishTransaction, taskInfo,
- leash, wct);
- }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTransitions.java
index 4f3b76f..24e98a4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTransitions.java
@@ -68,6 +68,8 @@
import com.android.wm.shell.bubbles.bar.BubbleBarExpandedView;
import com.android.wm.shell.bubbles.bar.BubbleBarLayerView;
import com.android.wm.shell.common.HomeIntentProvider;
+import com.android.wm.shell.shared.bubbles.BubbleAnythingFlagHelper;
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation;
import com.android.wm.shell.taskview.TaskView;
import com.android.wm.shell.taskview.TaskViewRepository;
import com.android.wm.shell.taskview.TaskViewTaskController;
@@ -78,6 +80,7 @@
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Executor;
+import java.util.function.Consumer;
/**
* Implements transition coordination for bubble operations.
@@ -107,6 +110,8 @@
private final Map<IBinder, TransitionHandler> mEnterTransitions =
new HashMap<>();
+ private BubbleController mBubbleController;
+
public BubbleTransitions(Context context,
@NonNull Transitions transitions, @NonNull ShellTaskOrganizer organizer,
@NonNull TaskViewRepository repository, @NonNull BubbleData bubbleData,
@@ -120,6 +125,10 @@
mContext = context;
}
+ void setBubbleController(BubbleController controller) {
+ mBubbleController = controller;
+ }
+
/**
* Returns whether there is a pending transition for the given request.
*/
@@ -176,9 +185,40 @@
BubbleExpandedViewManager expandedViewManager, BubbleTaskViewFactory factory,
BubblePositioner positioner, BubbleStackView stackView,
BubbleBarLayerView layerView, BubbleIconFactory iconFactory,
- boolean inflateSync) {
+ boolean inflateSync, @Nullable BubbleBarLocation bubbleBarLocation) {
new LaunchOrConvertToBubble(bubble, mContext, expandedViewManager, factory, positioner,
- stackView, layerView, iconFactory, inflateSync);
+ stackView, layerView, iconFactory, inflateSync, bubbleBarLocation);
+ }
+
+ /**
+ * Called to initiate axed bubble-to-bubble launch/convert for the given transition.
+ *
+ * @return whether a new transition was started for the launch
+ */
+ public boolean startBubbleToBubbleLaunch(@NonNull IBinder transition,
+ @NonNull ActivityManager.RunningTaskInfo launchingTask,
+ @NonNull Consumer<TransitionHandler> onInflatedCallback) {
+ TransitionHandler handler =
+ mBubbleController.expandStackAndSelectBubbleForExistingTransition(
+ launchingTask, transition, onInflatedCallback);
+ if (handler != null) {
+ mEnterTransitions.put(transition, handler);
+ }
+ return handler != null;
+ }
+
+ /**
+ * Starts a new launch or convert transition to show the given bubble.
+ */
+ public TransitionHandler startLaunchNewTaskBubbleForExistingTransition(Bubble bubble,
+ BubbleExpandedViewManager expandedViewManager, BubbleTaskViewFactory factory,
+ BubblePositioner positioner, BubbleStackView stackView,
+ BubbleBarLayerView layerView, BubbleIconFactory iconFactory,
+ boolean inflateSync, IBinder transition,
+ Consumer<TransitionHandler> onInflatedCallback) {
+ return new LaunchNewTaskBubbleForExistingTransition(bubble, mContext, expandedViewManager,
+ factory, positioner, stackView, layerView, iconFactory, inflateSync, transition,
+ onInflatedCallback);
}
/**
@@ -240,6 +280,29 @@
}
/**
+ * Returns a {@link WindowContainerTransaction} that includes the necessary operations of
+ * entering or leaving as Bubble.
+ */
+ private WindowContainerTransaction getBubbleTransaction(@NonNull WindowContainerToken token,
+ boolean toBubble) {
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ wct.setWindowingMode(token,
+ toBubble ? WINDOWING_MODE_MULTI_WINDOW : WINDOWING_MODE_UNDEFINED);
+ wct.setAlwaysOnTop(token, toBubble /* alwaysOnTop */);
+ wct.setLaunchNextToBubble(token, toBubble /* launchNextToBubble */);
+ if (com.android.window.flags.Flags.excludeTaskFromRecents()) {
+ wct.setTaskForceExcludedFromRecents(token, toBubble /* forceExcluded */);
+ }
+ if (com.android.window.flags.Flags.disallowBubbleToEnterPip()) {
+ wct.setDisablePip(token, toBubble /* disablePip */);
+ }
+ if (BubbleAnythingFlagHelper.enableBubbleAnything()) {
+ wct.setDisableLaunchAdjacent(token, toBubble /* disableLaunchAdjacent */);
+ }
+ return wct;
+ }
+
+ /**
* Interface to a bubble-specific transition. Bubble transitions have a multi-step lifecycle
* in order to coordinate with the bubble view logic. These steps are communicated on this
* interface.
@@ -252,7 +315,7 @@
}
/**
- * Information about the task when it is being dragged to a bubble
+ * Information about the task when it is being dragged to a bubble.
*/
public static class DragData {
private final boolean mReleasedOnLeft;
@@ -304,12 +367,14 @@
}
/**
- * Keeps track of internal state of different steps of a BubbleTransition.
+ * Keeps track of internal state of different steps of a BubbleTransition. Serves as a gating
+ * mechanism to block animations or updates until necessary states are set.
*/
private static class TransitionProgress {
private final Bubble mBubble;
private boolean mTransitionReady;
+ private boolean mInflated;
private boolean mReadyToExpand;
private boolean mSurfaceReady;
@@ -317,28 +382,37 @@
mBubble = bubble;
}
+ void setInflated() {
+ ProtoLog.d(WM_SHELL_BUBBLES_NOISY, "TransitionProgress.setInflated()");
+ mInflated = true;
+ onUpdate();
+ }
+
void setTransitionReady() {
+ ProtoLog.d(WM_SHELL_BUBBLES_NOISY, "TransitionProgress.setTransitionReady()");
mTransitionReady = true;
onUpdate();
}
void setReadyToExpand() {
+ ProtoLog.d(WM_SHELL_BUBBLES_NOISY, "TransitionProgress.setReadyToExpand()");
mReadyToExpand = true;
onUpdate();
}
void setSurfaceReady() {
+ ProtoLog.d(WM_SHELL_BUBBLES_NOISY, "TransitionProgress.setSurfaceReady()");
mSurfaceReady = true;
onUpdate();
}
boolean isReadyToAnimate() {
// Animation only depends on transition and surface state
- return mTransitionReady && mSurfaceReady;
+ return mTransitionReady && mSurfaceReady && mInflated;
}
private void onUpdate() {
- if (mTransitionReady && mReadyToExpand && mSurfaceReady) {
+ if (mTransitionReady && mReadyToExpand && mSurfaceReady && mInflated) {
// Clear the transition from bubble when all the steps are ready
mBubble.setPreparingTransition(null);
}
@@ -346,6 +420,250 @@
}
/**
+ * Starts a new bubble for an existing playing transition.
+ * TODO(b/408328557): To be consolidated with LaunchOrConvertToBubble and ConvertToBubble
+ */
+ @VisibleForTesting
+ class LaunchNewTaskBubbleForExistingTransition implements TransitionHandler, BubbleTransition {
+ final BubbleBarLayerView mLayerView;
+ private final TransitionProgress mTransitionProgress;
+ Bubble mBubble;
+ IBinder mTransition;
+ Transitions.TransitionFinishCallback mFinishCb;
+ WindowContainerTransaction mFinishWct = null;
+ final Rect mStartBounds = new Rect();
+ SurfaceControl mSnapshot = null;
+ // The task info is resolved once we find the task from the transition info using the
+ // pending launch cookie otherwise
+ @Nullable
+ TaskInfo mTaskInfo;
+ BubbleViewProvider mPriorBubble = null;
+ // Whether we should play the convert-task animation, or the launch-task animation
+ private boolean mPlayConvertTaskAnimation;
+
+ private SurfaceControl.Transaction mFinishT;
+ private SurfaceControl mTaskLeash;
+
+ LaunchNewTaskBubbleForExistingTransition(Bubble bubble, Context context,
+ BubbleExpandedViewManager expandedViewManager, BubbleTaskViewFactory factory,
+ BubblePositioner positioner, BubbleStackView stackView,
+ BubbleBarLayerView layerView, BubbleIconFactory iconFactory,
+ boolean inflateSync, IBinder transition,
+ Consumer<TransitionHandler> onInflatedCallback) {
+ mBubble = bubble;
+ mTransition = transition;
+ mTransitionProgress = new TransitionProgress(bubble);
+ mLayerView = layerView;
+ mBubble.setInflateSynchronously(inflateSync);
+ mBubble.setPreparingTransition(this);
+ mBubble.inflate(
+ b -> {
+ onInflated(b);
+ onInflatedCallback.accept(LaunchNewTaskBubbleForExistingTransition.this);
+ },
+ context,
+ expandedViewManager,
+ factory,
+ positioner,
+ stackView,
+ layerView,
+ iconFactory,
+ false /* skipInflation */);
+ }
+
+ @VisibleForTesting
+ void onInflated(Bubble b) {
+ if (b != mBubble) {
+ throw new IllegalArgumentException("inflate callback doesn't match bubble");
+ }
+ if (!mBubble.isShortcut() && !mBubble.isApp()) {
+ throw new IllegalArgumentException("Unsupported bubble type");
+ }
+ final Rect launchBounds = new Rect();
+ mLayerView.getExpandedViewRestBounds(launchBounds);
+
+ final TaskView tv = b.getTaskView();
+ tv.setSurfaceLifecycle(SurfaceView.SURFACE_LIFECYCLE_FOLLOWS_ATTACHMENT);
+ final TaskViewRepository.TaskViewState state = mRepository.byTaskView(
+ tv.getController());
+ if (state != null) {
+ state.mVisible = true;
+ }
+ mTransitionProgress.setInflated();
+ mTaskViewTransitions.enqueueExternal(tv.getController(), () -> {
+ return mTransition;
+ });
+ }
+
+ @Override
+ public void skip() {
+ mBubble.setPreparingTransition(null);
+ cleanup();
+ }
+
+ @Override
+ public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
+ @Nullable TransitionRequestInfo request) {
+ return null;
+ }
+
+ @Override
+ public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction startT,
+ @NonNull SurfaceControl.Transaction finishT,
+ @NonNull IBinder mergeTarget,
+ @NonNull Transitions.TransitionFinishCallback finishCallback) {
+ }
+
+ @Override
+ public void onTransitionConsumed(@NonNull IBinder transition, boolean aborted,
+ @NonNull SurfaceControl.Transaction finishTransaction) {
+ if (!aborted) return;
+ mTaskViewTransitions.onExternalDone(mTransition);
+ mTransition = null;
+ }
+
+ @Override
+ public boolean startAnimation(@NonNull IBinder transition,
+ @NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction,
+ @NonNull Transitions.TransitionFinishCallback finishCallback) {
+
+ // Identify the task that we are converting or launching. Note, we iterate back to front
+ // so that we can adjust alpha for revealed surfaces as needed.
+ boolean found = false;
+ mPlayConvertTaskAnimation = false;
+ for (int i = info.getChanges().size() - 1; i >= 0; i--) {
+ final TransitionInfo.Change chg = info.getChanges().get(i);
+ final boolean isTaskToConvertToBubble = (chg.getTaskInfo() != null)
+ && (chg.getMode() == TRANSIT_CHANGE || isOpeningMode(chg.getMode()));
+ if (isTaskToConvertToBubble) {
+ mStartBounds.set(chg.getStartAbsBounds());
+ // Converting a task into taskview, so treat as "new"
+ mFinishWct = new WindowContainerTransaction();
+ mTaskInfo = chg.getTaskInfo();
+ mFinishT = finishTransaction;
+ mTaskLeash = chg.getLeash();
+ mSnapshot = chg.getSnapshot();
+ // TODO: This should be set for the CHANGE transition, but for some reason there
+ // is no snapshot, so fallback to the open transition for now
+ mPlayConvertTaskAnimation = false;
+ found = true;
+ } else {
+ // In core-initiated launches, the transition is of an OPEN type, and we need to
+ // manually show the surfaces behind the newly bubbled task
+ if (info.getType() == TRANSIT_OPEN && isOpeningMode(chg.getMode())) {
+ startTransaction.setAlpha(chg.getLeash(), 1f);
+ }
+ }
+ }
+ if (!found) {
+ Slog.w(TAG, "Expected a TaskView conversion in this transition but didn't get "
+ + "one, cleaning up the task view");
+ mBubble.getTaskView().getController().setTaskNotFound();
+ mTaskViewTransitions.onExternalDone(mTransition);
+ return false;
+ }
+ mFinishCb = finishCallback;
+
+ // Now update state (and talk to launcher) in parallel with snapshot stuff
+ mBubbleData.notificationEntryUpdated(mBubble, /* suppressFlyout= */ true,
+ /* showInShade= */ false);
+
+ if (mPlayConvertTaskAnimation) {
+ final int left = mStartBounds.left - info.getRoot(0).getOffset().x;
+ final int top = mStartBounds.top - info.getRoot(0).getOffset().y;
+ startTransaction.setPosition(mTaskLeash, left, top);
+ startTransaction.show(mSnapshot);
+ // Move snapshot to root so that it remains visible while task is moved to taskview
+ startTransaction.reparent(mSnapshot, info.getRoot(0).getLeash());
+ startTransaction.setPosition(mSnapshot, left, top);
+ startTransaction.setLayer(mSnapshot, Integer.MAX_VALUE);
+ } else {
+ final int left = mStartBounds.left - info.getRoot(0).getOffset().x;
+ final int top = mStartBounds.top - info.getRoot(0).getOffset().y;
+ startTransaction.setPosition(mTaskLeash, left, top);
+ }
+ startTransaction.apply();
+
+ mTaskViewTransitions.onExternalDone(mTransition);
+ mTransitionProgress.setTransitionReady();
+ startExpandAnim();
+ return true;
+ }
+
+ private void startExpandAnim() {
+ ProtoLog.d(WM_SHELL_BUBBLES_NOISY, "BubbleTransitions.startExpandAnim(): "
+ + "readyToAnimate=%b", mTransitionProgress.isReadyToAnimate());
+ if (mLayerView.canExpandView(mBubble)) {
+ mPriorBubble = mLayerView.prepareConvertedView(mBubble);
+ } else if (mLayerView.isExpanded()) {
+ mTransitionProgress.setReadyToExpand();
+ }
+ if (mTransitionProgress.isReadyToAnimate()) {
+ playAnimation();
+ }
+ }
+
+ @Override
+ public void continueExpand() {
+ mTransitionProgress.setReadyToExpand();
+ }
+
+ @Override
+ public void surfaceCreated() {
+ mTransitionProgress.setSurfaceReady();
+ mMainExecutor.execute(() -> {
+ ProtoLog.d(WM_SHELL_BUBBLES_NOISY, "BubbleTransitions.surfaceCreated(): "
+ + "mTaskLeash=%s", mTaskLeash);
+ final TaskViewTaskController tvc = mBubble.getTaskView().getController();
+ final TaskViewRepository.TaskViewState state = mRepository.byTaskView(tvc);
+ if (state == null) return;
+ state.mVisible = true;
+ if (mTransitionProgress.isReadyToAnimate()) {
+ playAnimation();
+ }
+ });
+ }
+
+ private void playAnimation() {
+ ProtoLog.d(WM_SHELL_BUBBLES_NOISY, "BubbleTransitions.playAnimation()");
+ final TaskViewTaskController tv = mBubble.getTaskView().getController();
+ final SurfaceControl.Transaction startT = new SurfaceControl.Transaction();
+ // Set task position to 0,0 as it will be placed inside the TaskView
+ startT.setPosition(mTaskLeash, 0, 0)
+ .reparent(mTaskLeash, mBubble.getTaskView().getSurfaceControl())
+ .setAlpha(mTaskLeash, 1f)
+ .show(mTaskLeash);
+ mTaskViewTransitions.prepareOpenAnimation(tv, true /* new */, startT, mFinishT,
+ (ActivityManager.RunningTaskInfo) mTaskInfo, mTaskLeash, mFinishWct);
+ // Add the task view task listener manually since we aren't going through
+ // TaskViewTransitions (which normally sets up the listener via a pending launch cookie
+ mTaskOrganizer.addListenerForTaskId(tv, mTaskInfo.taskId);
+
+ if (mFinishWct.isEmpty()) {
+ mFinishWct = null;
+ }
+
+ float startScale = 1f;
+ if (mPlayConvertTaskAnimation) {
+ mLayerView.animateConvert(startT, mStartBounds, startScale, mSnapshot,
+ mTaskLeash,
+ this::cleanup);
+ } else {
+ startT.apply();
+ mLayerView.animateExpand(null, this::cleanup);
+ }
+ }
+
+ private void cleanup() {
+ mFinishCb.onTransitionFinished(mFinishWct);
+ mFinishCb = null;
+ }
+ }
+
+ /**
* Starts a new transition into a bubble, which will either play a launch animation (if the task
* was not previously visible) or a convert animation (if the task is currently visible).
*/
@@ -369,21 +687,23 @@
BubbleViewProvider mPriorBubble = null;
// Whether we should play the convert-task animation, or the launch-task animation
private boolean mPlayConvertTaskAnimation;
- private boolean mPlayingAnimation;
private SurfaceControl.Transaction mFinishT;
private SurfaceControl mTaskLeash;
+ @Nullable
+ private BubbleBarLocation mBubbleBarLocation;
LaunchOrConvertToBubble(Bubble bubble, Context context,
BubbleExpandedViewManager expandedViewManager, BubbleTaskViewFactory factory,
BubblePositioner positioner, BubbleStackView stackView,
BubbleBarLayerView layerView, BubbleIconFactory iconFactory,
- boolean inflateSync) {
+ boolean inflateSync, @Nullable BubbleBarLocation bubbleBarLocation) {
mBubble = bubble;
mTransitionProgress = new TransitionProgress(bubble);
mLayerView = layerView;
mBubble.setInflateSynchronously(inflateSync);
mBubble.setPreparingTransition(this);
+ mBubbleBarLocation = bubbleBarLocation;
mBubble.inflate(
this::onInflated,
context,
@@ -414,6 +734,7 @@
if (state != null) {
state.mVisible = true;
}
+ mTransitionProgress.setInflated();
mTaskViewTransitions.enqueueExternal(tv.getController(), () -> {
// We need to convert the next launch into a bubble
mLaunchCookie = new ActivityOptions.LaunchCookie();
@@ -545,7 +866,7 @@
// Now update state (and talk to launcher) in parallel with snapshot stuff
mBubbleData.notificationEntryUpdated(mBubble, /* suppressFlyout= */ true,
- /* showInShade= */ false);
+ /* showInShade= */ false, mBubbleBarLocation);
if (mPlayConvertTaskAnimation) {
final int left = mStartBounds.left - info.getRoot(0).getOffset().x;
@@ -596,11 +917,11 @@
public void surfaceCreated() {
mTransitionProgress.setSurfaceReady();
mMainExecutor.execute(() -> {
+ ProtoLog.d(WM_SHELL_BUBBLES_NOISY, "BubbleTransitions.surfaceCreated(): "
+ + "mTaskLeash=%s", mTaskLeash);
final TaskViewTaskController tvc = mBubble.getTaskView().getController();
final TaskViewRepository.TaskViewState state = mRepository.byTaskView(tvc);
if (state == null) return;
- ProtoLog.d(WM_SHELL_BUBBLES_NOISY, "BubbleTransitions.surfaceCreated(): "
- + "mTaskLeash=%s", mTaskLeash);
state.mVisible = true;
if (mTransitionProgress.isReadyToAnimate()) {
playAnimation(true /* animate */);
@@ -609,10 +930,6 @@
}
private void playAnimation(boolean animate) {
- if (mPlayingAnimation) {
- // Already playing
- return;
- }
ProtoLog.d(WM_SHELL_BUBBLES_NOISY, "BubbleTransitions.playAnimation(): animate=%b",
animate);
final TaskViewTaskController tv = mBubble.getTaskView().getController();
@@ -648,7 +965,6 @@
startT.apply();
cleanup();
}
- mPlayingAnimation = true;
}
private void cleanup() {
@@ -730,7 +1046,8 @@
}
final Rect launchBounds = new Rect();
mLayerView.getExpandedViewRestBounds(launchBounds);
- final WindowContainerTransaction wct = new WindowContainerTransaction();
+ final WindowContainerTransaction wct = getBubbleTransaction(mTaskInfo.token,
+ true /* toBubble */);
mHomeIntentProvider.addLaunchHomePendingIntent(wct, mTaskInfo.displayId,
mTaskInfo.userId);
@@ -739,16 +1056,6 @@
wct.reparent(mTaskInfo.token, null, true);
}
}
-
- wct.setAlwaysOnTop(mTaskInfo.token, true /* alwaysOnTop */);
- wct.setLaunchNextToBubble(mTaskInfo.token, true /* launchNextToBubble */);
- if (com.android.window.flags.Flags.excludeTaskFromRecents()) {
- wct.setTaskForceExcludedFromRecents(mTaskInfo.token, true /* forceExcluded */);
- }
- if (com.android.window.flags.Flags.disallowBubbleToEnterPip()) {
- wct.setDisablePip(mTaskInfo.token, true /* disablePip */);
- }
- wct.setWindowingMode(mTaskInfo.token, WINDOWING_MODE_MULTI_WINDOW);
wct.setBounds(mTaskInfo.token, launchBounds);
final TaskView tv = b.getTaskView();
@@ -758,6 +1065,7 @@
if (state != null) {
state.mVisible = true;
}
+ mTransitionProgress.setInflated();
mTaskViewTransitions.enqueueExternal(tv.getController(), () -> {
mTransition = mTransitions.startTransition(TRANSIT_CONVERT_TO_BUBBLE, wct, this);
return mTransition;
@@ -946,17 +1254,9 @@
mTaskInfo = taskInfo;
mBubble.setPreparingTransition(this);
- final WindowContainerTransaction wct = new WindowContainerTransaction();
final WindowContainerToken token = mTaskInfo.getToken();
- wct.setWindowingMode(token, WINDOWING_MODE_UNDEFINED);
- wct.setAlwaysOnTop(token, false /* alwaysOnTop */);
- wct.setLaunchNextToBubble(token, false /* launchNextToBubble */);
- if (com.android.window.flags.Flags.excludeTaskFromRecents()) {
- wct.setTaskForceExcludedFromRecents(token, false /* forceExcluded */);
- }
- if (com.android.window.flags.Flags.disallowBubbleToEnterPip()) {
- wct.setDisablePip(token, false /* disablePip */);
- }
+ final WindowContainerTransaction wct = getBubbleTransaction(token,
+ false /* toBubble */);
mTaskOrganizer.setInterceptBackPressedOnTaskRoot(token, false /* intercept */);
mTaskViewTransitions.enqueueExternal(
mBubble.getTaskView().getController(),
@@ -1115,18 +1415,12 @@
mTransactionProvider = transactionProvider;
bubble.setPreparingTransition(this);
final WindowContainerToken token = bubble.getTaskView().getTaskInfo().getToken();
- final WindowContainerTransaction wct = new WindowContainerTransaction();
- wct.setAlwaysOnTop(token, false /* alwaysOnTop */);
- wct.setLaunchNextToBubble(token, false /* launchNextToBubble */);
- if (com.android.window.flags.Flags.excludeTaskFromRecents()) {
- wct.setTaskForceExcludedFromRecents(token, false /* forceExcluded */);
- }
- if (com.android.window.flags.Flags.disallowBubbleToEnterPip()) {
- wct.setDisablePip(token, false /* disablePip */);
- }
- wct.setWindowingMode(token, WINDOWING_MODE_UNDEFINED);
+ final WindowContainerTransaction wct = getBubbleTransaction(token,
+ false /* toBubble */);
wct.reorder(token, /* onTop= */ true);
- wct.setHidden(token, false);
+ if (!BubbleAnythingFlagHelper.enableCreateAnyBubbleWithForceExcludedFromRecents()) {
+ wct.setHidden(token, false);
+ }
mTaskOrganizer.setInterceptBackPressedOnTaskRoot(token, false /* intercept */);
mTaskViewTransitions.enqueueExternal(bubble.getTaskView().getController(), () -> {
mTransition = mTransitions.startTransition(TRANSIT_TO_FRONT, wct, this);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/BoxShadowHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/BoxShadowHelper.java
new file mode 100644
index 0000000..43016f9
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/BoxShadowHelper.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.common;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.gui.BorderSettings;
+import android.gui.BoxShadowSettings;
+
+import com.android.wm.shell.R;
+
+/**
+ * This class has helper functions for obtaining box shadow and border parameters from
+ * resource ids.
+ */
+public class BoxShadowHelper {
+ /**
+ * Gets border settings from an id.
+ *
+ * @return the border settings.
+ */
+ public static BorderSettings getBorderSettings(Context context, int borderSettingsId) {
+ final TypedArray attr = context.obtainStyledAttributes(
+ borderSettingsId, R.styleable.BorderSettings);
+ final BorderSettings result = new BorderSettings();
+ result.strokeWidth =
+ attr.getDimension(
+ R.styleable.BorderSettings_borderStrokeWidth, 0f);
+ result.color =
+ attr.getColor(
+ R.styleable.BorderSettings_borderColor, 0);
+ attr.recycle();
+ return result;
+ }
+
+ /**
+ * Gets box shadow settings from an id.
+ *
+ * @return the box shadow settings.
+ */
+ public static BoxShadowSettings getBoxShadowSettings(Context context,
+ int[] boxShadowSettingsIds) {
+ final BoxShadowSettings result = new BoxShadowSettings();
+ result.boxShadows =
+ new BoxShadowSettings.BoxShadowParams[boxShadowSettingsIds.length];
+ for (int i = 0; i < boxShadowSettingsIds.length; i++) {
+ final TypedArray attr = context.obtainStyledAttributes(
+ boxShadowSettingsIds[i], R.styleable.BoxShadowSettings);
+
+ final BoxShadowSettings.BoxShadowParams box =
+ new BoxShadowSettings.BoxShadowParams();
+ box.blurRadius = attr.getDimension(
+ R.styleable.BoxShadowSettings_boxShadowBlurRadius, 0f);
+ box.spreadRadius = attr.getDimension(
+ R.styleable.BoxShadowSettings_boxShadowSpreadRadius, 0f);
+ box.offsetX = attr.getDimension(
+ R.styleable.BoxShadowSettings_boxShadowOffsetX, 0f);
+ box.offsetY = attr.getDimension(
+ R.styleable.BoxShadowSettings_boxShadowOffsetY, 0f);
+ box.color = attr.getColor(
+ R.styleable.BoxShadowSettings_boxShadowColor, 0);
+
+ result.boxShadows[i] = box;
+
+ attr.recycle();
+ }
+ return result;
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java
index 4263edb..4fa8a93 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java
@@ -39,7 +39,7 @@
import com.android.wm.shell.common.DisplayChangeController.OnDisplayChangingListener;
import com.android.wm.shell.shared.annotations.ShellMainThread;
-import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
+import com.android.wm.shell.shared.desktopmode.DesktopState;
import com.android.wm.shell.sysui.ShellInit;
import java.util.ArrayList;
@@ -63,6 +63,7 @@
private final DisplayManager mDisplayManager;
private final DisplayChangeController mChangeController;
private final IDisplayWindowListener mDisplayContainerListener;
+ private final DesktopState mDesktopState;
private final SparseArray<DisplayRecord> mDisplays = new SparseArray<>();
private final ArrayList<OnDisplaysChangedListener> mDisplayChangedListeners = new ArrayList<>();
@@ -70,11 +71,13 @@
private DisplayTopology mDisplayTopology;
public DisplayController(Context context, IWindowManager wmService, ShellInit shellInit,
- ShellExecutor mainExecutor, DisplayManager displayManager) {
+ ShellExecutor mainExecutor, DisplayManager displayManager,
+ DesktopState desktopState) {
mMainExecutor = mainExecutor;
mContext = context;
mWmService = wmService;
mDisplayManager = displayManager;
+ mDesktopState = desktopState;
// TODO: Inject this instead
mChangeController = new DisplayChangeController(mWmService, shellInit, mainExecutor);
mDisplayContainerListener = new DisplayWindowListenerImpl();
@@ -94,7 +97,7 @@
}
if (DesktopExperienceFlags.ENABLE_CONNECTED_DISPLAYS_WINDOW_DRAG.isTrue()
- && DesktopModeStatus.canEnterDesktopMode(mContext)) {
+ && mDesktopState.canEnterDesktopMode()) {
mDisplayManager.registerTopologyListener(mMainExecutor,
this::onDisplayTopologyChanged);
onDisplayTopologyChanged(mDisplayManager.getDisplayTopology());
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/HomeIntentProvider.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/HomeIntentProvider.kt
index 8751b65..ab24402 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/HomeIntentProvider.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/HomeIntentProvider.kt
@@ -24,8 +24,8 @@
import android.content.Intent
import android.os.UserHandle
import android.view.Display.DEFAULT_DISPLAY
+import android.window.DesktopExperienceFlags.ENABLE_PER_DISPLAY_DESKTOP_WALLPAPER_ACTIVITY
import android.window.WindowContainerTransaction
-import com.android.window.flags.Flags
/** Creates home intent **/
class HomeIntentProvider(
@@ -48,7 +48,7 @@
launchWindowingMode = WINDOWING_MODE_FULLSCREEN
pendingIntentBackgroundActivityStartMode =
ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS
- if (Flags.enablePerDisplayDesktopWallpaperActivity()) {
+ if (ENABLE_PER_DISPLAY_DESKTOP_WALLPAPER_ACTIVITY.isTrue) {
launchDisplayId = displayId
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/MultiDisplayDragMoveBoundsCalculator.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/MultiDisplayDragMoveBoundsCalculator.kt
index a13ad20..eff64e2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/MultiDisplayDragMoveBoundsCalculator.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/MultiDisplayDragMoveBoundsCalculator.kt
@@ -40,6 +40,7 @@
* @param y The current y-coordinate of the drag pointer (in pixels).
* @return A RectF object representing the calculated global DP bounds of the window.
*/
+ @JvmStatic
fun calculateGlobalDpBoundsForDrag(
startDisplayLayout: DisplayLayout,
repositionStartPoint: PointF,
@@ -73,6 +74,7 @@
* @param displayLayout The DisplayLayout representing the display to convert the bounds to.
* @return A Rect object representing the local pixel bounds on the specified display.
*/
+ @JvmStatic
fun convertGlobalDpToLocalPxForRect(rectDp: RectF, displayLayout: DisplayLayout): Rect {
val leftTopPxDisplay = displayLayout.globalDpToLocalPx(rectDp.left, rectDp.top)
val rightBottomPxDisplay = displayLayout.globalDpToLocalPx(rectDp.right, rectDp.bottom)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/MultiDisplayDragMoveIndicatorController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/MultiDisplayDragMoveIndicatorController.kt
index 7a5bc13..704253c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/MultiDisplayDragMoveIndicatorController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/MultiDisplayDragMoveIndicatorController.kt
@@ -58,6 +58,7 @@
continue
}
val displayLayout = displayController.getDisplayLayout(displayId) ?: continue
+ val displayContext = displayController.getDisplayContext(displayId) ?: continue
val shouldBeVisible =
RectF.intersects(RectF(boundsDp), displayLayout.globalBoundsDp())
if (
@@ -88,6 +89,7 @@
indicatorSurfaceFactory.create(
taskInfo,
displayController.getDisplay(displayId),
+ displayContext,
)
newIndicator.show(
transactionSupplier(),
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/MultiDisplayDragMoveIndicatorSurface.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/MultiDisplayDragMoveIndicatorSurface.kt
index d05d3b0..ac23e5e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/MultiDisplayDragMoveIndicatorSurface.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/MultiDisplayDragMoveIndicatorSurface.kt
@@ -22,12 +22,11 @@
import android.os.Trace
import android.view.Display
import android.view.SurfaceControl
-import androidx.compose.material3.dynamicDarkColorScheme
-import androidx.compose.material3.dynamicLightColorScheme
+import android.window.TaskConstants
import androidx.compose.ui.graphics.toArgb
import com.android.wm.shell.RootTaskDisplayAreaOrganizer
+import com.android.wm.shell.shared.R
import com.android.wm.shell.windowdecor.common.DecorThemeUtil
-import com.android.wm.shell.windowdecor.common.Theme
/**
* Represents the indicator surface that visualizes the current position of a dragged window during
@@ -48,8 +47,8 @@
private var veilSurface: SurfaceControl? = null
private val decorThemeUtil = DecorThemeUtil(context)
- private val lightColors = dynamicLightColorScheme(context)
- private val darkColors = dynamicDarkColorScheme(context)
+ private val cornerRadius = context.resources
+ .getDimensionPixelSize(R.dimen.desktop_windowing_freeform_rounded_corner_radius).toFloat()
init {
Trace.beginSection("DragIndicatorSurface#init")
@@ -88,17 +87,16 @@
displayId: Int,
bounds: Rect,
) {
- val backgroundColor =
- when (decorThemeUtil.getAppTheme(taskInfo)) {
- Theme.LIGHT -> lightColors.surfaceContainer
- Theme.DARK -> darkColors.surfaceContainer
- }
+ val backgroundColor = decorThemeUtil.getColorScheme(taskInfo).surfaceContainer
val veil = veilSurface ?: return
isVisible = true
rootTaskDisplayAreaOrganizer.reparentToDisplayArea(displayId, veil, transaction)
relayout(bounds, transaction, shouldBeVisible = true)
- transaction.show(veil).setColor(veil, Color.valueOf(backgroundColor.toArgb()).components)
+ transaction
+ .show(veil)
+ .setColor(veil, Color.valueOf(backgroundColor.toArgb()).components)
+ .setLayer(veil, MOVE_INDICATOR_LAYER)
transaction.apply()
}
@@ -113,13 +111,13 @@
}
isVisible = shouldBeVisible
val veil = veilSurface ?: return
- transaction.setCrop(veil, bounds)
+ transaction.setCrop(veil, bounds).setCornerRadius(veil, cornerRadius)
}
/**
* Factory for creating [MultiDisplayDragMoveIndicatorSurface] instances with the [context].
*/
- class Factory(private val context: Context) {
+ class Factory() {
private val surfaceControlBuilderFactory: SurfaceControlBuilderFactory =
object : SurfaceControlBuilderFactory {}
@@ -130,8 +128,9 @@
fun create(
taskInfo: RunningTaskInfo,
display: Display,
+ displayContext: Context,
) = MultiDisplayDragMoveIndicatorSurface(
- context,
+ displayContext,
taskInfo,
display,
surfaceControlBuilderFactory,
@@ -148,4 +147,8 @@
}
}
}
+
+ companion object {
+ private const val MOVE_INDICATOR_LAYER = TaskConstants.TASK_CHILD_LAYER_RESIZE_VEIL
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipDesktopState.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipDesktopState.kt
index e405f0d..af27ad9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipDesktopState.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipDesktopState.kt
@@ -18,7 +18,6 @@
import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
import android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED
import android.window.DesktopExperienceFlags
-import android.window.DesktopModeFlags
import com.android.wm.shell.Flags
import com.android.wm.shell.RootTaskDisplayAreaOrganizer
import com.android.wm.shell.common.DisplayLayout
@@ -40,17 +39,29 @@
* - DragToDesktopTransitionHandler is present
*/
fun isDesktopWindowingPipEnabled(): Boolean =
- DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PIP.isTrue &&
+ DesktopExperienceFlags.ENABLE_DESKTOP_WINDOWING_PIP.isTrue &&
desktopUserRepositoriesOptional.isPresent &&
dragToDesktopTransitionHandlerOptional.isPresent
/**
* Returns whether PiP in Connected Displays is enabled by checking the following:
+ * - PiP in Desktop Windowing is enabled
* - PiP in Connected Displays flag is enabled
* - PiP2 flag is enabled
*/
fun isConnectedDisplaysPipEnabled(): Boolean =
- DesktopExperienceFlags.ENABLE_CONNECTED_DISPLAYS_PIP.isTrue && Flags.enablePip2()
+ isDesktopWindowingPipEnabled() &&
+ DesktopExperienceFlags.ENABLE_CONNECTED_DISPLAYS_PIP.isTrue && Flags.enablePip2()
+
+ /**
+ * Returns whether dragging PiP in Connected Displays is enabled by checking the following:
+ * - Dragging PiP in Connected Displays flag is enabled
+ * - PiP in Connected Displays flag is enabled
+ * - PiP2 flag is enabled
+ */
+ fun isDraggingPipAcrossDisplaysEnabled(): Boolean =
+ DesktopExperienceFlags.ENABLE_DRAGGING_PIP_ACROSS_DISPLAYS.isTrue &&
+ isConnectedDisplaysPipEnabled()
/** Returns whether the display with the PiP task is in freeform windowing mode. */
private fun isDisplayInFreeform(): Boolean {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipMediaController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipMediaController.kt
index 427a555..9098544 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipMediaController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipMediaController.kt
@@ -303,6 +303,13 @@
}
/**
+ * Returns {@code true} if the pinned Activity has an active associated MediaSession.
+ */
+ fun hasActiveMediaSession(): Boolean {
+ return mMediaController != null
+ }
+
+ /**
* Sets the active media controller for the top PiP activity.
*/
private fun setActiveMediaController(controller: MediaController?) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt
index 8ef20d1..6910d13 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt
@@ -22,6 +22,8 @@
import android.content.ComponentName
import android.content.Context
import android.content.pm.PackageManager
+import android.content.res.Configuration
+import android.content.res.Configuration.UI_MODE_NIGHT_MASK
import android.graphics.PointF
import android.graphics.Rect
import android.os.RemoteException
@@ -322,4 +324,23 @@
}
return isPip2ExperimentEnabled as Boolean
}
+
+ private var isPipUmoExperienceEnabled: Boolean? = null
+
+ @JvmStatic
+ fun isPipUmoExperienceEnabled(): Boolean {
+ if (isPipUmoExperienceEnabled == null) {
+ isPipUmoExperienceEnabled = Flags.enablePipUmoExperience()
+ }
+ return isPipUmoExperienceEnabled as Boolean
+ }
+
+ /**
+ * Returns true if the system theme is the dark theme.
+ */
+ @JvmStatic
+ fun Context.isDarkSystemTheme(): Boolean {
+ return (resources.configuration.uiMode and UI_MODE_NIGHT_MASK) ==
+ Configuration.UI_MODE_NIGHT_YES
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
index d5f4a38..65550af 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
@@ -59,7 +59,7 @@
import com.android.wm.shell.compatui.impl.CompatUIEvents.SizeCompatRestartButtonClicked;
import com.android.wm.shell.compatui.impl.CompatUIRequests;
import com.android.wm.shell.desktopmode.DesktopUserRepositories;
-import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
+import com.android.wm.shell.shared.desktopmode.DesktopState;
import com.android.wm.shell.sysui.KeyguardChangeListener;
import com.android.wm.shell.sysui.ShellController;
import com.android.wm.shell.sysui.ShellInit;
@@ -208,6 +208,9 @@
@NonNull
private final Optional<DesktopUserRepositories> mDesktopUserRepositories;
+ @NonNull
+ private final DesktopState mDesktopState;
+
public CompatUIController(@NonNull Context context,
@NonNull ShellInit shellInit,
@NonNull ShellController shellController,
@@ -222,7 +225,8 @@
@NonNull CompatUIShellCommandHandler compatUIShellCommandHandler,
@NonNull AccessibilityManager accessibilityManager,
@NonNull CompatUIStatusManager compatUIStatusManager,
- @NonNull Optional<DesktopUserRepositories> desktopUserRepositories) {
+ @NonNull Optional<DesktopUserRepositories> desktopUserRepositories,
+ @NonNull DesktopState desktopState) {
mContext = context;
mShellController = shellController;
mDisplayController = displayController;
@@ -239,6 +243,7 @@
DISAPPEAR_DELAY_MS, flags);
mCompatUIStatusManager = compatUIStatusManager;
mDesktopUserRepositories = desktopUserRepositories;
+ mDesktopState = desktopState;
shellInit.addInitCallback(this::onInit, this);
}
@@ -520,7 +525,7 @@
return new CompatUIWindowManager(context,
taskInfo, mSyncQueue, mCallback, taskListener,
mDisplayController.getDisplayLayout(taskInfo.displayId), mCompatUIHintsState,
- mCompatUIConfiguration, this::onRestartButtonClicked);
+ mCompatUIConfiguration, this::onRestartButtonClicked, mDesktopState);
}
private void onRestartButtonClicked(
@@ -709,7 +714,7 @@
private void createOrUpdateUserAspectRatioSettingsLayout(@NonNull TaskInfo taskInfo,
@Nullable ShellTaskOrganizer.TaskListener taskListener) {
- boolean overridesShowAppHandle = DesktopModeStatus.overridesShowAppHandle(mContext);
+ boolean overridesShowAppHandle = mDesktopState.overridesShowAppHandle();
if (mUserAspectRatioSettingsLayout != null) {
if (mUserAspectRatioSettingsLayout.needsToBeRecreated(taskInfo, taskListener)
|| mIsInDesktopMode || overridesShowAppHandle) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java
index 0d16880..2dd0040 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java
@@ -39,7 +39,7 @@
import com.android.wm.shell.compatui.CompatUIController.CompatUIHintsState;
import com.android.wm.shell.compatui.api.CompatUIEvent;
import com.android.wm.shell.compatui.impl.CompatUIEvents.SizeCompatRestartButtonAppeared;
-import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
+import com.android.wm.shell.shared.desktopmode.DesktopState;
import java.util.function.Consumer;
@@ -74,6 +74,9 @@
@NonNull
private final Rect mLayoutBounds = new Rect();
+ @NonNull
+ private final DesktopState mDesktopState;
+
CompatUIWindowManager(@NonNull Context context, @NonNull TaskInfo taskInfo,
@NonNull SyncTransactionQueue syncQueue,
@NonNull Consumer<CompatUIEvent> callback,
@@ -82,11 +85,12 @@
@NonNull CompatUIHintsState compatUIHintsState,
@NonNull CompatUIConfiguration compatUIConfiguration,
@NonNull Consumer<Pair<TaskInfo, ShellTaskOrganizer.TaskListener>>
- onRestartButtonClicked) {
+ onRestartButtonClicked,
+ @NonNull DesktopState desktopState) {
super(context, taskInfo, syncQueue, taskListener, displayLayout);
mCallback = callback;
mHasSizeCompat = taskInfo.appCompatTaskInfo.isTopActivityInSizeCompat();
- if (DesktopModeStatus.canEnterDesktopMode(context)
+ if (desktopState.canEnterDesktopMode()
&& DesktopModeFlags.ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS.isTrue()) {
// Don't show the SCM button for freeform tasks
mHasSizeCompat &= !taskInfo.isFreeform();
@@ -95,6 +99,7 @@
mCompatUIConfiguration = compatUIConfiguration;
mOnRestartButtonClicked = onRestartButtonClicked;
mHideScmTolerance = mCompatUIConfiguration.getHideSizeCompatRestartButtonTolerance();
+ mDesktopState = desktopState;
}
@Override
@@ -143,7 +148,7 @@
boolean canShow) {
final boolean prevHasSizeCompat = mHasSizeCompat;
mHasSizeCompat = taskInfo.appCompatTaskInfo.isTopActivityInSizeCompat();
- if (DesktopModeStatus.canEnterDesktopMode(mContext)
+ if (mDesktopState.canEnterDesktopMode()
&& DesktopModeFlags.ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS.isTrue()) {
// Don't show the SCM button for freeform tasks
mHasSizeCompat &= !taskInfo.isFreeform();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxInputDetector.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxInputDetector.kt
index cbcc0b0..a5219ba 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxInputDetector.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxInputDetector.kt
@@ -166,7 +166,6 @@
windowSession.updateInputChannel(
inputChannel.token,
- null /* hostInputTransferToken */,
displayId,
inputSurface,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
@@ -186,13 +185,17 @@
}
fun stop(tx: Transaction) {
+ handler.post(::resetInputState)
+ inputSurface?.let { s ->
+ tx.remove(s)
+ }
+ }
+
+ private fun resetInputState() {
receiver?.dispose()
receiver = null
inputChannel.dispose()
windowSession.removeToken(inputToken)
- inputSurface?.let { s ->
- tx.remove(s)
- }
}
// Removes the provided token
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/animations/LetterboxAnimationHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/animations/LetterboxAnimationHandler.kt
index e205258..0b7d579 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/animations/LetterboxAnimationHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/animations/LetterboxAnimationHandler.kt
@@ -25,6 +25,7 @@
import android.window.TransitionInfo
import android.window.TransitionRequestInfo
import android.window.WindowContainerTransaction
+import com.android.app.animation.Interpolators
import com.android.internal.protolog.ProtoLog
import com.android.window.flags.Flags.appCompatRefactoring
import com.android.wm.shell.common.ShellExecutor
@@ -57,8 +58,12 @@
companion object {
@JvmStatic
private val TAG = "LetterboxAnimationHandler"
+
@JvmStatic
- private val ANIMATION_DURATION = 400L
+ private val ANIMATION_DURATION_MS = 500L
+
+ @JvmStatic
+ private val ANIMATION_INTERPOLATOR = Interpolators.EMPHASIZED
}
private var boundsAnimator: ValueAnimator? = null
@@ -114,49 +119,51 @@
finalX + startBounds.width(),
finalY + startBounds.height()
)
- boundsAnimator = ValueAnimator.ofObject(rectEvaluator, startBounds, endBounds)
- .setDuration(ANIMATION_DURATION)
- boundsAnimator?.addListener(object : Animator.AnimatorListener {
- override fun onAnimationStart(animation: Animator) {
- }
+ ValueAnimator.ofObject(rectEvaluator, startBounds, endBounds)
+ .setDuration(ANIMATION_DURATION_MS).apply {
+ setInterpolator { value -> ANIMATION_INTERPOLATOR.getInterpolation(value) }
+ addListener(object : Animator.AnimatorListener {
+ override fun onAnimationStart(animation: Animator) {
+ }
- override fun onAnimationEnd(animation: Animator) {
- finishTransaction.apply()
- finishCallback.onTransitionFinished(null)
- boundsAnimator = null
- }
+ override fun onAnimationEnd(animation: Animator) {
+ finishTransaction.apply()
+ finishCallback.onTransitionFinished(null)
+ boundsAnimator = null
+ }
- override fun onAnimationCancel(animation: Animator) {
- }
+ override fun onAnimationCancel(animation: Animator) {
+ }
- override fun onAnimationRepeat(animation: Animator) {
- }
- })
- boundsAnimator?.addUpdateListener { animation ->
- val rect =
- animation.getAnimatedValue() as Rect
+ override fun onAnimationRepeat(animation: Animator) {
+ }
+ })
+ addUpdateListener { animation ->
+ val rect =
+ animation.animatedValue as Rect
- for (c in info.changes) {
- tx.setPosition(
- c.leash,
- rect.left.toFloat(),
- rect.top.toFloat()
- )
+ for (c in info.changes) {
+ tx.setPosition(
+ c.leash,
+ rect.left.toFloat(),
+ rect.top.toFloat()
+ )
+ }
+ controller.updateLetterboxSurfaceBounds(
+ key,
+ tx,
+ taskBounds,
+ rect
+ )
+ tx.apply()
+ }
+ animExecutor.execute {
+ for (c in info.changes) {
+ tx.show(c.leash).apply()
+ }
+ start()
+ }
}
- controller.updateLetterboxSurfaceBounds(
- key,
- tx,
- taskBounds,
- rect
- )
- tx.apply()
- }
- animExecutor.execute {
- for (c in info.changes) {
- tx.show(c.leash).apply()
- }
- boundsAnimator?.start()
- }
return true
}
return false
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/crashhandling/ShellCrashHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/crashhandling/ShellCrashHandler.kt
index 2e34d043..f4207d7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/crashhandling/ShellCrashHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/crashhandling/ShellCrashHandler.kt
@@ -17,22 +17,21 @@
package com.android.wm.shell.crashhandling
import android.app.WindowConfiguration
-import android.content.Context
import android.view.Display.DEFAULT_DISPLAY
import android.window.DesktopExperienceFlags
import android.window.WindowContainerTransaction
import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.common.HomeIntentProvider
-import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
+import com.android.wm.shell.shared.desktopmode.DesktopState
import com.android.wm.shell.sysui.ShellInit
/** [ShellCrashHandler] for shell to use when it's being initialized. Currently it only restores
* the home task to top.
**/
class ShellCrashHandler(
- private val context: Context,
private val shellTaskOrganizer: ShellTaskOrganizer,
private val homeIntentProvider: HomeIntentProvider,
+ private val desktopState: DesktopState,
shellInit: ShellInit,
) {
init {
@@ -45,7 +44,7 @@
private fun handleCrashIfNeeded() {
// For now only handle crashes when desktop mode is enabled on the device.
- if (DesktopModeStatus.canEnterDesktopMode(context) &&
+ if (desktopState.canEnterDesktopMode &&
!DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) {
var freeformTaskExists = false
// If there are running tasks at init, WMShell has crashed but WMCore is still alive.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java
index 58fc603..2655c97 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java
@@ -16,6 +16,7 @@
package com.android.wm.shell.dagger;
+import android.app.IActivityTaskManager;
import android.content.Context;
import android.os.Handler;
@@ -97,12 +98,13 @@
@ShellMainThread Handler mainHandler,
SystemWindows systemWindows,
RootDisplayAreaOrganizer rootDisplayAreaOrganizer,
- DesktopState desktopState) {
+ DesktopState desktopState,
+ IActivityTaskManager activityTaskManager) {
return new TvSplitScreenController(context, shellInit, shellCommandHandler, shellController,
shellTaskOrganizer, syncQueue, rootTDAOrganizer, displayController,
displayImeController, displayInsetsController, transitions, transactionPool,
iconProvider, recentTasks, launchAdjacentController, multiInstanceHelper,
splitState, mainExecutor, mainHandler, systemWindows, rootDisplayAreaOrganizer,
- desktopState);
+ desktopState, activityTaskManager);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
index 00b5f5e..f39bd16 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
@@ -120,7 +120,6 @@
import com.android.wm.shell.shared.desktopmode.DesktopConfig;
import com.android.wm.shell.shared.desktopmode.DesktopConfigImpl;
import com.android.wm.shell.shared.desktopmode.DesktopModeCompatPolicy;
-import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
import com.android.wm.shell.shared.desktopmode.DesktopState;
import com.android.wm.shell.shared.desktopmode.DesktopStateImpl;
import com.android.wm.shell.splitscreen.SplitScreen;
@@ -185,8 +184,10 @@
IWindowManager wmService,
ShellInit shellInit,
@ShellMainThread ShellExecutor mainExecutor,
- DisplayManager displayManager) {
- return new DisplayController(context, wmService, shellInit, mainExecutor, displayManager);
+ DisplayManager displayManager,
+ DesktopState desktopState) {
+ return new DisplayController(context, wmService, shellInit, mainExecutor, displayManager,
+ desktopState);
}
@WMSingleton
@@ -297,7 +298,8 @@
@NonNull CompatUIState compatUIState,
@NonNull CompatUIComponentIdGenerator componentIdGenerator,
@NonNull CompatUIComponentFactory compatUIComponentFactory,
- CompatUIStatusManager compatUIStatusManager) {
+ CompatUIStatusManager compatUIStatusManager,
+ DesktopState desktopState) {
if (!context.getResources().getBoolean(R.bool.config_enableCompatUIController)) {
return Optional.empty();
}
@@ -322,7 +324,8 @@
compatUIShellCommandHandler.get(),
accessibilityManager.get(),
compatUIStatusManager,
- desktopUserRepositories));
+ desktopUserRepositories,
+ desktopState));
}
@WMSingleton
@@ -666,8 +669,8 @@
@Provides
static Optional<FreeformComponents> provideFreeformComponents(
@DynamicOverride Optional<FreeformComponents> freeformComponents,
- Context context) {
- if (FreeformComponents.requiresFreeformComponents(context)) {
+ DesktopState desktopState) {
+ if (FreeformComponents.requiresFreeformComponents(desktopState)) {
return freeformComponents;
}
return Optional.empty();
@@ -737,12 +740,14 @@
ActivityTaskManager activityTaskManager,
Optional<DesktopUserRepositories> desktopUserRepositories,
TaskStackTransitionObserver taskStackTransitionObserver,
- @ShellMainThread ShellExecutor mainExecutor
+ @ShellMainThread ShellExecutor mainExecutor,
+ DesktopState desktopState
) {
return Optional.ofNullable(
RecentTasksController.create(context, shellInit, shellController,
shellCommandHandler, taskStackListener, activityTaskManager,
- desktopUserRepositories, taskStackTransitionObserver, mainExecutor));
+ desktopUserRepositories, taskStackTransitionObserver, mainExecutor,
+ desktopState));
}
@BindsOptionalOf
@@ -1039,13 +1044,14 @@
@WMSingleton
@Provides
- static Optional<DesktopTasksController> providesDesktopTasksController(Context context,
+ static Optional<DesktopTasksController> providesDesktopTasksController(
+ DesktopState desktopState,
@DynamicOverride Optional<Lazy<DesktopTasksController>> desktopTasksController) {
// Use optional-of-lazy for the dependency that this provider relies on.
// Lazy ensures that this provider will not be the cause the dependency is created
// when it will not be returned due to the condition below.
return desktopTasksController.flatMap((lazy) -> {
- if (DesktopModeStatus.canEnterDesktopModeOrShowAppHandle(context)) {
+ if (desktopState.canEnterDesktopModeOrShowAppHandle()) {
return Optional.of(lazy.get());
}
return Optional.empty();
@@ -1058,13 +1064,14 @@
@WMSingleton
@Provides
- static Optional<DesktopUserRepositories> provideDesktopUserRepositories(Context context,
+ static Optional<DesktopUserRepositories> provideDesktopUserRepositories(
+ DesktopState desktopState,
@DynamicOverride Optional<Lazy<DesktopUserRepositories>> desktopUserRepositories) {
// Use optional-of-lazy for the dependency that this provider relies on.
// Lazy ensures that this provider will not be the cause the dependency is created
// when it will not be returned due to the condition below.
return desktopUserRepositories.flatMap((lazy) -> {
- if (DesktopModeStatus.canEnterDesktopMode(context)) {
+ if (desktopState.canEnterDesktopMode()) {
return Optional.of(lazy.get());
}
return Optional.empty();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index 713ee9d..a5e7402 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -22,10 +22,11 @@
import static android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_TASK_LIMIT;
import static android.window.DesktopModeFlags.ENABLE_WINDOWING_TRANSITION_HANDLERS_OBSERVERS;
-import static com.android.hardware.input.Flags.manageKeyGestures;
+import static com.android.systemui.Flags.enableViewCaptureTracing;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.IActivityTaskManager;
import android.app.KeyguardManager;
import android.content.Context;
import android.content.pm.LauncherApps;
@@ -41,6 +42,7 @@
import androidx.annotation.OptIn;
+import com.android.app.viewcapture.ViewCaptureAwareWindowManagerFactory;
import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.statusbar.IStatusBarService;
@@ -91,7 +93,6 @@
import com.android.wm.shell.desktopmode.DefaultDragToDesktopTransitionHandler;
import com.android.wm.shell.desktopmode.DesktopActivityOrientationChangeHandler;
import com.android.wm.shell.desktopmode.DesktopDisplayEventHandler;
-import com.android.wm.shell.desktopmode.DesktopDisplayModeController;
import com.android.wm.shell.desktopmode.DesktopImeHandler;
import com.android.wm.shell.desktopmode.DesktopImmersiveController;
import com.android.wm.shell.desktopmode.DesktopMinimizationTransitionHandler;
@@ -115,8 +116,10 @@
import com.android.wm.shell.desktopmode.ReturnToDragStartAnimator;
import com.android.wm.shell.desktopmode.SpringDragToDesktopTransitionHandler;
import com.android.wm.shell.desktopmode.ToggleResizeDesktopTaskTransitionHandler;
+import com.android.wm.shell.desktopmode.VisualIndicatorUpdateScheduler;
import com.android.wm.shell.desktopmode.WindowDecorCaptionHandleRepository;
import com.android.wm.shell.desktopmode.compatui.SystemModalsTransitionHandler;
+import com.android.wm.shell.desktopmode.desktopfirst.DesktopDisplayModeController;
import com.android.wm.shell.desktopmode.desktopwallpaperactivity.DesktopWallpaperActivityTokenProvider;
import com.android.wm.shell.desktopmode.education.AppHandleEducationController;
import com.android.wm.shell.desktopmode.education.AppHandleEducationFilter;
@@ -151,7 +154,6 @@
import com.android.wm.shell.shared.annotations.ShellMainThread;
import com.android.wm.shell.shared.desktopmode.DesktopConfig;
import com.android.wm.shell.shared.desktopmode.DesktopModeCompatPolicy;
-import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
import com.android.wm.shell.shared.desktopmode.DesktopState;
import com.android.wm.shell.splitscreen.SplitScreenController;
import com.android.wm.shell.sysui.ShellCommandHandler;
@@ -301,6 +303,9 @@
SyncTransactionQueue syncQueue,
IWindowManager wmService,
HomeIntentProvider homeIntentProvider) {
+ final WindowManager wm = enableViewCaptureTracing()
+ ? ViewCaptureAwareWindowManagerFactory.getInstance(context)
+ : windowManager;
return new BubbleController(
context,
shellInit,
@@ -316,7 +321,7 @@
new BubblePersistentRepository(context)),
bubbleTransitions,
statusBarService,
- windowManager,
+ wm,
displayInsetsController,
displayImeController,
userManager,
@@ -389,8 +394,9 @@
@WMSingleton
@Provides
static AppToWebGenericLinksParser provideGenericLinksParser(
- Context context, @ShellMainThread ShellExecutor mainExecutor) {
- return new AppToWebGenericLinksParser(context, mainExecutor);
+ Context context, @ShellMainThread ShellExecutor mainExecutor,
+ DesktopConfig desktopConfig) {
+ return new AppToWebGenericLinksParser(context, mainExecutor, desktopConfig);
}
@Provides
@@ -411,10 +417,12 @@
static WindowDecorViewHostSupplier<WindowDecorViewHost> provideWindowDecorViewHostSupplier(
@NonNull Context context,
@ShellMainThread @NonNull CoroutineScope mainScope,
- @NonNull ShellInit shellInit) {
- final int poolSize = DesktopModeStatus.getWindowDecorScvhPoolSize(context);
- final int preWarmSize = DesktopModeStatus.getWindowDecorPreWarmSize();
- if (DesktopModeStatus.canEnterDesktopModeOrShowAppHandle(context) && poolSize > 0) {
+ @NonNull ShellInit shellInit,
+ DesktopState desktopState,
+ DesktopConfig desktopConfig) {
+ final int poolSize = desktopConfig.getWindowDecorScvhPoolSize();
+ final int preWarmSize = desktopConfig.getWindowDecorPreWarmSize();
+ if (desktopState.canEnterDesktopModeOrShowAppHandle() && poolSize > 0) {
return new PooledWindowDecorViewHostSupplier(
context, mainScope, shellInit, poolSize, preWarmSize);
}
@@ -451,20 +459,19 @@
DesktopModeLoggerTransitionObserver desktopModeLoggerTransitionObserver,
LaunchAdjacentController launchAdjacentController,
WindowDecorViewModel windowDecorViewModel,
- Optional<TaskChangeListener> taskChangeListener) {
- // TODO(b/238217847): Temporarily add this check here until we can remove the dynamic
- // override for this controller from the base module
- ShellInit init = FreeformComponents.requiresFreeformComponents(context) ? shellInit : null;
+ Optional<TaskChangeListener> taskChangeListener,
+ DesktopState desktopState) {
return new FreeformTaskListener(
context,
- init,
+ shellInit,
shellTaskOrganizer,
desktopUserRepositories,
desktopTasksController,
desktopModeLoggerTransitionObserver,
launchAdjacentController,
windowDecorViewModel,
- taskChangeListener);
+ taskChangeListener,
+ desktopState);
}
@WMSingleton
@@ -482,23 +489,25 @@
@WMSingleton
@Provides
static FreeformTaskTransitionObserver provideFreeformTaskTransitionObserver(
- Context context,
ShellInit shellInit,
Transitions transitions,
Optional<DesktopImmersiveController> desktopImmersiveController,
WindowDecorViewModel windowDecorViewModel,
Optional<TaskChangeListener> taskChangeListener,
FocusTransitionObserver focusTransitionObserver,
- Optional<DesksTransitionObserver> desksTransitionObserver) {
+ Optional<DesksTransitionObserver> desksTransitionObserver,
+ DesktopState desktopState,
+ Optional<DesktopImeHandler> desktopImeHandler) {
return new FreeformTaskTransitionObserver(
- context,
shellInit,
transitions,
desktopImmersiveController,
windowDecorViewModel,
taskChangeListener,
focusTransitionObserver,
- desksTransitionObserver);
+ desksTransitionObserver,
+ desktopState,
+ desktopImeHandler);
}
@WMSingleton
@@ -585,7 +594,8 @@
@ShellMainThread ShellExecutor mainExecutor,
@ShellMainThread Handler mainHandler,
RootDisplayAreaOrganizer rootDisplayAreaOrganizer,
- DesktopState desktopState) {
+ DesktopState desktopState,
+ IActivityTaskManager activityTaskManager) {
return new SplitScreenController(
context,
shellInit,
@@ -611,7 +621,8 @@
mainExecutor,
mainHandler,
rootDisplayAreaOrganizer,
- desktopState);
+ desktopState,
+ activityTaskManager);
}
//
@@ -631,7 +642,6 @@
Optional<UnfoldTransitionHandler> unfoldHandler,
Optional<ActivityEmbeddingController> activityEmbeddingController,
BubbleTransitions bubbleTransitions,
- TaskViewTransitions taskViewTransitions,
Transitions transitions) {
return new DefaultMixedHandler(
shellInit,
@@ -643,8 +653,7 @@
desktopTasksController,
unfoldHandler,
activityEmbeddingController,
- bubbleTransitions,
- taskViewTransitions);
+ bubbleTransitions);
}
@WMSingleton
@@ -811,6 +820,7 @@
RecentsTransitionHandler recentsTransitionHandler,
MultiInstanceHelper multiInstanceHelper,
@ShellMainThread ShellExecutor mainExecutor,
+ @ShellMainThread CoroutineScope mainScope,
@ShellMainThread Handler mainHandler,
@ShellDesktopThread ShellExecutor desktopExecutor,
Optional<DesktopTasksLimiter> desktopTasksLimiter,
@@ -831,7 +841,8 @@
DesktopModeMoveToDisplayTransitionHandler moveToDisplayTransitionHandler,
HomeIntentProvider homeIntentProvider,
DesktopState desktopState,
- DesktopConfig desktopConfig) {
+ DesktopConfig desktopConfig,
+ VisualIndicatorUpdateScheduler visualIndicatorUpdateScheduler) {
return new DesktopTasksController(
context,
shellInit,
@@ -857,6 +868,7 @@
recentsTransitionHandler,
multiInstanceHelper,
mainExecutor,
+ mainScope,
desktopExecutor,
desktopTasksLimiter,
recentTasksController.orElse(null),
@@ -876,7 +888,8 @@
moveToDisplayTransitionHandler,
homeIntentProvider,
desktopState,
- desktopConfig);
+ desktopConfig,
+ visualIndicatorUpdateScheduler);
}
@WMSingleton
@@ -896,7 +909,8 @@
WindowDecorTaskResourceLoader windowDecorTaskResourceLoader,
FocusTransitionObserver focusTransitionObserver,
@ShellMainThread ShellExecutor mainExecutor,
- DesktopState desktopState) {
+ DesktopState desktopState,
+ ShellInit shellInit) {
return new DesktopTilingDecorViewModel(
context,
mainDispatcher,
@@ -913,7 +927,8 @@
windowDecorTaskResourceLoader,
focusTransitionObserver,
mainExecutor,
- desktopState
+ desktopState,
+ shellInit
);
}
@@ -924,7 +939,8 @@
DesktopState desktopState) {
if (ENABLE_WINDOWING_TRANSITION_HANDLERS_OBSERVERS.isTrue()
&& desktopState.canEnterDesktopMode()) {
- return Optional.of(new DesktopTaskChangeListener(desktopUserRepositories));
+ return Optional.of(new DesktopTaskChangeListener(
+ desktopUserRepositories, desktopState));
}
return Optional.empty();
}
@@ -937,7 +953,8 @@
ShellTaskOrganizer shellTaskOrganizer,
DesksOrganizer desksOrganizer,
DesktopConfig desktopConfig,
- DesktopState desktopState) {
+ DesktopState desktopState,
+ Optional<DesktopMixedTransitionHandler> desktopMixedTransitionHandler) {
if (!desktopState.canEnterDesktopMode()
|| !ENABLE_DESKTOP_WINDOWING_TASK_LIMIT.isTrue()) {
return Optional.empty();
@@ -949,6 +966,7 @@
desktopUserRepositories,
shellTaskOrganizer,
desksOrganizer,
+ desktopMixedTransitionHandler.get(),
maxTaskLimit <= 0 ? null : maxTaskLimit));
}
@@ -1039,7 +1057,6 @@
DisplayController displayController,
DesktopState desktopState) {
if (desktopState.canEnterDesktopMode()
- && manageKeyGestures()
&& (Flags.enableMoveToNextDisplayShortcut()
|| DesktopModeFlags.ENABLE_TASK_RESIZING_KEYBOARD_SHORTCUTS.isTrue())) {
return Optional.of(new DesktopModeKeyGestureHandler(context,
@@ -1135,8 +1152,8 @@
@WMSingleton
@Provides
static MultiDisplayDragMoveIndicatorSurface.Factory
- providesMultiDisplayDragMoveIndicatorSurfaceFactory(Context context) {
- return new MultiDisplayDragMoveIndicatorSurface.Factory(context);
+ providesMultiDisplayDragMoveIndicatorSurfaceFactory() {
+ return new MultiDisplayDragMoveIndicatorSurface.Factory();
}
@WMSingleton
@@ -1386,6 +1403,7 @@
ShellController shellController,
DisplayController displayController,
RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
+ DesksOrganizer desksOrganizer,
Optional<DesktopUserRepositories> desktopUserRepositories,
Optional<DesktopTasksController> desktopTasksController,
Optional<DesktopDisplayModeController> desktopDisplayModeController,
@@ -1403,6 +1421,7 @@
shellController,
displayController,
rootTaskDisplayAreaOrganizer,
+ desksOrganizer,
desktopRepositoryInitializer,
desktopUserRepositories.get(),
desktopTasksController.get(),
@@ -1602,6 +1621,17 @@
animExecutor, context, shellInit));
}
+ @WMSingleton
+ @Provides
+ static VisualIndicatorUpdateScheduler provideVisualIndicatorUpdateScheduler(
+ ShellInit shellInit,
+ @ShellMainThread MainCoroutineDispatcher mainDispatcher,
+ @ShellBackgroundThread CoroutineScope bgScope,
+ DisplayController displayController) {
+ return new VisualIndicatorUpdateScheduler(shellInit, mainDispatcher, bgScope,
+ displayController);
+ }
+
//
// App zoom out
//
@@ -1644,7 +1674,8 @@
GlobalDragListener globalDragListener,
Transitions transitions,
Lazy<BubbleController> bubbleControllerLazy,
- @ShellMainThread ShellExecutor mainExecutor) {
+ @ShellMainThread ShellExecutor mainExecutor,
+ DesktopState desktopState) {
return new DragAndDropController(
context,
shellInit,
@@ -1662,7 +1693,8 @@
return bubbleControllerLazy.get();
}
},
- mainExecutor);
+ mainExecutor,
+ desktopState);
}
//
@@ -1706,11 +1738,12 @@
@WMSingleton
@Provides
static ShellCrashHandler provideShellCrashHandler(
- Context context,
ShellTaskOrganizer shellTaskOrganizer,
HomeIntentProvider homeIntentProvider,
+ DesktopState desktopState,
ShellInit shellInit) {
- return new ShellCrashHandler(context, shellTaskOrganizer, homeIntentProvider, shellInit);
+ return new ShellCrashHandler(shellTaskOrganizer, homeIntentProvider, desktopState,
+ shellInit);
}
@WMSingleton
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java
index 729924a..eda3a70 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java
@@ -19,7 +19,7 @@
import android.annotation.NonNull;
import android.content.Context;
import android.os.Handler;
-import android.window.DesktopModeFlags;
+import android.window.DesktopExperienceFlags;
import com.android.internal.jank.InteractionJankMonitor;
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
@@ -47,8 +47,10 @@
import com.android.wm.shell.desktopmode.DesktopTasksController;
import com.android.wm.shell.desktopmode.DesktopUserRepositories;
import com.android.wm.shell.desktopmode.DragToDesktopTransitionHandler;
+import com.android.wm.shell.pip2.PipSurfaceTransactionHelper;
import com.android.wm.shell.pip2.phone.PhonePipMenuController;
import com.android.wm.shell.pip2.phone.PipController;
+import com.android.wm.shell.pip2.phone.PipDisplayTransferHandler;
import com.android.wm.shell.pip2.phone.PipInteractionHandler;
import com.android.wm.shell.pip2.phone.PipMotionHelper;
import com.android.wm.shell.pip2.phone.PipScheduler;
@@ -58,7 +60,7 @@
import com.android.wm.shell.pip2.phone.PipTransitionState;
import com.android.wm.shell.pip2.phone.PipUiStateChangeController;
import com.android.wm.shell.shared.annotations.ShellMainThread;
-import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
+import com.android.wm.shell.shared.desktopmode.DesktopState;
import com.android.wm.shell.splitscreen.SplitScreenController;
import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellController;
@@ -80,6 +82,7 @@
@WMSingleton
@Provides
static PipTransition providePipTransition(Context context,
+ @NonNull PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
@NonNull ShellInit shellInit,
@NonNull ShellTaskOrganizer shellTaskOrganizer,
@NonNull Transitions transitions,
@@ -97,7 +100,8 @@
PipDesktopState pipDesktopState,
Optional<DesktopPipTransitionController> desktopPipTransitionController,
PipInteractionHandler pipInteractionHandler) {
- return new PipTransition(context, shellInit, shellTaskOrganizer, transitions,
+ return new PipTransition(context, pipSurfaceTransactionHelper, shellInit,
+ shellTaskOrganizer, transitions,
pipBoundsState, null, pipBoundsAlgorithm, pipTaskListener,
pipScheduler, pipStackListenerController, pipDisplayLayoutState,
pipUiStateChangeController, displayController, splitScreenControllerOptional,
@@ -133,6 +137,8 @@
PipAppOpsListener pipAppOpsListener,
PhonePipMenuController pipMenuController,
PipUiEventLogger pipUiEventLogger,
+ PipMediaController pipMediaController,
+ PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
@ShellMainThread ShellExecutor mainExecutor) {
if (!PipUtils.isPip2ExperimentEnabled()) {
return Optional.empty();
@@ -142,13 +148,15 @@
displayInsetsController, pipBoundsState, pipBoundsAlgorithm,
pipDisplayLayoutState, pipScheduler, taskStackListener, shellTaskOrganizer,
pipTransitionState, pipTouchHandler, pipAppOpsListener, pipMenuController,
- pipUiEventLogger, mainExecutor));
+ pipUiEventLogger, pipMediaController, pipSurfaceTransactionHelper,
+ mainExecutor));
}
}
@WMSingleton
@Provides
static PipScheduler providePipScheduler(Context context,
+ @NonNull PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
PipBoundsState pipBoundsState,
@ShellMainThread ShellExecutor mainExecutor,
PipTransitionState pipTransitionState,
@@ -156,7 +164,8 @@
Optional<DesktopPipTransitionController> desktopPipTransitionController,
PipDesktopState pipDesktopState,
DisplayController displayController) {
- return new PipScheduler(context, pipBoundsState, mainExecutor, pipTransitionState,
+ return new PipScheduler(context, pipSurfaceTransactionHelper, pipBoundsState, mainExecutor,
+ pipTransitionState,
splitScreenControllerOptional, desktopPipTransitionController, pipDesktopState,
displayController);
}
@@ -181,6 +190,7 @@
@WMSingleton
@Provides
static PipTouchHandler providePipTouchHandler(Context context,
+ @NonNull PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
ShellInit shellInit,
ShellCommandHandler shellCommandHandler,
PhonePipMenuController menuPhoneController,
@@ -196,12 +206,25 @@
FloatingContentCoordinator floatingContentCoordinator,
PipUiEventLogger pipUiEventLogger,
@ShellMainThread ShellExecutor mainExecutor,
- Optional<PipPerfHintController> pipPerfHintControllerOptional) {
- return new PipTouchHandler(context, shellInit, shellCommandHandler, menuPhoneController,
+ Optional<PipPerfHintController> pipPerfHintControllerOptional,
+ PipDisplayTransferHandler pipDisplayTransferHandler) {
+ return new PipTouchHandler(context, pipSurfaceTransactionHelper, shellInit,
+ shellCommandHandler, menuPhoneController,
pipBoundsAlgorithm, pipBoundsState, pipTransitionState, pipScheduler,
sizeSpecSource, pipDisplayLayoutState, pipDesktopState, displayController,
pipMotionHelper, floatingContentCoordinator, pipUiEventLogger, mainExecutor,
- pipPerfHintControllerOptional);
+ pipPerfHintControllerOptional, pipDisplayTransferHandler);
+ }
+
+ @WMSingleton
+ @Provides
+ static PipDisplayTransferHandler providePipDisplayTransferHandler(Context context,
+ PipTransitionState pipTransitionState,
+ PipScheduler pipScheduler, RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
+ PipBoundsState pipBoundsState, DisplayController displayController
+ ) {
+ return new PipDisplayTransferHandler(context, pipTransitionState, pipScheduler,
+ rootTaskDisplayAreaOrganizer, pipBoundsState, displayController);
}
@WMSingleton
@@ -264,10 +287,10 @@
Context context, ShellTaskOrganizer shellTaskOrganizer,
Optional<DesktopTasksController> desktopTasksControllerOptional,
Optional<DesktopUserRepositories> desktopUserRepositoriesOptional,
- PipDesktopState pipDesktopState
+ PipDesktopState pipDesktopState, DesktopState desktopState
) {
- if (DesktopModeStatus.canEnterDesktopMode(context)
- && DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PIP.isTrue()) {
+ if (desktopState.canEnterDesktopMode()
+ && DesktopExperienceFlags.ENABLE_DESKTOP_WINDOWING_PIP.isTrue()) {
return Optional.of(
new DesktopPipTransitionController(shellTaskOrganizer,
desktopTasksControllerOptional.get(),
@@ -288,4 +311,10 @@
return new PipInteractionHandler(context, mainHandler,
InteractionJankMonitor.getInstance());
}
+
+ @WMSingleton
+ @Provides
+ static PipSurfaceTransactionHelper providePipSurfaceTransactionHelper(Context context) {
+ return new PipSurfaceTransactionHelper(context);
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandler.kt
index c28f5e6..e4b6b52 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandler.kt
@@ -17,13 +17,22 @@
package com.android.wm.shell.desktopmode
import android.content.Context
+import android.os.UserHandle
+import android.os.UserManager
import android.view.Display
import android.view.Display.DEFAULT_DISPLAY
import android.window.DesktopExperienceFlags
+import android.window.DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_ACTIVATION_IN_DESKTOP_FIRST_DISPLAYS
+import android.window.DesktopModeFlags
+import android.window.DisplayAreaInfo
import com.android.internal.protolog.ProtoLog
import com.android.wm.shell.RootTaskDisplayAreaOrganizer
+import com.android.wm.shell.RootTaskDisplayAreaOrganizer.RootTaskDisplayAreaListener
import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.common.DisplayController.OnDisplaysChangedListener
+import com.android.wm.shell.desktopmode.desktopfirst.DesktopDisplayModeController
+import com.android.wm.shell.desktopmode.desktopfirst.isDisplayDesktopFirst
+import com.android.wm.shell.desktopmode.multidesks.DesksOrganizer
import com.android.wm.shell.desktopmode.multidesks.DesksTransitionObserver
import com.android.wm.shell.desktopmode.multidesks.OnDeskDisplayChangeListener
import com.android.wm.shell.desktopmode.multidesks.OnDeskRemovedListener
@@ -44,6 +53,7 @@
private val shellController: ShellController,
private val displayController: DisplayController,
private val rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer,
+ private val desksOrganizer: DesksOrganizer,
private val desktopRepositoryInitializer: DesktopRepositoryInitializer,
private val desktopUserRepositories: DesktopUserRepositories,
private val desktopTasksController: DesktopTasksController,
@@ -52,6 +62,11 @@
private val desktopState: DesktopState,
) : OnDisplaysChangedListener, OnDeskRemovedListener, OnDeskDisplayChangeListener {
+ private val onDisplayAreaChangeListener = OnDisplayAreaChangeListener { displayId ->
+ logV("displayAreaChanged in displayId=%d", displayId)
+ createDefaultDesksIfNeeded(displayIds = listOf(displayId), userId = null)
+ }
+
init {
shellInit.addInitCallback({ onInit() }, this)
}
@@ -61,12 +76,12 @@
if (DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) {
desktopTasksController.onDeskRemovedListener = this
-
shellController.addUserChangeListener(
object : UserChangeListener {
override fun onUserChanged(newUserId: Int, userContext: Context) {
- val displayIds = rootTaskDisplayAreaOrganizer.displayIds
- createDefaultDesksIfNeeded(displayIds.toSet(), newUserId)
+ val displayIds = rootTaskDisplayAreaOrganizer.displayIds.toSet()
+ logV("onUserChanged newUserId=%d displays=%s", newUserId, displayIds)
+ createDefaultDesksIfNeeded(displayIds, newUserId)
}
}
)
@@ -78,17 +93,21 @@
}
override fun onDisplayAdded(displayId: Int) {
+ if (DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) {
+ rootTaskDisplayAreaOrganizer.registerListener(displayId, onDisplayAreaChangeListener)
+ }
if (displayId != DEFAULT_DISPLAY) {
desktopDisplayModeController.updateExternalDisplayWindowingMode(displayId)
// The default display's windowing mode depends on the availability of the external
// display. So updating the default display's windowing mode here.
desktopDisplayModeController.updateDefaultDisplayWindowingMode()
}
-
- createDefaultDesksIfNeeded(displayIds = setOf(displayId), userId = null)
}
override fun onDisplayRemoved(displayId: Int) {
+ if (DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) {
+ rootTaskDisplayAreaOrganizer.unregisterListener(displayId, onDisplayAreaChangeListener)
+ }
if (displayId != DEFAULT_DISPLAY) {
desktopDisplayModeController.updateDefaultDisplayWindowingMode()
}
@@ -108,35 +127,47 @@
}
override fun onDeskRemoved(lastDisplayId: Int, deskId: Int) {
- createDefaultDesksIfNeeded(setOf(lastDisplayId), userId = null)
+ if (!DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) return
+ logV("onDeskRemoved deskId=%d displayId=%d", deskId, lastDisplayId)
+ createDefaultDesksIfNeeded(listOf(lastDisplayId), userId = null)
}
- private fun createDefaultDesksIfNeeded(displayIds: Set<Int>, userId: Int?) {
+ private fun createDefaultDesksIfNeeded(displayIds: Collection<Int>, userId: Int?) {
if (!DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) return
- logV("createDefaultDesksIfNeeded displays=%s", displayIds)
+ logV("createDefaultDesksIfNeeded displays=%s userId=%d", displayIds, userId)
+ if (userId != null && !isUserDesktopEligible(userId)) {
+ logW("createDefaultDesksIfNeeded ignoring attempt for ineligible user")
+ return
+ }
mainScope.launch {
desktopRepositoryInitializer.isInitialized.collect { initialized ->
if (!initialized) return@collect
val repository =
userId?.let { desktopUserRepositories.getProfile(userId) }
?: desktopUserRepositories.current
- displayIds
- .filter { displayId -> displayId != Display.INVALID_DISPLAY }
- .filter { displayId -> supportsDesks(displayId) }
- .filter { displayId -> repository.getNumberOfDesks(displayId) == 0 }
- .also { displaysNeedingDesk ->
- logV(
- "createDefaultDesksIfNeeded creating default desks in displays=%s",
- displaysNeedingDesk,
- )
- }
- .forEach { displayId ->
+ if (!isUserDesktopEligible(repository.userId)) {
+ logW("createDefaultDesksIfNeeded ignoring attempt for ineligible user")
+ cancel()
+ return@collect
+ }
+ for (displayId in displayIds) {
+ if (!shouldCreateOrWarmUpDesk(displayId, repository)) continue
+ if (rootTaskDisplayAreaOrganizer.isDisplayDesktopFirst(displayId)) {
+ logV("Display %d is desktop-first and needs a default desk", displayId)
desktopTasksController.createDesk(
- displayId,
- repository.userId,
- isDesktopFirstDisplay(displayId),
+ displayId = displayId,
+ userId = repository.userId,
+ // TODO: b/393978539 - do not activate as a result of removing the
+ // last desk from Overview. Let overview activate it once it is
+ // selected or when the user goes home.
+ activateDesk =
+ ENABLE_MULTIPLE_DESKTOPS_ACTIVATION_IN_DESKTOP_FIRST_DISPLAYS.isTrue,
)
+ } else {
+ logV("Display %d is touch-first and needs to warm up a desk", displayId)
+ desksOrganizer.warmUpDefaultDesk(displayId, repository.userId)
}
+ }
cancel()
}
}
@@ -149,17 +180,51 @@
desktopTasksController.onDeskDisconnectTransition(deskDisplayChanges)
}
- // TODO: b/393978539 - implement this
- private fun isDesktopFirstDisplay(displayId: Int): Boolean = displayId != DEFAULT_DISPLAY
+ private fun shouldCreateOrWarmUpDesk(displayId: Int, repository: DesktopRepository): Boolean {
+ if (displayId == Display.INVALID_DISPLAY) {
+ logV("shouldCreateOrWarmUpDesk skipping reason: invalid display")
+ return false
+ }
+ if (!desktopState.isDesktopModeSupportedOnDisplay(displayId)) {
+ logV(
+ "shouldCreateOrWarmUpDesk skipping displayId=%d reason: desktop ineligible",
+ displayId,
+ )
+ return false
+ }
+ if (repository.getNumberOfDesks(displayId) > 0) {
+ logV("shouldCreateOrWarmUpDesk skipping displayId=%d reason: has desk(s)", displayId)
+ return false
+ }
+ return true
+ }
- // TODO: b/362720497 - connected/projected display considerations.
- private fun supportsDesks(displayId: Int): Boolean =
- desktopState.isDesktopModeSupportedOnDisplay(displayId)
+ private fun isUserDesktopEligible(userId: Int): Boolean =
+ !(DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_HSUM.isTrue &&
+ UserManager.isHeadlessSystemUserMode() &&
+ UserHandle.USER_SYSTEM == userId)
private fun logV(msg: String, vararg arguments: Any?) {
ProtoLog.v(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments)
}
+ private fun logW(msg: String, vararg arguments: Any?) {
+ ProtoLog.w(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments)
+ }
+
+ private class OnDisplayAreaChangeListener(
+ private val onDisplayAreaChanged: (displayId: Int) -> Unit
+ ) : RootTaskDisplayAreaListener {
+
+ override fun onDisplayAreaAppeared(displayAreaInfo: DisplayAreaInfo) {
+ onDisplayAreaChanged(displayAreaInfo.displayId)
+ }
+
+ override fun onDisplayAreaInfoChanged(displayAreaInfo: DisplayAreaInfo) {
+ onDisplayAreaChanged(displayAreaInfo.displayId)
+ }
+ }
+
companion object {
private const val TAG = "DesktopDisplayEventHandler"
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopImeHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopImeHandler.kt
index b40525c..dda5235 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopImeHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopImeHandler.kt
@@ -67,6 +67,7 @@
}
private val taskToImeTarget = mutableMapOf<Int, ImeTarget>()
+ private var imeTriggeredTransition: IBinder? = null
data class ImeTarget(
var topTask: ActivityManager.RunningTaskInfo,
@@ -133,7 +134,7 @@
logD("Moving task %d due to IME", imeTarget.topTask.taskId)
val wct = WindowContainerTransaction().setBounds(imeTarget.topTask.token, finalBounds)
- transitions.startTransition(TRANSIT_CHANGE, wct, this)
+ imeTriggeredTransition = transitions.startTransition(TRANSIT_CHANGE, wct, this)
taskToImeTarget[currentTopTask.taskId]?.newBounds = finalBounds
} else {
val wct = WindowContainerTransaction()
@@ -144,6 +145,7 @@
if (task.configuration.windowConfiguration.bounds == imeTarget.newBounds) {
val finalBounds = imeTarget.previousBounds ?: return@forEach
+ logD("Restoring task %d due to IME", taskId)
// Restore the previous bounds if they exist
wct.setBounds(imeTarget.topTask.token, finalBounds)
}
@@ -164,6 +166,22 @@
shellTaskOrganizer.getRunningTasks(displayId).find { taskInfo -> taskInfo.isFocused }
}
+ /**
+ * If a transition related to a target that we have previously moved up, remove it from the
+ * target list so we do not restore its bounds.
+ */
+ fun onTransitionReady(transition: IBinder, info: TransitionInfo) {
+ // Do nothing if we get the callback for IME triggered transition
+ if (transition == imeTriggeredTransition || taskToImeTarget.isEmpty()) return
+
+ // If there is a transition targeting the IME targets remove them from the list
+ info.changes.forEach { change ->
+ if (taskToImeTarget[change.taskInfo?.taskId] != null) {
+ taskToImeTarget.remove(change.taskInfo?.taskId)
+ }
+ }
+ }
+
override fun startAnimation(
transition: IBinder,
info: TransitionInfo,
@@ -171,6 +189,8 @@
finishTransaction: Transaction,
finishCallback: Transitions.TransitionFinishCallback,
): Boolean {
+ startTransaction.apply()
+
val animations = mutableListOf<Animator>()
val onAnimFinish: (Animator) -> Unit = { animator ->
mainExecutor.execute {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMinimizationTransitionHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMinimizationTransitionHandler.kt
index 6c6d830..431e874 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMinimizationTransitionHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMinimizationTransitionHandler.kt
@@ -20,6 +20,7 @@
import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
import android.os.Handler
import android.os.IBinder
+import android.os.SystemProperties
import android.view.SurfaceControl.Transaction
import android.view.WindowManager.TRANSIT_TO_BACK
import android.window.TransitionInfo
@@ -29,10 +30,12 @@
import com.android.internal.protolog.ProtoLog
import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.common.ShellExecutor
+import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_DESKTOP_MODE_TASK_LIMIT_MINIMIZE
import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
import com.android.wm.shell.shared.TransitionUtil
import com.android.wm.shell.shared.animation.MinimizeAnimator.create
import com.android.wm.shell.transition.Transitions
+import java.time.Duration
/**
* The [Transitions.TransitionHandler] that handles transitions for tasks that are:
@@ -63,7 +66,9 @@
finishCallback: Transitions.TransitionFinishCallback,
): Boolean {
val shouldAnimate =
- TransitionUtil.isClosingType(info.type) || info.type == Transitions.TRANSIT_MINIMIZE
+ TransitionUtil.isClosingType(info.type) ||
+ info.type == Transitions.TRANSIT_MINIMIZE ||
+ info.type == TRANSIT_DESKTOP_MODE_TASK_LIMIT_MINIMIZE
if (!shouldAnimate) return false
val animations = mutableListOf<Animator>()
@@ -80,14 +85,24 @@
val checkChangeMode = { change: TransitionInfo.Change ->
change.mode == info.type ||
- (info.type == Transitions.TRANSIT_MINIMIZE && change.mode == TRANSIT_TO_BACK)
+ (info.type == Transitions.TRANSIT_MINIMIZE && change.mode == TRANSIT_TO_BACK) ||
+ (info.type == TRANSIT_DESKTOP_MODE_TASK_LIMIT_MINIMIZE &&
+ change.mode == TRANSIT_TO_BACK)
}
+ val startAnimDelay =
+ if (info.type == TRANSIT_DESKTOP_MODE_TASK_LIMIT_MINIMIZE) {
+ TASK_LIMIT_ANIM_START_DELAY
+ } else {
+ Duration.ZERO
+ }
animations +=
info.changes
.filter {
checkChangeMode(it) && it.taskInfo?.windowingMode == WINDOWING_MODE_FREEFORM
}
- .mapNotNull { createMinimizeAnimation(it, finishTransaction, onAnimFinish) }
+ .mapNotNull {
+ createMinimizeAnimation(it, finishTransaction, onAnimFinish, startAnimDelay)
+ }
if (animations.isEmpty()) return false
animExecutor.execute { animations.forEach(Animator::start) }
return true
@@ -97,6 +112,7 @@
change: TransitionInfo.Change,
finishTransaction: Transaction,
onAnimFinish: (Animator) -> Unit,
+ startAnimDelay: Duration,
): Animator? {
val t = Transaction()
val sc = change.leash
@@ -117,6 +133,7 @@
onAnimFinish,
InteractionJankMonitor.getInstance(),
animHandler,
+ startAnimDelay,
)
}
@@ -125,6 +142,14 @@
ProtoLog.w(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments)
}
+ private val TASK_LIMIT_ANIM_START_DELAY =
+ Duration.ofMillis(
+ SystemProperties.getLong(
+ "persist.wm.debug.desktop_transitions.minimize.start_delay_ms",
+ /* def= */ 0,
+ )
+ )
+
const val TAG = "DesktopMinimizationTransitionHandler"
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandler.kt
index 27ce0df..644470b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandler.kt
@@ -25,6 +25,7 @@
import android.view.WindowManager
import android.view.WindowManager.TRANSIT_CLOSE
import android.view.WindowManager.TRANSIT_OPEN
+import android.window.DesktopExperienceFlags
import android.window.DesktopModeFlags
import android.window.TransitionInfo
import android.window.TransitionInfo.Change
@@ -34,6 +35,7 @@
import com.android.internal.jank.InteractionJankMonitor
import com.android.internal.protolog.ProtoLog
import com.android.wm.shell.RootTaskDisplayAreaOrganizer
+import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_DESKTOP_MODE_TASK_LIMIT_MINIMIZE
import com.android.wm.shell.freeform.FreeformTaskTransitionHandler
import com.android.wm.shell.freeform.FreeformTaskTransitionStarter
import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
@@ -97,6 +99,17 @@
}
}
+ /** Starts a task limit minimize transition for [taskId]. */
+ fun startTaskLimitMinimizeTransition(wct: WindowContainerTransaction, taskId: Int): IBinder {
+ return transitions
+ .startTransition(TRANSIT_DESKTOP_MODE_TASK_LIMIT_MINIMIZE, wct, /* handler= */ this)
+ .also { transition ->
+ pendingMixedTransitions.add(
+ PendingMixedTransition.Minimize(transition, taskId, isLastTask = false)
+ )
+ }
+ }
+
/** Delegates starting PiP transition to [FreeformTaskTransitionHandler]. */
override fun startPipTransition(wct: WindowContainerTransaction?): IBinder =
freeformTaskTransitionHandler.startPipTransition(wct)
@@ -324,6 +337,8 @@
val shouldAnimate =
if (info.type == Transitions.TRANSIT_MINIMIZE) {
DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_EXIT_BY_MINIMIZE_TRANSITION_BUGFIX.isTrue
+ } else if (info.type == TRANSIT_DESKTOP_MODE_TASK_LIMIT_MINIMIZE) {
+ DesktopExperienceFlags.ENABLE_DESKTOP_TASK_LIMIT_SEPARATE_TRANSITION.isTrue
} else {
DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION.isTrue
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt
index 96bf500..9f0d416 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt
@@ -44,6 +44,7 @@
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.TaskUpdate
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.UnminimizeReason
import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_DESKTOP_MODE_END_DRAG_TO_DESKTOP
+import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_DESKTOP_MODE_TASK_LIMIT_MINIMIZE
import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_ENTER_DESKTOP_FROM_APP_FROM_OVERVIEW
import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_ENTER_DESKTOP_FROM_APP_HANDLE_MENU_BUTTON
import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_ENTER_DESKTOP_FROM_KEYBOARD_SHORTCUT
@@ -411,6 +412,9 @@
if (transitionInfo?.type == Transitions.TRANSIT_MINIMIZE) {
return MinimizeReason.MINIMIZE_BUTTON
}
+ if (transitionInfo?.type == TRANSIT_DESKTOP_MODE_TASK_LIMIT_MINIMIZE) {
+ return MinimizeReason.TASK_LIMIT
+ }
val minimizingTask =
transition?.let { desktopTasksLimiter.getOrNull()?.getMinimizingTask(transition) }
if (minimizingTask?.taskId == taskInfo.taskId) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTransitionTypes.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTransitionTypes.kt
index 2a10edb..250af71 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTransitionTypes.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTransitionTypes.kt
@@ -47,6 +47,9 @@
/** Transition type to animate the toggle resize between the max and default desktop sizes. */
const val TRANSIT_DESKTOP_MODE_TOGGLE_RESIZE = TRANSIT_DESKTOP_MODE_TYPES + 12
+ /** Transition type to animate the minimization triggered when reaching the task limit. */
+ const val TRANSIT_DESKTOP_MODE_TASK_LIMIT_MINIMIZE = TRANSIT_DESKTOP_MODE_TYPES + 13
+
/** Return whether the [TransitionType] corresponds to a transition to enter desktop mode. */
@JvmStatic
fun @receiver:TransitionType Int.isEnterDesktopModeTransition(): Boolean {
@@ -116,6 +119,7 @@
TRANSIT_DESKTOP_MODE_END_DRAG_TO_DESKTOP -> "DESKTOP_MODE_END_DRAG_TO_DESKTOP"
TRANSIT_DESKTOP_MODE_CANCEL_DRAG_TO_DESKTOP -> "DESKTOP_MODE_CANCEL_DRAG_TO_DESKTOP"
TRANSIT_DESKTOP_MODE_TOGGLE_RESIZE -> "DESKTOP_MODE_TOGGLE_RESIZE"
+ TRANSIT_DESKTOP_MODE_TASK_LIMIT_MINIMIZE -> "DESKTOP_MODE_TASK_LIMIT_MINIMIZE"
else -> ""
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java
index 7d6a960..6785fda 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java
@@ -130,6 +130,7 @@
private final SnapEventHandler mSnapEventHandler;
private final boolean mUseSmallTabletRegions;
+ private boolean mIsReleased = false;
/**
* Ordered list of {@link Rect} zones that we will match an input coordinate against.
* List is traversed from first to last element. The first rect that contains the input event
@@ -235,6 +236,7 @@
/** Release the visual indicator view and its viewhost. */
public void releaseVisualIndicator() {
+ mIsReleased = true;
mVisualIndicatorViewContainer.releaseVisualIndicator();
}
@@ -253,25 +255,43 @@
/**
* Based on the coordinates of the current drag event, determine which indicator type we should
- * display, including no visible indicator.
+ * display, including no visible indicator, and update the indicator.
*/
@NonNull
IndicatorType updateIndicatorType(PointF inputCoordinates) {
+ final IndicatorType result = calculateIndicatorType(inputCoordinates);
+ updateIndicatorWithType(result);
+ return result;
+ }
+
+ /**
+ * Based on the coordinates of the current drag event, determine which indicator type we should
+ * display, including no visible indicator.
+ */
+ @NonNull
+ IndicatorType calculateIndicatorType(PointF inputCoordinates) {
final IndicatorType result;
if (mUseSmallTabletRegions) {
result = getIndicatorSmallTablet(inputCoordinates);
} else {
result = getIndicatorLargeTablet(inputCoordinates);
}
- if (mDragStartState != DragStartState.DRAGGED_INTENT) {
- mVisualIndicatorViewContainer.transitionIndicator(
- mTaskInfo, mDisplayController, mCurrentType, result
- );
- mCurrentType = result;
- }
return result;
}
+ /**
+ * Update the indicator based on IndicatorType.
+ */
+ @NonNull
+ void updateIndicatorWithType(IndicatorType type) {
+ if (!mIsReleased && mDragStartState != DragStartState.DRAGGED_INTENT) {
+ mVisualIndicatorViewContainer.transitionIndicator(
+ mTaskInfo, mDisplayController, mCurrentType, type
+ );
+ mCurrentType = type;
+ }
+ }
+
@NonNull
private IndicatorType getIndicatorLargeTablet(PointF inputCoordinates) {
// TODO(b/401596837): cache the regions to avoid recalculating on each motion event
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt
index 2eb6937..052d22f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt
@@ -348,25 +348,13 @@
/** Gets a registered left tiled task to desktop state or returns null. */
fun getLeftTiledTask(displayId: Int): Int? {
logD("getLeftTiledTask for displayId=%d", displayId)
- val activeDesk =
- checkNotNull(desktopData.getDefaultDesk(displayId)) {
- "Expected desk in display: $displayId"
- }
- val deskId = activeDesk.deskId
- val desk = checkNotNull(desktopData.getDesk(deskId)) { "Did not find desk: $deskId" }
- return desk.leftTiledTaskId
+ return desktopData.getActiveDesk(displayId)?.leftTiledTaskId
}
/** gets a registered right tiled task to desktop state or returns null. */
fun getRightTiledTask(displayId: Int): Int? {
logD("getRightTiledTask for displayId=%d", displayId)
- val activeDesk =
- checkNotNull(desktopData.getDefaultDesk(displayId)) {
- "Expected desk in display: $displayId"
- }
- val deskId = activeDesk.deskId
- val desk = checkNotNull(desktopData.getDesk(deskId)) { "Did not find desk: $deskId" }
- return desk.rightTiledTaskId
+ return desktopData.getActiveDesk(displayId)?.rightTiledTaskId
}
/* Unregisters a left tiled task from desktop state. */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListener.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListener.kt
index e04d14459..a74c1db 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListener.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListener.kt
@@ -18,17 +18,27 @@
import android.app.ActivityManager.RunningTaskInfo
import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
+import android.window.DesktopExperienceFlags
import android.window.DesktopModeFlags
import com.android.internal.protolog.ProtoLog
import com.android.wm.shell.freeform.TaskChangeListener
import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
+import com.android.wm.shell.shared.desktopmode.DesktopState
/** Manages tasks handling specific to Android Desktop Mode. */
-class DesktopTaskChangeListener(private val desktopUserRepositories: DesktopUserRepositories) :
- TaskChangeListener {
+class DesktopTaskChangeListener(
+ private val desktopUserRepositories: DesktopUserRepositories,
+ private val desktopState: DesktopState,
+) : TaskChangeListener {
override fun onTaskOpening(taskInfo: RunningTaskInfo) {
logD("onTaskOpening for taskId=%d, displayId=%d", taskInfo.taskId, taskInfo.displayId)
+ if (
+ !desktopState.isDesktopModeSupportedOnDisplay(taskInfo.displayId) &&
+ DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue
+ ) {
+ return
+ }
val desktopRepository: DesktopRepository =
desktopUserRepositories.getProfile(taskInfo.userId)
if (!isFreeformTask(taskInfo) && desktopRepository.isActiveTask(taskInfo.taskId)) {
@@ -42,6 +52,12 @@
override fun onTaskChanging(taskInfo: RunningTaskInfo) {
logD("onTaskChanging for taskId=%d, displayId=%d", taskInfo.taskId, taskInfo.displayId)
+ if (
+ !desktopState.isDesktopModeSupportedOnDisplay(taskInfo.displayId) &&
+ DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue
+ ) {
+ return
+ }
val desktopRepository: DesktopRepository =
desktopUserRepositories.getProfile(taskInfo.userId)
// TODO: b/394281403 - with multiple desks, it's possible to have a non-freeform task
@@ -78,6 +94,12 @@
override fun onTaskMovingToFront(taskInfo: RunningTaskInfo) {
logD("onTaskMovingToFront for taskId=%d, displayId=%d", taskInfo.taskId, taskInfo.displayId)
+ if (
+ !desktopState.isDesktopModeSupportedOnDisplay(taskInfo.displayId) &&
+ DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue
+ ) {
+ return
+ }
val desktopRepository: DesktopRepository =
desktopUserRepositories.getProfile(taskInfo.userId)
// When the task change is from a task in the desktop repository which is now fullscreen,
@@ -93,15 +115,27 @@
}
override fun onTaskMovingToBack(taskInfo: RunningTaskInfo) {
+ logD("onTaskMovingToBack for taskId=%d, displayId=%d", taskInfo.taskId, taskInfo.displayId)
+ if (
+ !desktopState.isDesktopModeSupportedOnDisplay(taskInfo.displayId) &&
+ DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue
+ ) {
+ return
+ }
val desktopRepository: DesktopRepository =
desktopUserRepositories.getProfile(taskInfo.userId)
if (!desktopRepository.isActiveTask(taskInfo.taskId)) return
- logD("onTaskMovingToBack for taskId=%d, displayId=%d", taskInfo.taskId, taskInfo.displayId)
desktopRepository.updateTask(taskInfo.displayId, taskInfo.taskId, /* isVisible= */ false)
}
override fun onTaskClosing(taskInfo: RunningTaskInfo) {
logD("onTaskClosing for taskId=%d, displayId=%d", taskInfo.taskId, taskInfo.displayId)
+ if (
+ !desktopState.isDesktopModeSupportedOnDisplay(taskInfo.displayId) &&
+ DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue
+ ) {
+ return
+ }
val desktopRepository: DesktopRepository =
desktopUserRepositories.getProfile(taskInfo.userId)
if (!desktopRepository.isActiveTask(taskInfo.taskId)) return
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
index 5dda405..d0af5cf 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
@@ -21,6 +21,7 @@
import android.app.ActivityManager.RecentTaskInfo
import android.app.ActivityManager.RunningTaskInfo
import android.app.ActivityOptions
+import android.app.AppOpsManager
import android.app.KeyguardManager
import android.app.PendingIntent
import android.app.TaskInfo
@@ -33,6 +34,7 @@
import android.app.WindowConfiguration.WindowingMode
import android.content.Context
import android.content.Intent
+import android.content.pm.PackageManager
import android.graphics.Point
import android.graphics.PointF
import android.graphics.Rect
@@ -61,6 +63,8 @@
import android.view.WindowManager.TRANSIT_TO_FRONT
import android.widget.Toast
import android.window.DesktopExperienceFlags
+import android.window.DesktopExperienceFlags.ENABLE_BUG_FIXES_FOR_SECONDARY_DISPLAY
+import android.window.DesktopExperienceFlags.ENABLE_PER_DISPLAY_DESKTOP_WALLPAPER_ACTIVITY
import android.window.DesktopModeFlags
import android.window.DesktopModeFlags.DISABLE_NON_RESIZABLE_APP_SNAP_RESIZE
import android.window.DesktopModeFlags.ENABLE_DESKTOP_WALLPAPER_ACTIVITY_FOR_SYSTEM_USER
@@ -112,6 +116,7 @@
import com.android.wm.shell.desktopmode.EnterDesktopTaskTransitionHandler.FREEFORM_ANIMATION_DURATION
import com.android.wm.shell.desktopmode.ExitDesktopTaskTransitionHandler.FULLSCREEN_ANIMATION_DURATION
import com.android.wm.shell.desktopmode.common.ToggleTaskSizeInteraction
+import com.android.wm.shell.desktopmode.desktopfirst.isDisplayDesktopFirst
import com.android.wm.shell.desktopmode.desktopwallpaperactivity.DesktopWallpaperActivityTokenProvider
import com.android.wm.shell.desktopmode.minimize.DesktopWindowLimitRemoteHandler
import com.android.wm.shell.desktopmode.multidesks.DeskTransition
@@ -168,6 +173,8 @@
import java.util.function.Consumer
import kotlin.coroutines.suspendCoroutine
import kotlin.jvm.optionals.getOrNull
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
/**
* A callback to be invoked when a transition is started via |Transitions.startTransition| with the
@@ -205,6 +212,7 @@
private val recentsTransitionHandler: RecentsTransitionHandler,
private val multiInstanceHelper: MultiInstanceHelper,
@ShellMainThread private val mainExecutor: ShellExecutor,
+ @ShellMainThread private val mainScope: CoroutineScope,
@ShellDesktopThread private val desktopExecutor: ShellExecutor,
private val desktopTasksLimiter: Optional<DesktopTasksLimiter>,
private val recentTasksController: RecentTasksController?,
@@ -225,6 +233,7 @@
private val homeIntentProvider: HomeIntentProvider,
private val desktopState: DesktopState,
private val desktopConfig: DesktopConfig,
+ private val visualIndicatorUpdateScheduler: VisualIndicatorUpdateScheduler,
) :
RemoteCallable<DesktopTasksController>,
Transitions.TransitionHandler,
@@ -292,7 +301,9 @@
if (DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) {
desktopRepositoryInitializer.deskRecreationFactory =
DeskRecreationFactory { deskUserId, destinationDisplayId, _ ->
- createDeskSuspending(displayId = destinationDisplayId, userId = deskUserId)
+ // TODO: b/393978539 - One of the recreated desks may need to be activated by
+ // default in desktop-first.
+ createDeskRootSuspending(displayId = destinationDisplayId, userId = deskUserId)
}
}
}
@@ -477,20 +488,30 @@
displayId = DEFAULT_DISPLAY,
willExitDesktop = true,
shouldEndUpAtHome = true,
- fromRecentsTransition = true,
+ // No need to clean up the wallpaper / home when coming from a recents transition.
+ skipWallpaperAndHomeOrdering = true,
)
runOnTransitStart?.invoke(transition)
}
- /** Adds a new desk to the given display for the given user. */
- fun createDesk(displayId: Int, userId: Int = this.userId, activateDesk: Boolean = false) {
+ /**
+ * Adds a new desk to the given display for the given user and invokes [onResult] once the desk
+ * is created, but necessarily activated.
+ */
+ fun createDesk(
+ displayId: Int,
+ userId: Int = this.userId,
+ activateDesk: Boolean = false,
+ onResult: ((Int) -> Unit) = {},
+ ) {
logV("addDesk displayId=%d, userId=%d", displayId, userId)
val repository = userRepositories.getProfile(userId)
- createDesk(displayId, userId) { deskId ->
+ createDeskRoot(displayId, userId) { deskId ->
if (deskId == null) {
logW("Failed to add desk in displayId=%d for userId=%d", displayId, userId)
} else {
repository.addDesk(displayId = displayId, deskId = deskId)
+ onResult(deskId)
if (activateDesk) {
activateDesk(deskId)
}
@@ -498,7 +519,29 @@
}
}
- private fun createDesk(displayId: Int, userId: Int = this.userId, onResult: (Int?) -> Unit) {
+ @Deprecated("Use createDeskSuspending() instead.", ReplaceWith("createDeskSuspending()"))
+ private fun createDeskImmediate(displayId: Int, userId: Int = this.userId): Int? {
+ logV("createDeskImmediate displayId=%d, userId=%d", displayId, userId)
+ val repository = userRepositories.getProfile(userId)
+ val deskId = createDeskRootImmediate(displayId, userId)
+ if (deskId == null) {
+ logW("Failed to add desk in displayId=%d for userId=%d", displayId, userId)
+ return null
+ }
+ repository.addDesk(displayId = displayId, deskId = deskId)
+ return deskId
+ }
+
+ private suspend fun createDeskSuspending(displayId: Int, userId: Int): Int =
+ suspendCoroutine { cont ->
+ createDesk(displayId, userId) { deskId -> cont.resumeWith(Result.success(deskId)) }
+ }
+
+ private fun createDeskRoot(
+ displayId: Int,
+ userId: Int = this.userId,
+ onResult: (Int?) -> Unit,
+ ) {
if (displayId == Display.INVALID_DISPLAY) {
logW("createDesk attempt with invalid displayId", displayId)
onResult(null)
@@ -516,6 +559,7 @@
UserHandle.USER_SYSTEM == userId
) {
logW("createDesk ignoring attempt for system user")
+ onResult(null)
return
}
desksOrganizer.createDesk(displayId, userId) { deskId ->
@@ -529,9 +573,34 @@
}
}
- private suspend fun createDeskSuspending(displayId: Int, userId: Int = this.userId): Int? =
+ @Deprecated(
+ "Use createDeskRootSuspending() instead.",
+ ReplaceWith("createDeskRootSuspending()"),
+ )
+ private fun createDeskRootImmediate(displayId: Int, userId: Int): Int? {
+ if (displayId == Display.INVALID_DISPLAY) {
+ logW("createDeskRootImmediate attempt with invalid displayId", displayId)
+ return null
+ }
+ if (!DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) {
+ // In single-desk, the desk reuses the display id.
+ logD("createDeskRootImmediate reusing displayId=%d for single-desk", displayId)
+ return displayId
+ }
+ if (
+ DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_HSUM.isTrue &&
+ UserManager.isHeadlessSystemUserMode() &&
+ UserHandle.USER_SYSTEM == userId
+ ) {
+ logW("createDeskRootImmediate ignoring attempt for system user")
+ return null
+ }
+ return desksOrganizer.createDeskImmediate(displayId, userId)
+ }
+
+ private suspend fun createDeskRootSuspending(displayId: Int, userId: Int = this.userId): Int? =
suspendCoroutine { cont ->
- createDesk(displayId, userId) { deskId -> cont.resumeWith(Result.success(deskId)) }
+ createDeskRoot(displayId, userId) { deskId -> cont.resumeWith(Result.success(deskId)) }
}
/**
@@ -592,6 +661,15 @@
return runOnTransitStart
}
+ private fun getDisplayIdForTaskOrDefault(task: TaskInfo): Int {
+ return when {
+ task.displayId != INVALID_DISPLAY -> task.displayId
+ focusTransitionObserver.globallyFocusedDisplayId != INVALID_DISPLAY ->
+ focusTransitionObserver.globallyFocusedDisplayId
+ else -> DEFAULT_DISPLAY
+ }
+ }
+
/** Moves task to desktop mode if task is running, else launches it in desktop mode. */
@JvmOverloads
fun moveTaskToDefaultDeskAndActivate(
@@ -608,14 +686,43 @@
logW("moveTaskToDefaultDeskAndActivate taskId=%d not found", taskId)
return false
}
- val deskId = getDefaultDeskId(task.displayId)
- return moveTaskToDesk(
- taskId = taskId,
- deskId = deskId,
- wct = wct,
- transitionSource = transitionSource,
- remoteTransition = remoteTransition,
- )
+ val displayId = getDisplayIdForTaskOrDefault(task)
+ if (
+ DesktopExperienceFlags.ENABLE_PROJECTED_DISPLAY_DESKTOP_MODE.isTrue &&
+ !desktopState.isDesktopModeSupportedOnDisplay(displayId) &&
+ transitionSource != DesktopModeTransitionSource.ADB_COMMAND &&
+ transitionSource != DesktopModeTransitionSource.APP_FROM_OVERVIEW
+ ) {
+ logW("moveTaskToDefaultDeskAndActivate display=$displayId does not support desk")
+ return false
+ }
+ if (
+ !DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue ||
+ !DesktopExperienceFlags.ENABLE_DEFAULT_DESK_WITHOUT_WARMUP_MIGRATION.isTrue
+ ) {
+ val deskId = getOrCreateDefaultDeskId(displayId) ?: return false
+ return moveTaskToDesk(
+ taskId = taskId,
+ deskId = deskId,
+ wct = wct,
+ transitionSource = transitionSource,
+ remoteTransition = remoteTransition,
+ )
+ }
+ mainScope.launch {
+ try {
+ moveTaskToDesk(
+ taskId = taskId,
+ deskId = getOrCreateDefaultDeskIdSuspending(displayId),
+ wct = wct,
+ transitionSource = transitionSource,
+ remoteTransition = remoteTransition,
+ )
+ } catch (t: Throwable) {
+ logE("Failed to move task to default desk: %s", t.message)
+ }
+ }
+ return true
}
/** Moves task to desktop mode if task is running, else launches it in desktop mode. */
@@ -641,9 +748,9 @@
}
val backgroundTask = recentTasksController?.findTaskInBackground(taskId)
if (backgroundTask != null) {
- // TODO: b/391484662 - add support for |deskId|.
return moveBackgroundTaskToDesktop(
taskId,
+ deskId,
wct,
transitionSource,
remoteTransition,
@@ -656,6 +763,7 @@
private fun moveBackgroundTaskToDesktop(
taskId: Int,
+ deskId: Int,
wct: WindowContainerTransaction,
transitionSource: DesktopModeTransitionSource,
remoteTransition: RemoteTransition? = null,
@@ -666,8 +774,8 @@
logW("moveBackgroundTaskToDesktop taskId=%d not found", taskId)
return false
}
- logV("moveBackgroundTaskToDesktop with taskId=%d", taskId)
- val deskId = getDefaultDeskId(task.displayId)
+ logV("moveBackgroundTaskToDesktop with taskId=%d to deskId=%d", taskId, deskId)
+
val runOnTransitStart = addDeskActivationChanges(deskId, wct, task)
val exitResult =
desktopImmersiveController.exitImmersiveIfApplicable(
@@ -792,7 +900,7 @@
* [startDragToDesktop].
*/
private fun finalizeDragToDesktop(taskInfo: RunningTaskInfo) {
- val deskId = getDefaultDeskId(taskInfo.displayId)
+ val deskId = getOrCreateDefaultDeskId(taskInfo.displayId) ?: return
ProtoLog.v(
WM_SHELL_DESKTOP_MODE,
"DesktopTasksController: finalizeDragToDesktop taskId=%d deskId=%d",
@@ -804,7 +912,7 @@
if (!DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) {
// |moveHomeTask| is also called in |bringDesktopAppsToFrontBeforeShowingNewTask|, so
// this shouldn't be necessary at all.
- if (Flags.enablePerDisplayDesktopWallpaperActivity()) {
+ if (ENABLE_PER_DISPLAY_DESKTOP_WALLPAPER_ACTIVITY.isTrue) {
moveHomeTask(taskInfo.displayId, wct)
} else {
moveHomeTask(context.displayId, wct)
@@ -911,7 +1019,7 @@
logW("minimizeTask: desk not found for task: ${taskInfo.taskId}")
return
} else {
- getDefaultDeskId(taskInfo.displayId)
+ getOrCreateDefaultDeskId(taskInfo.displayId)
}
val isLastTask =
if (DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) {
@@ -923,10 +1031,11 @@
} else {
taskRepository.isOnlyVisibleNonClosingTask(taskId = taskId, displayId = displayId)
}
- val isMinimizingToPip =
- DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PIP.isTrue &&
- (taskInfo.pictureInPictureParams?.isAutoEnterEnabled ?: false)
+ val isMinimizingToPip =
+ DesktopExperienceFlags.ENABLE_DESKTOP_WINDOWING_PIP.isTrue &&
+ (taskInfo.pictureInPictureParams?.isAutoEnterEnabled ?: false) &&
+ isPipAllowedInAppOps(taskInfo)
// If task is going to PiP, start a PiP transition instead of a minimize transition
if (isMinimizingToPip) {
val requestInfo =
@@ -999,6 +1108,36 @@
)
}
+ /** Checks whether the given [taskInfo] is allowed to enter PiP in AppOps. */
+ private fun isPipAllowedInAppOps(taskInfo: RunningTaskInfo): Boolean {
+ val packageName =
+ taskInfo.baseActivity?.packageName
+ ?: taskInfo.topActivity?.packageName
+ ?: taskInfo.origActivity?.packageName
+ ?: taskInfo.realActivity?.packageName
+ ?: return false
+
+ val appOpsManager =
+ checkNotNull(context.getSystemService(Context.APP_OPS_SERVICE) as AppOpsManager)
+ try {
+ val appInfo =
+ context.packageManager.getApplicationInfoAsUser(packageName, /* flags= */ 0, userId)
+ return appOpsManager.checkOpNoThrow(
+ AppOpsManager.OP_PICTURE_IN_PICTURE,
+ appInfo.uid,
+ packageName,
+ ) == AppOpsManager.MODE_ALLOWED
+ } catch (_: PackageManager.NameNotFoundException) {
+ logW(
+ "isPipAllowedInAppOps: Failed to find applicationInfo for packageName=%s " +
+ "and userId=%d",
+ packageName,
+ userId,
+ )
+ }
+ return false
+ }
+
/** Move or launch a task with given [taskId] to fullscreen */
@JvmOverloads
fun moveToFullscreen(
@@ -1167,7 +1306,10 @@
.apply { launchWindowingMode = WINDOWING_MODE_FREEFORM }
.toBundle(),
)
- val deskId = taskRepository.getDeskIdForTask(taskId) ?: getDefaultDeskId(DEFAULT_DISPLAY)
+ val deskId =
+ taskRepository.getDeskIdForTask(taskId)
+ ?: getOrCreateDefaultDeskId(DEFAULT_DISPLAY)
+ ?: return
startLaunchTransition(
TRANSIT_OPEN,
wct,
@@ -1191,8 +1333,7 @@
remoteTransition: RemoteTransition? = null,
unminimizeReason: UnminimizeReason = UnminimizeReason.UNKNOWN,
) {
- val deskId =
- taskRepository.getDeskIdForTask(taskInfo.taskId) ?: getDefaultDeskId(taskInfo.displayId)
+ val deskId = taskRepository.getDeskIdForTask(taskInfo.taskId)
logV("moveTaskToFront taskId=%s deskId=%s", taskInfo.taskId, deskId)
// If a task is tiled, another task should be brought to foreground with it so let
// tiling controller handle the request.
@@ -1200,10 +1341,12 @@
return
}
val wct = WindowContainerTransaction()
- if (DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) {
- desksOrganizer.reorderTaskToFront(wct, deskId, taskInfo)
- } else {
+ if (deskId == null || !DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) {
+ // Not a desktop task, just move to the front.
wct.reorder(taskInfo.token, /* onTop= */ true, /* includingParents= */ true)
+ } else {
+ // A desktop task with multiple desks enabled, reorder it within its desk.
+ desksOrganizer.reorderTaskToFront(wct, deskId, taskInfo)
}
startLaunchTransition(
transitionType = TRANSIT_TO_FRONT,
@@ -1216,13 +1359,26 @@
)
}
+ /**
+ * Starts a launch transition with [transitionType] using [wct].
+ *
+ * @param transitionType the type of transition to start.
+ * @param wct the wct to use in the transition, which may already container changes.
+ * @param launchingTaskId the id of task launching, may be null if starting the task through an
+ * intent in the [wct].
+ * @param remoteTransition the remote transition associated with this transition start.
+ * @param deskId may be null if the launching task isn't launching into a desk, such as when
+ * fullscreen or split tasks are just moved to front.
+ * @param displayId the display in which the launch is happening.
+ * @param unminimizeReason the reason to unminimize.
+ */
@VisibleForTesting
fun startLaunchTransition(
transitionType: Int,
wct: WindowContainerTransaction,
launchingTaskId: Int?,
remoteTransition: RemoteTransition? = null,
- deskId: Int,
+ deskId: Int?,
displayId: Int,
unminimizeReason: UnminimizeReason = UnminimizeReason.UNKNOWN,
): IBinder {
@@ -1235,13 +1391,16 @@
)
// TODO: b/397619806 - Consolidate sharable logic with [handleFreeformTaskLaunch].
var launchTransaction = wct
+ // TODO: b/32994943 - remove dead code when cleaning up task_limit_separate_transition flag
val taskIdToMinimize =
- addAndGetMinimizeChanges(
- deskId,
- launchTransaction,
- newTaskId = launchingTaskId,
- launchingNewIntent = launchingTaskId == null,
- )
+ deskId?.let {
+ addAndGetMinimizeChanges(
+ deskId = it,
+ wct = launchTransaction,
+ newTaskId = launchingTaskId,
+ launchingNewIntent = launchingTaskId == null,
+ )
+ }
val exitImmersiveResult =
desktopImmersiveController.exitImmersiveIfApplicable(
wct = launchTransaction,
@@ -1252,11 +1411,11 @@
var activationRunOnTransitStart: RunOnTransitStart? = null
val shouldActivateDesk =
when {
+ deskId == null -> false
DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue ->
!taskRepository.isDeskActive(deskId)
- DesktopExperienceFlags.ENABLE_DISPLAY_WINDOWING_MODE_SWITCHING.isTrue -> {
+ DesktopExperienceFlags.ENABLE_DISPLAY_WINDOWING_MODE_SWITCHING.isTrue ->
!isAnyDeskActive(displayId)
- }
else -> false
}
if (shouldActivateDesk) {
@@ -1264,7 +1423,11 @@
// TODO: b/391485148 - pass in the launching task here to apply task-limit policy,
// but make sure to not do it twice since it is also done at the start of this
// function.
- activationRunOnTransitStart = addDeskActivationChanges(deskId, activateDeskWct)
+ activationRunOnTransitStart =
+ addDeskActivationChanges(
+ deskId = checkNotNull(deskId) { "Desk id must be non-null when activating" },
+ wct = activateDeskWct,
+ )
// Desk activation must be handled before app launch-related transactions.
activateDeskWct.merge(launchTransaction, /* transfer= */ true)
launchTransaction = activateDeskWct
@@ -1302,6 +1465,9 @@
if (taskIdToMinimize != null) {
addPendingMinimizeTransition(t, taskIdToMinimize, MinimizeReason.TASK_LIMIT)
}
+ if (deskId != null) {
+ addPendingTaskLimitTransition(t, deskId, launchingTaskId)
+ }
if (launchingTaskId != null && taskRepository.isMinimizedTask(launchingTaskId)) {
addPendingUnminimizeTransition(t, displayId, launchingTaskId, unminimizeReason)
}
@@ -1354,8 +1520,9 @@
val wct = WindowContainerTransaction()
val displayLayout = displayController.getDisplayLayout(displayId) ?: return
val bounds = calculateDefaultDesktopTaskBounds(displayLayout)
+ val deskId = getOrCreateDefaultDeskId(displayId) ?: return
if (DesktopModeFlags.ENABLE_CASCADING_WINDOWS.isTrue) {
- cascadeWindow(bounds, displayLayout, displayId)
+ cascadeWindow(bounds, displayLayout, deskId)
}
val pendingIntent =
PendingIntent.getActivityAsUser(
@@ -1380,7 +1547,6 @@
}
wct.sendPendingIntent(pendingIntent, intent, ops.toBundle())
- val deskId = getDefaultDeskId(displayId)
startLaunchTransition(
TRANSIT_OPEN,
wct,
@@ -1439,7 +1605,7 @@
if (bounds != null) {
wct.setBounds(task.token, bounds)
} else if (Flags.enableMoveToNextDisplayShortcut()) {
- applyFreeformDisplayChange(wct, task, displayId)
+ applyFreeformDisplayChange(wct, task, displayId, destinationDeskId)
}
}
@@ -1449,7 +1615,7 @@
val activationRunnable = addDeskActivationChanges(destinationDeskId, wct, task)
- if (Flags.enableDisplayFocusInShellTransitions()) {
+ if (DesktopExperienceFlags.ENABLE_DISPLAY_FOCUS_IN_SHELL_TRANSITIONS.isTrue) {
// Bring the destination display to top with includingParents=true, so that the
// destination display gains the display focus, which makes the top task in the display
// gains the global focus.
@@ -1459,7 +1625,7 @@
val sourceDisplayId = task.displayId
val sourceDeskId = taskRepository.getDeskIdForTask(task.taskId)
val shouldExitDesktopIfNeeded =
- Flags.enablePerDisplayDesktopWallpaperActivity() ||
+ ENABLE_PER_DISPLAY_DESKTOP_WALLPAPER_ACTIVITY.isTrue ||
DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue
val deactivationRunnable =
if (shouldExitDesktopIfNeeded) {
@@ -1886,12 +2052,14 @@
// Move home to front, ensures that we go back home when all desktop windows are closed
val useParamDisplayId =
DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue ||
- Flags.enablePerDisplayDesktopWallpaperActivity()
+ ENABLE_PER_DISPLAY_DESKTOP_WALLPAPER_ACTIVITY.isTrue
moveHomeTask(displayId = if (useParamDisplayId) displayId else context.displayId, wct = wct)
// Currently, we only handle the desktop on the default display really.
if (
- (displayId == DEFAULT_DISPLAY || Flags.enablePerDisplayDesktopWallpaperActivity()) &&
- ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY.isTrue()
+ (displayId == DEFAULT_DISPLAY ||
+ ENABLE_PER_DISPLAY_DESKTOP_WALLPAPER_ACTIVITY.isTrue) &&
+ ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY.isTrue() &&
+ !desktopState.shouldShowHomeBehindDesktop
) {
// Add translucent wallpaper activity to show the wallpaper underneath.
addWallpaperActivity(displayId, wct)
@@ -1913,11 +2081,10 @@
val expandedTasksOrderedFrontToBack = taskRepository.getExpandedTasksOrdered(displayId)
// If we're adding a new Task we might need to minimize an old one
// TODO(b/365725441): Handle non running task minimization
+ // TODO: b/32994943 - remove dead code when cleaning up task_limit_separate_transition flag
val taskIdToMinimize: Int? =
- if (newTaskIdInFront != null && desktopTasksLimiter.isPresent) {
- desktopTasksLimiter
- .get()
- .getTaskIdToMinimize(expandedTasksOrderedFrontToBack, newTaskIdInFront)
+ if (newTaskIdInFront != null) {
+ getTaskIdToMinimize(expandedTasksOrderedFrontToBack, newTaskIdInFront)
} else {
null
}
@@ -1967,7 +2134,7 @@
}
val intent = Intent(context, DesktopWallpaperActivity::class.java)
- if (Flags.enablePerDisplayDesktopWallpaperActivity()) {
+ if (ENABLE_PER_DISPLAY_DESKTOP_WALLPAPER_ACTIVITY.isTrue) {
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK)
}
@@ -1976,7 +2143,7 @@
launchWindowingMode = WINDOWING_MODE_FULLSCREEN
pendingIntentBackgroundActivityStartMode =
ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS
- if (Flags.enablePerDisplayDesktopWallpaperActivity()) {
+ if (ENABLE_PER_DISPLAY_DESKTOP_WALLPAPER_ACTIVITY.isTrue) {
launchDisplayId = displayId
}
}
@@ -1994,7 +2161,7 @@
val intent = Intent(userContext, DesktopWallpaperActivity::class.java)
if (
desktopWallpaperActivityTokenProvider.getToken(displayId) == null &&
- Flags.enablePerDisplayDesktopWallpaperActivity()
+ ENABLE_PER_DISPLAY_DESKTOP_WALLPAPER_ACTIVITY.isTrue
) {
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK)
@@ -2005,7 +2172,7 @@
launchWindowingMode = WINDOWING_MODE_FULLSCREEN
pendingIntentBackgroundActivityStartMode =
ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS
- if (Flags.enablePerDisplayDesktopWallpaperActivity()) {
+ if (ENABLE_PER_DISPLAY_DESKTOP_WALLPAPER_ACTIVITY.isTrue) {
launchDisplayId = displayId
}
}
@@ -2043,7 +2210,7 @@
// explicitly going fullscreen, so there's no point in checking the desktop state.
return true
}
- if (Flags.enablePerDisplayDesktopWallpaperActivity()) {
+ if (ENABLE_PER_DISPLAY_DESKTOP_WALLPAPER_ACTIVITY.isTrue) {
if (!taskRepository.isOnlyVisibleNonClosingTask(triggerTaskId, displayId)) {
return false
}
@@ -2084,16 +2251,15 @@
displayId: Int,
willExitDesktop: Boolean,
shouldEndUpAtHome: Boolean = true,
- fromRecentsTransition: Boolean = false,
+ skipWallpaperAndHomeOrdering: Boolean = false,
): RunOnTransitStart? {
if (!willExitDesktop) return null
desktopModeEnterExitTransitionListener?.onExitDesktopModeTransitionStarted(
FULLSCREEN_ANIMATION_DURATION,
shouldEndUpAtHome,
)
- // No need to clean up the wallpaper / reorder home when coming from a recents transition.
if (
- !fromRecentsTransition ||
+ !skipWallpaperAndHomeOrdering ||
!DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue
) {
removeWallpaperActivity(wct, displayId)
@@ -2173,8 +2339,15 @@
reason = "transition type not handled (${request.type})"
false
}
- // Only handle standard type tasks
- triggerTask.activityType != ACTIVITY_TYPE_STANDARD -> {
+ // Home launches are only handled with multiple desktops enabled.
+ triggerTask.activityType == ACTIVITY_TYPE_HOME &&
+ !DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue -> {
+ reason = "ACTIVITY_TYPE_HOME not handled"
+ false
+ }
+ // Only handle standard and home tasks types.
+ triggerTask.activityType != ACTIVITY_TYPE_STANDARD &&
+ triggerTask.activityType != ACTIVITY_TYPE_HOME -> {
reason = "activityType not handled (${triggerTask.activityType})"
false
}
@@ -2194,6 +2367,8 @@
val result =
when {
+ triggerTask.activityType == ACTIVITY_TYPE_HOME ->
+ handleHomeTaskLaunch(triggerTask, transition)
// Check if freeform task launch during recents should be handled
shouldHandleMidRecentsFreeformLaunch ->
handleMidRecentsFreeformTaskLaunch(triggerTask, transition)
@@ -2309,10 +2484,12 @@
private fun shouldHandleTaskClosing(request: TransitionRequestInfo): Boolean =
ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY.isTrue() &&
TransitionUtil.isClosingType(request.type) &&
- request.triggerTask != null
+ request.triggerTask != null &&
+ request.triggerTask?.activityType != ACTIVITY_TYPE_HOME
/** Open an existing instance of an app. */
fun openInstance(callingTask: RunningTaskInfo, requestedTaskId: Int) {
+ val deskId = getOrCreateDefaultDeskId(callingTask.displayId) ?: return
if (callingTask.isFreeform) {
val requestedTaskInfo = shellTaskOrganizer.getRunningTaskInfo(requestedTaskId)
if (requestedTaskInfo?.isFreeform == true) {
@@ -2322,7 +2499,6 @@
unminimizeReason = UnminimizeReason.APP_HANDLE_MENU_BUTTON,
)
} else {
- val deskId = getDefaultDeskId(callingTask.displayId)
moveTaskToDesk(
requestedTaskId,
deskId,
@@ -2331,7 +2507,7 @@
)
}
} else {
- val options = createNewWindowOptions(callingTask)
+ val options = createNewWindowOptions(callingTask, deskId)
val splitPosition = splitScreenController.determineNewInstancePosition(callingTask)
splitScreenController.startTask(
requestedTaskId,
@@ -2366,7 +2542,11 @@
/* options= */ null,
userHandle,
)
- val options = createNewWindowOptions(callingTaskInfo)
+ val deskId =
+ taskRepository.getDeskIdForTask(callingTaskInfo.taskId)
+ ?: getOrCreateDefaultDeskId(callingTaskInfo.displayId)
+ ?: return
+ val options = createNewWindowOptions(callingTaskInfo, deskId)
when (options.launchWindowingMode) {
WINDOWING_MODE_MULTI_WINDOW -> {
val splitPosition =
@@ -2391,9 +2571,6 @@
WINDOWING_MODE_FREEFORM -> {
val wct = WindowContainerTransaction()
wct.sendPendingIntent(launchIntent, fillIn, options.toBundle())
- val deskId =
- taskRepository.getDeskIdForTask(callingTaskInfo.taskId)
- ?: getDefaultDeskId(callingTaskInfo.displayId)
startLaunchTransition(
transitionType = TRANSIT_OPEN,
wct = wct,
@@ -2405,7 +2582,7 @@
}
}
- private fun createNewWindowOptions(callingTask: RunningTaskInfo): ActivityOptions {
+ private fun createNewWindowOptions(callingTask: RunningTaskInfo, deskId: Int): ActivityOptions {
val newTaskWindowingMode =
when {
callingTask.isFreeform -> {
@@ -2422,7 +2599,7 @@
when (newTaskWindowingMode) {
WINDOWING_MODE_FREEFORM -> {
displayController.getDisplayLayout(callingTask.displayId)?.let {
- getInitialBounds(it, callingTask, callingTask.displayId)
+ getInitialBounds(it, callingTask, deskId)
}
}
WINDOWING_MODE_MULTI_WINDOW -> {
@@ -2440,6 +2617,28 @@
}
}
+ private fun handleHomeTaskLaunch(
+ task: RunningTaskInfo,
+ transition: IBinder,
+ ): WindowContainerTransaction? {
+ logV("DesktopTasksController: handleHomeTaskLaunch")
+ val activeDeskId = taskRepository.getActiveDeskId(task.displayId) ?: return null
+ val wct = WindowContainerTransaction()
+ // TODO: b/393978539 - desktop-first displays may need to keep the desk active.
+ val runOnTransitStart =
+ performDesktopExitCleanUp(
+ wct = wct,
+ deskId = activeDeskId,
+ displayId = task.displayId,
+ willExitDesktop = true,
+ shouldEndUpAtHome = true,
+ // No need to clean up the wallpaper / home order if Home is launching directly.
+ skipWallpaperAndHomeOrdering = true,
+ )
+ runOnTransitStart?.invoke(transition)
+ return wct
+ }
+
/**
* Handles the case where a freeform task is launched from recents.
*
@@ -2486,7 +2685,7 @@
logV("skip keyguard is locked")
return null
}
- val deskId = getDefaultDeskId(task.displayId)
+ val deskId = getOrCreateDefaultDeskId(task.displayId) ?: return null
val isKnownDesktopTask = taskRepository.isActiveTask(task.taskId)
val shouldEnterDesktop =
forceEnterDesktop
@@ -2554,7 +2753,7 @@
val displayLayout = displayController.getDisplayLayout(task.displayId)
if (displayLayout != null) {
val initialBounds = Rect(task.configuration.windowConfiguration.bounds)
- cascadeWindow(initialBounds, displayLayout, task.displayId)
+ cascadeWindow(initialBounds, displayLayout, deskId)
wct.setBounds(task.token, initialBounds)
}
}
@@ -2580,12 +2779,14 @@
reason = DesktopImmersiveController.ExitReason.TASK_LAUNCH,
)
// 2) minimize a Task if needed.
+ // TODO: b/32994943 - remove dead code when cleaning up task_limit_separate_transition flag
val taskIdToMinimize = addAndGetMinimizeChanges(deskId, wct, task.taskId)
addPendingAppLaunchTransition(transition, task.taskId, taskIdToMinimize)
if (taskIdToMinimize != null) {
addPendingMinimizeTransition(transition, taskIdToMinimize, MinimizeReason.TASK_LIMIT)
return wct
}
+ addPendingTaskLimitTransition(transition, deskId, task.taskId)
if (!wct.isEmpty) {
snapEventHandler.removeTaskIfTiled(task.displayId, task.taskId)
return wct
@@ -2601,7 +2802,7 @@
if (shouldFullscreenTaskLaunchSwitchToDesktop(task)) {
logD("Switch fullscreen task to freeform on transition: taskId=%d", task.taskId)
return WindowContainerTransaction().also { wct ->
- val deskId = getDefaultDeskId(task.displayId)
+ val deskId = getOrCreateDefaultDeskId(task.displayId) ?: return@also
addMoveToDeskTaskChanges(wct = wct, task = task, deskId = deskId)
val runOnTransitStart: RunOnTransitStart? =
if (
@@ -2625,6 +2826,8 @@
{ transition: IBinder ->
// The desk was already showing and we're launching a new Task - we
// might need to minimize another Task.
+ // TODO: b/32994943 - remove dead code when cleaning up
+ // task_limit_separate_transition flag
val taskIdToMinimize =
addAndGetMinimizeChanges(deskId, wct, task.taskId)
taskIdToMinimize?.let { minimizingTaskId ->
@@ -2634,6 +2837,7 @@
MinimizeReason.TASK_LIMIT,
)
}
+ addPendingTaskLimitTransition(transition, deskId, task.taskId)
// Also track the pending launching task.
addPendingAppLaunchTransition(transition, task.taskId, taskIdToMinimize)
}
@@ -2835,7 +3039,7 @@
// cascading positions.
wct.setBounds(task.token, inheritedTaskBounds)
} else {
- val initialBounds = getInitialBounds(displayLayout, task, targetDisplayId)
+ val initialBounds = getInitialBounds(displayLayout, task, deskId)
if (canChangeTaskPosition(task)) {
wct.setBounds(task.token, initialBounds)
}
@@ -2868,6 +3072,7 @@
wct: WindowContainerTransaction,
taskInfo: RunningTaskInfo,
destDisplayId: Int,
+ destDeskId: Int,
) {
val sourceLayout = displayController.getDisplayLayout(taskInfo.displayId) ?: return
val destLayout = displayController.getDisplayLayout(destDisplayId) ?: return
@@ -2899,7 +3104,7 @@
)
}
} else {
- getInitialBounds(destLayout, taskInfo, destDisplayId)
+ getInitialBounds(destLayout, taskInfo, destDeskId)
}
wct.setBounds(taskInfo.token, boundsWithinDisplay)
}
@@ -2907,7 +3112,7 @@
private fun getInitialBounds(
displayLayout: DisplayLayout,
taskInfo: RunningTaskInfo,
- displayId: Int,
+ deskId: Int,
): Rect {
val bounds =
if (ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS.isTrue) {
@@ -2926,7 +3131,7 @@
}
if (DesktopModeFlags.ENABLE_CASCADING_WINDOWS.isTrue) {
- cascadeWindow(bounds, displayLayout, displayId)
+ cascadeWindow(bounds, displayLayout, deskId)
}
return bounds
}
@@ -2954,6 +3159,10 @@
}
if (DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) {
wct.reparent(taskInfo.token, tdaInfo.token, /* onTop= */ true)
+ } else if (com.android.launcher3.Flags.enableAltTabKqsFlatenning()) {
+ // Until multiple desktops is enabled, we still want to reorder the task to top so that
+ // if the task is not on top we can still switch to it using Alt+Tab.
+ wct.reorder(taskInfo.token, /* onTop= */ true)
}
val deskId =
@@ -2973,11 +3182,11 @@
)
}
- private fun cascadeWindow(bounds: Rect, displayLayout: DisplayLayout, displayId: Int) {
+ private fun cascadeWindow(bounds: Rect, displayLayout: DisplayLayout, deskId: Int) {
val stableBounds = Rect()
displayLayout.getStableBoundsForDesktopMode(stableBounds)
- val activeTasks = taskRepository.getExpandedTasksOrdered(displayId)
+ val activeTasks = taskRepository.getExpandedTasksIdsInDeskOrdered(deskId)
activeTasks.firstOrNull()?.let { activeTask ->
shellTaskOrganizer.getRunningTaskInfo(activeTask)?.let {
cascadeWindow(
@@ -3028,11 +3237,19 @@
newTaskId: Int?,
launchingNewIntent: Boolean = false,
): Int? {
- if (!desktopTasksLimiter.isPresent) return null
+ if (DesktopExperienceFlags.ENABLE_DESKTOP_TASK_LIMIT_SEPARATE_TRANSITION.isTrue) return null
+ val limiter = desktopTasksLimiter.getOrNull() ?: return null
require(newTaskId == null || !launchingNewIntent)
- return desktopTasksLimiter
- .get()
- .addAndGetMinimizeTaskChanges(deskId, wct, newTaskId, launchingNewIntent)
+ return limiter.addAndGetMinimizeTaskChanges(deskId, wct, newTaskId, launchingNewIntent)
+ }
+
+ private fun getTaskIdToMinimize(
+ expandedTasksOrderedFrontToBack: List<Int>,
+ newTaskIdInFront: Int?,
+ ): Int? {
+ if (DesktopExperienceFlags.ENABLE_DESKTOP_TASK_LIMIT_SEPARATE_TRANSITION.isTrue) return null
+ val limiter = desktopTasksLimiter.getOrNull() ?: return null
+ return limiter.getTaskIdToMinimize(expandedTasksOrderedFrontToBack, newTaskIdInFront)
}
private fun addPendingMinimizeTransition(
@@ -3051,6 +3268,21 @@
}
}
+ private fun addPendingTaskLimitTransition(
+ transition: IBinder,
+ deskId: Int,
+ launchTaskId: Int?,
+ ) {
+ if (!DesktopExperienceFlags.ENABLE_DESKTOP_TASK_LIMIT_SEPARATE_TRANSITION.isTrue) return
+ desktopTasksLimiter.ifPresent {
+ it.addPendingTaskLimitTransition(
+ transition = transition,
+ deskId = deskId,
+ taskId = launchTaskId,
+ )
+ }
+ }
+
private fun addPendingUnminimizeTransition(
transition: IBinder,
displayId: Int,
@@ -3096,7 +3328,7 @@
displayId: Int,
remoteTransition: RemoteTransition? = null,
) {
- val deskId = getDefaultDeskId(displayId)
+ val deskId = getOrCreateDefaultDeskId(displayId) ?: return
activateDesk(deskId, remoteTransition)
}
@@ -3126,6 +3358,8 @@
if (!DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) {
val taskIdToMinimize = bringDesktopAppsToFront(displayId, wct, newTask?.taskId)
return { transition ->
+ // TODO: b/32994943 - remove dead code when cleaning up
+ // task_limit_separate_transition flag
taskIdToMinimize?.let { minimizingTaskId ->
addPendingMinimizeTransition(
transition = transition,
@@ -3133,6 +3367,11 @@
minimizeReason = MinimizeReason.TASK_LIMIT,
)
}
+ addPendingTaskLimitTransition(
+ transition = transition,
+ deskId = deskId,
+ launchTaskId = newTask?.taskId,
+ )
if (newTask != null && addPendingLaunchTransition) {
addPendingAppLaunchTransition(transition, newTask.taskId, taskIdToMinimize)
}
@@ -3146,10 +3385,9 @@
val expandedTasksOrderedFrontToBack =
taskRepository.getExpandedTasksIdsInDeskOrdered(deskId = deskId)
// If we're adding a new Task we might need to minimize an old one
+ // TODO: b/32994943 - remove dead code when cleaning up task_limit_separate_transition flag
val taskIdToMinimize =
- desktopTasksLimiter
- .getOrNull()
- ?.getTaskIdToMinimize(expandedTasksOrderedFrontToBack, newTaskIdInFront)
+ getTaskIdToMinimize(expandedTasksOrderedFrontToBack, newTaskIdInFront)
if (taskIdToMinimize != null) {
val taskToMinimize = shellTaskOrganizer.getRunningTaskInfo(taskIdToMinimize)
// TODO(b/365725441): Handle non running task minimization
@@ -3192,6 +3430,7 @@
taskIdToMinimize?.let { minimizingTask ->
addPendingMinimizeTransition(transition, minimizingTask, MinimizeReason.TASK_LIMIT)
}
+ addPendingTaskLimitTransition(transition, deskId, newTask?.taskId)
deactivationRunnable?.invoke(transition)
}
}
@@ -3311,14 +3550,44 @@
/** Removes the default desk in the given display. */
@Deprecated("Deprecated with multi-desks.", ReplaceWith("removeDesk()"))
fun removeDefaultDeskInDisplay(displayId: Int) {
- val deskId = getDefaultDeskId(displayId)
+ val deskId = getOrCreateDefaultDeskId(displayId) ?: return
removeDesk(displayId = displayId, deskId = deskId)
}
- private fun getDefaultDeskId(displayId: Int) =
- checkNotNull(taskRepository.getDefaultDeskId(displayId)) {
- "Expected a default desk to exist in display: $displayId"
+ /**
+ * Returns the default desk if it exists, or creates it if needed.
+ *
+ * Note: [DesktopDisplayEventHandler] is responsible for creating a default desk in
+ * desktop-first displays or warming up a desk-root in touch-first displays. This guarantees
+ * that a non-null desk can be returned by this function because even if one does not exist yet,
+ * [createDeskImmediate] should succeed.
+ *
+ * TODO: b/406890311 - replace callers with [getOrCreateDefaultDeskIdSuspending], which is safer
+ * because it does not depend on pre-creating desk roots.
+ */
+ @Deprecated(
+ "Use getOrCreateDefaultDeskIdSuspending() instead",
+ ReplaceWith("getOrCreateDefaultDeskIdSuspending()"),
+ )
+ private fun getOrCreateDefaultDeskId(displayId: Int): Int? {
+ val existingDefaultDeskId = taskRepository.getDefaultDeskId(displayId)
+ if (existingDefaultDeskId != null) {
+ return existingDefaultDeskId
}
+ val immediateDeskId = createDeskImmediate(displayId, userId)
+ if (immediateDeskId == null) {
+ logE(
+ "Failed to create immediate desk in displayId=%s for userId=%s:\n%s",
+ displayId,
+ userId,
+ Throwable().stackTraceToString(),
+ )
+ }
+ return immediateDeskId
+ }
+
+ private suspend fun getOrCreateDefaultDeskIdSuspending(displayId: Int): Int =
+ taskRepository.getDefaultDeskId(displayId) ?: createDeskSuspending(displayId, userId)
/** Removes the given desk. */
fun removeDesk(deskId: Int) {
@@ -3467,16 +3736,33 @@
taskInfo: RunningTaskInfo,
taskSurface: SurfaceControl,
inputX: Float,
+ inputY: Float,
taskBounds: Rect,
) {
if (taskInfo.windowingMode != WINDOWING_MODE_FREEFORM) return
snapEventHandler.removeTaskIfTiled(taskInfo.displayId, taskInfo.taskId)
- updateVisualIndicator(
- taskInfo,
- taskSurface,
+ if (!DesktopExperienceFlags.ENABLE_CONNECTED_DISPLAYS_WINDOW_DRAG.isTrue()) {
+ updateVisualIndicator(
+ taskInfo,
+ taskSurface,
+ inputX,
+ taskBounds.top.toFloat(),
+ DragStartState.FROM_FREEFORM,
+ )
+ return
+ }
+
+ val indicator =
+ getOrCreateVisualIndicator(taskInfo, taskSurface, DragStartState.FROM_FREEFORM)
+ val indicatorType =
+ indicator.calculateIndicatorType(PointF(inputX, taskBounds.top.toFloat()))
+ visualIndicatorUpdateScheduler.schedule(
+ taskInfo.displayId,
+ indicatorType,
inputX,
- taskBounds.top.toFloat(),
- DragStartState.FROM_FREEFORM,
+ inputY,
+ taskBounds,
+ indicator,
)
}
@@ -3486,7 +3772,17 @@
inputX: Float,
taskTop: Float,
dragStartState: DragStartState,
- ): DesktopModeVisualIndicator.IndicatorType {
+ ): IndicatorType {
+ return getOrCreateVisualIndicator(taskInfo, taskSurface, dragStartState)
+ .updateIndicatorType(PointF(inputX, taskTop))
+ }
+
+ @VisibleForTesting
+ fun getOrCreateVisualIndicator(
+ taskInfo: RunningTaskInfo,
+ taskSurface: SurfaceControl?,
+ dragStartState: DragStartState,
+ ): DesktopModeVisualIndicator {
// If the visual indicator has the wrong start state, it was never cleared from a previous
// drag event and needs to be cleared
if (visualIndicator != null && visualIndicator?.dragStartState != dragStartState) {
@@ -3502,7 +3798,7 @@
syncQueue,
taskInfo,
displayController,
- if (Flags.enableBugFixesForSecondaryDisplay()) {
+ if (ENABLE_BUG_FIXES_FOR_SECONDARY_DISPLAY.isTrue) {
displayController.getDisplayContext(taskInfo.displayId)
} else {
context
@@ -3514,7 +3810,7 @@
snapEventHandler,
)
if (visualIndicator == null) visualIndicator = indicator
- return indicator.updateIndicatorType(PointF(inputX, taskTop))
+ return indicator
}
/**
@@ -3549,7 +3845,13 @@
)
when (indicatorType) {
IndicatorType.TO_FULLSCREEN_INDICATOR -> {
- if (desktopConfig.shouldMaximizeWhenDragToTopEdge) {
+ val shouldMaximizeWhenDragToTopEdge =
+ if (DesktopExperienceFlags.ENABLE_DESKTOP_FIRST_BASED_DRAG_TO_MAXIMIZE.isTrue)
+ rootTaskDisplayAreaOrganizer.isDisplayDesktopFirst(
+ motionEvent.getDisplayId()
+ )
+ else desktopConfig.shouldMaximizeWhenDragToTopEdge
+ if (shouldMaximizeWhenDragToTopEdge) {
dragToMaximizeDesktopTask(taskInfo, taskSurface, currentDragBounds, motionEvent)
} else {
desktopModeUiEventLogger.log(
@@ -3851,7 +4153,7 @@
if (windowingMode == WINDOWING_MODE_FREEFORM) {
if (DesktopModeFlags.ENABLE_DESKTOP_TAB_TEARING_MINIMIZE_ANIMATION_BUGFIX.isTrue()) {
// TODO b/376389593: Use a custom tab tearing transition/animation
- val deskId = getDefaultDeskId(DEFAULT_DISPLAY)
+ val deskId = getOrCreateDefaultDeskId(DEFAULT_DISPLAY) ?: return false
startLaunchTransition(
TRANSIT_OPEN,
wct,
@@ -3887,7 +4189,7 @@
userId = newUserId
taskRepository = userRepositories.getProfile(userId)
if (this::snapEventHandler.isInitialized) {
- snapEventHandler.onUserChange()
+ snapEventHandler.onUserChange(userId)
}
}
@@ -4222,14 +4524,18 @@
}
private fun registerListeners(c: DesktopTasksController) {
- c.taskRepository.addDeskChangeListener(deskChangeListener, c.mainExecutor)
+ if (c.desktopState.enableMultipleDesktops) {
+ c.taskRepository.addDeskChangeListener(deskChangeListener, c.mainExecutor)
+ }
c.taskRepository.addVisibleTasksListener(visibleTasksListener, c.mainExecutor)
c.taskbarDesktopTaskListener = taskbarDesktopTaskListener
c.desktopModeEnterExitTransitionListener = desktopModeEntryExitTransitionListener
}
private fun unregisterListeners(c: DesktopTasksController) {
- c.taskRepository.removeDeskChangeListener(deskChangeListener)
+ if (c.desktopState.enableMultipleDesktops) {
+ c.taskRepository.removeDeskChangeListener(deskChangeListener)
+ }
c.taskRepository.removeVisibleTasksListener(visibleTasksListener)
c.taskbarDesktopTaskListener = null
c.desktopModeEnterExitTransitionListener = null
@@ -4248,6 +4554,10 @@
ProtoLog.w(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments)
}
+ private fun logE(msg: String, vararg arguments: Any?) {
+ ProtoLog.e(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments)
+ }
+
companion object {
@JvmField
val DESKTOP_MODE_INITIAL_BOUNDS_SCALE =
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt
index 58a8440..467f43c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt
@@ -44,10 +44,11 @@
* TODO(b/400634379): Separate two responsibilities of this class into two classes.
*/
class DesktopTasksLimiter(
- transitions: Transitions,
+ private val transitions: Transitions,
private val desktopUserRepositories: DesktopUserRepositories,
private val shellTaskOrganizer: ShellTaskOrganizer,
private val desksOrganizer: DesksOrganizer,
+ private val desktopMixedTransitionHandler: DesktopMixedTransitionHandler,
private val maxTasksLimit: Int?,
) {
private val minimizeTransitionObserver = MinimizeTransitionObserver()
@@ -71,6 +72,9 @@
}
}
+ /** Describes a task launch that might trigger a task limit minimize transition. */
+ data class LaunchDetails(val deskId: Int, val taskId: Int?)
+
data class TaskDetails(
val displayId: Int,
val taskId: Int,
@@ -97,6 +101,7 @@
// TODO(b/333018485): replace this observer when implementing the minimize-animation
private inner class MinimizeTransitionObserver : TransitionObserver {
+ private val pendingTaskLimitTransitionTokens = mutableMapOf<IBinder, LaunchDetails>()
private val pendingTransitionTokensAndTasks = mutableMapOf<IBinder, TaskDetails>()
private val activeTransitionTokensAndTasks = mutableMapOf<IBinder, TaskDetails>()
private val pendingUnminimizeTransitionTokensAndTasks = mutableMapOf<IBinder, TaskDetails>()
@@ -106,6 +111,10 @@
pendingTransitionTokensAndTasks[transition] = taskDetails
}
+ fun addPendingTaskLimitTransitionToken(transition: IBinder, details: LaunchDetails) {
+ pendingTaskLimitTransitionTokens[transition] = details
+ }
+
fun addPendingUnminimizeTransitionToken(transition: IBinder, taskDetails: TaskDetails) {
pendingUnminimizeTransitionTokensAndTasks[transition] = taskDetails
}
@@ -115,6 +124,9 @@
?: activeTransitionTokensAndTasks[transition]
}
+ fun hasTaskLimitTransition(transition: IBinder): Boolean =
+ pendingTaskLimitTransitionTokens.contains(transition)
+
fun getUnminimizingTask(transition: IBinder): TaskDetails? {
return pendingUnminimizeTransitionTokensAndTasks[transition]
?: activeUnminimizeTransitionTokensAndTasks[transition]
@@ -127,10 +139,48 @@
finishTransaction: SurfaceControl.Transaction,
) {
val taskRepository = desktopUserRepositories.current
+ handleTaskLimitTransitionReady(taskRepository, transition, info)
handleMinimizeTransitionReady(taskRepository, transition, info)
handleUnminimizeTransitionReady(transition)
}
+ /**
+ * Handles [#onTransitionReady()] for transitions that might trigger task limit minimize.
+ */
+ private fun handleTaskLimitTransitionReady(
+ taskRepository: DesktopRepository,
+ transition: IBinder,
+ info: TransitionInfo,
+ ) {
+ val launchDetails = pendingTaskLimitTransitionTokens.remove(transition) ?: return
+ logV("handleTaskLimitTransitionReady, transition=$transition, info=$info")
+ transitions.runOnIdle {
+ val expandedTaskIds =
+ taskRepository.getExpandedTasksIdsInDeskOrdered(launchDetails.deskId)
+ logV("runOnIdle, expandedTasks=$expandedTaskIds")
+ val taskIdToMinimize =
+ getTaskIdToMinimize(expandedTaskIds, /* launchingNewIntent= */ false)
+ if (taskIdToMinimize != null) {
+ triggerMinimizeTransition(launchDetails.deskId, taskIdToMinimize)
+ }
+ }
+ }
+
+ private fun triggerMinimizeTransition(deskId: Int, taskIdToMinimize: Int) {
+ val task = shellTaskOrganizer.getRunningTaskInfo(taskIdToMinimize) ?: return
+ logV("triggerMinimizeTransition, found running task -> start transition, %s", task)
+ val wct = WindowContainerTransaction()
+ addMinimizeChange(deskId, task, wct)
+ val transition =
+ desktopMixedTransitionHandler.startTaskLimitMinimizeTransition(wct, task.taskId)
+ addPendingMinimizeChange(
+ transition,
+ task.displayId,
+ task.taskId,
+ MinimizeReason.TASK_LIMIT,
+ )
+ }
+
private fun handleMinimizeTransitionReady(
taskRepository: DesktopRepository,
transition: IBinder,
@@ -270,16 +320,36 @@
)
taskIdToMinimize
?.let { shellTaskOrganizer.getRunningTaskInfo(it) }
- ?.let { task ->
- if (!DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) {
- wct.reorder(task.token, /* onTop= */ false)
- } else {
- desksOrganizer.minimizeTask(wct, deskId, task)
- }
- }
+ ?.let { task -> addMinimizeChange(deskId, task, wct) }
return taskIdToMinimize
}
+ private fun addMinimizeChange(
+ deskId: Int,
+ task: ActivityManager.RunningTaskInfo,
+ wct: WindowContainerTransaction,
+ ) =
+ if (DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) {
+ desksOrganizer.minimizeTask(wct, deskId, task)
+ } else {
+ wct.reorder(task.token, /* onTop= */ false)
+ }
+
+ /**
+ * Add a pending transition to trigger a new minimize transition in case the pending transition
+ * takes us over the task limit.
+ */
+ fun addPendingTaskLimitTransition(transition: IBinder, deskId: Int, taskId: Int?) =
+ minimizeTransitionObserver.addPendingTaskLimitTransitionToken(
+ transition,
+ LaunchDetails(deskId, taskId),
+ )
+
+ /** For testing only: returns whether there are any pending task limit transitions. */
+ @VisibleForTesting
+ fun hasTaskLimitTransitionForTesting(transition: IBinder) =
+ minimizeTransitionObserver.hasTaskLimitTransition(transition)
+
/**
* Add a pending minimize transition change to update the list of minimized apps once the
* transition goes through.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/VisualIndicatorUpdateScheduler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/VisualIndicatorUpdateScheduler.kt
new file mode 100644
index 0000000..60a6f23
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/VisualIndicatorUpdateScheduler.kt
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.desktopmode
+
+import android.graphics.Rect
+import android.hardware.display.DisplayTopology
+import android.hardware.display.DisplayTopology.TreeNode.POSITION_LEFT
+import android.hardware.display.DisplayTopology.TreeNode.POSITION_RIGHT
+import android.hardware.display.DisplayTopology.TreeNode.POSITION_TOP
+import android.hardware.display.DisplayTopologyGraph
+import com.android.wm.shell.common.DisplayController
+import com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.IndicatorType
+import com.android.wm.shell.shared.annotations.ShellBackgroundThread
+import com.android.wm.shell.shared.annotations.ShellMainThread
+import com.android.wm.shell.sysui.ShellInit
+import kotlin.math.abs
+import kotlin.math.max
+import kotlin.math.min
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.isActive
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+
+/**
+ * Manages scheduling updates for the visual indicator shown during task drags in desktop mode.
+ *
+ * This class introduces a delay before updating the indicator if the drag gesture is potentially
+ * moving towards an adjacent display (cross-display drag). This prevents flickering or premature
+ * updates while the user is dragging near the edge.
+ */
+class VisualIndicatorUpdateScheduler(
+ shellInit: ShellInit,
+ @ShellMainThread private val mainDispatcher: CoroutineDispatcher,
+ @ShellBackgroundThread private val bgScope: CoroutineScope,
+ private val displayController: DisplayController,
+) {
+ private var updateJob: Job? = null
+ private val previousBounds = Rect()
+ private var previousIndicatorType = IndicatorType.NO_INDICATOR
+ private var displayTopologyGraph: DisplayTopologyGraph? = null
+
+ private val displayTopologyListener =
+ object : DisplayController.OnDisplaysChangedListener {
+ override fun onTopologyChanged(topology: DisplayTopology?) {
+ displayTopologyGraph = topology?.getGraph()
+ }
+ }
+
+ init {
+ shellInit.addInitCallback({ onInit() }, this)
+ }
+
+ private fun onInit() {
+ displayController.addDisplayWindowListener(displayTopologyListener)
+ }
+
+ /**
+ * Requests an update for the visual indicator based on the current drag state.
+ *
+ * This function determines whether to update the indicator immediately or schedule a delayed
+ * update. A delay is introduced if the drag gesture, defined by the pointer coordinates
+ * [inputX] and [inputY] (in pixels relative to the display identified by [displayId]), is
+ * potentially moving towards an adjacent display, considering the requested [indicatorType]
+ * (e.g., fullscreen, split).
+ *
+ * An immediate update occurs if the drag is not deemed a potential cross-display move. If a
+ * delay *is* scheduled, it can be preempted and updated immediately if the dragged task's
+ * bounds, provided in [taskBounds], change significantly compared to the last recorded bounds.
+ *
+ * The actual UI update logic should be encapsulated in the [performUpdateAction] lambda, which
+ * will be executed either immediately or after the delay (on the [mainDispatcher] if delayed).
+ */
+ fun schedule(
+ displayId: Int,
+ indicatorType: IndicatorType,
+ inputX: Float,
+ inputY: Float,
+ taskBounds: Rect,
+ visualIndicator: DesktopModeVisualIndicator?,
+ ) {
+ if (!isPotentialCrossDisplayDrag(displayId, indicatorType, inputX, inputY)) {
+ updateJob?.cancel()
+ visualIndicator?.updateIndicatorWithType(indicatorType)
+ return
+ }
+
+ if (previousIndicatorType != indicatorType || didBoundsChangeSignificantly(taskBounds)) {
+ updateJob?.cancel()
+ updateJob =
+ bgScope.launch {
+ if (!isActive) return@launch
+ delay(timeMillis = DELAY_MILLIS)
+ withContext(mainDispatcher) {
+ if (!isActive) return@withContext
+ visualIndicator?.updateIndicatorWithType(indicatorType)
+ }
+ }
+ }
+
+ previousIndicatorType = indicatorType
+ previousBounds.set(taskBounds)
+ }
+
+ private fun isPotentialCrossDisplayDrag(
+ displayId: Int,
+ indicatorType: IndicatorType,
+ inputX: Float,
+ inputY: Float,
+ ): Boolean {
+ return when (indicatorType) {
+ IndicatorType.TO_FULLSCREEN_INDICATOR ->
+ isCursorNearAdjacentDisplayEdge(displayId, POSITION_TOP, inputX, inputY)
+ IndicatorType.TO_SPLIT_LEFT_INDICATOR ->
+ isCursorNearAdjacentDisplayEdge(displayId, POSITION_LEFT, inputX, inputY)
+ IndicatorType.TO_SPLIT_RIGHT_INDICATOR ->
+ isCursorNearAdjacentDisplayEdge(displayId, POSITION_RIGHT, inputX, inputY)
+ // Ignore indicators that don't represent dragging towards a relevant display edge.
+ // TO_FULLSCREEN (top edge), TO_SPLIT_LEFT (left edge), and TO_SPLIT_RIGHT (right edge)
+ // are the only types considered for potential cross-display transitions.
+ else -> false
+ }
+ }
+
+ private fun isCursorNearAdjacentDisplayEdge(
+ displayId: Int,
+ position: Int,
+ inputX: Float,
+ inputY: Float,
+ ): Boolean {
+ val adjacentDisplays =
+ displayTopologyGraph
+ ?.displayNodes
+ ?.find { node -> node.displayId == displayId }
+ ?.adjacentDisplays ?: return false
+ val adjacentDisplayId =
+ adjacentDisplays.find { adjDisplay -> adjDisplay.position == position }?.displayId
+ ?: return false
+
+ val currentDisplayLayout = displayController.getDisplayLayout(displayId) ?: return false
+
+ val adjacentDisplayLayout =
+ displayController.getDisplayLayout(adjacentDisplayId) ?: return false
+
+ val currentBounds = currentDisplayLayout.globalBoundsDp()
+ val adjacentBounds = adjacentDisplayLayout.globalBoundsDp()
+ return if (position == POSITION_TOP) {
+ // Horizontal border: Calculate horizontal overlap and check inputX
+ val overlapStart = max(currentBounds.left, adjacentBounds.left)
+ val overlapEnd = min(currentBounds.right, adjacentBounds.right)
+ currentDisplayLayout.pxToDp(inputX) in overlapStart..overlapEnd
+ } else {
+ // Vertical border (must be LEFT or RIGHT): Calculate vertical overlap and check inputY
+ val overlapStart = max(currentBounds.top, adjacentBounds.top)
+ val overlapEnd = min(currentBounds.bottom, adjacentBounds.bottom)
+ currentDisplayLayout.pxToDp(inputY) in overlapStart..overlapEnd
+ }
+ }
+
+ private fun didBoundsChangeSignificantly(currentBounds: Rect) =
+ abs(currentBounds.left - previousBounds.left) > BOUNDS_CHANGE_THRESHOLD_PX ||
+ abs(currentBounds.top - previousBounds.top) > BOUNDS_CHANGE_THRESHOLD_PX ||
+ abs(currentBounds.right - previousBounds.right) > BOUNDS_CHANGE_THRESHOLD_PX ||
+ abs(currentBounds.bottom - previousBounds.bottom) > BOUNDS_CHANGE_THRESHOLD_PX
+
+ companion object {
+ private const val DELAY_MILLIS: Long = 800L
+ private const val BOUNDS_CHANGE_THRESHOLD_PX = 5
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/VisualIndicatorViewContainer.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/VisualIndicatorViewContainer.kt
index 5e4122b..6e3a642 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/VisualIndicatorViewContainer.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/VisualIndicatorViewContainer.kt
@@ -34,9 +34,9 @@
import android.view.WindowlessWindowManager
import android.view.animation.DecelerateInterpolator
import android.widget.FrameLayout
+import android.window.DesktopExperienceFlags
import androidx.core.animation.doOnEnd
import com.android.internal.annotations.VisibleForTesting
-import com.android.window.flags.Flags
import com.android.wm.shell.R
import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.common.DisplayLayout
@@ -92,7 +92,7 @@
val metrics = resources.displayMetrics
val screenWidth: Int
val screenHeight: Int
- if (Flags.enableBugFixesForSecondaryDisplay()) {
+ if (DesktopExperienceFlags.ENABLE_BUG_FIXES_FOR_SECONDARY_DISPLAY.isTrue) {
screenWidth = layout.width()
screenHeight = layout.height()
} else {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayModeController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/desktopfirst/DesktopDisplayModeController.kt
similarity index 87%
rename from libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayModeController.kt
rename to libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/desktopfirst/DesktopDisplayModeController.kt
index ddcb12e..de66791 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayModeController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/desktopfirst/DesktopDisplayModeController.kt
@@ -14,10 +14,9 @@
* limitations under the License.
*/
-package com.android.wm.shell.desktopmode
+package com.android.wm.shell.desktopmode.desktopfirst
import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD
-import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN
import android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED
import android.app.WindowConfiguration.windowingModeToString
@@ -103,7 +102,7 @@
if (!desktopModeSupported) return
// An external display should always be a freeform display when desktop mode is enabled.
- updateDisplayWindowingMode(displayId, WINDOWING_MODE_FREEFORM)
+ updateDisplayWindowingMode(displayId, DESKTOP_FIRST_DISPLAY_WINDOWING_MODE)
}
fun updateDefaultDisplayWindowingMode() {
@@ -143,13 +142,25 @@
.getRunningTasks(displayId)
.filter { it.activityType == ACTIVITY_TYPE_STANDARD }
.forEach {
- // TODO: b/391965153 - Reconsider the logic under multi-desk window hierarchy
- when (it.windowingMode) {
- currentDisplayWindowingMode -> {
- wct.setWindowingMode(it.token, currentDisplayWindowingMode)
+ if (DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) {
+ // With multi-desks, display windowing mode doesn't affect the windowing
+ // mode of freeform tasks but fullscreen tasks which are the direct children
+ // of TDA.
+ if (it.windowingMode == WINDOWING_MODE_FULLSCREEN) {
+ if (targetDisplayWindowingMode == DESKTOP_FIRST_DISPLAY_WINDOWING_MODE) {
+ wct.setWindowingMode(it.token, WINDOWING_MODE_FULLSCREEN)
+ } else {
+ wct.setWindowingMode(it.token, WINDOWING_MODE_UNDEFINED)
+ }
}
- targetDisplayWindowingMode -> {
- wct.setWindowingMode(it.token, WINDOWING_MODE_UNDEFINED)
+ } else {
+ when (it.windowingMode) {
+ currentDisplayWindowingMode -> {
+ wct.setWindowingMode(it.token, currentDisplayWindowingMode)
+ }
+ targetDisplayWindowingMode -> {
+ wct.setWindowingMode(it.token, WINDOWING_MODE_UNDEFINED)
+ }
}
}
}
@@ -162,8 +173,8 @@
transitions.startTransition(TRANSIT_CHANGE, wct, /* handler= */ null)
}
- // Do not directly use this method to check the state of desktop-first mode. Check the display
- // windowing mode instead.
+ // Do not directly use this method to check the state of desktop-first mode. Use
+ // [isDisplayDesktopFirst] instead.
private fun canDesktopFirstModeBeEnabledOnDefaultDisplay(): Boolean {
if (FORCE_DESKTOP_FIRST_ON_DEFAULT_DISPLAY) {
logW(
@@ -207,14 +218,16 @@
return false
}
+ // Do not directly use this method to check the state of desktop-first mode. Use
+ // [isDisplayDesktopFirst] instead.
@VisibleForTesting
fun getTargetWindowingModeForDefaultDisplay(): Int {
if (canDesktopFirstModeBeEnabledOnDefaultDisplay()) {
- return WINDOWING_MODE_FREEFORM
+ return DESKTOP_FIRST_DISPLAY_WINDOWING_MODE
}
return if (DesktopExperienceFlags.FORM_FACTOR_BASED_DESKTOP_FIRST_SWITCH.isTrue) {
- WINDOWING_MODE_FULLSCREEN
+ TOUCH_FIRST_DISPLAY_WINDOWING_MODE
} else {
// If form factor-based desktop first switch is disabled, use the default display
// windowing mode here to keep the freeform mode for some form factors (e.g.,
@@ -296,13 +309,8 @@
pw.println("Current Desktop Display Modes:")
pw.increaseIndent()
rootTaskDisplayAreaOrganizer.displayIds.forEach { displayId ->
- val desktopFirstEnabled =
- rootTaskDisplayAreaOrganizer
- .getDisplayAreaInfo(displayId)
- ?.configuration
- ?.windowConfiguration
- ?.windowingMode == WINDOWING_MODE_FREEFORM ?: false
- pw.println("Display#$displayId desktopFirstEnabled=$desktopFirstEnabled")
+ val isDesktopFirst = rootTaskDisplayAreaOrganizer.isDisplayDesktopFirst(displayId)
+ pw.println("Display#$displayId isDesktopFirst=$isDesktopFirst")
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/desktopfirst/DesktopFirstUtils.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/desktopfirst/DesktopFirstUtils.kt
new file mode 100644
index 0000000..8399789
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/desktopfirst/DesktopFirstUtils.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.desktopmode.desktopfirst
+
+import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
+import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN
+import com.android.internal.protolog.ProtoLog
+import com.android.wm.shell.RootTaskDisplayAreaOrganizer
+import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
+
+/** The display windowing mode for desktop-first display. */
+const val DESKTOP_FIRST_DISPLAY_WINDOWING_MODE = WINDOWING_MODE_FREEFORM
+
+/** The display windowing mode for touch-first display. */
+const val TOUCH_FIRST_DISPLAY_WINDOWING_MODE = WINDOWING_MODE_FULLSCREEN
+
+/** Returns true if a display is desktop-first. */
+fun RootTaskDisplayAreaOrganizer.isDisplayDesktopFirst(displayId: Int) =
+ getDisplayAreaInfo(displayId)?.configuration?.windowConfiguration?.windowingMode?.let {
+ it == DESKTOP_FIRST_DISPLAY_WINDOWING_MODE
+ }
+ ?: run {
+ ProtoLog.w(
+ WM_SHELL_DESKTOP_MODE,
+ "isDisplayDesktopFirst display=%d not found",
+ displayId,
+ )
+ false
+ }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/DesksOrganizer.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/DesksOrganizer.kt
index 3d9d31d..22d3b58 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/DesksOrganizer.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/DesksOrganizer.kt
@@ -21,9 +21,19 @@
/** An organizer of desk containers in which to host child desktop windows. */
interface DesksOrganizer {
+ /** Creates a new desk for the given user if none exist. */
+ fun warmUpDefaultDesk(displayId: Int, userId: Int)
+
/** Creates a new desk container to use in the given display for the given user. */
fun createDesk(displayId: Int, userId: Int, callback: OnCreateCallback)
+ /**
+ * Creates and returns the id of a new desk container to use in the given display for the given
+ * user if it can be created synchronously, or null if it cannot.
+ */
+ @Deprecated("Use createDesk() instead.", ReplaceWith("createDesk()"))
+ fun createDeskImmediate(displayId: Int, userId: Int): Int?
+
/** Activates the given desk, making it visible in its display. */
fun activateDesk(wct: WindowContainerTransaction, deskId: Int)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/RootTaskDesksOrganizer.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/RootTaskDesksOrganizer.kt
index cc90ec0..3329df1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/RootTaskDesksOrganizer.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/RootTaskDesksOrganizer.kt
@@ -73,26 +73,70 @@
}
}
+ override fun warmUpDefaultDesk(displayId: Int, userId: Int) {
+ logV("warmUpDefaultDesk in displayId=%d userId=%d", displayId, userId)
+ // Check if a desk in this display is already created.
+ deskRootsByDeskId.forEach { deskId, root ->
+ if (root.taskInfo.displayId == displayId && deskId !in removeDeskRootRequests) {
+ // A desk already exists.
+ return
+ }
+ }
+ val requestInProgress =
+ createDeskRootRequests.any { request -> request.displayId == displayId }
+ if (requestInProgress) {
+ // There isn't one ready yet, but a request for one is already in progress.
+ return
+ }
+ // Request a new one, but do not associate to the user.
+ createDeskRoot(displayId, userId = null) { deskId ->
+ logV("warmUpDefaultDesk created new desk root: %d", deskId)
+ }
+ }
+
override fun createDesk(displayId: Int, userId: Int, callback: OnCreateCallback) {
logV("createDesk in displayId=%d userId=%s", displayId, userId)
// Find an existing desk that is not yet used by this user.
- val unassignedDesk =
- deskRootsByDeskId
- .valueIterator()
- .asSequence()
- .filterNot { desk -> userId in desk.users }
- .filterNot { desk -> desk.deskId in removeDeskRootRequests }
- .filter { desk -> desk.taskInfo.displayId == displayId }
- .firstOrNull()
+ val unassignedDesk = firstUnassignedDesk(displayId, userId)
if (unassignedDesk != null) {
unassignedDesk.users.add(userId)
callback.onCreated(unassignedDesk.deskId)
return
}
+ // When there is an in-progress request without a user (as would be the case for a warm up
+ // request), use that for this create request instead of creating another root.
+ val unassignedRequest = createDeskRootRequests.firstOrNull { it.userId == null }
+ if (unassignedRequest != null) {
+ createDeskRootRequests.remove(unassignedRequest)
+ createDeskRootRequests += unassignedRequest.copy(userId = userId)
+ return
+ }
+ // Must request a new root.
createDeskRoot(displayId, userId, callback)
}
- private fun createDeskRoot(displayId: Int, userId: Int, callback: OnCreateCallback) {
+ @Deprecated("Use createDesk() instead.", replaceWith = ReplaceWith("createDesk()"))
+ override fun createDeskImmediate(displayId: Int, userId: Int): Int? {
+ logV("createDeskImmediate in displayId=%d userId=%s", displayId, userId)
+ // Find an existing desk that is not yet used by this user.
+ val unassignedDesk = firstUnassignedDesk(displayId, userId)
+ if (unassignedDesk != null) {
+ unassignedDesk.users.add(userId)
+ return unassignedDesk.deskId
+ }
+ return null
+ }
+
+ private fun firstUnassignedDesk(displayId: Int, userId: Int): DeskRoot? {
+ return deskRootsByDeskId
+ .valueIterator()
+ .asSequence()
+ .filterNot { desk -> userId in desk.users }
+ .filterNot { desk -> desk.deskId in removeDeskRootRequests }
+ .firstOrNull { desk -> desk.taskInfo.displayId == displayId }
+ }
+
+ private fun createDeskRoot(displayId: Int, userId: Int?, callback: OnCreateCallback) {
logV("createDeskRoot in display: %d for user: %d", displayId, userId)
createDeskRootRequests += CreateDeskRequest(displayId, userId, callback)
shellTaskOrganizer.createRootTask(
@@ -334,7 +378,12 @@
deskId = deskId,
taskInfo = taskInfo,
leash = leash,
- users = mutableSetOf(deskRequest.userId),
+ users =
+ if (deskRequest.userId != null) {
+ mutableSetOf(deskRequest.userId)
+ } else {
+ mutableSetOf()
+ },
)
createDeskRootRequests.remove(deskRequest)
deskRequest.onCreateCallback.onCreated(deskId)
@@ -512,7 +561,7 @@
private data class CreateDeskRequest(
val displayId: Int,
- val userId: Int,
+ val userId: Int?,
val onCreateCallback: OnCreateCallback,
)
@@ -542,6 +591,7 @@
)
pw.println("${innerPrefix}createDeskRootRequests=$createDeskRootRequests")
pw.println("${innerPrefix}removeDeskRootRequests=$removeDeskRootRequests")
+ pw.println("${innerPrefix}numOfDeskRoots=${deskRootsByDeskId.size()}")
pw.println("${innerPrefix}Desk Roots:")
deskRootsByDeskId.forEach { deskId, root ->
val minimizationRoot = deskMinimizationRootsByDeskId[deskId]
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
index a67557b..a15a201 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
@@ -69,20 +69,20 @@
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.shared.annotations.ExternalMainThread;
-import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
+import com.android.wm.shell.shared.desktopmode.DesktopState;
import com.android.wm.shell.splitscreen.SplitScreenController;
import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellController;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.Transitions;
+import dagger.Lazy;
+
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.function.Consumer;
import java.util.function.Function;
-import dagger.Lazy;
-
/**
* Handles the global drag and drop handling for the Shell.
*/
@@ -103,6 +103,7 @@
private final IconProvider mIconProvider;
private final GlobalDragListener mGlobalDragListener;
private final Transitions mTransitions;
+ private final DesktopState mDesktopState;
private SplitScreenController mSplitScreen;
private Lazy<BubbleBarDragListener> mBubbleBarDragController;
private ShellExecutor mMainExecutor;
@@ -148,7 +149,8 @@
GlobalDragListener globalDragListener,
Transitions transitions,
Lazy<BubbleBarDragListener> bubbleBarDragController,
- ShellExecutor mainExecutor) {
+ ShellExecutor mainExecutor,
+ DesktopState desktopState) {
mContext = context;
mShellController = shellController;
mShellCommandHandler = shellCommandHandler;
@@ -160,6 +162,7 @@
mTransitions = transitions;
mBubbleBarDragController = bubbleBarDragController;
mMainExecutor = mainExecutor;
+ mDesktopState = desktopState;
shellInit.addInitCallback(this::onInit, this);
}
@@ -347,7 +350,7 @@
final ActivityManager.RunningTaskInfo taskInfo = dragSession.runningTaskInfo;
// Desktop tasks will have their own drag handling.
final boolean isDesktopDrag = taskInfo != null && taskInfo.isFreeform()
- && DesktopModeStatus.canEnterDesktopMode(mContext);
+ && mDesktopState.canEnterDesktopMode();
pd.isHandlingDrag = DragUtils.canHandleDrag(event) && !isDesktopDrag;
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP,
"Clip description: handlingDrag=%b itemCount=%d mimeTypes=%s flags=%s",
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java
index d608413..c9671e6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java
@@ -663,9 +663,11 @@
mSession = null;
}
});
- // notify bubbles of drag cancel
- mCurrentBubbleBarTarget = null;
- mBubbleBarDragListener.onItemDraggedOutsideBubbleBarDropZone();
+ if (mCurrentBubbleBarTarget != null) {
+ // bubble bar is still showing drop target, notify bubbles of drag cancel
+ mCurrentBubbleBarTarget = null;
+ mBubbleBarDragListener.onItemDraggedOutsideBubbleBarDropZone();
+ }
// Reset the state if we previously force-ignore the bottom margin
mDropZoneView1.setForceIgnoreBottomMargin(false);
mDropZoneView2.setForceIgnoreBottomMargin(false);
@@ -693,6 +695,7 @@
mBubbleBarDragListener.onItemDroppedOverBubbleBarDragZone(mCurrentBubbleBarTarget,
appData);
}
+ mCurrentBubbleBarTarget = null;
// Start animating the drop UI out with the drag surface
hide(event, dropCompleteCallback);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformComponents.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformComponents.java
index 24b74c6..e83b713 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformComponents.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformComponents.java
@@ -16,14 +16,8 @@
package com.android.wm.shell.freeform;
-import static android.content.pm.PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT;
-import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT;
-
-import android.content.Context;
-import android.provider.Settings;
-
import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
+import com.android.wm.shell.shared.desktopmode.DesktopState;
import com.android.wm.shell.transition.Transitions;
import java.util.Optional;
@@ -53,19 +47,10 @@
}
/**
- * Returns if this device supports freeform.
- */
- public static boolean isFreeformEnabled(Context context) {
- return context.getPackageManager().hasSystemFeature(FEATURE_FREEFORM_WINDOW_MANAGEMENT)
- || Settings.Global.getInt(context.getContentResolver(),
- DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT, 0) != 0;
- }
-
- /**
* Freeform is enabled or we need the components to enable the app handle when desktop mode is
* not enabled
*/
- public static boolean requiresFreeformComponents(Context context) {
- return isFreeformEnabled(context) || DesktopModeStatus.overridesShowAppHandle(context);
+ public static boolean requiresFreeformComponents(DesktopState desktopState) {
+ return desktopState.isFreeformEnabled() || desktopState.overridesShowAppHandle();
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
index bca0166..d4b57a1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
@@ -35,7 +35,7 @@
import com.android.wm.shell.desktopmode.DesktopTasksController;
import com.android.wm.shell.desktopmode.DesktopUserRepositories;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
-import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
+import com.android.wm.shell.shared.desktopmode.DesktopState;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.windowdecor.WindowDecorViewModel;
@@ -58,6 +58,7 @@
private final WindowDecorViewModel mWindowDecorationViewModel;
private final LaunchAdjacentController mLaunchAdjacentController;
private final Optional<TaskChangeListener> mTaskChangeListener;
+ private final DesktopState mDesktopState;
private final SparseArray<State> mTasks = new SparseArray<>();
@@ -70,7 +71,8 @@
DesktopModeLoggerTransitionObserver desktopModeLoggerTransitionObserver,
LaunchAdjacentController launchAdjacentController,
WindowDecorViewModel windowDecorationViewModel,
- Optional<TaskChangeListener> taskChangeListener) {
+ Optional<TaskChangeListener> taskChangeListener,
+ DesktopState desktopState) {
mContext = context;
mShellTaskOrganizer = shellTaskOrganizer;
mWindowDecorationViewModel = windowDecorationViewModel;
@@ -79,14 +81,15 @@
mDesktopModeLoggerTransitionObserver = desktopModeLoggerTransitionObserver;
mLaunchAdjacentController = launchAdjacentController;
mTaskChangeListener = taskChangeListener;
- if (shellInit != null) {
+ mDesktopState = desktopState;
+ if (FreeformComponents.requiresFreeformComponents(desktopState)) {
shellInit.addInitCallback(this::onInit, this);
}
}
private void onInit() {
mShellTaskOrganizer.addListenerForType(this, TASK_LISTENER_TYPE_FREEFORM);
- if (DesktopModeStatus.canEnterDesktopMode(mContext)) {
+ if (mDesktopState.canEnterDesktopMode()) {
mShellTaskOrganizer.addFocusListener(this);
}
}
@@ -104,7 +107,7 @@
mTasks.put(taskInfo.taskId, state);
if (!DesktopModeFlags.ENABLE_WINDOWING_TRANSITION_HANDLERS_OBSERVERS.isTrue() &&
- DesktopModeStatus.canEnterDesktopMode(mContext)) {
+ mDesktopState.canEnterDesktopMode()) {
mDesktopUserRepositories.ifPresent(userRepositories -> {
DesktopRepository currentRepo = userRepositories.getProfile(taskInfo.userId);
currentRepo.addTask(taskInfo.displayId, taskInfo.taskId, taskInfo.isVisible);
@@ -120,7 +123,7 @@
mTasks.remove(taskInfo.taskId);
if (!DesktopModeFlags.ENABLE_WINDOWING_TRANSITION_HANDLERS_OBSERVERS.isTrue() &&
- DesktopModeStatus.canEnterDesktopMode(mContext)
+ mDesktopState.canEnterDesktopMode()
&& mDesktopUserRepositories.isPresent()) {
DesktopRepository repository =
mDesktopUserRepositories.get().getProfile(taskInfo.userId);
@@ -155,7 +158,7 @@
mDesktopTasksController.ifPresent(c -> c.onTaskInfoChanged(taskInfo));
mWindowDecorationViewModel.onTaskInfoChanged(taskInfo);
state.mTaskInfo = taskInfo;
- if (DesktopModeStatus.canEnterDesktopMode(mContext)) {
+ if (mDesktopState.canEnterDesktopMode()) {
if (DesktopModeFlags.ENABLE_WINDOWING_TRANSITION_HANDLERS_OBSERVERS.isTrue()) {
// Pass task info changes to the [TaskChangeListener] since [TransitionsObserver]
// does not propagate all task info changes.
@@ -195,7 +198,7 @@
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG,
"Freeform Task Focus Changed: #%d focused=%b",
taskInfo.taskId, taskInfo.isFocused);
- if (DesktopModeStatus.canEnterDesktopMode(mContext) && taskInfo.isFocused
+ if (mDesktopState.canEnterDesktopMode() && taskInfo.isFocused
&& mDesktopUserRepositories.isPresent()) {
DesktopRepository repository =
mDesktopUserRepositories.get().getProfile(taskInfo.userId);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java
index 2476ee1..3a426ec 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java
@@ -16,8 +16,9 @@
package com.android.wm.shell.freeform;
+import static com.android.wm.shell.transition.Transitions.TRANSIT_START_RECENTS_TRANSITION;
+
import android.app.ActivityManager;
-import android.content.Context;
import android.os.IBinder;
import android.view.SurfaceControl;
import android.view.WindowManager;
@@ -28,8 +29,10 @@
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
+import com.android.wm.shell.desktopmode.DesktopImeHandler;
import com.android.wm.shell.desktopmode.DesktopImmersiveController;
import com.android.wm.shell.desktopmode.multidesks.DesksTransitionObserver;
+import com.android.wm.shell.shared.desktopmode.DesktopState;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.FocusTransitionObserver;
import com.android.wm.shell.transition.Transitions;
@@ -54,26 +57,32 @@
private final Optional<TaskChangeListener> mTaskChangeListener;
private final FocusTransitionObserver mFocusTransitionObserver;
private final Optional<DesksTransitionObserver> mDesksTransitionObserver;
+ private final Optional<DesktopImeHandler> mDesktopImeHandler;
private final Map<IBinder, List<ActivityManager.RunningTaskInfo>> mTransitionToTaskInfo =
new HashMap<>();
+ private final Map<Integer, ActivityManager.RunningTaskInfo> mPendingHiddenTasks =
+ new HashMap<>();
+ private IBinder mTransientTransition;
public FreeformTaskTransitionObserver(
- Context context,
ShellInit shellInit,
Transitions transitions,
Optional<DesktopImmersiveController> desktopImmersiveController,
WindowDecorViewModel windowDecorViewModel,
Optional<TaskChangeListener> taskChangeListener,
FocusTransitionObserver focusTransitionObserver,
- Optional<DesksTransitionObserver> desksTransitionObserver) {
+ Optional<DesksTransitionObserver> desksTransitionObserver,
+ DesktopState desktopState,
+ Optional<DesktopImeHandler> desktopImeHandler) {
mTransitions = transitions;
mDesktopImmersiveController = desktopImmersiveController;
mWindowDecorViewModel = windowDecorViewModel;
mTaskChangeListener = taskChangeListener;
mFocusTransitionObserver = focusTransitionObserver;
mDesksTransitionObserver = desksTransitionObserver;
- if (FreeformComponents.requiresFreeformComponents(context)) {
+ mDesktopImeHandler = desktopImeHandler;
+ if (FreeformComponents.requiresFreeformComponents(desktopState)) {
shellInit.addInitCallback(this::onInit, this);
}
}
@@ -104,6 +113,9 @@
// TODO(371503964): Remove this once the unified task repository is ready.
mFocusTransitionObserver.updateFocusState(info);
+ // Call after the focus state update to have the correct focused window.
+ mDesktopImeHandler.ifPresent(o -> o.onTransitionReady(transition, info));
+
final ArrayList<ActivityManager.RunningTaskInfo> taskInfoList = new ArrayList<>();
final ArrayList<WindowContainerToken> taskParents = new ArrayList<>();
for (TransitionInfo.Change change : info.getChanges()) {
@@ -135,9 +147,13 @@
case WindowManager.TRANSIT_TO_FRONT:
onToFrontTransitionReady(change, startT, finishT);
break;
- case WindowManager.TRANSIT_TO_BACK:
+ case WindowManager.TRANSIT_TO_BACK: {
+ if (info.getType() == TRANSIT_START_RECENTS_TRANSITION) {
+ mTransientTransition = transition;
+ }
onToBackTransitionReady(change, startT, finishT);
break;
+ }
case WindowManager.TRANSIT_CLOSE: {
taskInfoList.add(change.getTaskInfo());
onCloseTransitionReady(change, startT, finishT);
@@ -158,6 +174,7 @@
mTaskChangeListener.ifPresent(listener -> listener.onTaskOpening(change.getTaskInfo()));
mWindowDecorViewModel.onTaskOpening(
change.getTaskInfo(), change.getLeash(), startT, finishT);
+ mPendingHiddenTasks.remove(change.getTaskInfo().taskId);
}
private void onCloseTransitionReady(
@@ -175,6 +192,7 @@
mTaskChangeListener.ifPresent(listener -> listener.onTaskChanging(change.getTaskInfo()));
mWindowDecorViewModel.onTaskChanging(
change.getTaskInfo(), change.getLeash(), startT, finishT);
+ mPendingHiddenTasks.remove(change.getTaskInfo().taskId);
}
private void onToFrontTransitionReady(
@@ -185,14 +203,20 @@
listener -> listener.onTaskMovingToFront(change.getTaskInfo()));
mWindowDecorViewModel.onTaskChanging(
change.getTaskInfo(), change.getLeash(), startT, finishT);
+ mPendingHiddenTasks.remove(change.getTaskInfo().taskId);
}
private void onToBackTransitionReady(
TransitionInfo.Change change,
SurfaceControl.Transaction startT,
SurfaceControl.Transaction finishT) {
- mTaskChangeListener.ifPresent(
- listener -> listener.onTaskMovingToBack(change.getTaskInfo()));
+ if (mTransientTransition != null) {
+ // The tasks will be transiently hidden, which means they are still visible.
+ mPendingHiddenTasks.put(change.getTaskInfo().taskId, change.getTaskInfo());
+ } else {
+ mTaskChangeListener.ifPresent(
+ listener -> listener.onTaskMovingToBack(change.getTaskInfo()));
+ }
mWindowDecorViewModel.onTaskChanging(
change.getTaskInfo(), change.getLeash(), startT, finishT);
}
@@ -245,5 +269,13 @@
for (int i = 0; i < taskInfo.size(); ++i) {
mWindowDecorViewModel.destroyWindowDecoration(taskInfo.get(i));
}
+
+ if (transition == mTransientTransition) {
+ for (ActivityManager.RunningTaskInfo task : mPendingHiddenTasks.values()) {
+ mTaskChangeListener.ifPresent(it -> it.onTaskMovingToBack(task));
+ }
+ mPendingHiddenTasks.clear();
+ mTransientTransition = null;
+ }
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java
index 955a981..912aa11 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java
@@ -22,12 +22,17 @@
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.RectF;
+import android.gui.BorderSettings;
+import android.gui.BoxShadowSettings;
import android.view.Choreographer;
import android.view.SurfaceControl;
import androidx.annotation.Nullable;
+import com.android.wm.shell.Flags;
import com.android.wm.shell.R;
+import com.android.wm.shell.common.BoxShadowHelper;
+import com.android.wm.shell.common.pip.PipUtils;
import com.android.wm.shell.transition.Transitions;
/**
@@ -44,8 +49,12 @@
private int mCornerRadius;
private int mShadowRadius;
+ private BoxShadowSettings mBoxShadowSettings;
+ private BorderSettings mBorderSettings;
+
public PipSurfaceTransactionHelper(Context context) {
onDensityOrFontScaleChanged(context);
+ onThemeChanged(context);
}
/**
@@ -322,10 +331,43 @@
*/
public PipSurfaceTransactionHelper shadow(SurfaceControl.Transaction tx, SurfaceControl leash,
boolean applyShadowRadius) {
- tx.setShadowRadius(leash, applyShadowRadius ? mShadowRadius : 0);
+ if (Flags.enablePipBoxShadows()) {
+ if (applyShadowRadius) {
+ tx.setBoxShadowSettings(leash, mBoxShadowSettings);
+ tx.setBorderSettings(leash, mBorderSettings);
+ } else {
+ tx.setBoxShadowSettings(leash, new BoxShadowSettings());
+ tx.setBorderSettings(leash, new BorderSettings());
+ }
+ } else {
+ tx.setShadowRadius(leash, applyShadowRadius ? mShadowRadius : 0);
+ }
return this;
}
+ /**
+ * Called when theme changes.
+ *
+ * @param context the current context
+ */
+ public void onThemeChanged(Context context) {
+ if (PipUtils.isDarkSystemTheme(context)) {
+ mBoxShadowSettings = BoxShadowHelper.getBoxShadowSettings(context,
+ new int[]{R.style.BoxShadowParamsPIPDark1,
+ R.style.BoxShadowParamsPIPDark2});
+ mBorderSettings = BoxShadowHelper.getBorderSettings(context,
+ R.style.BorderSettingsPIPDark);
+ } else {
+
+ mBoxShadowSettings = BoxShadowHelper.getBoxShadowSettings(context,
+ new int[]{R.style.BoxShadowParamsPIPLight1,
+ R.style.BoxShadowParamsPIPLight2});
+
+ mBorderSettings = BoxShadowHelper.getBorderSettings(context,
+ R.style.BorderSettingsPIPLight);
+ }
+ }
+
public interface SurfaceControlTransactionFactory {
SurfaceControl.Transaction getTransaction();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index 3ecabcf..07fba2f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -64,7 +64,7 @@
import android.view.Display;
import android.view.Surface;
import android.view.SurfaceControl;
-import android.window.DesktopModeFlags;
+import android.window.DesktopExperienceFlags;
import android.window.DisplayAreaInfo;
import android.window.TaskOrganizer;
import android.window.TaskSnapshot;
@@ -727,7 +727,7 @@
// TODO(b/377581840): Update this check to include non-minimized cases, e.g. split to PiP etc.
private boolean isPipExitingToDesktopMode() {
DesktopRepository currentRepo = getCurrentRepo();
- return DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PIP.isTrue() && currentRepo != null
+ return DesktopExperienceFlags.ENABLE_DESKTOP_WINDOWING_PIP.isTrue() && currentRepo != null
&& (currentRepo.isAnyDeskActive(mTaskInfo.displayId)
|| isDisplayInFreeform());
}
@@ -1884,7 +1884,7 @@
return;
}
final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction();
- tx.setShadowRadius(mLeash, 0f);
+ mSurfaceTransactionHelper.shadow(tx, mLeash, false /* applyShadowRadius */);
tx.apply();
}
@@ -1939,4 +1939,16 @@
public String toString() {
return TAG + ":" + taskListenerTypeToString(TASK_LISTENER_TYPE_PIP);
}
+
+ /**
+ * Called when the theme changes.
+ */
+ public void onThemeChanged(Context context) {
+ if (isInPip()) {
+ mSurfaceTransactionHelper.onThemeChanged(context);
+ SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction();
+ mSurfaceTransactionHelper.shadow(tx, mLeash, true /* applyShadowRadius */);
+ tx.apply();
+ }
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
index 129c923..aee0909 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
@@ -399,6 +399,12 @@
}
/**
+ * Callback when the transition is aborted.
+ */
+ public void onTransitionAborted() {
+ }
+
+ /**
* End the currently-playing PiP animation.
*
* @param onTransitionEnd callback to run upon finishing the playing transition.
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 ef638b4..afdfa30 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
@@ -60,6 +60,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.protolog.ProtoLog;
+import com.android.wm.shell.Flags;
import com.android.wm.shell.R;
import com.android.wm.shell.WindowManagerShellWrapper;
import com.android.wm.shell.common.DisplayChangeController;
@@ -772,6 +773,10 @@
mTouchHandler.onOverlayChanged();
onDisplayChanged(new DisplayLayout(mContext, mContext.getDisplay()),
false /* saveRestoreSnapFraction */);
+
+ if (Flags.enablePipBoxShadows()) {
+ mPipTaskOrganizer.onThemeChanged(mContext);
+ }
}
private void onDisplayChanged(DisplayLayout layout, boolean saveRestoreSnapFraction) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
index 5706f19..6a77ea1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
@@ -39,7 +39,7 @@
import android.view.InputMonitor;
import android.view.MotionEvent;
import android.view.ViewConfiguration;
-import android.window.DesktopModeFlags;
+import android.window.DesktopExperienceFlags;
import androidx.annotation.VisibleForTesting;
@@ -183,7 +183,7 @@
private void reloadResources() {
final Resources res = mContext.getResources();
mDelta = res.getDimensionPixelSize(R.dimen.pip_resize_edge_size);
- mEnableDragCornerResize = DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PIP.isTrue();
+ mEnableDragCornerResize = DesktopExperienceFlags.ENABLE_DESKTOP_WINDOWING_PIP.isTrue();
mTouchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java
index 0fa6a11..0962519 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java
@@ -343,7 +343,8 @@
final SurfaceControl.Transaction transaction = mTransactionFactory.getTransaction();
for (SurfaceControl leash : closeLeashes) {
- startTransaction.setShadowRadius(leash, 0f);
+ mSurfaceTransactionHelper.shadow(startTransaction, leash,
+ false /* applyShadowRadius */);
}
ValueAnimator closeFadeOutAnimator = createAnimator();
@@ -361,7 +362,8 @@
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
"%s: close animation: start", TAG);
for (SurfaceControl leash : closeLeashes) {
- startTransaction.setShadowRadius(leash, 0f);
+ mSurfaceTransactionHelper.shadow(startTransaction, leash,
+ false /* applyShadowRadius */);
}
startTransaction.apply();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/PipSurfaceTransactionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/PipSurfaceTransactionHelper.java
index e0cae81..3880d32 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/PipSurfaceTransactionHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/PipSurfaceTransactionHelper.java
@@ -41,6 +41,14 @@
}
/**
+ * Gets corner radius which is loaded from resources.
+ * @return the corner radius.
+ */
+ public int getCornerRadius() {
+ return mCornerRadius;
+ }
+
+ /**
* Operates the scale (setMatrix) on a given transaction and leash
* @return same {@link PipSurfaceTransactionHelper} instance for method chaining
*/
@@ -155,6 +163,30 @@
return this;
}
+
+ /**
+ * Sets PiP translational, scaling and rotational transformations on a given transaction.
+ *
+ * @param leash PiP leash to apply the transformations on
+ * @param outTransaction transaction to set the matrix on
+ * @param baseBounds base bounds from PipBoundsState
+ * @param toBounds bounds to position the PiP to
+ * @param degrees the angle to rotate the bounds to
+ */
+ public PipSurfaceTransactionHelper setPipTransformations(SurfaceControl leash,
+ SurfaceControl.Transaction outTransaction, Rect baseBounds, Rect toBounds,
+ float degrees) {
+ final float scale = (float) toBounds.width() / baseBounds.width();
+
+ mTmpTransform.setScale(scale, scale);
+ mTmpTransform.postTranslate(toBounds.left, toBounds.top);
+ mTmpTransform.postRotate(degrees, toBounds.centerX(), toBounds.centerY());
+
+ round(outTransaction, leash, baseBounds, toBounds);
+ outTransaction.setMatrix(leash, mTmpTransform, mTmpFloat9);
+ return this;
+ }
+
/**
* Interface to standardize {@link SurfaceControl.Transaction} generation across PiP.
*/
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipAlphaAnimator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipAlphaAnimator.java
index 7918a21..37a119329 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipAlphaAnimator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipAlphaAnimator.java
@@ -53,9 +53,6 @@
private final SurfaceControl.Transaction mFinishTransaction;
private final int mDirection;
- private final int mCornerRadius;
- private final int mShadowRadius;
-
private final Animator.AnimatorListener mAnimatorListener = new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
@@ -99,8 +96,10 @@
@NonNull private PipSurfaceTransactionHelper.SurfaceControlTransactionFactory
mSurfaceControlTransactionFactory;
+ @NonNull private final PipSurfaceTransactionHelper mSurfaceTransactionHelper;
public PipAlphaAnimator(Context context,
+ @NonNull PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
SurfaceControl leash,
SurfaceControl.Transaction startTransaction,
SurfaceControl.Transaction finishTransaction,
@@ -108,6 +107,7 @@
mLeash = leash;
mStartTransaction = startTransaction;
mFinishTransaction = finishTransaction;
+ mSurfaceTransactionHelper = pipSurfaceTransactionHelper;
mDirection = direction;
setFloatValues(getStartAlphaValue(), getEndAlphaValue());
@@ -115,8 +115,6 @@
new PipSurfaceTransactionHelper.VsyncSurfaceControlTransactionFactory();
final int enterAnimationDuration = context.getResources()
.getInteger(R.integer.config_pipEnterAnimationDuration);
- mCornerRadius = context.getResources().getDimensionPixelSize(R.dimen.pip_corner_radius);
- mShadowRadius = context.getResources().getDimensionPixelSize(R.dimen.pip_shadow_radius);
setDuration(enterAnimationDuration);
addListener(mAnimatorListener);
addUpdateListener(mAnimatorUpdateListener);
@@ -132,9 +130,9 @@
private void onAlphaAnimationUpdate(float alpha, SurfaceControl.Transaction tx) {
// only set shadow radius on fade in
- tx.setAlpha(mLeash, alpha)
- .setCornerRadius(mLeash, mCornerRadius)
- .setShadowRadius(mLeash, mDirection == FADE_IN ? mShadowRadius : 0f);
+ tx.setAlpha(mLeash, alpha);
+ mSurfaceTransactionHelper.round(tx, mLeash, true /* applyCornerRadius */);
+ mSurfaceTransactionHelper.shadow(tx, mLeash, mDirection == FADE_IN /* applyCornerRadius */);
tx.apply();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipEnterAnimator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipEnterAnimator.java
index 35c90ac..4532ad6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipEnterAnimator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipEnterAnimator.java
@@ -52,9 +52,6 @@
private final SurfaceControl.Transaction mStartTransaction;
private final SurfaceControl.Transaction mFinishTransaction;
- private final int mCornerRadius;
- private final int mShadowRadius;
-
// Bounds updated by the evaluator as animator is running.
private final Rect mAnimatedRect = new Rect();
@@ -113,7 +110,10 @@
}
};
+ private final @NonNull PipSurfaceTransactionHelper mSurfaceTransactionHelper;
+
public PipEnterAnimator(Context context,
+ @NonNull PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
@NonNull SurfaceControl leash,
SurfaceControl.Transaction startTransaction,
SurfaceControl.Transaction finishTransaction,
@@ -128,11 +128,10 @@
mSurfaceControlTransactionFactory =
new PipSurfaceTransactionHelper.VsyncSurfaceControlTransactionFactory();
mPipAppIconOverlaySupplier = this::getAppIconOverlay;
+ mSurfaceTransactionHelper = pipSurfaceTransactionHelper;
final int enterAnimationDuration = context.getResources()
.getInteger(R.integer.config_pipEnterAnimationDuration);
- mCornerRadius = context.getResources().getDimensionPixelSize(R.dimen.pip_corner_radius);
- mShadowRadius = context.getResources().getDimensionPixelSize(R.dimen.pip_shadow_radius);
setDuration(enterAnimationDuration);
setFloatValues(0f, 1f);
setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
@@ -182,7 +181,8 @@
mTransformTensor.postRotate(degrees);
tx.setMatrix(mLeash, mTransformTensor, mMatrixTmp);
- tx.setCornerRadius(mLeash, mCornerRadius).setShadowRadius(mLeash, mShadowRadius);
+ mSurfaceTransactionHelper.round(tx, mLeash, true /* applyCornerRadius */);
+ mSurfaceTransactionHelper.shadow(tx, mLeash, true /* applyShadow */);
if (mContentOverlay != null) {
mContentOverlay.onAnimationUpdate(tx, 1f / scaleX, fraction, mEndBounds);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipExpandAnimator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipExpandAnimator.java
index 7472221..a15982f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipExpandAnimator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipExpandAnimator.java
@@ -66,7 +66,7 @@
mSurfaceControlTransactionFactory;
private final RectEvaluator mRectEvaluator;
private final RectEvaluator mInsetEvaluator;
- private final PipSurfaceTransactionHelper mPipSurfaceTransactionHelper;
+ private @NonNull final PipSurfaceTransactionHelper mPipSurfaceTransactionHelper;
private final Animator.AnimatorListener mAnimatorListener = new AnimatorListenerAdapter() {
@Override
@@ -106,6 +106,7 @@
};
public PipExpandAnimator(Context context,
+ @NonNull PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
@NonNull SurfaceControl leash,
SurfaceControl.Transaction startTransaction,
SurfaceControl.Transaction finishTransaction,
@@ -124,7 +125,7 @@
mEndBounds.set(endBounds);
mRectEvaluator = new RectEvaluator(mAnimatedRect);
mInsetEvaluator = new RectEvaluator(new Rect());
- mPipSurfaceTransactionHelper = new PipSurfaceTransactionHelper(context);
+ mPipSurfaceTransactionHelper = pipSurfaceTransactionHelper;
mRotation = rotation;
mIsPipInDesktopMode = isPipInDesktopMode;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipResizeAnimator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipResizeAnimator.java
index eb5fe88..2762906 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipResizeAnimator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipResizeAnimator.java
@@ -29,7 +29,6 @@
import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.wm.shell.R;
import com.android.wm.shell.pip2.PipSurfaceTransactionHelper;
import com.android.wm.shell.shared.animation.Interpolators;
@@ -51,9 +50,6 @@
private Runnable mAnimationEndCallback;
private RectEvaluator mRectEvaluator;
- private final int mCornerRadius;
- private final int mShadowRadius;
-
// Bounds relative to which scaling/cropping must be done.
private final Rect mBaseBounds = new Rect();
@@ -70,6 +66,8 @@
private PipSurfaceTransactionHelper.SurfaceControlTransactionFactory
mSurfaceControlTransactionFactory;
+ private final @NonNull PipSurfaceTransactionHelper mSurfaceTransactionHelper;
+
private final Animator.AnimatorListener mAnimatorListener = new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
@@ -79,7 +77,7 @@
}
if (mStartTx != null) {
setBoundsAndRotation(mStartTx, mLeash, mBaseBounds, mStartBounds, mDelta,
- mCornerRadius, mShadowRadius);
+ mSurfaceTransactionHelper);
mStartTx.apply();
}
}
@@ -89,7 +87,7 @@
super.onAnimationEnd(animation);
if (mFinishTx != null) {
setBoundsAndRotation(mFinishTx, mLeash, mBaseBounds, mEndBounds, 0f,
- mCornerRadius, mShadowRadius);
+ mSurfaceTransactionHelper);
}
if (mAnimationEndCallback != null) {
mAnimationEndCallback.run();
@@ -106,12 +104,13 @@
final float fraction = getAnimatedFraction();
final float degrees = (1.0f - fraction) * mDelta;
setBoundsAndRotation(tx, mLeash, mBaseBounds, mAnimatedRect, degrees,
- mCornerRadius, mShadowRadius);
+ mSurfaceTransactionHelper);
tx.apply();
}
};
public PipResizeAnimator(@NonNull Context context,
+ @NonNull PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
@NonNull SurfaceControl leash,
@Nullable SurfaceControl.Transaction startTransaction,
@Nullable SurfaceControl.Transaction finishTransaction,
@@ -132,12 +131,10 @@
mAnimatedRect.set(startBounds);
mEndBounds.set(endBounds);
mDelta = delta;
+ mSurfaceTransactionHelper = pipSurfaceTransactionHelper;
mRectEvaluator = new RectEvaluator(mAnimatedRect);
- mCornerRadius = mContext.getResources().getDimensionPixelSize(R.dimen.pip_corner_radius);
- mShadowRadius = mContext.getResources().getDimensionPixelSize(R.dimen.pip_shadow_radius);
-
setObjectValues(startBounds, endBounds);
setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
addListener(mAnimatorListener);
@@ -162,7 +159,8 @@
* @param degrees degrees of rotation - counter-clockwise is positive by convention.
*/
private static void setBoundsAndRotation(SurfaceControl.Transaction tx, SurfaceControl leash,
- Rect baseBounds, Rect targetBounds, float degrees, int cornerRadius, int shadowRadius) {
+ Rect baseBounds, Rect targetBounds, float degrees,
+ @NonNull PipSurfaceTransactionHelper helper) {
Matrix transformTensor = new Matrix();
final float[] mMatrixTmp = new float[9];
final float scaleX = (float) targetBounds.width() / baseBounds.width();
@@ -173,8 +171,8 @@
transformTensor.postRotate(degrees, targetBounds.centerX(), targetBounds.centerY());
tx.setMatrix(leash, transformTensor, mMatrixTmp)
- .setCornerRadius(leash, cornerRadius / scaleX)
- .setShadowRadius(leash, shadowRadius);
+ .setCornerRadius(leash, helper.getCornerRadius() / scaleX);
+ helper.shadow(tx, leash, true /* applyShadowRadius */);
}
@VisibleForTesting
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PhonePipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PhonePipMenuController.java
index 671eae3..e617f35 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PhonePipMenuController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PhonePipMenuController.java
@@ -43,6 +43,7 @@
import com.android.wm.shell.common.pip.PipMediaController.ActionListener;
import com.android.wm.shell.common.pip.PipMenuController;
import com.android.wm.shell.common.pip.PipUiEventLogger;
+import com.android.wm.shell.common.pip.PipUtils;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import java.io.PrintWriter;
@@ -486,6 +487,12 @@
* @return the best set of actions to show in the PiP menu.
*/
private List<RemoteAction> resolveMenuActions() {
+ // If UMO Experience is enabled, default to MediaSession actions if present.
+ // Note that mMediaActions can be pulled asynchronously, we use the active media session
+ // check here instead.
+ if (PipUtils.isPipUmoExperienceEnabled() && mMediaController.hasActiveMediaSession()) {
+ return mMediaActions;
+ }
if (isValidActions(mAppActions)) {
return mAppActions;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java
index 0e974ef..a4c2273 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java
@@ -31,7 +31,6 @@
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Bundle;
-import android.os.Debug;
import android.util.Log;
import android.view.SurfaceControl;
import android.window.DesktopExperienceFlags;
@@ -63,9 +62,11 @@
import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
import com.android.wm.shell.common.pip.PipBoundsState;
import com.android.wm.shell.common.pip.PipDisplayLayoutState;
+import com.android.wm.shell.common.pip.PipMediaController;
import com.android.wm.shell.common.pip.PipUiEventLogger;
import com.android.wm.shell.common.pip.PipUtils;
import com.android.wm.shell.pip.Pip;
+import com.android.wm.shell.pip2.PipSurfaceTransactionHelper;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.sysui.ConfigurationChangeListener;
import com.android.wm.shell.sysui.ShellCommandHandler;
@@ -87,6 +88,8 @@
private static final String TAG = PipController.class.getSimpleName();
private static final String SWIPE_TO_PIP_APP_BOUNDS = "pip_app_bounds";
private static final String SWIPE_TO_PIP_OVERLAY = "swipe_to_pip_overlay";
+ private static final String DISPLAY_CHANGE_PIP_BOUNDS_UPDATE =
+ "display_change_pip_bounds_update";
private final Context mContext;
private final ShellCommandHandler mShellCommandHandler;
@@ -107,10 +110,15 @@
private final ShellExecutor mMainExecutor;
private final PipImpl mImpl;
private final List<Consumer<Boolean>> mOnIsInPipStateChangedListeners = new ArrayList<>();
+ private final PipMediaController mMediaController;
// Wrapper for making Binder calls into PiP animation listener hosted in launcher's Recents.
@Nullable private PipAnimationListener mPipRecentsAnimationListener;
+ private final PipSurfaceTransactionHelper mPipSurfaceTransactionHelper;
+
+ private boolean mWaitingToPlayDisplayChangeBoundsUpdate;
+
@VisibleForTesting
interface PipAnimationListener {
/**
@@ -150,6 +158,8 @@
PipAppOpsListener pipAppOpsListener,
PhonePipMenuController pipMenuController,
PipUiEventLogger pipUiEventLogger,
+ PipMediaController pipMediaController,
+ PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
ShellExecutor mainExecutor) {
mContext = context;
mShellCommandHandler = shellCommandHandler;
@@ -168,6 +178,8 @@
mPipAppOpsListener = pipAppOpsListener;
mPipMenuController = pipMenuController;
mPipUiEventLogger = pipUiEventLogger;
+ mMediaController = pipMediaController;
+ mPipSurfaceTransactionHelper = pipSurfaceTransactionHelper;
mMainExecutor = mainExecutor;
mImpl = new PipImpl();
@@ -196,6 +208,8 @@
PipAppOpsListener pipAppOpsListener,
PhonePipMenuController pipMenuController,
PipUiEventLogger pipUiEventLogger,
+ PipMediaController pipMediaController,
+ PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
ShellExecutor mainExecutor) {
if (!context.getPackageManager().hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)) {
ProtoLog.w(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
@@ -206,7 +220,8 @@
displayController, displayInsetsController, pipBoundsState, pipBoundsAlgorithm,
pipDisplayLayoutState, pipScheduler, taskStackListener, shellTaskOrganizer,
pipTransitionState, pipTouchHandler, pipAppOpsListener, pipMenuController,
- pipUiEventLogger, mainExecutor);
+ pipUiEventLogger, pipMediaController, pipSurfaceTransactionHelper,
+ mainExecutor);
}
public PipImpl getPipImpl() {
@@ -332,7 +347,6 @@
return;
}
final float snapFraction = mPipBoundsAlgorithm.getSnapFraction(mPipBoundsState.getBounds());
- final float boundsScale = mPipBoundsState.getBoundsScale();
// Update the display layout caches even if we are not in PiP.
setDisplayLayout(mDisplayController.getDisplayLayout(displayId));
@@ -342,8 +356,7 @@
mPipDisplayLayoutState.rotateTo(toRotation);
}
- if (!mPipTransitionState.isInPip()
- && mPipTransitionState.getState() != PipTransitionState.ENTERING_PIP) {
+ if (!shouldUpdatePipStateOnDisplayChange()) {
// Skip the PiP-relevant updates if we aren't in a valid PiP state.
if (mPipTransitionState.isInFixedRotation()) {
ProtoLog.e(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
@@ -360,6 +373,10 @@
mPipTouchHandler.updateMovementBounds();
mPipTransitionState.setInFixedRotation(false);
} else {
+ final float boundsScale = mPipBoundsState.getBoundsScale();
+ // Before calculating the PiP bounds, the PiP minimum and maximum sizes
+ // need to be recalculated for the current display.
+ mPipBoundsState.updateMinMaxSize(mPipBoundsState.getAspectRatio());
Rect toBounds = new Rect(0, 0,
(int) Math.ceil(mPipBoundsState.getMaxSize().x * boundsScale),
(int) Math.ceil(mPipBoundsState.getMaxSize().y * boundsScale));
@@ -370,11 +387,16 @@
// The policy is to keep PiP snap fraction invariant.
mPipBoundsAlgorithm.applySnapFraction(toBounds, snapFraction);
mPipBoundsState.setBounds(toBounds);
+ mPipTouchHandler.setUserResizeBounds(toBounds);
}
if (mPipTransitionState.getPipTaskToken() == null) {
- Log.wtf(TAG, "PipController.onDisplayChange no PiP task token"
- + " state=" + mPipTransitionState.getState()
- + " callers=\n" + Debug.getCallers(4, " "));
+ Log.d(TAG, "PipController.onDisplayChange no PiP task token"
+ + " state=" + mPipTransitionState.getState());
+ mPipTransitionState.setOnIdlePipTransitionStateRunnable(() -> {
+ final Bundle extra = new Bundle();
+ extra.putBoolean(DISPLAY_CHANGE_PIP_BOUNDS_UPDATE, true);
+ mPipTransitionState.setState(PipTransitionState.SCHEDULED_BOUNDS_CHANGE, extra);
+ });
} else {
t.setBounds(mPipTransitionState.getPipTaskToken(), mPipBoundsState.getBounds());
}
@@ -386,6 +408,14 @@
mPipDisplayLayoutState.setDisplayLayout(layout);
}
+ private boolean shouldUpdatePipStateOnDisplayChange() {
+ // We should at least update internal PiP state, such as PiP bounds state or movement bounds
+ // if we are either in PiP or about to enter PiP.
+ return mPipTransitionState.isInPip()
+ || mPipTransitionState.getState() == PipTransitionState.ENTERING_PIP
+ || mPipTransitionState.getState() == PipTransitionState.SCHEDULED_ENTER_PIP;
+ }
+
//
// IPip Binder stub helpers
//
@@ -491,6 +521,7 @@
if (taskInfo != null && taskInfo.topActivity != null) {
mPipAppOpsListener.onActivityPinned(taskInfo.topActivity.getPackageName());
mPipUiEventLogger.setTaskInfo(taskInfo);
+ mMediaController.onActivityPinned();
}
if (mPipTransitionState.isInSwipePipToHomeTransition()) {
mPipUiEventLogger.log(
@@ -510,9 +541,49 @@
listener.accept(false /* inPip */);
}
break;
+ case PipTransitionState.SCHEDULED_BOUNDS_CHANGE:
+ mWaitingToPlayDisplayChangeBoundsUpdate =
+ extra.getBoolean(DISPLAY_CHANGE_PIP_BOUNDS_UPDATE);
+ if (mWaitingToPlayDisplayChangeBoundsUpdate) {
+ // If we reach this point, it means display change did not send through a WCT to
+ // update the pinned task bounds in Core. Instead, the local Shell-side
+ // PiP-relevant bounds state and movement bounds were updated.
+ // So schedule a jumpcut animation to those bounds now.
+ mPipScheduler.scheduleAnimateResizePip(mPipBoundsState.getBounds());
+ }
+ break;
+ case PipTransitionState.CHANGING_PIP_BOUNDS:
+ if (!mWaitingToPlayDisplayChangeBoundsUpdate) {
+ break;
+ }
+ mWaitingToPlayDisplayChangeBoundsUpdate = false;
+ final SurfaceControl.Transaction startTx = extra.getParcelable(
+ PipTransition.PIP_START_TX, SurfaceControl.Transaction.class);
+ final SurfaceControl.Transaction finishTx = extra.getParcelable(
+ PipTransition.PIP_FINISH_TX, SurfaceControl.Transaction.class);
+ final Rect destinationBounds = extra.getParcelable(
+ PipTransition.PIP_DESTINATION_BOUNDS, Rect.class);
+ handleJumpcutBoundsUpdate(startTx, finishTx, destinationBounds);
+ break;
}
}
+ private void handleJumpcutBoundsUpdate(SurfaceControl.Transaction startTx,
+ SurfaceControl.Transaction finishTx, Rect destinationBounds) {
+ SurfaceControl pipLeash = mPipTransitionState.getPinnedTaskLeash();
+
+ startTx.merge(finishTx);
+ startTx.setPosition(pipLeash, destinationBounds.left, destinationBounds.top);
+ mPipSurfaceTransactionHelper.round(startTx, pipLeash, true /* applyCornerRadius */)
+ .shadow(startTx, pipLeash, true /* applyShadowRadius */);
+ mPipSurfaceTransactionHelper.round(finishTx, pipLeash, true /* applyCornerRadius */)
+ .shadow(finishTx, pipLeash, true /* applyShadowRadius */);
+ startTx.apply();
+
+ // Signal that the transition is done - should update transition state by default.
+ mPipScheduler.scheduleFinishPipBoundsChange(destinationBounds);
+ }
+
//
// IPipAnimationListener Binder proxy helpers
//
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipDisplayTransferHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipDisplayTransferHandler.java
index a977d6b..288aa22 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipDisplayTransferHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipDisplayTransferHandler.java
@@ -15,14 +15,25 @@
*/
package com.android.wm.shell.pip2.phone;
-import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.Context;
import android.graphics.Rect;
+import android.graphics.RectF;
import android.os.Bundle;
+import android.util.ArrayMap;
import android.view.SurfaceControl;
+import android.view.SurfaceControl.Transaction;
+import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
+import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
+import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.DisplayLayout;
+import com.android.wm.shell.common.MultiDisplayDragMoveBoundsCalculator;
+import com.android.wm.shell.common.pip.PipBoundsState;
+import com.android.wm.shell.pip2.PipSurfaceTransactionHelper;
+
/**
* Handler for moving PiP window to another display when the device is connected to external
* display(s) in extended mode.
@@ -34,15 +45,31 @@
static final String ORIGIN_DISPLAY_ID_KEY = "origin_display_id";
static final String TARGET_DISPLAY_ID_KEY = "target_display_id";
- @NonNull private final PipTransitionState mPipTransitionState;
- @NonNull private final PipScheduler mPipScheduler;
- @VisibleForTesting boolean mWaitingForDisplayTransfer;
+ private final PipBoundsState mPipBoundsState;
+ private PipSurfaceTransactionHelper.SurfaceControlTransactionFactory
+ mSurfaceControlTransactionFactory;
+ private final RootTaskDisplayAreaOrganizer mRootTaskDisplayAreaOrganizer;
+ private PipSurfaceTransactionHelper mPipSurfaceTransactionHelper;
+ private final DisplayController mDisplayController;
+ private final PipTransitionState mPipTransitionState;
+ private final PipScheduler mPipScheduler;
- public PipDisplayTransferHandler(PipTransitionState pipTransitionState,
- PipScheduler pipScheduler) {
+ @VisibleForTesting boolean mWaitingForDisplayTransfer;
+ @VisibleForTesting
+ ArrayMap<Integer, SurfaceControl> mOnDragMirrorPerDisplayId = new ArrayMap<>();
+
+ public PipDisplayTransferHandler(Context context, PipTransitionState pipTransitionState,
+ PipScheduler pipScheduler, RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
+ PipBoundsState pipBoundsState, DisplayController displayController) {
mPipTransitionState = pipTransitionState;
- mPipTransitionState.addPipTransitionStateChangedListener(this::onPipTransitionStateChanged);
+ mPipTransitionState.addPipTransitionStateChangedListener(this);
mPipScheduler = pipScheduler;
+ mSurfaceControlTransactionFactory =
+ new PipSurfaceTransactionHelper.VsyncSurfaceControlTransactionFactory();
+ mRootTaskDisplayAreaOrganizer = rootTaskDisplayAreaOrganizer;
+ mPipSurfaceTransactionHelper = new PipSurfaceTransactionHelper(context);
+ mPipBoundsState = pipBoundsState;
+ mDisplayController = displayController;
}
void scheduleMovePipToDisplay(int originDisplayId, int targetDisplayId) {
@@ -72,8 +99,8 @@
break;
}
- final SurfaceControl.Transaction startTx = extra.getParcelable(
- PipTransition.PIP_START_TX, SurfaceControl.Transaction.class);
+ final Transaction startTx = extra.getParcelable(
+ PipTransition.PIP_START_TX, Transaction.class);
final Rect destinationBounds = extra.getParcelable(
PipTransition.PIP_DESTINATION_BOUNDS, Rect.class);
@@ -81,11 +108,78 @@
}
}
- private void startMoveToDisplayAnimation(SurfaceControl.Transaction startTx,
- Rect destinationBounds) {
+ private void startMoveToDisplayAnimation(Transaction startTx, Rect destinationBounds) {
if (startTx == null) return;
startTx.apply();
mPipScheduler.scheduleFinishPipBoundsChange(destinationBounds);
}
+
+ /**
+ * Show a drag indicator mirror on each connected display according to the current pointer
+ * position.
+ *
+ * @param originDisplayId the display ID where the drag originated from
+ * @param globalDpPipBounds the PiP bounds in display topology-aware global DP
+ */
+ public void showDragMirrorOnConnectedDisplays(int originDisplayId, RectF globalDpPipBounds) {
+ final Transaction transaction = mSurfaceControlTransactionFactory.getTransaction();
+ // Iterate through each connected display ID to ensure partial PiP bounds are shown on
+ // all corresponding displays while dragging
+ for (int displayId : mRootTaskDisplayAreaOrganizer.getDisplayIds()) {
+ if (displayId == originDisplayId) continue;
+
+ DisplayLayout displayLayout = mDisplayController.getDisplayLayout(displayId);
+ if (displayLayout == null) continue;
+
+ // If PiP does not cross the boundaries of a given display bounds, skip
+ boolean shouldShowOnDisplay = RectF.intersects(globalDpPipBounds,
+ displayLayout.globalBoundsDp());
+ if (!shouldShowOnDisplay) continue;
+
+ // Create a mirror for the current display if it hasn't been created yet
+ SurfaceControl mirror;
+ if (!mOnDragMirrorPerDisplayId.containsKey(displayId)) {
+ mirror = SurfaceControl.mirrorSurface(mPipTransitionState.getPinnedTaskLeash());
+ mOnDragMirrorPerDisplayId.put(displayId, mirror);
+ } else {
+ mirror = mOnDragMirrorPerDisplayId.get(displayId);
+ }
+
+ // Convert the PiP bounds in dp to px based on the current display layout
+ final Rect boundsOnCurrentDisplay =
+ MultiDisplayDragMoveBoundsCalculator.convertGlobalDpToLocalPxForRect(
+ globalDpPipBounds, displayLayout);
+ mPipSurfaceTransactionHelper.setPipTransformations(mirror, transaction,
+ mPipBoundsState.getBounds(), boundsOnCurrentDisplay, /* degrees= */ 0);
+ mRootTaskDisplayAreaOrganizer.reparentToDisplayArea(displayId, mirror, transaction);
+ transaction.show(mirror);
+ }
+ transaction.apply();
+ }
+
+ /**
+ * Remove all drag indicator mirrors from each connected display.
+ */
+ // TODO(b/408981327): Remove mirrors on screen lock
+ // TODO(b/408982524): Remove mirrors on opening app while dragging
+ public void removeMirrors() {
+ final Transaction transaction = mSurfaceControlTransactionFactory.getTransaction();
+ for (SurfaceControl mirror : mOnDragMirrorPerDisplayId.values()) {
+ transaction.remove(mirror);
+ }
+ transaction.apply();
+ mOnDragMirrorPerDisplayId.clear();
+ }
+
+ @VisibleForTesting
+ void setSurfaceControlTransactionFactory(
+ @NonNull PipSurfaceTransactionHelper.SurfaceControlTransactionFactory factory) {
+ mSurfaceControlTransactionFactory = factory;
+ }
+
+ @VisibleForTesting
+ void setSurfaceTransactionHelper(PipSurfaceTransactionHelper surfaceTransactionHelper) {
+ mPipSurfaceTransactionHelper = surfaceTransactionHelper;
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMotionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMotionHelper.java
index d3774ba..3b666b8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMotionHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMotionHelper.java
@@ -47,6 +47,7 @@
import com.android.wm.shell.common.pip.PipPerfHintController;
import com.android.wm.shell.common.pip.PipSnapAlgorithm;
import com.android.wm.shell.common.pip.PipUiEventLogger;
+import com.android.wm.shell.pip2.PipSurfaceTransactionHelper;
import com.android.wm.shell.pip2.animation.PipResizeAnimator;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.shared.animation.PhysicsAnimator;
@@ -164,6 +165,8 @@
*/
private Runnable mPostPipTransitionCallback;
+ private final @NonNull PipSurfaceTransactionHelper mSurfaceTransactionHelper;
+
public PipMotionHelper(Context context, @NonNull PipBoundsState pipBoundsState,
PhonePipMenuController menuController, PipSnapAlgorithm snapAlgorithm,
FloatingContentCoordinator floatingContentCoordinator, PipScheduler pipScheduler,
@@ -185,6 +188,7 @@
mPipTransitionState = pipTransitionState;
mPipTransitionState.addPipTransitionStateChangedListener(this);
mPipUiEventLogger = pipUiEventLogger;
+ mSurfaceTransactionHelper = new PipSurfaceTransactionHelper(context);
}
void init() {
@@ -773,6 +777,7 @@
if (mWaitingForFlingTransition) {
mWaitingForFlingTransition = false;
handleFlingTransition(startTx, finishTx, destinationBounds);
+ settlePipBoundsAfterFling();
} else if (mWaitingToPlayBoundsChangeTransition) {
mWaitingToPlayBoundsChangeTransition = false;
startResizeAnimation(startTx, finishTx, destinationBounds, duration);
@@ -787,6 +792,7 @@
case PipTransitionState.CHANGED_PIP_BOUNDS:
// Check whether changed bounds imply we need to update stash state too.
stashEndActionIfNeeded();
+ settlePipBoundsAfterPhysicsAnimation(false /* animatingAfter */);
break;
}
}
@@ -794,15 +800,13 @@
private void handleFlingTransition(SurfaceControl.Transaction startTx,
SurfaceControl.Transaction finishTx, Rect destinationBounds) {
SurfaceControl pipLeash = mPipTransitionState.getPinnedTaskLeash();
- int cornerRadius = mContext.getResources().getDimensionPixelSize(R.dimen.pip_corner_radius);
- int shadowRadius = mContext.getResources().getDimensionPixelSize(R.dimen.pip_shadow_radius);
// merge transactions so everything is done on startTx
startTx.merge(finishTx);
- startTx.setPosition(pipLeash, destinationBounds.left, destinationBounds.top)
- .setCornerRadius(pipLeash, cornerRadius)
- .setShadowRadius(pipLeash, shadowRadius);
+ startTx.setPosition(pipLeash, destinationBounds.left, destinationBounds.top);
+ mSurfaceTransactionHelper.round(startTx, pipLeash, true /* applyCornerRadius */);
+ mSurfaceTransactionHelper.shadow(startTx, pipLeash, true /* applyShadow */);
startTx.apply();
// All motion operations have actually finished, so make bounds cache updates.
@@ -813,13 +817,25 @@
mPipScheduler.scheduleFinishPipBoundsChange(destinationBounds);
}
+ private void settlePipBoundsAfterFling() {
+ mPipTransitionState.setOnIdlePipTransitionStateRunnable(() -> {
+ final int delta =
+ mPipBoundsState.getMovementBounds().bottom - mPipBoundsState.getBounds().top;
+ if (delta < 0) {
+ // Move the PiP window to the movementBounds.
+ animateToOffset(mPipBoundsState.getBounds(), delta);
+ }
+ });
+ }
+
private void startResizeAnimation(SurfaceControl.Transaction startTx,
SurfaceControl.Transaction finishTx, Rect destinationBounds, int duration) {
SurfaceControl pipLeash = mPipTransitionState.getPinnedTaskLeash();
Preconditions.checkState(pipLeash != null,
"No leash cached by mPipTransitionState=" + mPipTransitionState);
- PipResizeAnimator animator = new PipResizeAnimator(mContext, pipLeash,
+ PipResizeAnimator animator = new PipResizeAnimator(mContext, mSurfaceTransactionHelper,
+ pipLeash,
startTx, finishTx, destinationBounds, mPipBoundsState.getBounds(),
destinationBounds, duration, 0f /* angle */);
animator.setAnimationEndCallback(() -> {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipResizeGestureHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipResizeGestureHandler.java
index 3494f16..2753e0a9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipResizeGestureHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipResizeGestureHandler.java
@@ -18,6 +18,7 @@
import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_NONE;
import static com.android.wm.shell.pip2.phone.PipTransition.ANIMATING_BOUNDS_CHANGE_DURATION;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.content.res.Resources;
@@ -48,6 +49,7 @@
import com.android.wm.shell.common.pip.PipDisplayLayoutState;
import com.android.wm.shell.common.pip.PipPerfHintController;
import com.android.wm.shell.common.pip.PipUiEventLogger;
+import com.android.wm.shell.pip2.PipSurfaceTransactionHelper;
import com.android.wm.shell.pip2.animation.PipResizeAnimator;
import java.io.PrintWriter;
@@ -113,7 +115,10 @@
private int mCtrlType;
private int mOhmOffset;
+ private final @NonNull PipSurfaceTransactionHelper mSurfaceTransactionHelper;
+
public PipResizeGestureHandler(Context context,
+ @NonNull PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
PipBoundsAlgorithm pipBoundsAlgorithm,
PipBoundsState pipBoundsState,
PipTouchState pipTouchState,
@@ -127,6 +132,7 @@
ShellExecutor mainExecutor,
@Nullable PipPerfHintController pipPerfHintController) {
mContext = context;
+ mSurfaceTransactionHelper = pipSurfaceTransactionHelper;
mMainExecutor = mainExecutor;
mPipPerfHintController = pipPerfHintController;
mPipBoundsAlgorithm = pipBoundsAlgorithm;
@@ -146,6 +152,7 @@
menuActivityController, pipBoundsAlgorithm, pipScheduler, movementBoundsSupplier);
mPipPinchToResizeHandler = new PipPinchToResizeHandler(this, pipBoundsState,
menuActivityController, pipScheduler);
+
}
void init() {
@@ -521,7 +528,8 @@
final int duration = extra.getInt(ANIMATING_BOUNDS_CHANGE_DURATION,
PipTransition.BOUNDS_CHANGE_JUMPCUT_DURATION);
- PipResizeAnimator animator = new PipResizeAnimator(mContext, pipLeash,
+ PipResizeAnimator animator = new PipResizeAnimator(mContext,
+ mSurfaceTransactionHelper, pipLeash,
startTx, finishTx, destinationBounds, mStartBoundsAfterRelease,
destinationBounds, duration, mAngle);
animator.setAnimationEndCallback(() -> {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java
index 82df9497..54d2470 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java
@@ -21,7 +21,6 @@
import android.app.PictureInPictureParams;
import android.content.Context;
-import android.graphics.Matrix;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.SystemProperties;
@@ -78,7 +77,8 @@
private PipTransitionController mPipTransitionController;
private PipSurfaceTransactionHelper.SurfaceControlTransactionFactory
mSurfaceControlTransactionFactory;
- private final PipSurfaceTransactionHelper mPipSurfaceTransactionHelper;
+
+ @NonNull private final PipSurfaceTransactionHelper mPipSurfaceTransactionHelper;
@Nullable private Runnable mUpdateMovementBoundsRunnable;
@Nullable private PipAlphaAnimator mOverlayFadeoutAnimator;
@@ -87,6 +87,7 @@
private Supplier<PictureInPictureParams> mPipParamsSupplier;
public PipScheduler(Context context,
+ @NonNull PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
PipBoundsState pipBoundsState,
ShellExecutor mainExecutor,
PipTransitionState pipTransitionState,
@@ -105,7 +106,7 @@
mDisplayController = displayController;
mSurfaceControlTransactionFactory =
new PipSurfaceTransactionHelper.VsyncSurfaceControlTransactionFactory();
- mPipSurfaceTransactionHelper = new PipSurfaceTransactionHelper(mContext);
+ mPipSurfaceTransactionHelper = pipSurfaceTransactionHelper;
mPipAlphaAnimatorSupplier = PipAlphaAnimator::new;
}
@@ -279,23 +280,15 @@
SurfaceControl leash = mPipTransitionState.getPinnedTaskLeash();
final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction();
- Matrix transformTensor = new Matrix();
- final float[] mMatrixTmp = new float[9];
- final float scale = (float) toBounds.width() / mPipBoundsState.getBounds().width();
-
- transformTensor.setScale(scale, scale);
- transformTensor.postTranslate(toBounds.left, toBounds.top);
- transformTensor.postRotate(degrees, toBounds.centerX(), toBounds.centerY());
-
- mPipSurfaceTransactionHelper.round(tx, leash, mPipBoundsState.getBounds(), toBounds);
-
- tx.setMatrix(leash, transformTensor, mMatrixTmp);
+ mPipSurfaceTransactionHelper.setPipTransformations(leash, tx, mPipBoundsState.getBounds(),
+ toBounds, degrees);
tx.apply();
}
void startOverlayFadeoutAnimation(@NonNull SurfaceControl overlayLeash,
boolean withStartDelay, @NonNull Runnable onAnimationEnd) {
- mOverlayFadeoutAnimator = mPipAlphaAnimatorSupplier.get(mContext, overlayLeash,
+ mOverlayFadeoutAnimator = mPipAlphaAnimatorSupplier.get(mContext,
+ mPipSurfaceTransactionHelper, overlayLeash,
null /* startTx */, null /* finishTx */, PipAlphaAnimator.FADE_OUT);
mOverlayFadeoutAnimator.setDuration(CONTENT_OVERLAY_FADE_OUT_DURATION_MS);
mOverlayFadeoutAnimator.setStartDelay(withStartDelay
@@ -368,6 +361,7 @@
@VisibleForTesting
interface PipAlphaAnimatorSupplier {
PipAlphaAnimator get(@NonNull Context context,
+ @NonNull PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
SurfaceControl leash,
SurfaceControl.Transaction startTransaction,
SurfaceControl.Transaction finishTransaction,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTaskListener.java
index 7aa3cdf..d326753 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTaskListener.java
@@ -37,6 +37,7 @@
import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
import com.android.wm.shell.common.pip.PipBoundsState;
import com.android.wm.shell.common.pip.PipUtils;
+import com.android.wm.shell.pip2.PipSurfaceTransactionHelper;
import com.android.wm.shell.pip2.animation.PipResizeAnimator;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.shared.annotations.ShellMainThread;
@@ -70,6 +71,8 @@
private PipResizeAnimatorSupplier mPipResizeAnimatorSupplier;
+ private final @NonNull PipSurfaceTransactionHelper mSurfaceTransactionHelper;
+
public PipTaskListener(Context context,
ShellTaskOrganizer shellTaskOrganizer,
PipTransitionState pipTransitionState,
@@ -98,6 +101,8 @@
// PiP Activity.
mPipBoundsState.addOnPipComponentChangedListener(((oldPipComponent, newPipComponent) ->
mPictureInPictureParams = new PictureInPictureParams.Builder().build()));
+
+ mSurfaceTransactionHelper = new PipSurfaceTransactionHelper(context);
}
void setPictureInPictureParams(@Nullable PictureInPictureParams params) {
@@ -204,6 +209,7 @@
if (mWaitingForAspectRatioChange) {
mWaitingForAspectRatioChange = false;
PipResizeAnimator animator = mPipResizeAnimatorSupplier.get(mContext,
+ mSurfaceTransactionHelper,
mPipTransitionState.getPinnedTaskLeash(), startTx, finishTx,
destinationBounds,
mPipBoundsState.getBounds(), destinationBounds, duration,
@@ -227,6 +233,7 @@
@VisibleForTesting
interface PipResizeAnimatorSupplier {
PipResizeAnimator get(@NonNull Context context,
+ @NonNull PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
@NonNull SurfaceControl leash,
@Nullable SurfaceControl.Transaction startTx,
@Nullable SurfaceControl.Transaction finishTx,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java
index 873eddd..58d14b9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java
@@ -23,7 +23,6 @@
import static com.android.wm.shell.common.pip.PipBoundsState.STASH_TYPE_LEFT;
import static com.android.wm.shell.common.pip.PipBoundsState.STASH_TYPE_NONE;
import static com.android.wm.shell.common.pip.PipBoundsState.STASH_TYPE_RIGHT;
-import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_TO_PIP;
import static com.android.wm.shell.pip2.phone.PhonePipMenuController.MENU_STATE_FULL;
import static com.android.wm.shell.pip2.phone.PhonePipMenuController.MENU_STATE_NONE;
import static com.android.wm.shell.pip2.phone.PipMenuView.ANIM_TYPE_NONE;
@@ -34,9 +33,9 @@
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.Resources;
-import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
+import android.graphics.RectF;
import android.os.Bundle;
import android.os.SystemProperties;
import android.provider.DeviceConfig;
@@ -50,13 +49,14 @@
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityWindowInfo;
-import android.window.DesktopExperienceFlags;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.ProtoLog;
import com.android.wm.shell.R;
import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.FloatingContentCoordinator;
+import com.android.wm.shell.common.MultiDisplayDragMoveBoundsCalculator;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
import com.android.wm.shell.common.pip.PipBoundsState;
@@ -67,8 +67,7 @@
import com.android.wm.shell.common.pip.PipUiEventLogger;
import com.android.wm.shell.common.pip.PipUtils;
import com.android.wm.shell.common.pip.SizeSpecSource;
-import com.android.wm.shell.pip.PipAnimationController;
-import com.android.wm.shell.pip.PipTransitionController;
+import com.android.wm.shell.pip2.PipSurfaceTransactionHelper;
import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellInit;
@@ -91,6 +90,7 @@
private final Context mContext;
private final ShellCommandHandler mShellCommandHandler;
private final PipBoundsAlgorithm mPipBoundsAlgorithm;
+ private final PipDesktopState mPipDesktopState;
@NonNull private final PipBoundsState mPipBoundsState;
@NonNull private final PipTransitionState mPipTransitionState;
@NonNull private final PipScheduler mPipScheduler;
@@ -105,12 +105,13 @@
private final PipDisplayTransferHandler mPipDisplayTransferHandler;
private final PhonePipMenuController mMenuController;
private final AccessibilityManager mAccessibilityManager;
+ private final DisplayController mDisplayController;
/**
* Whether PIP stash is enabled or not. When enabled, if the user flings toward the edge of the
* screen, it will be shown in "stashed" mode, where PIP will only show partially.
*/
- private boolean mEnableStash = true;
+ @VisibleForTesting boolean mEnableStash = true;
private float mStashVelocityThreshold;
@@ -133,7 +134,7 @@
private boolean mMovementWithinDismiss;
// Touch state
- private final PipTouchState mTouchState;
+ private PipTouchState mTouchState;
private final FloatingContentCoordinator mFloatingContentCoordinator;
private PipMotionHelper mMotionHelper;
private PipTouchGesture mGesture;
@@ -145,6 +146,7 @@
// Callbacks
private final Runnable mMoveOnShelVisibilityChanged;
+ private final @NonNull PipSurfaceTransactionHelper mSurfaceTransactionHelper;
/**
* A listener for the PIP menu activity.
@@ -180,6 +182,7 @@
@SuppressLint("InflateParams")
public PipTouchHandler(Context context,
+ @NonNull PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
ShellInit shellInit,
ShellCommandHandler shellCommandHandler,
PhonePipMenuController menuController,
@@ -195,14 +198,18 @@
FloatingContentCoordinator floatingContentCoordinator,
PipUiEventLogger pipUiEventLogger,
ShellExecutor mainExecutor,
- Optional<PipPerfHintController> pipPerfHintControllerOptional) {
+ Optional<PipPerfHintController> pipPerfHintControllerOptional,
+ PipDisplayTransferHandler pipDisplayTransferHandler) {
mContext = context;
+ mSurfaceTransactionHelper = pipSurfaceTransactionHelper;
mShellCommandHandler = shellCommandHandler;
mMainExecutor = mainExecutor;
mPipPerfHintController = pipPerfHintControllerOptional.orElse(null);
mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
mPipBoundsAlgorithm = pipBoundsAlgorithm;
mPipBoundsState = pipBoundsState;
+ mPipDesktopState = pipDesktopState;
+ mDisplayController = displayController;
mPipTransitionState = pipTransitionState;
mPipTransitionState.addPipTransitionStateChangedListener(this::onPipTransitionStateChanged);
@@ -215,9 +222,10 @@
mMenuController.addListener(new PipMenuListener());
mGesture = new DefaultPipTouchGesture();
mMotionHelper = pipMotionHelper;
+ mPipDisplayTransferHandler = pipDisplayTransferHandler;
mPipScheduler.setUpdateMovementBoundsRunnable(this::updateMovementBounds);
mPipDismissTargetHandler = new PipDismissTargetHandler(context, pipUiEventLogger,
- mMotionHelper, mPipDisplayLayoutState, displayController, mainExecutor);
+ mMotionHelper, mPipDisplayLayoutState, mDisplayController, mainExecutor);
mTouchState = new PipTouchState(ViewConfiguration.get(context),
() -> {
mMenuController.showMenuWithPossibleDelay(MENU_STATE_FULL,
@@ -227,12 +235,11 @@
},
menuController::hideMenu,
mainExecutor);
- mPipResizeGestureHandler = new PipResizeGestureHandler(context, pipBoundsAlgorithm,
+ mPipResizeGestureHandler = new PipResizeGestureHandler(context, mSurfaceTransactionHelper,
+ pipBoundsAlgorithm,
pipBoundsState, mTouchState, mPipScheduler, mPipTransitionState, pipUiEventLogger,
menuController, this::getMovementBounds, mPipDisplayLayoutState, pipDesktopState,
mainExecutor, mPipPerfHintController);
- mPipDisplayTransferHandler = new PipDisplayTransferHandler(mPipTransitionState,
- mPipScheduler);
mPipBoundsState.addOnAspectRatioChangedCallback(aspectRatio -> onAspectRatioChanged());
mMoveOnShelVisibilityChanged = () -> {
@@ -303,11 +310,6 @@
});
}
- public PipTransitionController getTransitionHandler() {
- // return mPipTaskOrganizer.getTransitionController();
- return null;
- }
-
private void reloadResources() {
final Resources res = mContext.getResources();
mBottomOffsetBufferPx = res.getDimensionPixelSize(R.dimen.pip_bottom_offset_buffer);
@@ -328,6 +330,10 @@
mGesture = gesture;
}
+ @VisibleForTesting PipTouchGesture getTouchGesture() {
+ return mGesture;
+ }
+
void setTouchEnabled(boolean enabled) {
mTouchState.setAllowTouches(enabled);
}
@@ -358,17 +364,6 @@
mPipBoundsState.setHasUserResizedPip(false);
}
- void onPinnedStackAnimationEnded(
- @PipAnimationController.TransitionDirection int direction) {
- // Always synchronize the motion helper bounds once PiP animations finish
- mMotionHelper.synchronizePinnedStackBounds();
- updateMovementBounds();
- if (direction == TRANSITION_DIRECTION_TO_PIP) {
- // Set the initial bounds as the user resize bounds.
- mPipResizeGestureHandler.setUserResizeBounds(mPipBoundsState.getBounds());
- }
- }
-
void onConfigurationChanged() {
mPipResizeGestureHandler.onConfigurationChanged();
mMotionHelper.synchronizePinnedStackBounds();
@@ -386,8 +381,12 @@
// Cache new movement bounds using the new potential IME height.
updateMovementBounds();
-
mPipTransitionState.setOnIdlePipTransitionStateRunnable(() -> {
+ if (imeVisible && mPipBoundsState.getMotionBoundsState().isInMotion()) {
+ // Skip updating bounds now as it will be done after the animation settles
+ return;
+ }
+
int delta = mPipBoundsState.getMovementBounds().bottom
- mPipBoundsState.getBounds().top;
boolean hasUserInteracted = (mPipBoundsState.hasUserMovedPip()
@@ -795,6 +794,10 @@
mMotionHelper = pipMotionHelper;
}
+ @VisibleForTesting public void setPipTouchState(PipTouchState pipTouchState) {
+ mTouchState = pipTouchState;
+ }
+
Rect getUserResizeBounds() {
return mPipResizeGestureHandler.getUserResizeBounds();
}
@@ -810,10 +813,11 @@
* Gesture controlling normal movement of the PIP.
*/
private class DefaultPipTouchGesture extends PipTouchGesture {
- private final Point mStartPosition = new Point();
private final PointF mDelta = new PointF();
+ private final PointF mPointerPositionOnDown = new PointF();
private int mDisplayIdOnDown;
private boolean mShouldHideMenuAfterFling;
+ private final Rect mStartBounds = new Rect();
@Nullable private PipPerfHintController.PipHighPerfSession mPipHighPerfSession;
@@ -842,9 +846,10 @@
Rect bounds = getPossiblyMotionBounds();
mDelta.set(0f, 0f);
- mStartPosition.set(bounds.left, bounds.top);
- mMovementWithinDismiss = touchState.getDownTouchPosition().y
- >= mPipBoundsState.getMovementBounds().bottom;
+ mStartBounds.set(bounds);
+ final PointF touchPosition = touchState.getDownTouchPosition();
+ mPointerPositionOnDown.set(touchPosition.x, touchPosition.y);
+ mMovementWithinDismiss = touchPosition.y >= mPipBoundsState.getMovementBounds().bottom;
mMotionHelper.setSpringingToTouch(false);
mPipDismissTargetHandler.setTaskLeash(mPipTransitionState.getPinnedTaskLeash());
mDisplayIdOnDown = touchState.getLastTouchDisplayId();
@@ -868,23 +873,51 @@
if (touchState.isDragging()) {
mPipBoundsState.setHasUserMovedPip(true);
+ final PointF curPos = touchState.getLastTouchPosition();
- // Move the pinned stack freely
- final PointF lastDelta = touchState.getLastTouchDelta();
- float lastX = mStartPosition.x + mDelta.x;
- float lastY = mStartPosition.y + mDelta.y;
- float left = lastX + lastDelta.x;
- float top = lastY + lastDelta.y;
+ if (mPipDesktopState.isDraggingPipAcrossDisplaysEnabled()) {
+ DisplayLayout currentDisplayLayout = mDisplayController.getDisplayLayout(
+ touchState.getLastTouchDisplayId());
+ DisplayLayout displayLayoutOnDown = mDisplayController.getDisplayLayout(
+ mDisplayIdOnDown);
- // Add to the cumulative delta after bounding the position
- mDelta.x += left - lastX;
- mDelta.y += top - lastY;
+ if (displayLayoutOnDown == null || currentDisplayLayout == null) {
+ ProtoLog.w(WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: Failed to show drag mirror on connected displays because "
+ + "displayLayout is null", TAG);
+ return false;
+ }
- mTmpBounds.set(getPossiblyMotionBounds());
- mTmpBounds.offsetTo((int) left, (int) top);
+ RectF globalDpPipBounds =
+ MultiDisplayDragMoveBoundsCalculator.calculateGlobalDpBoundsForDrag(
+ displayLayoutOnDown, mPointerPositionOnDown, mStartBounds,
+ currentDisplayLayout, curPos.x, curPos.y);
+
+ // Create mirrors on connected displays to simulate dragging PiP across displays
+ mPipDisplayTransferHandler.showDragMirrorOnConnectedDisplays(mDisplayIdOnDown,
+ globalDpPipBounds);
+ // Set PiP bounds on the origin display in display topology-aware local px
+ mTmpBounds.set(
+ MultiDisplayDragMoveBoundsCalculator.convertGlobalDpToLocalPxForRect(
+ globalDpPipBounds, displayLayoutOnDown));
+ } else {
+ // Move the pinned stack freely
+ final PointF lastDelta = touchState.getLastTouchDelta();
+ float lastX = mStartBounds.left + mDelta.x;
+ float lastY = mStartBounds.top + mDelta.y;
+ float left = lastX + lastDelta.x;
+ float top = lastY + lastDelta.y;
+
+ // Add to the cumulative delta after bounding the position
+ mDelta.x += left - lastX;
+ mDelta.y += top - lastY;
+
+ mTmpBounds.set(getPossiblyMotionBounds());
+ mTmpBounds.offsetTo((int) left, (int) top);
+ }
+
mMotionHelper.movePip(mTmpBounds, true /* isDragging */);
- final PointF curPos = touchState.getLastTouchPosition();
if (mMovementWithinDismiss) {
// Track if movement remains near the bottom edge to identify swipe to dismiss
mMovementWithinDismiss = curPos.y >= mPipBoundsState.getMovementBounds().bottom;
@@ -898,6 +931,9 @@
public boolean onUp(PipTouchState touchState) {
mPipDismissTargetHandler.hideDismissTargetMaybe();
mPipDismissTargetHandler.setTaskLeash(null);
+ if (mPipDesktopState.isDraggingPipAcrossDisplaysEnabled()) {
+ mPipDisplayTransferHandler.removeMirrors();
+ }
if (!touchState.isUserInteracting()) {
return false;
@@ -928,7 +964,7 @@
mPipBoundsState.setStashed(STASH_TYPE_NONE);
}
- if (DesktopExperienceFlags.ENABLE_DRAGGING_PIP_ACROSS_DISPLAYS.isTrue()
+ if (mPipDesktopState.isDraggingPipAcrossDisplaysEnabled()
&& mDisplayIdOnDown != displayIdOnUp) {
mPipDisplayTransferHandler.scheduleMovePipToDisplay(mDisplayIdOnDown,
displayIdOnUp);
@@ -1044,6 +1080,7 @@
*/
void updateMovementBounds() {
Rect insetBounds = new Rect();
+ mPipBoundsState.setImeVisibility(mIsImeShowing, mIsImeShowing ? mImeHeight : 0);
mPipBoundsAlgorithm.getInsetBounds(insetBounds);
mPipBoundsAlgorithm.getMovementBounds(mPipBoundsState.getBounds(),
insetBounds, mPipBoundsState.getMovementBounds(), mIsImeShowing ? mImeHeight : 0);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
index 2cd41cd..9add2f5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
@@ -16,6 +16,7 @@
package com.android.wm.shell.pip2.phone;
+import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.view.Surface.ROTATION_0;
@@ -62,6 +63,7 @@
import androidx.annotation.Nullable;
+import com.android.internal.protolog.ProtoLog;
import com.android.internal.util.Preconditions;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.ComponentUtils;
@@ -80,6 +82,7 @@
import com.android.wm.shell.pip2.animation.PipEnterAnimator;
import com.android.wm.shell.pip2.phone.transition.PipExpandHandler;
import com.android.wm.shell.pip2.phone.transition.PipTransitionUtils;
+import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.shared.TransitionUtil;
import com.android.wm.shell.splitscreen.SplitScreenController;
import com.android.wm.shell.sysui.ShellInit;
@@ -148,6 +151,7 @@
public PipTransition(
Context context,
+ @NonNull PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
@NonNull ShellInit shellInit,
@NonNull ShellTaskOrganizer shellTaskOrganizer,
@NonNull Transitions transitions,
@@ -175,12 +179,13 @@
mPipTransitionState.addPipTransitionStateChangedListener(this);
mPipDisplayLayoutState = pipDisplayLayoutState;
mDisplayController = displayController;
- mPipSurfaceTransactionHelper = new PipSurfaceTransactionHelper(mContext);
+ mPipSurfaceTransactionHelper = pipSurfaceTransactionHelper;
mPipDesktopState = pipDesktopState;
mDesktopPipTransitionController = desktopPipTransitionController;
mPipInteractionHandler = pipInteractionHandler;
- mExpandHandler = new PipExpandHandler(mContext, pipBoundsState, pipBoundsAlgorithm,
+ mExpandHandler = new PipExpandHandler(mContext, mPipSurfaceTransactionHelper,
+ pipBoundsState, pipBoundsAlgorithm,
pipTransitionState, pipDisplayLayoutState, pipDesktopState, pipInteractionHandler,
splitScreenControllerOptional);
}
@@ -234,6 +239,7 @@
@NonNull TransitionRequestInfo request) {
if (isAutoEnterInButtonNavigation(request) || isEnterPictureInPictureModeRequest(request)) {
mEnterTransition = transition;
+ mPipTransitionState.setState(PipTransitionState.SCHEDULED_ENTER_PIP);
final WindowContainerTransaction wct = getEnterPipTransaction(transition,
request.getPipChange());
@@ -257,6 +263,7 @@
outWct.merge(getEnterPipTransaction(transition, request.getPipChange()),
true /* transfer */);
mEnterTransition = transition;
+ mPipTransitionState.setState(PipTransitionState.SCHEDULED_ENTER_PIP);
}
}
@@ -279,7 +286,11 @@
@Override
public void onTransitionConsumed(@NonNull IBinder transition, boolean aborted,
- @Nullable SurfaceControl.Transaction finishT) {}
+ @Nullable SurfaceControl.Transaction finishT) {
+ if (transition == mBoundsChangeTransition && aborted) {
+ onTransitionAborted();
+ }
+ }
@Override
public boolean startAnimation(@NonNull IBinder transition,
@@ -300,6 +311,10 @@
// playing PiP transitions, so reset those transforms if needed.
prepareOtherTargetTransforms(info, startTransaction, finishTransaction);
+ // This PiP transition might have caused a previous PiP to be dismissed. If so, we need
+ // to clean up the PiP state.
+ cleanUpPrevPipIfPresent(info, startTransaction, finishTransaction);
+
// Update the PipTransitionState while supplying the PiP leash and token to be cached.
Bundle extra = new Bundle();
extra.putParcelable(PIP_TASK_LEASH, pipChange.getLeash());
@@ -502,7 +517,8 @@
pipActivityChange);
startTransaction.merge(finishTransaction);
- PipEnterAnimator animator = new PipEnterAnimator(mContext, pipLeash,
+ PipEnterAnimator animator = new PipEnterAnimator(mContext, mPipSurfaceTransactionHelper,
+ pipLeash,
startTransaction, finishTransaction, destinationBounds, delta);
animator.setEnterStartState(pipChange);
animator.onEnterAnimationUpdate(1.0f /* fraction */, startTransaction);
@@ -551,7 +567,8 @@
updatePipChangesForFixedRotation(info, pipChange, pipActivityChange);
}
- PipEnterAnimator animator = new PipEnterAnimator(mContext, pipLeash,
+ PipEnterAnimator animator = new PipEnterAnimator(mContext, mPipSurfaceTransactionHelper,
+ pipLeash,
startTransaction, finishTransaction, endBounds, delta);
if (PipBoundsAlgorithm.getValidSourceHintRect(params, startBounds, endBounds) == null) {
// If app provided src-rect-hint is invalid, use app icon overlay.
@@ -635,10 +652,22 @@
}
mFinishCallback = finishCallback;
- Rect destinationBounds = pipChange.getEndAbsBounds();
- SurfaceControl pipLeash = mPipTransitionState.getPinnedTaskLeash();
+ final Rect destinationBounds = pipChange.getEndAbsBounds();
+ if (pipChange.getEndRotation() != ROTATION_UNDEFINED
+ && pipChange.getStartRotation() != pipChange.getEndRotation()) {
+ // If we are playing an enter PiP animation with display change collected together
+ // in the same transition, then PipController#onDisplayChange() must have already
+ // updated the PiP bounds state to reflect the final desired destination bounds.
+ // This might not be in the WM state yet as PiP task token might have been null then.
+ // WM state will be updated via a follow-up bounds change transition after.
+ destinationBounds.set(mPipBoundsState.getBounds());
+ }
+
+ final SurfaceControl pipLeash = mPipTransitionState.getPinnedTaskLeash();
Preconditions.checkNotNull(pipLeash, "Leash is null for alpha transition.");
+ // Note that fixed rotation is different from the same transition display change rotation;
+ // with fixed rotation, we expect a follow-up async rotation transition after this one.
final int delta = getFixedRotationDelta(info, pipChange, mPipDisplayLayoutState);
if (delta != ROTATION_0) {
updatePipChangesForFixedRotation(info, pipChange,
@@ -665,9 +694,11 @@
finishTransaction.setMatrix(pipLeash, transformTensor, matrixTmp);
} else {
startTransaction.setPosition(pipLeash, destinationBounds.left, destinationBounds.top);
+ finishTransaction.setPosition(pipLeash, destinationBounds.left, destinationBounds.top);
}
- PipAlphaAnimator animator = new PipAlphaAnimator(mContext, pipLeash, startTransaction,
+ PipAlphaAnimator animator = new PipAlphaAnimator(mContext, mPipSurfaceTransactionHelper,
+ pipLeash, startTransaction,
finishTransaction, PipAlphaAnimator.FADE_IN);
// This should update the pip transition state accordingly after we stop playing.
animator.setAnimationEndCallback(this::finishTransition);
@@ -691,7 +722,8 @@
finishTransaction.setAlpha(pipChange.getLeash(), 0f);
if (mPendingRemoveWithFadeout) {
- PipAlphaAnimator animator = new PipAlphaAnimator(mContext, pipChange.getLeash(),
+ PipAlphaAnimator animator = new PipAlphaAnimator(mContext, mPipSurfaceTransactionHelper,
+ pipChange.getLeash(),
startTransaction, finishTransaction, PipAlphaAnimator.FADE_OUT);
animator.setAnimationEndCallback(this::finishTransition);
animator.start();
@@ -932,6 +964,46 @@
}
/**
+ * This is called by [startAnimation] when a enter PiP transition is received, and before
+ * mPipTransitionState is updated with the incoming PiP task info. If a change is found
+ * for the previous PiP with change TO_BACK, the previous PiP was dismissed by Core. We want to
+ * update the state in PipTransitionState so everything is cleaned up and also ensure the
+ * previous PiP is no longer visible.
+ */
+ private void cleanUpPrevPipIfPresent(@NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction startTx,
+ @NonNull SurfaceControl.Transaction finishTx) {
+ TransitionInfo.Change previousPipChange = null;
+ TaskInfo previousPipTaskInfo = mPipTransitionState.getPipTaskInfo();
+ if (previousPipTaskInfo == null) {
+ return;
+ }
+
+ for (TransitionInfo.Change change : info.getChanges()) {
+ if (change.getTaskInfo() != null
+ && change.getTaskInfo().getTaskId() == previousPipTaskInfo.getTaskId()
+ && TransitionUtil.isClosingMode(change.getMode())) {
+ previousPipChange = change;
+ break;
+ }
+ }
+
+ if (previousPipChange == null) {
+ return;
+ }
+
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "cleanUpPrevPipIfPresent: Previous PiP with taskId=%d found with closing mode, "
+ + "clean up PiP state",
+ previousPipTaskInfo.getTaskId());
+
+ mPipTransitionState.setState(PipTransitionState.EXITING_PIP);
+ mPipTransitionState.setState(PipTransitionState.EXITED_PIP);
+ startTx.setAlpha(previousPipChange.getLeash(), 0);
+ finishTx.setAlpha(previousPipChange.getLeash(), 0);
+ }
+
+ /**
* Sets the type of animation to run upon entering PiP.
*
* By default, {@link PipTransition} uses various signals from Transitions to figure out
@@ -987,6 +1059,25 @@
}
@Override
+ public void onTransitionAborted() {
+ final int currentState = mPipTransitionState.getState();
+ int nextState = PipTransitionState.UNDEFINED;
+ switch (currentState) {
+ case PipTransitionState.SCHEDULED_BOUNDS_CHANGE:
+ nextState = PipTransitionState.CHANGED_PIP_BOUNDS;
+ break;
+ }
+
+ if (nextState == PipTransitionState.UNDEFINED) {
+ Log.wtf(TAG, String.format("""
+ PipTransitionState resolved to an undefined state in abortTransition().
+ callers=%s""", Debug.getCallers(4)));
+ }
+
+ mPipTransitionState.setState(nextState);
+ }
+
+ @Override
public void onPipTransitionStateChanged(@PipTransitionState.TransitionState int oldState,
@PipTransitionState.TransitionState int newState, @Nullable Bundle extra) {
switch (newState) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransitionState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransitionState.java
index b5556dd..db690e1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransitionState.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransitionState.java
@@ -76,27 +76,30 @@
// State for Launcher animating the swipe PiP to home animation.
public static final int SWIPING_TO_PIP = 1;
+ // State for scheduling enter PiP transition; could be after SWIPING_TO_PIP
+ public static final int SCHEDULED_ENTER_PIP = 2;
+
// State for Shell animating enter PiP or jump-cutting to PiP mode after Launcher animation.
- public static final int ENTERING_PIP = 2;
+ public static final int ENTERING_PIP = 3;
// State for app finishing drawing in PiP mode as a final step in enter PiP flow.
- public static final int ENTERED_PIP = 3;
+ public static final int ENTERED_PIP = 4;
// State to indicate we have scheduled a PiP bounds change transition.
- public static final int SCHEDULED_BOUNDS_CHANGE = 4;
+ public static final int SCHEDULED_BOUNDS_CHANGE = 5;
// State for the start of playing a transition to change PiP bounds. At this point, WM Core
// is aware of the new PiP bounds, but Shell might still be continuing animating.
- public static final int CHANGING_PIP_BOUNDS = 5;
+ public static final int CHANGING_PIP_BOUNDS = 6;
// State for finishing animating into new PiP bounds after resize is complete.
- public static final int CHANGED_PIP_BOUNDS = 6;
+ public static final int CHANGED_PIP_BOUNDS = 7;
// State for starting exiting PiP.
- public static final int EXITING_PIP = 7;
+ public static final int EXITING_PIP = 8;
// State for finishing exit PiP flow.
- public static final int EXITED_PIP = 8;
+ public static final int EXITED_PIP = 9;
private static final int FIRST_CUSTOM_STATE = 1000;
@@ -105,6 +108,7 @@
@IntDef(prefix = { "TRANSITION_STATE_" }, value = {
UNDEFINED,
SWIPING_TO_PIP,
+ SCHEDULED_ENTER_PIP,
ENTERING_PIP,
ENTERED_PIP,
SCHEDULED_BOUNDS_CHANGE,
@@ -203,6 +207,9 @@
* @param extra a bundle passed to the subscribed listeners to resolve/cache extra info.
*/
public void setState(@TransitionState int state, @Nullable Bundle extra) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s setState from=%s to=%s",
+ TAG, stateToString(mState), stateToString(state));
if (state == ENTERING_PIP || state == SWIPING_TO_PIP
|| state == SCHEDULED_BOUNDS_CHANGE || state == CHANGING_PIP_BOUNDS) {
// States listed above require extra bundles to be provided.
@@ -418,6 +425,7 @@
switch (state) {
case UNDEFINED: return "undefined";
case SWIPING_TO_PIP: return "swiping_to_pip";
+ case SCHEDULED_ENTER_PIP: return "scheduled_enter_pip";
case ENTERING_PIP: return "entering-pip";
case ENTERED_PIP: return "entered-pip";
case SCHEDULED_BOUNDS_CHANGE: return "scheduled_bounds_change";
@@ -425,8 +433,8 @@
case CHANGED_PIP_BOUNDS: return "changed-bounds";
case EXITING_PIP: return "exiting-pip";
case EXITED_PIP: return "exited-pip";
+ default: return "custom-state(" + state + ")";
}
- throw new IllegalStateException("Unknown state: " + state);
}
public boolean isPipStateIdle() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/transition/PipExpandHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/transition/PipExpandHandler.java
index 84ec986..6ba2c71 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/transition/PipExpandHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/transition/PipExpandHandler.java
@@ -47,6 +47,7 @@
import com.android.wm.shell.common.pip.PipBoundsState;
import com.android.wm.shell.common.pip.PipDesktopState;
import com.android.wm.shell.common.pip.PipDisplayLayoutState;
+import com.android.wm.shell.pip2.PipSurfaceTransactionHelper;
import com.android.wm.shell.pip2.animation.PipExpandAnimator;
import com.android.wm.shell.pip2.phone.PipInteractionHandler;
import com.android.wm.shell.pip2.phone.PipTransitionState;
@@ -72,8 +73,10 @@
private ValueAnimator mTransitionAnimator;
private PipExpandAnimatorSupplier mPipExpandAnimatorSupplier;
+ private final @NonNull PipSurfaceTransactionHelper mSurfaceTransactionHelper;
public PipExpandHandler(Context context,
+ @NonNull PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
PipBoundsState pipBoundsState,
PipBoundsAlgorithm pipBoundsAlgorithm,
PipTransitionState pipTransitionState,
@@ -89,6 +92,7 @@
mPipDesktopState = pipDesktopState;
mPipInteractionHandler = pipInteractionHandler;
mSplitScreenControllerOptional = splitScreenControllerOptional;
+ mSurfaceTransactionHelper = pipSurfaceTransactionHelper;
mPipExpandAnimatorSupplier = PipExpandAnimator::new;
}
@@ -190,7 +194,8 @@
handleExpandFixedRotation(pipChange, delta);
}
- PipExpandAnimator animator = mPipExpandAnimatorSupplier.get(mContext, pipLeash,
+ PipExpandAnimator animator = mPipExpandAnimatorSupplier.get(mContext,
+ mSurfaceTransactionHelper, pipLeash,
startTransaction, finishTransaction, endBounds, startBounds, endBounds,
sourceRectHint, delta, mPipDesktopState.isPipInDesktopMode());
animator.setAnimationStartCallback(() -> {
@@ -303,7 +308,8 @@
}
final SurfaceControl pipLeash = pipChange.getLeash();
- PipExpandAnimator animator = mPipExpandAnimatorSupplier.get(mContext, pipLeash,
+ PipExpandAnimator animator = mPipExpandAnimatorSupplier.get(mContext,
+ mSurfaceTransactionHelper, pipLeash,
startTransaction, finishTransaction, endBounds, startBounds, endBounds,
null /* srcRectHint */, ROTATION_0 /* delta */,
mPipDesktopState.isPipInDesktopMode());
@@ -382,6 +388,7 @@
@VisibleForTesting
interface PipExpandAnimatorSupplier {
PipExpandAnimator get(Context context,
+ @NonNull PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
@NonNull SurfaceControl leash,
SurfaceControl.Transaction startTransaction,
SurfaceControl.Transaction finishTransaction,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
index 382fa96..355d799 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
@@ -66,7 +66,7 @@
import com.android.wm.shell.shared.GroupedTaskInfo;
import com.android.wm.shell.shared.annotations.ExternalThread;
import com.android.wm.shell.shared.annotations.ShellMainThread;
-import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
+import com.android.wm.shell.shared.desktopmode.DesktopState;
import com.android.wm.shell.shared.split.SplitBounds;
import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellController;
@@ -109,6 +109,7 @@
private final ActivityTaskManager mActivityTaskManager;
private final TaskStackTransitionObserver mTaskStackTransitionObserver;
private final RecentsShellCommandHandler mRecentsShellCommandHandler;
+ private final DesktopState mDesktopState;
private RecentsTransitionHandler mTransitionHandler = null;
private IRecentTasksListener mListener;
private final boolean mPcFeatureEnabled;
@@ -150,14 +151,15 @@
ActivityTaskManager activityTaskManager,
Optional<DesktopUserRepositories> desktopUserRepositories,
TaskStackTransitionObserver taskStackTransitionObserver,
- @ShellMainThread ShellExecutor mainExecutor
+ @ShellMainThread ShellExecutor mainExecutor,
+ DesktopState desktopState
) {
if (!context.getResources().getBoolean(com.android.internal.R.bool.config_hasRecents)) {
return null;
}
return new RecentTasksController(context, shellInit, shellController, shellCommandHandler,
taskStackListener, activityTaskManager, desktopUserRepositories,
- taskStackTransitionObserver, mainExecutor);
+ taskStackTransitionObserver, mainExecutor, desktopState);
}
RecentTasksController(Context context,
@@ -168,7 +170,8 @@
ActivityTaskManager activityTaskManager,
Optional<DesktopUserRepositories> desktopUserRepositories,
TaskStackTransitionObserver taskStackTransitionObserver,
- ShellExecutor mainExecutor) {
+ ShellExecutor mainExecutor,
+ DesktopState desktopState) {
mContext = context;
mShellController = shellController;
mShellCommandHandler = shellCommandHandler;
@@ -179,6 +182,7 @@
mTaskStackTransitionObserver = taskStackTransitionObserver;
mMainExecutor = mainExecutor;
mRecentsShellCommandHandler = new RecentsShellCommandHandler(this);
+ mDesktopState = desktopState;
shellInit.addInitCallback(this::onInit, this);
}
@@ -489,7 +493,7 @@
private boolean shouldEnableRunningTasksForDesktopMode() {
return mPcFeatureEnabled
- || (DesktopModeStatus.canEnterDesktopMode(mContext)
+ || (mDesktopState.canEnterDesktopMode()
&& DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_TASKBAR_RUNNING_APPS.isTrue());
}
@@ -583,7 +587,7 @@
private void initializeDesksMap(boolean multipleDesktopsEnabled) {
mTmpDesks.clear();
- if (DesktopModeStatus.canEnterDesktopMode(mContext)
+ if (mDesktopState.canEnterDesktopMode()
&& mDesktopUserRepositories.isPresent()) {
if (multipleDesktopsEnabled) {
for (var deskId : mDesktopUserRepositories.get().getCurrent().getAllDeskIds()) {
@@ -675,8 +679,8 @@
}
// Desktop tasks
- if (DesktopModeStatus.canEnterDesktopMode(mContext) &&
- mDesktopUserRepositories.isPresent()
+ if (mDesktopState.canEnterDesktopMode()
+ && mDesktopUserRepositories.isPresent()
&& mDesktopUserRepositories.get().getCurrent().isActiveTask(taskId)) {
// If task has their app bounds set to null which happens after reboot, set the
// app bounds to persisted lastFullscreenBounds. Also set the position in parent
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index 75be818..048991c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -44,6 +44,7 @@
import android.app.ActivityManager;
import android.app.ActivityOptions;
import android.app.ActivityTaskManager;
+import android.app.IActivityTaskManager;
import android.app.PendingIntent;
import android.app.TaskInfo;
import android.content.ComponentName;
@@ -207,6 +208,7 @@
private final MultiInstanceHelper mMultiInstanceHelpher;
private final SplitState mSplitState;
private final RootDisplayAreaOrganizer mRootDisplayAreaOrganizer;
+ private final IActivityTaskManager mActivityTaskManager;
private final SplitScreenShellCommandHandler mSplitScreenShellCommandHandler;
private final DesktopState mDesktopState;
@@ -241,7 +243,8 @@
ShellExecutor mainExecutor,
Handler mainHandler,
RootDisplayAreaOrganizer rootDisplayAreaOrganizer,
- DesktopState desktopState) {
+ DesktopState desktopState,
+ IActivityTaskManager activityTaskManager) {
mShellCommandHandler = shellCommandHandler;
mShellController = shellController;
mTaskOrganizer = shellTaskOrganizer;
@@ -266,6 +269,7 @@
mMultiInstanceHelpher = multiInstanceHelper;
mSplitState = splitState;
mRootDisplayAreaOrganizer = rootDisplayAreaOrganizer;
+ mActivityTaskManager = activityTaskManager;
mSplitScreenShellCommandHandler = new SplitScreenShellCommandHandler(this);
mDesktopState = desktopState;
// TODO(b/238217847): Temporarily add this check here until we can remove the dynamic
@@ -312,7 +316,7 @@
mDisplayInsetsController, mTransitions, mTransactionPool, mIconProvider,
mMainExecutor, mMainHandler, mRecentTasksOptional, mLaunchAdjacentController,
mWindowDecorViewModel, mSplitState, mDesktopTasksController, mRootTDAOrganizer,
- mRootDisplayAreaOrganizer, mDesktopState);
+ mRootDisplayAreaOrganizer, mDesktopState, mActivityTaskManager);
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index dc322ee..1717934 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -213,6 +213,7 @@
private final int mDisplayId;
private SplitLayout mSplitLayout;
+ private final IActivityTaskManager mActivityTaskManager;
private ValueAnimator mDividerFadeInAnimator;
private boolean mDividerVisible;
private boolean mKeyguardActive;
@@ -388,7 +389,8 @@
Optional<DesktopTasksController> desktopTasksController,
RootTaskDisplayAreaOrganizer rootTDAOrganizer,
RootDisplayAreaOrganizer rootDisplayAreaOrganizer,
- DesktopState desktopState) {
+ DesktopState desktopState,
+ IActivityTaskManager activityTaskManager) {
mContext = context;
mDisplayId = displayId;
mSyncQueue = syncQueue;
@@ -459,6 +461,7 @@
mStatusBarHider = new SplitStatusBarHider(taskOrganizer, splitState,
rootDisplayAreaOrganizer);
mSplitTransitionModifier = new SplitTransitionModifier();
+ mActivityTaskManager = activityTaskManager;
}
@VisibleForTesting
@@ -474,7 +477,8 @@
Optional<DesktopTasksController> desktopTasksController,
RootTaskDisplayAreaOrganizer rootTDAOrganizer,
RootDisplayAreaOrganizer rootDisplayAreaOrganizer,
- DesktopState desktopState) {
+ DesktopState desktopState,
+ IActivityTaskManager activityTaskManager) {
mContext = context;
mDisplayId = displayId;
mSyncQueue = syncQueue;
@@ -487,6 +491,7 @@
mDisplayInsetsController = displayInsetsController;
mTransactionPool = transactionPool;
mSplitLayout = splitLayout;
+ mActivityTaskManager = activityTaskManager;
mSplitTransitions = new SplitScreenTransitions(transactionPool, transitions,
this::onTransitionAnimationComplete, this);
mLogger = new SplitscreenEventLogger();
@@ -1638,10 +1643,8 @@
* Grants focus to the main or the side stages.
*/
protected void grantFocusToStage(@SplitPosition int stageToFocus) {
- IActivityTaskManager activityTaskManagerService = IActivityTaskManager.Stub.asInterface(
- ServiceManager.getService(Context.ACTIVITY_TASK_SERVICE));
try {
- activityTaskManagerService.setFocusedTask(getTaskId(stageToFocus));
+ mActivityTaskManager.setFocusedTask(getTaskId(stageToFocus));
} catch (RemoteException | NullPointerException e) {
ProtoLog.e(WM_SHELL_SPLIT_SCREEN,
"Unable to update focus on the chosen stage: %s", e.getMessage());
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitScreenController.java
index 08509a0..e5a5f24 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitScreenController.java
@@ -18,6 +18,7 @@
import static android.view.Display.DEFAULT_DISPLAY;
+import android.app.IActivityTaskManager;
import android.content.Context;
import android.os.Handler;
@@ -69,6 +70,7 @@
private final Handler mMainHandler;
private final SystemWindows mSystemWindows;
+ private final IActivityTaskManager mActivityTaskManager;
public TvSplitScreenController(Context context,
ShellInit shellInit,
@@ -91,13 +93,15 @@
Handler mainHandler,
SystemWindows systemWindows,
RootDisplayAreaOrganizer rootDisplayAreaOrganizer,
- DesktopState desktopState) {
+ DesktopState desktopState,
+ IActivityTaskManager activityTaskManager) {
super(context, shellInit, shellCommandHandler, shellController, shellTaskOrganizer,
syncQueue, rootTDAOrganizer, displayController, displayImeController,
displayInsetsController, null, transitions, transactionPool,
iconProvider, recentTasks, launchAdjacentController, Optional.empty(),
Optional.empty(), null /* stageCoordinator */, multiInstanceHelper, splitState,
- mainExecutor, mainHandler, rootDisplayAreaOrganizer, desktopState);
+ mainExecutor, mainHandler, rootDisplayAreaOrganizer, desktopState,
+ activityTaskManager);
mTaskOrganizer = shellTaskOrganizer;
mSyncQueue = syncQueue;
@@ -117,6 +121,7 @@
mSystemWindows = systemWindows;
mRootTDAOrganizer = rootTDAOrganizer;
mRootDisplayAreaOrganizer = rootDisplayAreaOrganizer;
+ mActivityTaskManager = activityTaskManager;
}
/**
@@ -130,7 +135,8 @@
mDisplayInsetsController, mTransitions, mTransactionPool,
mIconProvider, mMainExecutor, mMainHandler,
mRecentTasksOptional, mLaunchAdjacentController, mSplitState, mSystemWindows,
- mRootTDAOrganizer, mRootDisplayAreaOrganizer, getDesktopState());
+ mRootTDAOrganizer, mRootDisplayAreaOrganizer, getDesktopState(),
+ mActivityTaskManager);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvStageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvStageCoordinator.java
index c7361c1..938f0bf 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvStageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvStageCoordinator.java
@@ -16,6 +16,7 @@
package com.android.wm.shell.splitscreen.tv;
+import android.app.IActivityTaskManager;
import android.content.Context;
import android.os.Handler;
@@ -60,12 +61,13 @@
SplitState splitState,
SystemWindows systemWindows, RootTaskDisplayAreaOrganizer rootTDAOrganizer,
RootDisplayAreaOrganizer rootDisplayAreaOrganizer,
- DesktopState desktopState) {
+ DesktopState desktopState,
+ IActivityTaskManager activityTaskManager) {
super(context, displayId, syncQueue, taskOrganizer, displayController, displayImeController,
displayInsetsController, transitions, transactionPool, iconProvider,
mainExecutor, mainHandler, recentTasks, launchAdjacentController,
Optional.empty(), splitState, Optional.empty(), rootTDAOrganizer,
- rootDisplayAreaOrganizer, desktopState);
+ rootDisplayAreaOrganizer, desktopState, activityTaskManager);
mTvSplitMenuController = new TvSplitMenuController(context, this,
systemWindows, mainHandler);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java
index f18c0c4..05a9e35 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java
@@ -26,8 +26,12 @@
import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static com.android.window.flags.Flags.FLAG_EXCLUDE_TASK_FROM_RECENTS;
+import static com.android.window.flags.Flags.enableHandlersDebuggingMode;
import static com.android.wm.shell.Flags.FLAG_ENABLE_CREATE_ANY_BUBBLE;
import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_BUBBLES_NOISY;
+import static com.android.wm.shell.transition.TransitionDispatchState.CAPTURED_CHANGE_IN_WRONG_TRANSITION;
+import static com.android.wm.shell.transition.TransitionDispatchState.CAPTURED_UNRELATED_CHANGE;
+import static com.android.wm.shell.transition.TransitionDispatchState.LOST_RELEVANT_CHANGE;
import static com.android.wm.shell.transition.Transitions.transitTypeToString;
import android.annotation.NonNull;
@@ -59,6 +63,7 @@
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.shared.TransitionUtil;
import com.android.wm.shell.shared.bubbles.BubbleAnythingFlagHelper;
+import com.android.wm.shell.transition.TransitionDispatchState;
import com.android.wm.shell.transition.Transitions;
import java.util.ArrayList;
@@ -462,6 +467,9 @@
if (com.android.window.flags.Flags.disallowBubbleToEnterPip()) {
wct.setDisablePip(taskToken, false /* disablePip */);
}
+ if (BubbleAnythingFlagHelper.enableBubbleAnything()) {
+ wct.setDisableLaunchAdjacent(taskToken, false);
+ }
mShellExecutor.execute(() -> {
mTaskOrganizer.setInterceptBackPressedOnTaskRoot(taskToken, false /* intercept */);
mPending.add(new PendingTransition(TRANSIT_CHANGE, wct, taskView, null /* cookie */));
@@ -716,10 +724,25 @@
@NonNull SurfaceControl.Transaction startTransaction,
@NonNull SurfaceControl.Transaction finishTransaction,
@NonNull Transitions.TransitionFinishCallback finishCallback) {
- if (!Flags.taskViewTransitionsRefactor()) {
- return startAnimationLegacy(transition, info, startTransaction, finishTransaction,
- finishCallback);
+ return startAnimation(transition, info, TransitionDispatchState.getDummyInstance(),
+ startTransaction, finishTransaction, finishCallback);
+ }
+
+ @Override
+ public boolean startAnimation(@NonNull IBinder transition,
+ @Nullable TransitionInfo transitionInfo,
+ @NonNull TransitionDispatchState dispatchState,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction,
+ @NonNull Transitions.TransitionFinishCallback finishCallback) {
+ if (!Flags.taskViewTransitionsRefactor() && !enableHandlersDebuggingMode()) {
+ return startAnimationLegacy(transition, transitionInfo, startTransaction,
+ finishTransaction, finishCallback);
}
+ final boolean inDataCollectionModeOnly =
+ enableHandlersDebuggingMode() && transitionInfo == null;
+ final boolean inAnimationMode = !inDataCollectionModeOnly;
+ final TransitionInfo info = inDataCollectionModeOnly ? dispatchState.mInfo : transitionInfo;
final PendingTransition pending = findPending(transition);
ProtoLog.d(WM_SHELL_BUBBLES_NOISY, "Transitions.startAnimation(): taskView=%d "
@@ -744,10 +767,16 @@
final TransitionInfo.Change chg = info.getChanges().get(i);
if (isValidTaskView(chg, pending)) {
taskViews.add(chg);
+ if (inDataCollectionModeOnly) {
+ dispatchState.addError(this, chg, LOST_RELEVANT_CHANGE);
+ }
} else {
alienChanges.add(chg);
}
}
+ if (inDataCollectionModeOnly) {
+ return false;
+ }
// Prepare taskViews for animation
for (int i = 0; i < taskViews.size(); ++i) {
@@ -822,10 +851,12 @@
Slog.e(TAG, "Found a launching TaskView in the wrong transition. All "
+ "TaskView launches should be initiated by shell and in their "
+ "own transition: " + taskInfo.taskId);
+ dispatchState.addError(this, change, CAPTURED_CHANGE_IN_WRONG_TRANSITION);
} else {
Slog.w(TAG, "Found a non-TaskView task in a TaskView Transition. This "
+ "shouldn't happen, so there may be a visual artifact: "
+ taskInfo.taskId);
+ dispatchState.addError(this, change, CAPTURED_UNRELATED_CHANGE);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
index 3e504fe..e667c7a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
@@ -54,7 +54,6 @@
import com.android.wm.shell.splitscreen.SplitScreenController;
import com.android.wm.shell.splitscreen.StageCoordinator;
import com.android.wm.shell.sysui.ShellInit;
-import com.android.wm.shell.taskview.TaskViewTransitions;
import com.android.wm.shell.unfold.UnfoldTransitionHandler;
import java.util.ArrayList;
@@ -77,7 +76,6 @@
private final KeyguardTransitionHandler mKeyguardHandler;
private DesktopTasksController mDesktopTasksController;
private BubbleTransitions mBubbleTransitions;
- private TaskViewTransitions mTaskViewTransitions;
private UnfoldTransitionHandler mUnfoldHandler;
private ActivityEmbeddingController mActivityEmbeddingController;
@@ -125,10 +123,13 @@
static final int TYPE_OPEN_IN_DESKTOP = 12;
/** Transition of a visible app into a bubble. */
- static final int TYPE_ENTER_BUBBLES = 13;
+ static final int TYPE_LAUNCH_OR_CONVERT_TO_BUBBLE = 13;
/** Transition of a visible app in a split pair into a bubble. */
- static final int TYPE_ENTER_BUBBLES_FROM_SPLIT = 14;
+ static final int TYPE_LAUNCH_OR_CONVERT_SPLIT_TASK_TO_BUBBLE = 14;
+
+ /** Transition of a visible app into a bubble when launched from another bubble. */
+ static final int TYPE_LAUNCH_OR_CONVERT_TO_BUBBLE_FROM_EXISTING_BUBBLE = 15;
// Mixed transition sub-animation types
@@ -252,8 +253,7 @@
Optional<DesktopTasksController> desktopTasksControllerOptional,
Optional<UnfoldTransitionHandler> unfoldHandler,
Optional<ActivityEmbeddingController> activityEmbeddingController,
- BubbleTransitions bubbleTransitions,
- TaskViewTransitions taskViewTransitions) {
+ BubbleTransitions bubbleTransitions) {
mPlayer = player;
mKeyguardHandler = keyguardHandler;
if (pipTransitionController != null
@@ -275,7 +275,6 @@
mUnfoldHandler = unfoldHandler.orElse(null);
mActivityEmbeddingController = activityEmbeddingController.orElse(null);
mBubbleTransitions = bubbleTransitions;
- mTaskViewTransitions = taskViewTransitions;
}, this);
}
}
@@ -287,29 +286,44 @@
// Transitions involving a task that is being bubbled
if (requestHasBubbleEnter(request)) {
if (mSplitHandler.requestImpliesSplitToBubble(request)) {
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Got a Bubble-enter request "
- + "while Split-Screen is active, so treat it as Mixed.");
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
+ " Got a Bubble-enter request from a split task");
if (request.getRemoteTransition() != null) {
throw new IllegalStateException("Unexpected remote transition in"
+ "bubbles-enter-from-split request");
}
mBubbleTransitions.storePendingEnterTransition(transition, request);
mActiveTransitions.add(createDefaultMixedTransition(
- MixedTransition.TYPE_ENTER_BUBBLES_FROM_SPLIT, transition));
+ MixedTransition.TYPE_LAUNCH_OR_CONVERT_SPLIT_TASK_TO_BUBBLE, transition));
WindowContainerTransaction out = new WindowContainerTransaction();
mSplitHandler.addExitForBubblesIfNeeded(request, out);
return out;
} else {
// This check should happen after we've checked for split + bubble enter
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Got a Bubble-enter request");
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
+ " Got a Bubble-enter request");
if (request.getRemoteTransition() != null) {
throw new IllegalStateException("Unexpected remote transition in "
+ "bubbles-enter");
}
mBubbleTransitions.storePendingEnterTransition(transition, request);
mActiveTransitions.add(createDefaultMixedTransition(
- MixedTransition.TYPE_ENTER_BUBBLES, transition));
+ MixedTransition.TYPE_LAUNCH_OR_CONVERT_TO_BUBBLE, transition));
+ return new WindowContainerTransaction();
+ }
+ } else if (requestHasBubbleEnterFromAppBubble(request)) {
+ if (mSplitHandler.requestImpliesSplitToBubble(request)) {
+ // TODO: Handle from split
+ } else {
+ // Note: This will currently "intercept" launches even while the bubble is collapsed
+ // but we will not actually play any animation in DefaultMixedTransition unless the
+ // launch contains an appBubble task as well
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
+ " Got a Bubble-enter request from an app bubble");
+ mActiveTransitions.add(createDefaultMixedTransition(
+ MixedTransition.TYPE_LAUNCH_OR_CONVERT_TO_BUBBLE_FROM_EXISTING_BUBBLE,
+ transition));
return new WindowContainerTransaction();
}
}
@@ -750,6 +764,16 @@
&& mBubbleTransitions.hasPendingEnterTransition(request);
}
+ /**
+ * Returns whether the given request for a launching task is from an app bubble and should be
+ * handled by the bubbles transition.
+ */
+ public boolean requestHasBubbleEnterFromAppBubble(TransitionRequestInfo request) {
+ return BubbleAnythingFlagHelper.enableCreateAnyBubble()
+ && request.getTriggerTask() != null
+ && request.getTriggerTask().isAppBubble;
+ }
+
@Override
public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction startT,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedTransition.java
index 3a8eacc..5ea713c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedTransition.java
@@ -16,6 +16,8 @@
package com.android.wm.shell.transition;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_PIP;
import static android.view.WindowManager.TRANSIT_TO_BACK;
@@ -37,6 +39,7 @@
import com.android.wm.shell.pip.PipTransitionController;
import com.android.wm.shell.pip2.phone.transition.PipTransitionUtils;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
+import com.android.wm.shell.shared.TransitionUtil;
import com.android.wm.shell.splitscreen.StageCoordinator;
import com.android.wm.shell.unfold.UnfoldTransitionHandler;
@@ -90,12 +93,15 @@
animateEnterPipFromSplit(this, info, startTransaction, finishTransaction,
finishCallback, mPlayer, mMixedHandler, mPipHandler, mSplitHandler,
/*replacingPip*/ true);
- case TYPE_ENTER_BUBBLES ->
+ case TYPE_LAUNCH_OR_CONVERT_TO_BUBBLE ->
animateEnterBubbles(transition, info, startTransaction, finishTransaction,
finishCallback, mBubbleTransitions);
- case TYPE_ENTER_BUBBLES_FROM_SPLIT ->
+ case TYPE_LAUNCH_OR_CONVERT_SPLIT_TASK_TO_BUBBLE ->
animateEnterBubblesFromSplit(this, transition, info, startTransaction,
finishTransaction, finishCallback, mSplitHandler, mBubbleTransitions);
+ case TYPE_LAUNCH_OR_CONVERT_TO_BUBBLE_FROM_EXISTING_BUBBLE ->
+ animateEnterBubblesFromBubble(transition, info, startTransaction,
+ finishTransaction, finishCallback, mBubbleTransitions);
case TYPE_KEYGUARD ->
animateKeyguard(this, info, startTransaction, finishTransaction, finishCallback,
mKeyguardHandler, mPipHandler);
@@ -325,6 +331,7 @@
@NonNull BubbleTransitions bubbleTransitions) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Animating a mixed transition for "
+ "entering Bubbles while an app is in the foreground");
+ // TODO(b/408328557): Migrate to checking transition token
bubbleTransitions.getRunningEnterTransition(transition).startAnimation(
transition, info, startTransaction, finishTransaction, finishCallback);
return true;
@@ -341,11 +348,54 @@
@NonNull BubbleTransitions bubbleTransitions) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Animating a mixed transition for "
+ "entering Bubbles while Split-Screen is foreground.");
+ // TODO(b/408328557): Migrate to checking transition token
bubbleTransitions.getRunningEnterTransition(transition).startAnimation(
transition, info, startTransaction, finishTransaction, finishCallback);
return true;
}
+ static boolean animateEnterBubblesFromBubble(
+ @NonNull IBinder transition,
+ @NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction,
+ @NonNull Transitions.TransitionFinishCallback finishCallback,
+ @NonNull BubbleTransitions bubbleTransitions) {
+ // Identify the task being launched into a bubble
+ TransitionInfo.Change bubblingTask = null;
+ for (int i = 0; i < info.getChanges().size(); i++) {
+ final TransitionInfo.Change chg = info.getChanges().get(i);
+ if (chg.getTaskInfo() != null
+ && chg.getTaskInfo().getActivityType() == ACTIVITY_TYPE_STANDARD) {
+ if (!TransitionUtil.isOpeningMode(chg.getMode())
+ && chg.getMode() != TRANSIT_CHANGE) {
+ continue;
+ }
+ if (!chg.getTaskInfo().isAppBubble) {
+ continue;
+ }
+ bubblingTask = chg;
+ }
+ }
+ if (bubblingTask == null) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " No bubbling task found");
+ return false;
+ }
+
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Animating a mixed transition for "
+ + "entering Bubbles from another bubbled task");
+ boolean started = bubbleTransitions.startBubbleToBubbleLaunch(transition,
+ bubblingTask.getTaskInfo(), handler -> {
+ bubbleTransitions.getRunningEnterTransition(transition).startAnimation(
+ transition, info, startTransaction, finishTransaction, finishCallback);
+ });
+ if (!started) {
+ // If nothing started, we are still consuming it since nothing else should handle it
+ finishCallback.onTransitionFinished(null);
+ }
+ return true;
+ }
+
private boolean animateUnfold(
@NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction startTransaction,
@@ -464,8 +514,9 @@
mDesktopTasksController.mergeAnimation(
transition, info, startT, finishT, mergeTarget, finishCallback);
return;
- case TYPE_ENTER_BUBBLES:
- case TYPE_ENTER_BUBBLES_FROM_SPLIT:
+ case TYPE_LAUNCH_OR_CONVERT_TO_BUBBLE:
+ case TYPE_LAUNCH_OR_CONVERT_SPLIT_TASK_TO_BUBBLE:
+ case TYPE_LAUNCH_OR_CONVERT_TO_BUBBLE_FROM_EXISTING_BUBBLE:
final Transitions.TransitionHandler handler =
mBubbleTransitions.getRunningEnterTransition(transition);
if (handler != null) {
@@ -503,8 +554,9 @@
case TYPE_OPEN_IN_DESKTOP:
mDesktopTasksController.onTransitionConsumed(transition, aborted, finishT);
break;
- case TYPE_ENTER_BUBBLES:
- case TYPE_ENTER_BUBBLES_FROM_SPLIT:
+ case TYPE_LAUNCH_OR_CONVERT_TO_BUBBLE:
+ case TYPE_LAUNCH_OR_CONVERT_SPLIT_TASK_TO_BUBBLE:
+ case TYPE_LAUNCH_OR_CONVERT_TO_BUBBLE_FROM_EXISTING_BUBBLE:
final Transitions.TransitionHandler handler =
mBubbleTransitions.getRunningEnterTransition(transition);
if (handler != 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 277229c..940c54b 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
@@ -319,6 +319,22 @@
}
@Override
+ public boolean startAnimation(@NonNull IBinder transition,
+ @Nullable TransitionInfo info,
+ @NonNull TransitionDispatchState dispatchState,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction,
+ @NonNull Transitions.TransitionFinishCallback finishCallback) {
+ if (info == null) {
+ // In data collection mode: there can't be errors - nothing to do
+ return false;
+ }
+ // In animation mode: always play everything
+ return startAnimation(
+ transition, info, startTransaction, finishTransaction, finishCallback);
+ }
+
+ @Override
public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction startTransaction,
@NonNull SurfaceControl.Transaction finishTransaction,
@@ -658,10 +674,16 @@
for (int i = 0; i < info.getRootCount(); ++i) {
final int displayId = info.getRoot(i).getDisplayId();
- final SurfaceControl.Builder colorLayerBuilder = new SurfaceControl.Builder()
- .setName("animation-background")
+ final SurfaceControl backgroundSurface = new SurfaceControl.Builder()
+ .setName("animation-background for #" + info.getDebugId())
.setCallsite("DefaultTransitionHandler")
- .setColorLayer();
+ .setColorLayer()
+ .setParent(info.getRoot(i).getLeash())
+ .build();
+
+ startTransaction.setColor(backgroundSurface, colorArray)
+ .setLayer(backgroundSurface, -1)
+ .show(backgroundSurface);
// Attaching the background surface to the transition root could unexpectedly make it
// cover one of the split root tasks. To avoid this, put the background surface just
@@ -670,15 +692,10 @@
info.getChanges().stream().anyMatch(c-> c.getTaskInfo() != null
&& c.getTaskInfo().getWindowingMode() == WINDOWING_MODE_MULTI_WINDOW);
if (isSplitTaskInvolved) {
- mRootTDAOrganizer.attachToDisplayArea(displayId, colorLayerBuilder);
- } else {
- colorLayerBuilder.setParent(info.getRootLeash());
+ mRootTDAOrganizer.relZToDisplayArea(displayId, backgroundSurface, startTransaction,
+ -1);
}
- final SurfaceControl backgroundSurface = colorLayerBuilder.build();
- startTransaction.setColor(backgroundSurface, colorArray)
- .setLayer(backgroundSurface, -1)
- .show(backgroundSurface);
finishTransaction.remove(backgroundSurface);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/FocusTransitionObserver.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/FocusTransitionObserver.java
index 273db598..e6d73e4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/FocusTransitionObserver.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/FocusTransitionObserver.java
@@ -21,10 +21,10 @@
import static android.view.Display.INVALID_DISPLAY;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_OPEN;
+import static android.window.DesktopExperienceFlags.ENABLE_DISPLAY_FOCUS_IN_SHELL_TRANSITIONS;
import static android.window.TransitionInfo.FLAG_IS_DISPLAY;
import static android.window.TransitionInfo.FLAG_MOVED_TO_TOP;
-import static com.android.window.flags.Flags.enableDisplayFocusInShellTransitions;
import static com.android.wm.shell.transition.Transitions.TransitionObserver;
import android.annotation.NonNull;
@@ -68,7 +68,7 @@
* Update display/window focus state from the given transition info and notifies changes if any.
*/
public void updateFocusState(@NonNull TransitionInfo info) {
- if (!enableDisplayFocusInShellTransitions()) {
+ if (!ENABLE_DISPLAY_FOCUS_IN_SHELL_TRANSITIONS.isTrue()) {
return;
}
final SparseArray<RunningTaskInfo> lastTransitionFocusedTasks =
@@ -149,7 +149,7 @@
*/
public void setLocalFocusTransitionListener(FocusTransitionListener listener,
Executor executor) {
- if (!enableDisplayFocusInShellTransitions()) {
+ if (!ENABLE_DISPLAY_FOCUS_IN_SHELL_TRANSITIONS.isTrue()) {
return;
}
mLocalListeners.put(listener, executor);
@@ -165,7 +165,7 @@
*
*/
public void unsetLocalFocusTransitionListener(FocusTransitionListener listener) {
- if (!enableDisplayFocusInShellTransitions()) {
+ if (!ENABLE_DISPLAY_FOCUS_IN_SHELL_TRANSITIONS.isTrue()) {
return;
}
mLocalListeners.remove(listener);
@@ -177,7 +177,7 @@
*/
public void setRemoteFocusTransitionListener(Transitions transitions,
IFocusTransitionListener listener) {
- if (!enableDisplayFocusInShellTransitions()) {
+ if (!ENABLE_DISPLAY_FOCUS_IN_SHELL_TRANSITIONS.isTrue()) {
return;
}
mRemoteListener = listener;
@@ -211,7 +211,7 @@
}
private boolean isFocusedOnDisplay(@NonNull RunningTaskInfo task) {
- if (!enableDisplayFocusInShellTransitions()) {
+ if (!ENABLE_DISPLAY_FOCUS_IN_SHELL_TRANSITIONS.isTrue()) {
return task.isFocused;
}
final RunningTaskInfo focusedTaskOnDisplay = mFocusedTaskOnDisplay.get(task.displayId);
@@ -220,7 +220,8 @@
/** Returns the globally focused display id. */
public int getGloballyFocusedDisplayId() {
- if (!enableDisplayFocusInShellTransitions() || mFocusedDisplayId == INVALID_DISPLAY) {
+ if (!ENABLE_DISPLAY_FOCUS_IN_SHELL_TRANSITIONS.isTrue()
+ || mFocusedDisplayId == INVALID_DISPLAY) {
return INVALID_DISPLAY;
}
return mFocusedDisplayId;
@@ -230,7 +231,8 @@
* Gets the globally focused task ID.
*/
public int getGloballyFocusedTaskId() {
- if (!enableDisplayFocusInShellTransitions() || mFocusedDisplayId == INVALID_DISPLAY) {
+ if (!ENABLE_DISPLAY_FOCUS_IN_SHELL_TRANSITIONS.isTrue()
+ || mFocusedDisplayId == INVALID_DISPLAY) {
return INVALID_TASK_ID;
}
final RunningTaskInfo globallyFocusedTask = mFocusedTaskOnDisplay.get(mFocusedDisplayId);
@@ -242,7 +244,7 @@
* (Note {@link RunningTaskInfo#isFocused} represents per-display focus.)
*/
public boolean hasGlobalFocus(@NonNull RunningTaskInfo task) {
- if (!enableDisplayFocusInShellTransitions()) {
+ if (!ENABLE_DISPLAY_FOCUS_IN_SHELL_TRANSITIONS.isTrue()) {
return task.isFocused;
}
return task.displayId == mFocusedDisplayId && isFocusedOnDisplay(task);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionDispatchState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionDispatchState.java
index 820ee25..1070775 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionDispatchState.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionDispatchState.java
@@ -36,6 +36,7 @@
// Change-related errors
public static final int LOST_RELEVANT_CHANGE = 3;
public static final int CAPTURED_UNRELATED_CHANGE = 4;
+ public static final int CAPTURED_CHANGE_IN_WRONG_TRANSITION = 5;
@IntDef(
value = {
@@ -44,6 +45,7 @@
CAPTURED_UNRELATED_FLAG,
LOST_RELEVANT_CHANGE,
CAPTURED_UNRELATED_CHANGE,
+ CAPTURED_CHANGE_IN_WRONG_TRANSITION,
})
public @interface ErrorCode {}
@@ -54,6 +56,7 @@
case CAPTURED_UNRELATED_FLAG -> "CAPTURED_UNRELATED_FLAG";
case LOST_RELEVANT_CHANGE -> "LOST_RELEVANT_CHANGE";
case CAPTURED_UNRELATED_CHANGE -> "CAPTURED_UNRELATED_CHANGE";
+ case CAPTURED_CHANGE_IN_WRONG_TRANSITION -> "CAPTURED_CHANGE_IN_WRONG_TRANSITION";
default -> "UNKNOWN";
};
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
index 32f2c1e..832bde2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
@@ -23,8 +23,7 @@
import static android.content.pm.PackageManager.FEATURE_PC;
import static android.provider.Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS;
import static android.view.WindowManager.TRANSIT_CHANGE;
-
-import static com.android.window.flags.Flags.enableDisplayFocusInShellTransitions;
+import static android.window.DesktopExperienceFlags.ENABLE_DISPLAY_FOCUS_IN_SHELL_TRANSITIONS;
import android.annotation.NonNull;
import android.app.ActivityManager.RunningTaskInfo;
@@ -210,7 +209,7 @@
return;
}
- if (enableDisplayFocusInShellTransitions()) {
+ if (ENABLE_DISPLAY_FOCUS_IN_SHELL_TRANSITIONS.isTrue()) {
// Pass the current global focus status to avoid updates outside of a ShellTransition.
decoration.relayout(taskInfo, decoration.mHasGlobalFocus, decoration.mExclusionRegion);
} else {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
index 8793229..673c7d9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
@@ -27,9 +27,9 @@
import static android.view.MotionEvent.ACTION_MOVE;
import static android.view.MotionEvent.ACTION_UP;
import static android.view.WindowInsets.Type.statusBars;
+import static android.window.DesktopExperienceFlags.ENABLE_DISPLAY_FOCUS_IN_SHELL_TRANSITIONS;
import static com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_ENTER_MODE_APP_HANDLE_MENU;
-import static com.android.window.flags.Flags.enableDisplayFocusInShellTransitions;
import static com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.InputMethod;
import static com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.MinimizeReason;
import static com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ResizeTrigger;
@@ -590,7 +590,7 @@
removeTaskFromEventReceiver(oldTaskInfo.displayId);
incrementEventReceiverTasks(taskInfo.displayId);
}
- if (enableDisplayFocusInShellTransitions()) {
+ if (ENABLE_DISPLAY_FOCUS_IN_SHELL_TRANSITIONS.isTrue()) {
// Pass the current global focus status to avoid updates outside of a ShellTransition.
decoration.relayout(taskInfo, decoration.mHasGlobalFocus, decoration.mExclusionRegion);
} else {
@@ -633,6 +633,7 @@
if (decoration == null) {
createWindowDecoration(taskInfo, taskSurface, startT, finishT);
+ initializeTiling(taskInfo);
} else {
decoration.relayout(taskInfo, startT, finishT, false /* applyStartTransactionOnDraw */,
false /* shouldSetTaskPositionAndCrop */,
@@ -640,6 +641,30 @@
}
}
+ private void initializeTiling(RunningTaskInfo taskInfo) {
+ DesktopRepository taskRepository = mDesktopUserRepositories.getCurrent();
+ Integer leftTiledTaskId = taskRepository.getLeftTiledTask(taskInfo.displayId);
+ Integer rightTiledTaskId = taskRepository.getRightTiledTask(taskInfo.displayId);
+ boolean tilingAndPersistenceEnabled = DesktopModeFlags.ENABLE_TILE_RESIZING.isTrue()
+ && DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PERSISTENCE.isTrue();
+ if (leftTiledTaskId != null && leftTiledTaskId == taskInfo.taskId
+ && tilingAndPersistenceEnabled) {
+ snapPersistedTaskToHalfScreen(
+ taskInfo,
+ taskInfo.configuration.windowConfiguration.getBounds(),
+ SnapPosition.LEFT
+ );
+ }
+ if (rightTiledTaskId != null && rightTiledTaskId == taskInfo.taskId
+ && tilingAndPersistenceEnabled) {
+ snapPersistedTaskToHalfScreen(
+ taskInfo,
+ taskInfo.configuration.windowConfiguration.getBounds(),
+ SnapPosition.RIGHT
+ );
+ }
+ }
+
@Override
public void onTaskClosing(
RunningTaskInfo taskInfo,
@@ -754,6 +779,7 @@
} else {
mDesktopModeUiEventLogger.log(decoration.mTaskInfo,
DesktopUiEventEnum.DESKTOP_WINDOW_MAXIMIZE_BUTTON_MENU_TAP_TO_IMMERSIVE);
+ removeTaskIfTiled(decoration.mTaskInfo.displayId, decoration.mTaskInfo.taskId);
mDesktopImmersiveController.moveTaskToImmersive(decoration.mTaskInfo);
}
decoration.closeMaximizeMenu();
@@ -950,7 +976,15 @@
public boolean snapToHalfScreen(@NonNull RunningTaskInfo taskInfo,
@NonNull Rect currentDragBounds, @NonNull SnapPosition position) {
return mDesktopTilingDecorViewModel.snapToHalfScreen(taskInfo,
- mWindowDecorByTaskId.get(taskInfo.taskId), position, currentDragBounds);
+ mWindowDecorByTaskId.get(taskInfo.taskId), position, currentDragBounds, null);
+ }
+
+ @Override
+ public boolean snapPersistedTaskToHalfScreen(@NotNull RunningTaskInfo taskInfo,
+ @NotNull Rect currentDragBounds, @NotNull SnapPosition position) {
+ return mDesktopTilingDecorViewModel.snapToHalfScreen(taskInfo,
+ mWindowDecorByTaskId.get(taskInfo.taskId), position, currentDragBounds,
+ currentDragBounds);
}
@Override
@@ -959,8 +993,8 @@
}
@Override
- public void onUserChange() {
- mDesktopTilingDecorViewModel.onUserChange();
+ public void onUserChange(int userId) {
+ mDesktopTilingDecorViewModel.onUserChange(userId);
}
@Override
@@ -1218,6 +1252,12 @@
private void moveTaskToFront(RunningTaskInfo taskInfo) {
if (!mFocusTransitionObserver.hasGlobalFocus(taskInfo)) {
+ ProtoLog.d(WM_SHELL_DESKTOP_MODE,
+ "%s: task#%d in display#%d does not have global focus, moving to front "
+ + "globallyFocusedTaskId=%d globallyFocusedDisplayId=%d",
+ TAG, taskInfo.taskId, taskInfo.displayId,
+ mFocusTransitionObserver.getGloballyFocusedTaskId(),
+ mFocusTransitionObserver.getGloballyFocusedDisplayId());
mDesktopModeUiEventLogger.log(taskInfo,
DesktopUiEventEnum.DESKTOP_WINDOW_HEADER_TAP_TO_REFOCUS);
mDesktopTasksController.moveTaskToFront(taskInfo);
@@ -1323,6 +1363,7 @@
mDesktopTasksController.onDragPositioningMove(taskInfo,
decoration.mTaskSurface,
e.getRawX(dragPointerIdx),
+ e.getRawY(dragPointerIdx),
newTaskBounds);
// Flip mIsDragging only if the bounds actually changed.
if (mIsDragging || !newTaskBounds.equals(mOnDragStartInitialBounds)) {
@@ -1795,7 +1836,7 @@
}
final DesktopModeWindowDecoration windowDecoration =
mDesktopModeWindowDecorFactory.create(
- Flags.enableBugFixesForSecondaryDisplay()
+ DesktopExperienceFlags.ENABLE_BUG_FIXES_FOR_SECONDARY_DISPLAY.isTrue()
? mDisplayController.getDisplayContext(taskInfo.displayId)
: mContext,
mContext.createContextAsUser(UserHandle.of(taskInfo.userId), 0 /* flags */),
@@ -1811,6 +1852,7 @@
mMainDispatcher,
mBgScope,
mBgExecutor,
+ mTransitions,
mMainChoreographer,
mSyncQueue,
mAppHeaderViewHolderFactory,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
index dd60f49..ad68180 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
@@ -24,6 +24,7 @@
import static android.view.MotionEvent.ACTION_CANCEL;
import static android.view.MotionEvent.ACTION_DOWN;
import static android.view.MotionEvent.ACTION_UP;
+import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.window.DesktopModeFlags.ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION;
import static android.window.DesktopModeFlags.ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION_ALWAYS;
@@ -104,6 +105,7 @@
import com.android.wm.shell.shared.desktopmode.DesktopState;
import com.android.wm.shell.shared.multiinstance.ManageWindowsViewContainer;
import com.android.wm.shell.splitscreen.SplitScreenController;
+import com.android.wm.shell.transition.Transitions;
import com.android.wm.shell.windowdecor.common.DecorThemeUtil;
import com.android.wm.shell.windowdecor.common.Theme;
import com.android.wm.shell.windowdecor.common.WindowDecorTaskResourceLoader;
@@ -145,6 +147,7 @@
private final @ShellMainThread MainCoroutineDispatcher mMainDispatcher;
private final @ShellBackgroundThread CoroutineScope mBgScope;
private final @ShellBackgroundThread ShellExecutor mBgExecutor;
+ private final Transitions mTransitions;
private final Choreographer mChoreographer;
private final SyncTransactionQueue mSyncQueue;
private final SplitScreenController mSplitScreenController;
@@ -238,6 +241,7 @@
@ShellMainThread MainCoroutineDispatcher mainDispatcher,
@ShellBackgroundThread CoroutineScope bgScope,
@ShellBackgroundThread ShellExecutor bgExecutor,
+ Transitions transitions,
Choreographer choreographer,
SyncTransactionQueue syncQueue,
AppHeaderViewHolder.Factory appHeaderViewHolderFactory,
@@ -255,8 +259,8 @@
DesktopConfig desktopConfig) {
this (context, userContext, displayController, taskResourceLoader, splitScreenController,
desktopUserRepositories, taskOrganizer, taskInfo, taskSurface, handler,
- mainExecutor, mainDispatcher, bgScope, bgExecutor, choreographer, syncQueue,
- appHeaderViewHolderFactory, appHandleViewHolderFactory,
+ mainExecutor, mainDispatcher, bgScope, bgExecutor, transitions, choreographer,
+ syncQueue, appHeaderViewHolderFactory, appHandleViewHolderFactory,
rootTaskDisplayAreaOrganizer, genericLinksParser, assistContentRequester,
SurfaceControl.Builder::new, SurfaceControl.Transaction::new,
WindowContainerTransaction::new, SurfaceControl::new, new WindowManagerWrapper(
@@ -285,6 +289,7 @@
@ShellMainThread MainCoroutineDispatcher mainDispatcher,
@ShellBackgroundThread CoroutineScope bgScope,
@ShellBackgroundThread ShellExecutor bgExecutor,
+ Transitions transitions,
Choreographer choreographer,
SyncTransactionQueue syncQueue,
AppHeaderViewHolder.Factory appHeaderViewHolderFactory,
@@ -318,6 +323,7 @@
mMainDispatcher = mainDispatcher;
mBgScope = bgScope;
mBgExecutor = bgExecutor;
+ mTransitions = transitions;
mChoreographer = choreographer;
mSyncQueue = syncQueue;
mAppHeaderViewHolderFactory = appHeaderViewHolderFactory;
@@ -513,7 +519,6 @@
updateDragResizeListenerIfNeeded(mDecorationContainerSurface, inFullImmersive);
}
-
void relayout(ActivityManager.RunningTaskInfo taskInfo,
SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT,
boolean applyStartTransactionOnDraw, boolean shouldSetTaskVisibilityPositionAndCrop,
@@ -524,6 +529,13 @@
setCapturedLink(taskInfo.capturedLink, taskInfo.capturedLinkTimestamp);
}
+ if (DesktopExperienceFlags.ENABLE_BUG_FIXES_FOR_SECONDARY_DISPLAY.isTrue()) {
+ final Context dc = mDisplayController.getDisplayContext(taskInfo.displayId);
+ if (dc != null) {
+ mWindowManagerWrapper.updateWindowManager(dc.getSystemService(WindowManager.class));
+ }
+ }
+
if (isHandleMenuActive()) {
mHandleMenu.relayout(
startT,
@@ -558,8 +570,19 @@
relayout(mRelayoutParams, startT, finishT, wct, oldRootView, mResult);
// After this line, mTaskInfo is up-to-date and should be used instead of taskInfo
- Trace.beginSection("DesktopModeWindowDecoration#relayout-applyWCT");
- mBgExecutor.execute(() -> mTaskOrganizer.applyTransaction(wct));
+ if (DesktopExperienceFlags.ENABLE_DESKTOP_WINDOWING_PIP.isTrue()
+ && mRelayoutParams.mShouldSetAppBounds) {
+ // When expanding from PiP to freeform, we need to start a Transition for applying the
+ // inset changes so that PiP receives the insets for the final bounds. This is because
+ // |mShouldSetAppBounds| applies the insets by modifying app bounds, which can cause a
+ // bounds offset that needs to be reported to transition handlers.
+ Trace.beginSection("DesktopModeWindowDecoration#relayout-startTransition");
+ mHandler.post(
+ () -> mTransitions.startTransition(TRANSIT_CHANGE, wct, /* handler= */ null));
+ } else {
+ Trace.beginSection("DesktopModeWindowDecoration#relayout-applyWCT");
+ mBgExecutor.execute(() -> mTaskOrganizer.applyTransaction(wct));
+ }
Trace.endSection();
if (mResult.mRootView == null) {
@@ -1994,6 +2017,7 @@
@ShellMainThread MainCoroutineDispatcher mainDispatcher,
@ShellBackgroundThread CoroutineScope bgScope,
@ShellBackgroundThread ShellExecutor bgExecutor,
+ Transitions transitions,
Choreographer choreographer,
SyncTransactionQueue syncQueue,
AppHeaderViewHolder.Factory appHeaderViewHolderFactory,
@@ -2025,6 +2049,7 @@
mainDispatcher,
bgScope,
bgExecutor,
+ transitions,
choreographer,
syncQueue,
appHeaderViewHolderFactory,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
index 97a47c6..c2b8c55 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
@@ -23,12 +23,12 @@
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_CONSUMER;
-import static com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ResizeTrigger;
import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE;
import static com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_BOTTOM;
import static com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_LEFT;
import static com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_RIGHT;
import static com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_TOP;
+import static com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ResizeTrigger;
import static com.android.wm.shell.windowdecor.DragResizeWindowGeometry.isEdgeResizePermitted;
import static com.android.wm.shell.windowdecor.DragResizeWindowGeometry.isEventFromTouchscreen;
@@ -342,7 +342,6 @@
try {
mWindowSession.updateInputChannel(
mInputChannel.getToken(),
- null /* hostInputToken */,
mDisplayId,
mDecorationSurface,
FLAG_NOT_FOCUSABLE,
@@ -384,7 +383,6 @@
try {
mWindowSession.updateInputChannel(
mSinkInputChannel.getToken(),
- null /* hostInputToken */,
mDisplayId,
mInputSinkSurface,
FLAG_NOT_FOCUSABLE,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt
index 329f536..455a7bb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt
@@ -29,6 +29,7 @@
import android.graphics.PointF
import android.graphics.Rect
import android.os.Bundle
+import android.view.Display.DEFAULT_DISPLAY
import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.MotionEvent.ACTION_OUTSIDE
@@ -551,6 +552,9 @@
private val splitscreenBtn = windowingPill.requireViewById<ImageButton>(
R.id.split_screen_button
)
+ private val splitscreenBtnSpace = windowingPill.requireViewById<Space>(
+ R.id.split_screen_button_space
+ )
private val floatingBtn = windowingPill.requireViewById<ImageButton>(R.id.floating_button)
private val floatingBtnSpace = windowingPill.requireViewById<Space>(
R.id.floating_button_space
@@ -822,6 +826,13 @@
floatingBtnSpace.visibility = View.GONE
}
+ // TODO: b/362720126 - remove this check after entering split screen from handle menu
+ // is supported on external display.
+ if (taskInfo.displayId != DEFAULT_DISPLAY) {
+ splitscreenBtn.visibility = View.GONE
+ splitscreenBtnSpace.visibility = View.GONE
+ }
+
fullscreenBtn.isSelected = taskInfo.isFullscreen
fullscreenBtn.isEnabled = !taskInfo.isFullscreen
fullscreenBtn.imageTintList = style.windowingButtonColor
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositioner.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositioner.kt
index cadb51e..84e3161 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositioner.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositioner.kt
@@ -382,8 +382,7 @@
displayIds.clear()
if (topology == null) return
- val displayBounds = topology.getAbsoluteBounds()
- displayIds.addAll(List(displayBounds.size()) { displayBounds.keyAt(it) })
+ displayIds.addAll(topology.allNodesIdMap().keys)
}
companion object {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
index 83307a4..87a99a7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
@@ -31,7 +31,6 @@
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
-import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.PixelFormat;
import android.graphics.Point;
@@ -58,8 +57,8 @@
import android.window.WindowContainerTransaction;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.wm.shell.R;
import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.BoxShadowHelper;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.desktopmode.DesktopModeEventLogger;
import com.android.wm.shell.windowdecor.WindowDecoration.RelayoutParams.OccludingCaptionElement;
@@ -295,45 +294,13 @@
outResult.mCaptionTopPadding = params.mCaptionTopPadding;
if (params.mBorderSettingsId != Resources.ID_NULL) {
- TypedArray attr = mDecorWindowContext.obtainStyledAttributes(
- params.mBorderSettingsId, R.styleable.BorderSettings);
-
- outResult.mBorderSettings = new BorderSettings();
- outResult.mBorderSettings.strokeWidth =
- attr.getDimension(
- R.styleable.BorderSettings_borderStrokeWidth, 0f);
- outResult.mBorderSettings.color =
- attr.getColor(
- R.styleable.BorderSettings_borderColor, 0);
-
- attr.recycle();
+ outResult.mBorderSettings = BoxShadowHelper.getBorderSettings(mDecorWindowContext,
+ params.mBorderSettingsId);
}
if (params.mBoxShadowSettingsIds != null) {
- outResult.mBoxShadowSettings = new BoxShadowSettings();
- outResult.mBoxShadowSettings.boxShadows =
- new BoxShadowSettings.BoxShadowParams[params.mBoxShadowSettingsIds.length];
- for (int i = 0; i < params.mBoxShadowSettingsIds.length; i++) {
- TypedArray attr = mDecorWindowContext.obtainStyledAttributes(
- params.mBoxShadowSettingsIds[i], R.styleable.BoxShadowSettings);
-
- BoxShadowSettings.BoxShadowParams box =
- new BoxShadowSettings.BoxShadowParams();
- box.blurRadius = attr.getDimension(
- R.styleable.BoxShadowSettings_boxShadowBlurRadius, 0f);
- box.spreadRadius = attr.getDimension(
- R.styleable.BoxShadowSettings_boxShadowSpreadRadius, 0f);
- box.offsetX = attr.getDimension(
- R.styleable.BoxShadowSettings_boxShadowOffsetX, 0f);
- box.offsetY = attr.getDimension(
- R.styleable.BoxShadowSettings_boxShadowOffsetY, 0f);
- box.color = attr.getColor(
- R.styleable.BoxShadowSettings_boxShadowColor, 0);
-
- outResult.mBoxShadowSettings.boxShadows[i] = box;
-
- attr.recycle();
- }
+ outResult.mBoxShadowSettings = BoxShadowHelper.getBoxShadowSettings(mDecorWindowContext,
+ params.mBoxShadowSettingsIds);
}
if (DesktopExperienceFlags.ENABLE_DYNAMIC_RADIUS_COMPUTATION_BUGFIX.isTrue()) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowManagerWrapper.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowManagerWrapper.kt
index 5c2ff1b..9a05cac 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowManagerWrapper.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowManagerWrapper.kt
@@ -24,9 +24,8 @@
* decors more testable.
*/
class WindowManagerWrapper (
- private val windowManager: WindowManager
+ private var windowManager: WindowManager
){
-
fun addView(v: View, lp: WindowManager.LayoutParams) {
windowManager.addView(v, lp)
}
@@ -38,4 +37,8 @@
fun updateViewLayout(v: View, lp: WindowManager.LayoutParams) {
windowManager.updateViewLayout(v, lp)
}
+
+ fun updateWindowManager(windowManager: WindowManager) {
+ this.windowManager = windowManager
+ }
}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/AppHandleAndHeaderVisibilityHelper.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/AppHandleAndHeaderVisibilityHelper.kt
index 3f0234c..5a7594d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/AppHandleAndHeaderVisibilityHelper.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/AppHandleAndHeaderVisibilityHelper.kt
@@ -44,23 +44,24 @@
* handle/header should show or not for this task.
*/
fun shouldShowAppHandleOrHeader(taskInfo: ActivityManager.RunningTaskInfo): Boolean {
- if (!ENABLE_BUG_FIXES_FOR_SECONDARY_DISPLAY.isTrue) {
- return allowedForTask(taskInfo)
- }
- return allowedForTask(taskInfo) && allowedForDisplay(taskInfo.displayId)
- }
- private fun allowedForTask(taskInfo: ActivityManager.RunningTaskInfo): Boolean {
- // TODO (b/382023296): Remove once we no longer rely on
- // Flags.enableBugFixesForSecondaryDisplay as it is taken care of in #allowedForDisplay
- val display = displayController.getDisplay(taskInfo.displayId)
+ val display = displayController.getDisplay(taskInfo.displayId) ?: return false
if (display == null) {
// If DisplayController doesn't have it tracked, it could be a private/managed display.
return false
}
+ // All freeform windows should show the app header.
if (taskInfo.windowingMode == WindowConfiguration.WINDOWING_MODE_FREEFORM) {
return true
}
+
+ if (!ENABLE_BUG_FIXES_FOR_SECONDARY_DISPLAY.isTrue) {
+ return allowedForTask(taskInfo, display)
+ }
+ return allowedForTask(taskInfo, display) && allowedForDisplay(display)
+ }
+
+ private fun allowedForTask(taskInfo: ActivityManager.RunningTaskInfo, display: Display): Boolean {
if (splitScreenController?.isTaskRootOrStageRoot(taskInfo.taskId) == true) {
return false
}
@@ -96,13 +97,9 @@
&& !taskInfo.configuration.windowConfiguration.isAlwaysOnTop
}
- private fun allowedForDisplay(displayId: Int): Boolean {
- // If DisplayController doesn't have it tracked, it could be a private/managed display.
- val display = displayController.getDisplay(displayId)
- if (display == null) return false
-
+ private fun allowedForDisplay(display: Display): Boolean {
if (display.type != Display.TYPE_INTERNAL
- && !displayController.isDisplayInTopology(displayId)) {
+ && !displayController.isDisplayInTopology(display.displayId)) {
return false
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDecorViewModel.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDecorViewModel.kt
index e4c105c..6d8677c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDecorViewModel.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDecorViewModel.kt
@@ -23,6 +23,7 @@
import android.util.SparseArray
import android.window.DisplayAreaInfo
import android.window.WindowContainerTransaction
+import androidx.core.util.getOrElse
import androidx.core.util.valueIterator
import com.android.internal.annotations.VisibleForTesting
import com.android.wm.shell.R
@@ -41,6 +42,7 @@
import com.android.wm.shell.shared.annotations.ShellBackgroundThread
import com.android.wm.shell.shared.annotations.ShellMainThread
import com.android.wm.shell.shared.desktopmode.DesktopState
+import com.android.wm.shell.sysui.ShellInit
import com.android.wm.shell.transition.FocusTransitionObserver
import com.android.wm.shell.transition.Transitions
import com.android.wm.shell.windowdecor.DesktopModeWindowDecoration
@@ -66,28 +68,36 @@
private val focusTransitionObserver: FocusTransitionObserver,
private val mainExecutor: ShellExecutor,
private val desktopState: DesktopState,
+ private val shellInit: ShellInit,
) : DisplayChangeController.OnDisplayChangingListener {
@VisibleForTesting
- var tilingTransitionHandlerByDisplayId = SparseArray<DesktopTilingWindowDecoration>()
+ var tilingHandlerByUserAndDisplayId = SparseArray<SparseArray<DesktopTilingWindowDecoration>>()
+ var currentUserId: Int = -1
init {
// TODO(b/374309287): Move this interface implementation to
// [DesktopModeWindowDecorViewModel] when the migration is done.
- displayController.addDisplayChangingController(this)
+ shellInit.addInitCallback({ displayController.addDisplayChangingController(this) }, this)
}
fun snapToHalfScreen(
taskInfo: ActivityManager.RunningTaskInfo,
desktopModeWindowDecoration: DesktopModeWindowDecoration,
position: DesktopTasksController.SnapPosition,
- destinationBounds: Rect,
+ currentBounds: Rect,
+ destinationBounds: Rect? = null,
): Boolean {
val displayId = taskInfo.displayId
val handler =
- tilingTransitionHandlerByDisplayId.get(displayId)
- ?: run {
- val newHandler =
- DesktopTilingWindowDecoration(
+ tilingHandlerByUserAndDisplayId
+ .getOrElse(currentUserId) {
+ SparseArray<DesktopTilingWindowDecoration>().also {
+ tilingHandlerByUserAndDisplayId[currentUserId] = it
+ }
+ }
+ .getOrElse(displayId) {
+ val userHandlerList = tilingHandlerByUserAndDisplayId[currentUserId]
+ DesktopTilingWindowDecoration(
context,
mainDispatcher,
bgScope,
@@ -106,45 +116,56 @@
mainExecutor,
desktopState,
)
- tilingTransitionHandlerByDisplayId.put(displayId, newHandler)
- newHandler
+ .also { userHandlerList[displayId] = it }
}
transitions.registerObserver(handler)
- return handler.onAppTiled(
+ return destinationBounds?.let { handler.onAppTiled(
taskInfo,
desktopModeWindowDecoration,
position,
- destinationBounds,
- )
+ currentBounds, it)} ?: handler.onAppTiled(
+ taskInfo = taskInfo,
+ desktopModeWindowDecoration = desktopModeWindowDecoration,
+ position = position,
+ currentBounds = currentBounds)
}
fun removeTaskIfTiled(displayId: Int, taskId: Int) {
- tilingTransitionHandlerByDisplayId.get(displayId)?.removeTaskIfTiled(taskId)
+ tilingHandlerByUserAndDisplayId[currentUserId]?.get(displayId)?.removeTaskIfTiled(taskId)
}
fun moveTaskToFrontIfTiled(taskInfo: RunningTaskInfo): Boolean {
// Always pass focus=true because taskInfo.isFocused is not updated yet.
- return tilingTransitionHandlerByDisplayId
- .get(taskInfo.displayId)
+ return tilingHandlerByUserAndDisplayId[currentUserId]
+ ?.get(taskInfo.displayId)
?.moveTiledPairToFront(taskInfo.taskId, isFocusedOnDisplay = true) ?: false
}
fun onOverviewAnimationStateChange(
@RecentsTransitionStateListener.RecentsTransitionState state: Int
) {
- for (tilingHandler in tilingTransitionHandlerByDisplayId.valueIterator()) {
+ val activeUserHandlers = tilingHandlerByUserAndDisplayId[currentUserId] ?: return
+ for (tilingHandler in activeUserHandlers.valueIterator()) {
tilingHandler.onOverviewAnimationStateChange(state)
}
}
- fun onUserChange() {
- for (tilingHandler in tilingTransitionHandlerByDisplayId.valueIterator()) {
- tilingHandler.resetTilingSession()
+ fun onUserChange(userId: Int) {
+ if (userId == currentUserId) return
+ try {
+ val activeUserHandlers = tilingHandlerByUserAndDisplayId[currentUserId] ?: return
+ for (tilingHandler in activeUserHandlers.valueIterator()) {
+ tilingHandler.hideDividerBar()
+ }
+ } finally {
+ currentUserId = userId
}
}
fun onTaskInfoChange(taskInfo: RunningTaskInfo) {
- tilingTransitionHandlerByDisplayId.get(taskInfo.displayId)?.onTaskInfoChange(taskInfo)
+ tilingHandlerByUserAndDisplayId[currentUserId]
+ ?.get(taskInfo.displayId)
+ ?.onTaskInfoChange(taskInfo)
}
override fun onDisplayChange(
@@ -157,12 +178,14 @@
// Exit if the rotation hasn't changed or is changed by 180 degrees. [fromRotation] and
// [toRotation] can be one of the [@Surface.Rotation] values.
if ((fromRotation % 2 == toRotation % 2)) return
- tilingTransitionHandlerByDisplayId.get(displayId)?.resetTilingSession()
+ tilingHandlerByUserAndDisplayId[currentUserId]?.get(displayId)?.resetTilingSession()
}
fun getRightSnapBoundsIfTiled(displayId: Int): Rect {
val tilingBounds =
- tilingTransitionHandlerByDisplayId.get(displayId)?.getRightSnapBoundsIfTiled()
+ tilingHandlerByUserAndDisplayId[currentUserId]
+ ?.get(displayId)
+ ?.getRightSnapBoundsIfTiled()
if (tilingBounds != null) {
return tilingBounds
}
@@ -183,7 +206,9 @@
fun getLeftSnapBoundsIfTiled(displayId: Int): Rect {
val tilingBounds =
- tilingTransitionHandlerByDisplayId.get(displayId)?.getLeftSnapBoundsIfTiled()
+ tilingHandlerByUserAndDisplayId[currentUserId]
+ ?.get(displayId)
+ ?.getLeftSnapBoundsIfTiled()
if (tilingBounds != null) {
return tilingBounds
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecoration.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecoration.kt
index cc8c78b3..9d5ab2f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecoration.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecoration.kt
@@ -32,13 +32,13 @@
import android.view.WindowManager.TRANSIT_PIP
import android.view.WindowManager.TRANSIT_TO_BACK
import android.view.WindowManager.TRANSIT_TO_FRONT
+import android.window.DesktopExperienceFlags
import android.window.TransitionInfo
import android.window.TransitionInfo.Change
import android.window.TransitionRequestInfo
import android.window.WindowContainerTransaction
import com.android.internal.annotations.VisibleForTesting
import com.android.launcher3.icons.BaseIconFactory
-import com.android.window.flags.Flags
import com.android.wm.shell.R
import com.android.wm.shell.RootTaskDisplayAreaOrganizer
import com.android.wm.shell.ShellTaskOrganizer
@@ -119,8 +119,8 @@
desktopModeWindowDecoration: DesktopModeWindowDecoration,
position: SnapPosition,
currentBounds: Rect,
+ destinationBounds: Rect = getSnapBounds(position)
): Boolean {
- val destinationBounds = getSnapBounds(position)
val resizeMetadata =
AppResizingHelper(
taskInfo,
@@ -204,7 +204,7 @@
desktopTilingDividerWindowManager = initTilingManagerForDisplay(displayId, config)
isTilingManagerInitialised = true
- if (Flags.enableDisplayFocusInShellTransitions()) {
+ if (DesktopExperienceFlags.ENABLE_DISPLAY_FOCUS_IN_SHELL_TRANSITIONS.isTrue) {
focusTransitionObserver.setLocalFocusTransitionListener(this, mainExecutor)
} else {
shellTaskOrganizer.addFocusListener(this)
@@ -445,7 +445,7 @@
removeTaskIfTiled(it.taskId, /* taskVanished= */ false, it.isFullscreen)
} else if (isEnteringPip(change, info.type)) {
removeTaskIfTiled(it.taskId, /* taskVanished= */ true, it.isFullscreen)
- } else if (isTransitionToFront(change.mode, info.type)) {
+ } else if (isTransitionToFront(change.mode)) {
handleTaskBroughtToFront(it.taskId)
leftTaskBroughtToFront =
leftTaskBroughtToFront ||
@@ -496,8 +496,8 @@
return false
}
- private fun isTransitionToFront(changeMode: Int, transitionType: Int): Boolean =
- changeMode == TRANSIT_TO_FRONT && transitionType == TRANSIT_TO_FRONT
+ private fun isTransitionToFront(changeMode: Int): Boolean =
+ changeMode == TRANSIT_TO_FRONT
class AppResizingHelper(
val taskInfo: RunningTaskInfo,
@@ -580,7 +580,7 @@
// Overriding ShellTaskOrganizer.FocusListener
override fun onFocusTaskChanged(taskInfo: RunningTaskInfo?) {
- if (Flags.enableDisplayFocusInShellTransitions()) return
+ if (DesktopExperienceFlags.ENABLE_DISPLAY_FOCUS_IN_SHELL_TRANSITIONS.isTrue) return
if (taskInfo != null) {
moveTiledPairToFront(taskInfo.taskId, taskInfo.isFocused)
}
@@ -592,7 +592,7 @@
isFocusedOnDisplay: Boolean,
isFocusedGlobally: Boolean,
) {
- if (!Flags.enableDisplayFocusInShellTransitions()) return
+ if (!DesktopExperienceFlags.ENABLE_DISPLAY_FOCUS_IN_SHELL_TRANSITIONS.isTrue) return
moveTiledPairToFront(taskId, isFocusedOnDisplay)
}
@@ -707,6 +707,9 @@
removeTaskIfTiled(taskId, taskVanished = true, shouldDelayUpdate = true)
}
+ fun hideDividerBar() {
+ desktopTilingDividerWindowManager?.hideDividerBar()
+ }
/**
* Moves the tiled pair to the front of the task stack, if the [taskInfo] is focused and one of
* the two tiled tasks.
@@ -720,7 +723,7 @@
if (!isFocusedOnDisplay) return false
// If a task that isn't tiled is being focused, let the generic handler do the work.
- if (!Flags.enableDisplayFocusInShellTransitions() && isTilingFocusRemoved(taskId)) {
+ if (!DesktopExperienceFlags.ENABLE_DISPLAY_FOCUS_IN_SHELL_TRANSITIONS.isTrue && isTilingFocusRemoved(taskId)) {
isTilingFocused = false
return false
}
@@ -731,7 +734,7 @@
val isLeftOnTop = taskId == leftTiledTask.taskInfo.taskId
if (!isTilingRefocused(taskId)) return false
val t = transactionSupplier.get()
- if (!Flags.enableDisplayFocusInShellTransitions()) isTilingFocused = true
+ if (!DesktopExperienceFlags.ENABLE_DISPLAY_FOCUS_IN_SHELL_TRANSITIONS.isTrue) isTilingFocused = true
if (taskId == leftTaskResizingHelper?.taskInfo?.taskId) {
desktopTilingDividerWindowManager?.onRelativeLeashChanged(leftTiledTask.getLeash(), t)
}
@@ -836,7 +839,7 @@
private fun tearDownTiling() {
if (isTilingManagerInitialised) {
- if (Flags.enableDisplayFocusInShellTransitions()) {
+ if (DesktopExperienceFlags.ENABLE_DISPLAY_FOCUS_IN_SHELL_TRANSITIONS.isTrue) {
focusTransitionObserver.unsetLocalFocusTransitionListener(this)
} else {
shellTaskOrganizer.removeFocusListener(this)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/SnapEventHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/SnapEventHandler.kt
index 4cf8443..ef3d694 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/SnapEventHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/SnapEventHandler.kt
@@ -30,11 +30,18 @@
position: SnapPosition,
): Boolean
+ /** Snaps an app to half the screen for tiling after a persistence trigger. */
+ fun snapPersistedTaskToHalfScreen(
+ taskInfo: RunningTaskInfo,
+ currentDragBounds: Rect,
+ position: SnapPosition,
+ ): Boolean
+
/** Removes a task from tiling if it's tiled, for example on task exiting. */
fun removeTaskIfTiled(displayId: Int, taskId: Int)
/** Notifies the tiling handler of user switch. */
- fun onUserChange()
+ fun onUserChange(userId: Int)
/** Notifies the tiling handler of overview animation state change. */
fun onOverviewAnimationStateChange(@RecentsTransitionState state: Int)
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/ScaleDensityForExternalDisplayTest.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/ScaleDensityForExternalDisplayTest.kt
new file mode 100644
index 0000000..cf8301f
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/ScaleDensityForExternalDisplayTest.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.functional
+
+import android.platform.test.annotations.Postsubmit
+import com.android.wm.shell.scenarios.ScaleDensityForExternalDisplay
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+
+/* Functional test for [ScaleDensityForExternalDisplay]. */
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+class ScaleDensityForExternalDisplayTest : ScaleDensityForExternalDisplay()
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ExitDesktopWithDragToTopDragZone.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ExitDesktopWithDragToTopDragZone.kt
index d82c066..4d64fa4 100644
--- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ExitDesktopWithDragToTopDragZone.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ExitDesktopWithDragToTopDragZone.kt
@@ -20,7 +20,7 @@
import android.tools.Rotation
import com.android.window.flags.Flags
import com.android.wm.shell.Utils
-import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
+import com.android.wm.shell.shared.desktopmode.DesktopConfig
import org.junit.After
import org.junit.Assume
import org.junit.Before
@@ -29,8 +29,7 @@
import org.junit.Test
@Ignore("Test Base Class")
-abstract class ExitDesktopWithDragToTopDragZone
-constructor(
+abstract class ExitDesktopWithDragToTopDragZone(
val rotation: Rotation = Rotation.ROTATION_0,
isResizeable: Boolean = true,
isLandscapeApp: Boolean = true,
@@ -40,10 +39,10 @@
@Before
fun setup() {
+ val desktopConfig = DesktopConfig.fromContext(instrumentation.context)
Assume.assumeTrue(Flags.enableDesktopWindowingMode() && tapl.isTablet)
// Skip the test when the drag-to-maximize is enabled on this device.
- Assume.assumeFalse(
- DesktopModeStatus.shouldMaximizeWhenDragToTopEdge(instrumentation.context))
+ Assume.assumeFalse(desktopConfig.shouldMaximizeWhenDragToTopEdge)
tapl.setEnableRotation(true)
tapl.setExpectedRotation(rotation.value)
testApp.enterDesktopMode(wmHelper, device)
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MaximizeAppWindowWithDragToTopDragZone.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MaximizeAppWindowWithDragToTopDragZone.kt
index a4cda50..7a6d1fc 100644
--- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MaximizeAppWindowWithDragToTopDragZone.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MaximizeAppWindowWithDragToTopDragZone.kt
@@ -28,7 +28,7 @@
import com.android.server.wm.flicker.helpers.SimpleAppHelper
import com.android.window.flags.Flags
import com.android.wm.shell.Utils
-import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
+import com.android.wm.shell.shared.desktopmode.DesktopConfig
import org.junit.After
import org.junit.Assume
import org.junit.Before
@@ -40,8 +40,9 @@
* Base scenario test for maximizing a desktop app window by dragging it to the top drag zone.
*/
@Ignore("Test Base Class")
-abstract class MaximizeAppWindowWithDragToTopDragZone
-constructor(private val rotation: Rotation = Rotation.ROTATION_0) : TestScenarioBase() {
+abstract class MaximizeAppWindowWithDragToTopDragZone(
+ private val rotation: Rotation = Rotation.ROTATION_0
+) : TestScenarioBase() {
private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
private val tapl = LauncherInstrumentation()
private val wmHelper = WindowManagerStateHelper(instrumentation)
@@ -52,10 +53,10 @@
@Before
fun setup() {
+ val desktopConfig = DesktopConfig.fromContext(instrumentation.context)
Assume.assumeTrue(Flags.enableDesktopWindowingMode() && tapl.isTablet)
// Skip the test when the drag-to-maximize is disabled on this device.
- Assume.assumeTrue(
- DesktopModeStatus.shouldMaximizeWhenDragToTopEdge(instrumentation.context))
+ Assume.assumeTrue(desktopConfig.shouldMaximizeWhenDragToTopEdge)
tapl.setEnableRotation(true)
tapl.setExpectedRotation(rotation.value)
ChangeDisplayOrientationRule.setRotation(rotation)
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MinimizeWindowOnAppOpen.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MinimizeWindowOnAppOpen.kt
index e8b6dee..344aa56 100644
--- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MinimizeWindowOnAppOpen.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MinimizeWindowOnAppOpen.kt
@@ -25,7 +25,7 @@
import com.android.server.wm.flicker.helpers.MailAppHelper
import com.android.server.wm.flicker.helpers.SimpleAppHelper
import com.android.window.flags.Flags
-import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
+import com.android.wm.shell.shared.desktopmode.DesktopConfig
import org.junit.After
import org.junit.Assume
import org.junit.Before
@@ -38,8 +38,7 @@
* is 4.
*/
@Ignore("Test Base Class")
-abstract class MinimizeWindowOnAppOpen() : TestScenarioBase()
-{
+abstract class MinimizeWindowOnAppOpen() : TestScenarioBase() {
private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
private val tapl = LauncherInstrumentation()
private val wmHelper = WindowManagerStateHelper(instrumentation)
@@ -47,8 +46,9 @@
private val testApp = DesktopModeAppHelper(SimpleAppHelper(instrumentation))
private val mailApp = DesktopModeAppHelper(MailAppHelper(instrumentation))
+ private val desktopConfig = DesktopConfig.fromContext(instrumentation.context)
- private val maxNum = DesktopModeStatus.getMaxTaskLimit(instrumentation.context)
+ private val maxNum = desktopConfig.maxTaskLimit
@Before
fun setup() {
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/OpenUnlimitedApps.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/OpenUnlimitedApps.kt
index ac48098..587aac9 100644
--- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/OpenUnlimitedApps.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/OpenUnlimitedApps.kt
@@ -27,12 +27,13 @@
import com.android.server.wm.flicker.helpers.MailAppHelper
import com.android.server.wm.flicker.helpers.SimpleAppHelper
import com.android.window.flags.Flags
-import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
+import com.android.wm.shell.shared.desktopmode.DesktopConfig
import org.junit.After
import org.junit.Assume
import org.junit.Before
import org.junit.Ignore
import org.junit.Test
+
/**
* Base scenario test for opening many apps on the device without the window limit.
*/
@@ -46,8 +47,9 @@
private val testApp = DesktopModeAppHelper(SimpleAppHelper(instrumentation))
private val mailApp = MailAppHelper(instrumentation)
+ private val desktopConfig = DesktopConfig.fromContext(instrumentation.context)
- private val maxNum = DesktopModeStatus.getMaxTaskLimit(instrumentation.context)
+ private val maxNum = desktopConfig.maxTaskLimit
@Before
fun setup() {
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ScaleDensityForExternalDisplay.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ScaleDensityForExternalDisplay.kt
new file mode 100644
index 0000000..3b896a3
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ScaleDensityForExternalDisplay.kt
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.scenarios
+
+import android.app.ActivityManager
+import android.app.Instrumentation
+import android.content.Intent
+import android.os.UserHandle
+import android.platform.test.annotations.RequiresFlagsEnabled
+import android.platform.test.flag.junit.DeviceFlagsValueProvider
+import android.provider.Settings
+import android.tools.NavBar
+import android.tools.Rotation
+import android.view.IWindowManager
+import android.view.WindowManagerGlobal
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.By.desc
+import androidx.test.uiautomator.By.text
+import androidx.test.uiautomator.UiDevice
+import com.android.compatibility.common.util.UiAutomatorUtils2.waitFindObject
+import com.android.window.flags.Flags
+import com.android.wm.shell.Utils
+import org.junit.After
+import com.google.common.truth.Truth.assertThat
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+import platform.test.desktop.SimulatedConnectedDisplayTestRule
+
+
+const val FLAG_DISPLAY_SIZE_CONNECTED_DISPLAY_SETTING: String =
+ "com.android.settings.flags.display_size_connected_display_setting"
+const val FLAG_RESOLUTION_AND_ENABLE_CONNECTED_DISPLAY_SETTING =
+ "com.android.settings.flags.resolution_and_enable_connected_display_setting"
+
+@Ignore("Test Base Class")
+@RequiresFlagsEnabled(
+ Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
+ FLAG_DISPLAY_SIZE_CONNECTED_DISPLAY_SETTING,
+ FLAG_RESOLUTION_AND_ENABLE_CONNECTED_DISPLAY_SETTING
+)
+abstract class ScaleDensityForExternalDisplay : TestScenarioBase() {
+
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val device = UiDevice.getInstance(instrumentation)
+ private val wm: IWindowManager = requireNotNull(WindowManagerGlobal.getWindowManagerService())
+ private val activityManager: ActivityManager? = instrumentation.context.getSystemService(ActivityManager::class.java)
+
+ private val settingsResources =
+ instrumentation.context.packageManager.getResourcesForApplication(SETTINGS_PACKAGE_NAME)
+ private val externalDisplaySettings = getSettingsString(EXTERNAL_DISPLAY_SETTING_RES)
+ private val increaseDensityDescription = getSettingsString(INCREASE_DENSITY_DESCRIPTION_RES)
+ private val decreaseDensityDescription = getSettingsString(DECREASE_DENSITY_DESCRIPTION_RES)
+
+ @get:Rule(order = 0) val checkFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()
+ @get:Rule(order = 1) val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL, Rotation.ROTATION_0)
+ @get:Rule(order = 2) val connectedDisplayRule = SimulatedConnectedDisplayTestRule()
+
+ @Test
+ fun increaseDensity() {
+ val connectedDisplayId = connectedDisplayRule.setupTestDisplay()
+ device.waitForIdle()
+ wm.clearForcedDisplayDensityForUser(connectedDisplayId, UserHandle.myUserId())
+ val initialDensity = wm.getBaseDisplayDensity(connectedDisplayId)
+
+ instrumentation.context
+ .startActivity(
+ Intent(Settings.ACTION_BLUETOOTH_SETTINGS)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
+ )
+
+ waitFindObject(text(externalDisplaySettings)).click()
+ waitFindObject(desc(increaseDensityDescription)).click()
+ device.waitForWindowUpdate(SETTINGS_PACKAGE_NAME, SETTINGS_UPDATE_TIME_OUT)
+ device.waitForIdle()
+ val currentDensity = wm.getBaseDisplayDensity(connectedDisplayId)
+
+ assertThat(initialDensity).isLessThan(currentDensity)
+ }
+
+ @Test
+ fun decreaseDensity() {
+ val connectedDisplayId = connectedDisplayRule.setupTestDisplay()
+ device.waitForIdle()
+ wm.clearForcedDisplayDensityForUser(connectedDisplayId, UserHandle.myUserId())
+ val initialDensity = wm.getBaseDisplayDensity(connectedDisplayId)
+
+ instrumentation.context
+ .startActivity(
+ Intent(Settings.ACTION_BLUETOOTH_SETTINGS)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
+ )
+ waitFindObject(text(externalDisplaySettings)).click()
+ waitFindObject(desc(decreaseDensityDescription)).click()
+ device.waitForWindowUpdate(SETTINGS_PACKAGE_NAME, SETTINGS_UPDATE_TIME_OUT)
+ device.waitForIdle()
+ val currentDensity = wm.getBaseDisplayDensity(connectedDisplayId)
+
+ assertThat(initialDensity).isGreaterThan(currentDensity)
+ }
+
+ @Test
+ fun restoreDensityAfterReconnection() {
+ var connectedDisplayId = connectedDisplayRule.setupTestDisplay()
+ device.waitForIdle()
+ wm.clearForcedDisplayDensityForUser(connectedDisplayId, UserHandle.myUserId())
+
+ instrumentation.context
+ .startActivity(
+ Intent(Settings.ACTION_BLUETOOTH_SETTINGS)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
+ )
+
+ waitFindObject(text(externalDisplaySettings)).click()
+ waitFindObject(desc(increaseDensityDescription)).click()
+ device.waitForWindowUpdate(SETTINGS_PACKAGE_NAME, SETTINGS_UPDATE_TIME_OUT)
+ val lastDensity = wm.getBaseDisplayDensity(connectedDisplayId)
+
+ var idAfterReconnection = connectedDisplayRule.setupTestDisplay()
+ device.waitForIdle()
+ val densityAfterReconnection = wm.getBaseDisplayDensity(idAfterReconnection)
+
+ assertThat(lastDensity).isEqualTo(densityAfterReconnection)
+ }
+
+ @After
+ fun teardown() {
+ activityManager?.forceStopPackage(SETTINGS_PACKAGE_NAME)
+ }
+
+ private fun getSettingsString(resName: String): String {
+ val identifier = settingsResources.getIdentifier(resName, "string", SETTINGS_PACKAGE_NAME)
+ return settingsResources.getString(identifier)
+ }
+
+ private companion object {
+ const val SETTINGS_PACKAGE_NAME = "com.android.settings"
+ const val EXTERNAL_DISPLAY_SETTING_RES = "external_display_settings_title"
+ const val INCREASE_DENSITY_DESCRIPTION_RES = "screen_zoom_make_larger_desc"
+ const val DECREASE_DENSITY_DESCRIPTION_RES = "screen_zoom_make_smaller_desc"
+ const val SETTINGS_UPDATE_TIME_OUT: Long = 2000
+ }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt
index e39fa3a..35b66c9 100644
--- a/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt
@@ -97,7 +97,7 @@
listOf(
ComponentNameMatcher.SPLASH_SCREEN,
ComponentNameMatcher.SNAPSHOT,
- ComponentNameMatcher.IME_SNAPSHOT,
+ ComponentNameMatcher.IME_SCREENSHOT,
magnifierLayer,
popupWindowLayer
)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/RootTaskDisplayAreaOrganizerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/RootTaskDisplayAreaOrganizerTest.kt
new file mode 100644
index 0000000..e006dba
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/RootTaskDisplayAreaOrganizerTest.kt
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.wm.shell
+
+import android.view.Display.DEFAULT_DISPLAY
+import android.view.SurfaceControl
+import android.window.DisplayAreaInfo
+import android.window.DisplayAreaOrganizer
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.wm.shell.RootTaskDisplayAreaOrganizer.RootTaskDisplayAreaListener
+import com.android.wm.shell.sysui.ShellInit
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Tests for [RootTaskDisplayAreaOrganizerTest].
+ *
+ * Build/Install/Run:
+ * atest WMShellUnitTests:RootTaskDisplayAreaOrganizerTest
+ */
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class RootTaskDisplayAreaOrganizerTest : ShellTestCase() {
+
+ private val executor = TestShellExecutor()
+ private val shellInit = ShellInit(executor)
+
+ private lateinit var organizer: RootTaskDisplayAreaOrganizer
+
+ @Before
+ fun setUp() {
+ organizer = RootTaskDisplayAreaOrganizer(executor, context, shellInit)
+ }
+
+ @Test
+ fun registerListener_callsBackWithExistingRootTDA() {
+ organizer.onDisplayAreaAppeared(createDisplayAreaInfo(FIRST_DISPLAY), SurfaceControl())
+ organizer.onDisplayAreaAppeared(createDisplayAreaInfo(SECOND_DISPLAY), SurfaceControl())
+
+ val listener = FakeRootTaskDisplayAreaListener()
+ organizer.registerListener(FIRST_DISPLAY, listener)
+
+ assertThat(listener.displayAreas).containsExactly(FIRST_DISPLAY)
+ }
+
+ @Test
+ fun registerListener_otherForSameDisplay_callsBothBackWithExistingRootTDAs() {
+ organizer.onDisplayAreaAppeared(createDisplayAreaInfo(FIRST_DISPLAY), SurfaceControl())
+ organizer.onDisplayAreaAppeared(createDisplayAreaInfo(SECOND_DISPLAY), SurfaceControl())
+
+ val listener1 = FakeRootTaskDisplayAreaListener()
+ val listener2 = FakeRootTaskDisplayAreaListener()
+ organizer.registerListener(SECOND_DISPLAY, listener1)
+ organizer.registerListener(SECOND_DISPLAY, listener2)
+
+ assertThat(listener1.displayAreas).containsExactly(SECOND_DISPLAY)
+ assertThat(listener2.displayAreas).containsExactly(SECOND_DISPLAY)
+ }
+
+ @Test
+ fun unregisterListener() {
+ val listener = FakeRootTaskDisplayAreaListener()
+
+ organizer.unregisterListener(FIRST_DISPLAY, listener)
+ organizer.onDisplayAreaAppeared(createDisplayAreaInfo(FIRST_DISPLAY), SurfaceControl())
+
+ assertThat(listener.displayAreas).doesNotContain(FIRST_DISPLAY)
+ }
+
+ private fun createDisplayAreaInfo(displayId: Int) = DisplayAreaInfo(
+ MockToken().token(), displayId, DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER
+ )
+
+ private class FakeRootTaskDisplayAreaListener : RootTaskDisplayAreaListener {
+ val displayAreas = mutableListOf<Int>()
+
+ override fun onDisplayAreaAppeared(displayAreaInfo: DisplayAreaInfo) {
+ displayAreas.add(displayAreaInfo.displayId)
+ }
+ }
+
+ companion object {
+ private const val FIRST_DISPLAY = DEFAULT_DISPLAY
+ private const val SECOND_DISPLAY = 2
+ private const val THIRD_DISPLAY = 3
+ }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRunningTaskInfoBuilder.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRunningTaskInfoBuilder.java
index ec3fe95..44023ec 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRunningTaskInfoBuilder.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRunningTaskInfoBuilder.java
@@ -53,6 +53,7 @@
private ActivityManager.TaskDescription.Builder mTaskDescriptionBuilder = null;
private final Point mPositionInParent = new Point();
private boolean mIsVisible = false;
+ private boolean mIsVisibleRequested = false;
private boolean mIsTopActivityTransparent = false;
private boolean mIsActivityStackTransparent = false;
private int mNumActivities = 1;
@@ -154,6 +155,11 @@
return this;
}
+ public TestRunningTaskInfoBuilder setVisibleRequested(boolean isVisible) {
+ mIsVisibleRequested = isVisible;
+ return this;
+ }
+
public TestRunningTaskInfoBuilder setTopActivityTransparent(boolean isTopActivityTransparent) {
mIsTopActivityTransparent = isTopActivityTransparent;
return this;
@@ -193,6 +199,7 @@
mTaskDescriptionBuilder != null ? mTaskDescriptionBuilder.build() : null;
info.positionInParent = mPositionInParent;
info.isVisible = mIsVisible;
+ info.isVisibleRequested = mIsVisibleRequested;
info.isTopActivityTransparent = mIsTopActivityTransparent;
info.isActivityStackTransparent = mIsActivityStackTransparent;
info.numActivities = mNumActivities;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apptoweb/AppToWebGenericLinksParserTests.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apptoweb/AppToWebGenericLinksParserTests.kt
index 053027f..1aa884c 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apptoweb/AppToWebGenericLinksParserTests.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apptoweb/AppToWebGenericLinksParserTests.kt
@@ -21,14 +21,11 @@
import android.testing.TestableLooper
import android.testing.TestableResources
import androidx.test.filters.SmallTest
-import com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn
-import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
-import com.android.dx.mockito.inline.extended.StaticMockitoSession
import com.android.wm.shell.R
import com.android.wm.shell.ShellTestCase
import com.android.wm.shell.common.ShellExecutor
-import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
import com.android.wm.shell.apptoweb.AppToWebGenericLinksParser.Companion.FLAG_GENERIC_LINKS
+import com.android.wm.shell.shared.desktopmode.FakeDesktopConfig
import junit.framework.TestCase.assertEquals
import junit.framework.TestCase.assertNull
import org.junit.After
@@ -37,7 +34,6 @@
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.MockitoAnnotations
-import org.mockito.quality.Strictness
/**
* Tests for [AppToWebGenericLinksParser].
@@ -51,18 +47,13 @@
@Mock private lateinit var mockExecutor: ShellExecutor
private lateinit var genericLinksParser: AppToWebGenericLinksParser
- private lateinit var mockitoSession: StaticMockitoSession
private lateinit var resources: TestableResources
+ private lateinit var mocksInit: AutoCloseable
@Before
fun setup() {
- MockitoAnnotations.initMocks(this)
+ mocksInit = MockitoAnnotations.openMocks(this)
- mockitoSession =
- mockitoSession()
- .strictness(Strictness.LENIENT)
- .spyStatic(DesktopModeStatus::class.java)
- .startMocking()
resources = mContext.getOrCreateTestableResources()
resources.addOverride(R.string.generic_links_list, BUILD_TIME_LIST)
DeviceConfig.setProperty(
@@ -75,33 +66,36 @@
@After
fun tearDown() {
- mockitoSession.finishMocking()
+ mocksInit.close()
}
@Test
fun init_usingBuildTimeList() {
- doReturn(true).`when` { DesktopModeStatus.useAppToWebBuildTimeGenericLinks() }
- genericLinksParser = AppToWebGenericLinksParser(mContext, mockExecutor)
+ val desktopConfig = FakeDesktopConfig()
+ desktopConfig.useAppToWebBuildTimeGenericLinks = true
+ genericLinksParser = AppToWebGenericLinksParser(mContext, mockExecutor, desktopConfig)
// Assert build-time list correctly parsed
assertEquals(URL_B, genericLinksParser.getGenericLink(PACKAGE_NAME_1))
}
@Test
fun init_usingServerSideList() {
- doReturn(false).`when` { DesktopModeStatus.useAppToWebBuildTimeGenericLinks() }
- genericLinksParser = AppToWebGenericLinksParser(mContext, mockExecutor)
+ val desktopConfig = FakeDesktopConfig()
+ desktopConfig.useAppToWebBuildTimeGenericLinks = false
+ genericLinksParser = AppToWebGenericLinksParser(mContext, mockExecutor, desktopConfig)
// Assert server side list correctly parsed
assertEquals(URL_S, genericLinksParser.getGenericLink(PACKAGE_NAME_1))
}
@Test
fun init_ignoresMalformedPair() {
- doReturn(true).`when` { DesktopModeStatus.useAppToWebBuildTimeGenericLinks() }
+ val desktopConfig = FakeDesktopConfig()
+ desktopConfig.useAppToWebBuildTimeGenericLinks = true
val packageName2 = "com.google.android.slides"
val url2 = "https://docs.google.com"
resources.addOverride(R.string.generic_links_list,
"$PACKAGE_NAME_1:$URL_B error $packageName2:$url2")
- genericLinksParser = AppToWebGenericLinksParser(mContext, mockExecutor)
+ genericLinksParser = AppToWebGenericLinksParser(mContext, mockExecutor, desktopConfig)
// Assert generics links list correctly parsed
assertEquals(URL_B, genericLinksParser.getGenericLink(PACKAGE_NAME_1))
assertEquals(url2, genericLinksParser.getGenericLink(packageName2))
@@ -110,9 +104,10 @@
@Test
fun onlySavesValidPackageToUrlMaps() {
- doReturn(true).`when` { DesktopModeStatus.useAppToWebBuildTimeGenericLinks() }
+ val desktopConfig = FakeDesktopConfig()
+ desktopConfig.useAppToWebBuildTimeGenericLinks = true
resources.addOverride(R.string.generic_links_list, "$PACKAGE_NAME_1:www.yout")
- genericLinksParser = AppToWebGenericLinksParser(mContext, mockExecutor)
+ genericLinksParser = AppToWebGenericLinksParser(mContext, mockExecutor, desktopConfig)
// Verify map with invalid url not saved
assertNull(genericLinksParser.getGenericLink(PACKAGE_NAME_1))
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/appzoomout/AppZoomOutControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/appzoomout/AppZoomOutControllerTest.java
index e91a123..d970b91 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/appzoomout/AppZoomOutControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/appzoomout/AppZoomOutControllerTest.java
@@ -48,7 +48,8 @@
@Mock private ShellTaskOrganizer mTaskOrganizer;
@Mock private DisplayController mDisplayController;
- @Mock private AppZoomOutDisplayAreaOrganizer mDisplayAreaOrganizer;
+ @Mock private AppZoomOutDisplayAreaOrganizer mAppDisplayAreaOrganizer;
+ @Mock private TopLevelZoomOutDisplayAreaOrganizer mTopLevelDisplayAreaOrganizer;
@Mock private ShellExecutor mExecutor;
@Mock private ActivityManager.RunningTaskInfo mRunningTaskInfo;
@@ -64,7 +65,8 @@
ShellInit shellInit = spy(new ShellInit(mExecutor));
mController = spy(new AppZoomOutController(mContext, shellInit, mTaskOrganizer,
- mDisplayController, mDisplayAreaOrganizer, mExecutor));
+ mDisplayController, mAppDisplayAreaOrganizer, mTopLevelDisplayAreaOrganizer,
+ mExecutor));
}
@Test
@@ -73,7 +75,7 @@
when(mRunningTaskInfo.getActivityType()).thenReturn(ACTIVITY_TYPE_HOME);
mController.onFocusTaskChanged(mRunningTaskInfo);
- verify(mDisplayAreaOrganizer).setIsHomeTaskFocused(true);
+ verify(mAppDisplayAreaOrganizer).setIsHomeTaskFocused(true);
}
@Test
@@ -82,6 +84,6 @@
when(mRunningTaskInfo.getActivityType()).thenReturn(ACTIVITY_TYPE_HOME);
mController.onFocusTaskChanged(mRunningTaskInfo);
- verify(mDisplayAreaOrganizer).setIsHomeTaskFocused(false);
+ verify(mAppDisplayAreaOrganizer).setIsHomeTaskFocused(false);
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
index b139c00..88668eb 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
@@ -17,6 +17,7 @@
package com.android.wm.shell.back;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_CLOSE_PREPARE_BACK_NAVIGATION;
import static android.view.WindowManager.TRANSIT_OPEN;
@@ -881,7 +882,8 @@
/* touchX */ coordinate,
/* touchY */ coordinate,
/* keyAction */ actionDown,
- /* swipeEdge */ BackEvent.EDGE_LEFT);
+ /* swipeEdge */ BackEvent.EDGE_LEFT,
+ /* displayId */ DEFAULT_DISPLAY);
}
/**
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleTransitionsTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleTransitionsTest.java
index 98ff7db..9f8b9a6 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleTransitionsTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleTransitionsTest.java
@@ -20,6 +20,7 @@
import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static com.android.window.flags.Flags.FLAG_EXCLUDE_TASK_FROM_RECENTS;
+import static com.android.wm.shell.Flags.FLAG_ENABLE_BUBBLE_ANYTHING;
import static com.android.wm.shell.transition.Transitions.TRANSIT_CONVERT_TO_BUBBLE;
import static com.google.common.truth.Truth.assertThat;
@@ -77,6 +78,7 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.List;
import java.util.Map;
/**
@@ -249,10 +251,34 @@
// Verify that the WCT has the task force exclude from recents.
final WindowContainerTransaction wct = wctCaptor.getValue();
final Map<IBinder, WindowContainerTransaction.Change> chgs = wct.getChanges();
- assertThat(chgs).hasSize(1);
- final WindowContainerTransaction.Change chg = chgs.get(taskInfo.token.asBinder());
- assertThat(chg).isNotNull();
- assertThat(chg.getForceExcludedFromRecents()).isTrue();
+ final boolean hasForceExcludedFromRecents = chgs.entrySet().stream()
+ .filter((entry) -> entry.getKey().equals(taskInfo.token.asBinder()))
+ .anyMatch((entry) -> entry.getValue().getForceExcludedFromRecents());
+ assertThat(hasForceExcludedFromRecents).isTrue();
+ }
+
+ @Test
+ @EnableFlags(FLAG_ENABLE_BUBBLE_ANYTHING)
+ public void testConvertToBubble_disallowFlagLaunchAdjacent() {
+ final ActivityManager.RunningTaskInfo taskInfo = setupBubble();
+ final BubbleTransitions.BubbleTransition bt = mBubbleTransitions.startConvertToBubble(
+ mBubble, taskInfo, mExpandedViewManager, mTaskViewFactory, mBubblePositioner,
+ mStackView, mLayerView, mIconFactory, mHomeIntentProvider, null /* dragData */,
+ true /* inflateSync */);
+ final BubbleTransitions.ConvertToBubble ctb = (BubbleTransitions.ConvertToBubble) bt;
+
+ ctb.onInflated(mBubble);
+ final ArgumentCaptor<WindowContainerTransaction> wctCaptor =
+ ArgumentCaptor.forClass(WindowContainerTransaction.class);
+ verify(mTransitions).startTransition(anyInt(), wctCaptor.capture(), eq(ctb));
+
+ // Verify that the WCT has the disallow-launch-adjacent hierarchy op
+ final WindowContainerTransaction wct = wctCaptor.getValue();
+ final List<WindowContainerTransaction.HierarchyOp> ops = wct.getHierarchyOps();
+ final boolean hasLaunchAdjacentDisabled = ops.stream()
+ .filter((op) -> op.getContainer().equals(taskInfo.token.asBinder()))
+ .anyMatch((op) -> op.isLaunchAdjacentDisabled());
+ assertThat(hasLaunchAdjacentDisabled).isTrue();
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayControllerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayControllerTests.java
index 1e459d5..eaf34c2 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayControllerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayControllerTests.java
@@ -47,12 +47,10 @@
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
-import com.android.dx.mockito.inline.extended.ExtendedMockito;
-import com.android.dx.mockito.inline.extended.StaticMockitoSession;
import com.android.window.flags.Flags;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.TestSyncExecutor;
-import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
+import com.android.wm.shell.shared.desktopmode.FakeDesktopState;
import com.android.wm.shell.sysui.ShellInit;
import org.junit.After;
@@ -60,7 +58,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
-import org.mockito.quality.Strictness;
+import org.mockito.MockitoAnnotations;
import java.util.function.Consumer;
@@ -78,25 +76,22 @@
@Mock private DisplayManager mDisplayManager;
@Mock private DisplayTopology mMockTopology;
@Mock private DisplayController.OnDisplaysChangedListener mListener;
- private StaticMockitoSession mMockitoSession;
private TestSyncExecutor mMainExecutor;
private IDisplayWindowListener mDisplayContainerListener;
private Consumer<DisplayTopology> mCapturedTopologyListener;
private Display mMockDisplay;
private DisplayController mController;
+ private FakeDesktopState mDesktopState;
private static final int DISPLAY_ID_0 = 0;
private static final int DISPLAY_ID_1 = 1;
private static final RectF DISPLAY_ABS_BOUNDS_0 = new RectF(10, 10, 20, 20);
private static final RectF DISPLAY_ABS_BOUNDS_1 = new RectF(11, 11, 22, 22);
+ private AutoCloseable mMocksInit = null;
@Before
public void setUp() throws RemoteException {
- mMockitoSession =
- ExtendedMockito.mockitoSession()
- .initMocks(this)
- .mockStatic(DesktopModeStatus.class)
- .strictness(Strictness.LENIENT)
- .startMocking();
+ mDesktopState = new FakeDesktopState();
+ mMocksInit = MockitoAnnotations.openMocks(this);
mContext = spy(new TestableContext(
androidx.test.platform.app.InstrumentationRegistry.getInstrumentation()
@@ -104,7 +99,7 @@
mMainExecutor = new TestSyncExecutor();
mController = new DisplayController(
- mContext, mWM, mShellInit, mMainExecutor, mDisplayManager);
+ mContext, mWM, mShellInit, mMainExecutor, mDisplayManager, mDesktopState);
mMockDisplay = mock(Display.class);
when(mMockDisplay.getDisplayAdjustments()).thenReturn(
@@ -126,9 +121,10 @@
}
@After
- public void tearDown() {
- if (mMockitoSession != null) {
- mMockitoSession.finishMocking();
+ public void tearDown() throws Exception {
+ if (mMocksInit != null) {
+ mMocksInit.close();
+ mMocksInit = null;
}
}
@@ -140,8 +136,7 @@
@Test
@EnableFlags(Flags.FLAG_ENABLE_CONNECTED_DISPLAYS_WINDOW_DRAG)
public void onInit_canEnterDesktopMode_registerListeners() throws RemoteException {
- ExtendedMockito.doReturn(true)
- .when(() -> DesktopModeStatus.canEnterDesktopMode(any()));
+ mDesktopState.setCanEnterDesktopMode(true);
mController.onInit();
@@ -154,8 +149,7 @@
@EnableFlags(Flags.FLAG_ENABLE_CONNECTED_DISPLAYS_WINDOW_DRAG)
public void onInit_canNotEnterDesktopMode_onlyRegisterDisplayWindowListener()
throws RemoteException {
- ExtendedMockito.doReturn(false)
- .when(() -> DesktopModeStatus.canEnterDesktopMode(any()));
+ mDesktopState.setCanEnterDesktopMode(false);
mController.onInit();
@@ -167,8 +161,7 @@
@Test
@EnableFlags(Flags.FLAG_ENABLE_CONNECTED_DISPLAYS_WINDOW_DRAG)
public void addDisplayWindowListener_notifiesExistingDisplaysAndTopology() {
- ExtendedMockito.doReturn(true)
- .when(() -> DesktopModeStatus.canEnterDesktopMode(any()));
+ mDesktopState.setCanEnterDesktopMode(true);
mController.onInit();
mController.addDisplayWindowListener(mListener);
@@ -198,8 +191,7 @@
@Test
@EnableFlags(Flags.FLAG_ENABLE_CONNECTED_DISPLAYS_WINDOW_DRAG)
public void onDisplayTopologyChanged_updateDisplayLayout() throws RemoteException {
- ExtendedMockito.doReturn(true)
- .when(() -> DesktopModeStatus.canEnterDesktopMode(any()));
+ mDesktopState.setCanEnterDesktopMode(true);
mController.onInit();
mController.addDisplayWindowListener(mListener);
mDisplayContainerListener.onDisplayAdded(DISPLAY_ID_1);
@@ -216,8 +208,7 @@
@EnableFlags(Flags.FLAG_ENABLE_CONNECTED_DISPLAYS_WINDOW_DRAG)
public void onDisplayTopologyChanged_topologyBeforeDisplayAdded_appliesBoundsOnAdd()
throws RemoteException {
- ExtendedMockito.doReturn(true)
- .when(() -> DesktopModeStatus.canEnterDesktopMode(any()));
+ mDesktopState.setCanEnterDesktopMode(true);
mController.onInit();
mController.addDisplayWindowListener(mListener);
@@ -237,8 +228,7 @@
@EnableFlags(Flags.FLAG_ENABLE_CONNECTED_DISPLAYS_WINDOW_DRAG)
public void onDisplayConfigurationChanged_reInitDisplayLayout()
throws RemoteException {
- ExtendedMockito.doReturn(true)
- .when(() -> DesktopModeStatus.canEnterDesktopMode(any()));
+ mDesktopState.setCanEnterDesktopMode(true);
mController.onInit();
mController.addDisplayWindowListener(mListener);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/MultiDisplayDragMoveBoundsCalculatorTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/MultiDisplayDragMoveBoundsCalculatorTest.kt
index bd924c2..ea215e0 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/MultiDisplayDragMoveBoundsCalculatorTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/MultiDisplayDragMoveBoundsCalculatorTest.kt
@@ -22,6 +22,7 @@
import android.graphics.RectF
import android.testing.TestableResources
import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.common.MultiDisplayTestUtil.TestDisplay
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
@@ -48,18 +49,8 @@
val boundsAtDragStart = Rect(10, 20, 110, 120)
val x = 300f
val y = 400f
- val displayLayout0 =
- MultiDisplayTestUtil.createSpyDisplayLayout(
- MultiDisplayTestUtil.DISPLAY_GLOBAL_BOUNDS_0,
- MultiDisplayTestUtil.DISPLAY_DPI_0,
- resources.resources,
- )
- val displayLayout1 =
- MultiDisplayTestUtil.createSpyDisplayLayout(
- MultiDisplayTestUtil.DISPLAY_GLOBAL_BOUNDS_1,
- MultiDisplayTestUtil.DISPLAY_DPI_1,
- resources.resources,
- )
+ val displayLayout0 = TestDisplay.DISPLAY_0.getSpyDisplayLayout(resources.resources)
+ val displayLayout1 = TestDisplay.DISPLAY_1.getSpyDisplayLayout(resources.resources)
val actualBoundsDp =
MultiDisplayDragMoveBoundsCalculator.calculateGlobalDpBoundsForDrag(
@@ -77,12 +68,8 @@
@Test
fun testConvertGlobalDpToLocalPxForRect() {
- val displayLayout =
- MultiDisplayTestUtil.createSpyDisplayLayout(
- MultiDisplayTestUtil.DISPLAY_GLOBAL_BOUNDS_1,
- MultiDisplayTestUtil.DISPLAY_DPI_1,
- resources.resources,
- )
+ val displayLayout = TestDisplay.DISPLAY_1.getSpyDisplayLayout(resources.resources)
+
val rectDp = RectF(150f, -350f, 300f, -250f)
val actualBoundsPx =
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/MultiDisplayDragMoveIndicatorControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/MultiDisplayDragMoveIndicatorControllerTest.kt
index abd2388..2016088 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/MultiDisplayDragMoveIndicatorControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/MultiDisplayDragMoveIndicatorControllerTest.kt
@@ -27,6 +27,7 @@
import com.android.wm.shell.RootTaskDisplayAreaOrganizer
import com.android.wm.shell.ShellTestCase
import com.android.wm.shell.TestShellExecutor
+import com.android.wm.shell.common.MultiDisplayTestUtil.TestDisplay
import java.util.function.Supplier
import org.junit.Before
import org.junit.Test
@@ -78,26 +79,17 @@
executor,
)
- val spyDisplayLayout0 =
- MultiDisplayTestUtil.createSpyDisplayLayout(
- MultiDisplayTestUtil.DISPLAY_GLOBAL_BOUNDS_0,
- MultiDisplayTestUtil.DISPLAY_DPI_0,
- resources.resources,
- )
- val spyDisplayLayout1 =
- MultiDisplayTestUtil.createSpyDisplayLayout(
- MultiDisplayTestUtil.DISPLAY_GLOBAL_BOUNDS_1,
- MultiDisplayTestUtil.DISPLAY_DPI_1,
- resources.resources,
- )
+ val spyDisplayLayout0 = TestDisplay.DISPLAY_0.getSpyDisplayLayout(resources.resources)
+ val spyDisplayLayout1 = TestDisplay.DISPLAY_1.getSpyDisplayLayout(resources.resources)
taskInfo.taskId = TASK_ID
whenever(displayController.getDisplayLayout(0)).thenReturn(spyDisplayLayout0)
whenever(displayController.getDisplayLayout(1)).thenReturn(spyDisplayLayout1)
+ whenever(displayController.getDisplayContext(any())).thenReturn(mContext)
whenever(displayController.getDisplay(0)).thenReturn(display0)
whenever(displayController.getDisplay(1)).thenReturn(display1)
- whenever(indicatorSurfaceFactory.create(taskInfo, display0)).thenReturn(indicatorSurface0)
- whenever(indicatorSurfaceFactory.create(taskInfo, display1)).thenReturn(indicatorSurface1)
+ whenever(indicatorSurfaceFactory.create(eq(taskInfo), eq(display0), any())).thenReturn(indicatorSurface0)
+ whenever(indicatorSurfaceFactory.create(eq(taskInfo), eq(display1), any())).thenReturn(indicatorSurface1)
whenever(transactionSupplier.get()).thenReturn(transaction)
}
@@ -111,7 +103,7 @@
) { transaction }
executor.flushAll()
- verify(indicatorSurfaceFactory, never()).create(any(), any())
+ verify(indicatorSurfaceFactory, never()).create(any(), any(), any())
}
@Test
@@ -124,7 +116,7 @@
) { transaction }
executor.flushAll()
- verify(indicatorSurfaceFactory, never()).create(any(), any())
+ verify(indicatorSurfaceFactory, never()).create(any(), any(), any())
}
@Test
@@ -137,7 +129,7 @@
) { transaction }
executor.flushAll()
- verify(indicatorSurfaceFactory, times(1)).create(taskInfo, display1)
+ verify(indicatorSurfaceFactory, times(1)).create(eq(taskInfo), eq(display1), any())
verify(indicatorSurface1, times(1))
.show(transaction, taskInfo, rootTaskDisplayAreaOrganizer, 1, Rect(0, 1800, 200, 2400))
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/MultiDisplayDragMoveIndicatorSurfaceTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/MultiDisplayDragMoveIndicatorSurfaceTest.kt
new file mode 100644
index 0000000..9f59732
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/MultiDisplayDragMoveIndicatorSurfaceTest.kt
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.wm.shell.common
+
+import android.app.ActivityManager.RunningTaskInfo
+import android.graphics.Rect
+import android.testing.AndroidTestingRunner
+import android.view.Display
+import android.view.Display.DEFAULT_DISPLAY
+import android.view.SurfaceControl
+import com.android.wm.shell.RootTaskDisplayAreaOrganizer
+import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.shared.R
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.clearInvocations
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.never
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+
+/**
+ * Tests for [MultiDisplayDragMoveIndicatorSurface].
+ *
+ * Build/Install/Run: atest WMShellUnitTests:MultiDisplayDragMoveIndicatorSurfaceTest
+ */
+@RunWith(AndroidTestingRunner::class)
+class MultiDisplayDragMoveIndicatorSurfaceTest : ShellTestCase() {
+ private lateinit var display: Display
+ private val mockTaskInfo = mock<RunningTaskInfo>()
+ private val mockSurfaceControlBuilderFactory =
+ mock<MultiDisplayDragMoveIndicatorSurface.Factory.SurfaceControlBuilderFactory>()
+ private val mockSurfaceControlBuilder = mock<SurfaceControl.Builder>()
+ private val mockVeilSurface = mock<SurfaceControl>()
+ private val mockTransaction = mock<SurfaceControl.Transaction>()
+ private val mockRootTaskDisplayAreaOrganizer = mock<RootTaskDisplayAreaOrganizer>()
+
+ private lateinit var dragIndicatorSurface: MultiDisplayDragMoveIndicatorSurface
+
+ @Before
+ fun setUp() {
+ display = mContext.display
+ mockTaskInfo.taskId = TASK_ID
+ whenever(
+ mContext.orCreateTestableResources.resources.getDimensionPixelSize(
+ R.dimen.desktop_windowing_freeform_rounded_corner_radius
+ )
+ )
+ .thenReturn(CORNER_RADIUS)
+ whenever(mockSurfaceControlBuilderFactory.create(any()))
+ .thenReturn(mockSurfaceControlBuilder)
+ whenever(mockSurfaceControlBuilder.setColorLayer()).thenReturn(mockSurfaceControlBuilder)
+ whenever(mockSurfaceControlBuilder.setCallsite(any())).thenReturn(mockSurfaceControlBuilder)
+ whenever(mockSurfaceControlBuilder.setHidden(any())).thenReturn(mockSurfaceControlBuilder)
+ whenever(mockSurfaceControlBuilder.build()).thenReturn(mockVeilSurface)
+ whenever(mockTransaction.remove(any())).thenReturn(mockTransaction)
+ whenever(mockTransaction.setCrop(any(), any())).thenReturn(mockTransaction)
+ whenever(mockTransaction.show(any())).thenReturn(mockTransaction)
+ whenever(mockTransaction.setColor(any(), any())).thenReturn(mockTransaction)
+
+ dragIndicatorSurface =
+ MultiDisplayDragMoveIndicatorSurface(
+ mContext,
+ mockTaskInfo,
+ display,
+ mockSurfaceControlBuilderFactory,
+ )
+ }
+
+ @Test
+ fun init_createsVeilSurfaceWithCorrectProperties() {
+ verify(mockSurfaceControlBuilderFactory).create(any())
+ verify(mockSurfaceControlBuilder).setColorLayer()
+ verify(mockSurfaceControlBuilder).setCallsite(any())
+ verify(mockSurfaceControlBuilder).setHidden(eq(true))
+ verify(mockSurfaceControlBuilder).build()
+ }
+
+ @Test
+ fun disposeSurface_removesVeilSurface() {
+ dragIndicatorSurface.disposeSurface(mockTransaction)
+
+ verify(mockTransaction).remove(eq(mockVeilSurface))
+ }
+
+ @Test
+ fun disposeSurface_doesNothingIfAlreadyDisposed() {
+ dragIndicatorSurface.disposeSurface(mockTransaction)
+ clearInvocations(mockTransaction)
+
+ dragIndicatorSurface.disposeSurface(mockTransaction)
+
+ verify(mockTransaction, never()).remove(any())
+ }
+
+ @Test
+ fun show_reparentsSetsCropShowsSetsColorAppliesTransaction() {
+ dragIndicatorSurface.show(
+ mockTransaction,
+ mockTaskInfo,
+ mockRootTaskDisplayAreaOrganizer,
+ DEFAULT_DISPLAY,
+ BOUNDS,
+ )
+
+ verify(mockRootTaskDisplayAreaOrganizer)
+ .reparentToDisplayArea(eq(DEFAULT_DISPLAY), eq(mockVeilSurface), eq(mockTransaction))
+ verify(mockTransaction).setCrop(eq(mockVeilSurface), eq(BOUNDS))
+ verify(mockTransaction).setCornerRadius(eq(mockVeilSurface), eq(CORNER_RADIUS.toFloat()))
+ verify(mockTransaction).show(eq(mockVeilSurface))
+ verify(mockTransaction).setColor(eq(mockVeilSurface), any<FloatArray>())
+ verify(mockTransaction).apply()
+ }
+
+ @Test
+ fun relayout_whenVisibleAndShouldBeVisible_setsCrop() {
+ dragIndicatorSurface.show(
+ mockTransaction,
+ mockTaskInfo,
+ mockRootTaskDisplayAreaOrganizer,
+ DEFAULT_DISPLAY,
+ BOUNDS,
+ )
+ clearInvocations(mockTransaction)
+
+ dragIndicatorSurface.relayout(NEW_BOUNDS, mockTransaction, shouldBeVisible = true)
+
+ verify(mockTransaction).setCrop(eq(mockVeilSurface), eq(NEW_BOUNDS))
+ }
+
+ @Test
+ fun relayout_whenVisibleAndShouldBeInvisible_setsCrop() {
+ dragIndicatorSurface.show(
+ mockTransaction,
+ mockTaskInfo,
+ mockRootTaskDisplayAreaOrganizer,
+ DEFAULT_DISPLAY,
+ BOUNDS,
+ )
+ clearInvocations(mockTransaction)
+ dragIndicatorSurface.relayout(NEW_BOUNDS, mockTransaction, shouldBeVisible = false)
+
+ verify(mockTransaction).setCrop(eq(mockVeilSurface), eq(NEW_BOUNDS))
+ }
+
+ @Test
+ fun relayout_whenInvisibleAndShouldBeVisible_setsCrop() {
+ dragIndicatorSurface.relayout(NEW_BOUNDS, mockTransaction, shouldBeVisible = true)
+
+ verify(mockTransaction).setCrop(eq(mockVeilSurface), eq(NEW_BOUNDS))
+ }
+
+ @Test
+ fun relayout_whenInvisibleAndShouldBeInvisible_doesNotSetCrop() {
+ dragIndicatorSurface.relayout(NEW_BOUNDS, mockTransaction, shouldBeVisible = false)
+
+ verify(mockTransaction, never()).setCrop(any(), any())
+ }
+
+ companion object {
+ private const val TASK_ID = 10
+ private const val CORNER_RADIUS = 32
+ private val BOUNDS = Rect(10, 20, 100, 200)
+ private val NEW_BOUNDS = Rect(50, 50, 150, 250)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/MultiDisplayTestUtil.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/MultiDisplayTestUtil.kt
index c8bebf1..c3377b7 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/MultiDisplayTestUtil.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/MultiDisplayTestUtil.kt
@@ -24,22 +24,27 @@
/** Utility class for tests of [DesktopModeWindowDecorViewModel] */
object MultiDisplayTestUtil {
- // We have two displays, display#1 is placed on middle top of display#0:
- // +---+
- // | 1 |
- // +-+---+-+
- // | 0 |
- // +-------+
- val DISPLAY_GLOBAL_BOUNDS_0 = RectF(0f, 0f, 1200f, 800f)
- val DISPLAY_GLOBAL_BOUNDS_1 = RectF(100f, -1000f, 1100f, 0f)
- val DISPLAY_DPI_0 = DisplayMetrics.DENSITY_DEFAULT
- val DISPLAY_DPI_1 = DisplayMetrics.DENSITY_DEFAULT * 2
+ // We have four displays, display#1 is placed on the center top of display#0, display#2 is
+ // adjacent to display#1 and display#3 is on the right of display#0.
+ // +---+-------+
+ // | 1 | 2 |
+ // +-+---+-+---+-+
+ // | 0 | 3 |
+ // +-------+ |
+ // +---+
- fun createSpyDisplayLayout(globalBounds: RectF, dpi: Int, resources: Resources): DisplayLayout {
- val displayInfo = DisplayInfo()
- displayInfo.logicalDensityDpi = dpi
- val displayLayout = spy(DisplayLayout(displayInfo, resources, true, true))
- displayLayout.setGlobalBoundsDp(globalBounds)
- return displayLayout
+ enum class TestDisplay(val id: Int, val bounds: RectF, val dpi: Int) {
+ DISPLAY_0(0, RectF(0f, 0f, 1200f, 800f), DisplayMetrics.DENSITY_DEFAULT),
+ DISPLAY_1(1, RectF(100f, -1000f, 1100f, 0f), DisplayMetrics.DENSITY_DEFAULT * 2),
+ DISPLAY_2(2, RectF(1100f, -1000f, 2100f, 0f), DisplayMetrics.DENSITY_DEFAULT),
+ DISPLAY_3(3, RectF(1200f, 0f, 1600f, 1200f), DisplayMetrics.DENSITY_DEFAULT / 4);
+
+ fun getSpyDisplayLayout(resources: Resources): DisplayLayout {
+ val displayInfo = DisplayInfo()
+ displayInfo.logicalDensityDpi = dpi
+ val displayLayout = spy(DisplayLayout(displayInfo, resources, true, true))
+ displayLayout.setGlobalBoundsDp(bounds)
+ return displayLayout
+ }
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/pip/PipDesktopStateTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/pip/PipDesktopStateTest.kt
index 2c50cd9..899ba55 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/pip/PipDesktopStateTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/pip/PipDesktopStateTest.kt
@@ -27,6 +27,7 @@
import androidx.test.filters.SmallTest
import com.android.window.flags.Flags.FLAG_ENABLE_CONNECTED_DISPLAYS_PIP
import com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_PIP
+import com.android.window.flags.Flags.FLAG_ENABLE_DRAGGING_PIP_ACROSS_DISPLAYS
import com.android.wm.shell.Flags.FLAG_ENABLE_PIP2
import com.android.wm.shell.RootTaskDisplayAreaOrganizer
import com.android.wm.shell.ShellTestCase
@@ -93,6 +94,16 @@
}
@Test
+ @EnableFlags(
+ FLAG_ENABLE_CONNECTED_DISPLAYS_PIP,
+ FLAG_ENABLE_PIP2,
+ FLAG_ENABLE_DRAGGING_PIP_ACROSS_DISPLAYS
+ )
+ fun isDraggingPipAcrossDisplaysEnabled_returnsTrue() {
+ assertThat(pipDesktopState.isDraggingPipAcrossDisplaysEnabled()).isTrue()
+ }
+
+ @Test
fun isPipInDesktopMode_anyDeskActive_returnsTrue() {
whenever(mockDesktopRepository.isAnyDeskActive(DISPLAY_ID)).thenReturn(true)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
index 9035df2..fbdd75f 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
@@ -62,6 +62,7 @@
import com.android.wm.shell.compatui.impl.CompatUIRequests;
import com.android.wm.shell.desktopmode.DesktopRepository;
import com.android.wm.shell.desktopmode.DesktopUserRepositories;
+import com.android.wm.shell.shared.desktopmode.FakeDesktopState;
import com.android.wm.shell.sysui.ShellController;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.Transitions;
@@ -135,12 +136,15 @@
@NonNull
private CompatUIStatusManager mCompatUIStatusManager;
+ @NonNull
+ private FakeDesktopState mDesktopState;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
+ mDesktopState = new FakeDesktopState();
doReturn(mMockDisplayLayout).when(mMockDisplayController).getDisplayLayout(anyInt());
doReturn(DISPLAY_ID).when(mMockCompatLayout).getDisplayId();
doReturn(TASK_ID).when(mMockCompatLayout).getTaskId();
@@ -164,7 +168,8 @@
mMockDisplayController, mMockDisplayInsetsController, mMockImeController,
mMockSyncQueue, mMockExecutor, mMockTransitionsLazy, mDockStateReader,
mCompatUIConfiguration, mCompatUIShellCommandHandler, mAccessibilityManager,
- mCompatUIStatusManager, Optional.of(mDesktopUserRepositories)) {
+ mCompatUIStatusManager, Optional.of(mDesktopUserRepositories),
+ mDesktopState) {
@Override
CompatUIWindowManager createCompatUiWindowManager(Context context, TaskInfo taskInfo,
ShellTaskOrganizer.TaskListener taskListener) {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUILayoutTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUILayoutTest.java
index c567b5f..c64d4f1 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUILayoutTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUILayoutTest.java
@@ -43,6 +43,7 @@
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.compatui.CompatUIController.CompatUIHintsState;
import com.android.wm.shell.compatui.api.CompatUIEvent;
+import com.android.wm.shell.shared.desktopmode.FakeDesktopState;
import junit.framework.Assert;
@@ -77,15 +78,17 @@
private CompatUIWindowManager mWindowManager;
private CompatUILayout mLayout;
private TaskInfo mTaskInfo;
+ private FakeDesktopState mDesktopState;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
+ mDesktopState = new FakeDesktopState();
doReturn(100).when(mCompatUIConfiguration).getHideSizeCompatRestartButtonTolerance();
mTaskInfo = createTaskInfo(/* hasSizeCompat= */ false);
mWindowManager = new CompatUIWindowManager(mContext, mTaskInfo, mSyncTransactionQueue,
mCallback, mTaskListener, new DisplayLayout(), new CompatUIHintsState(),
- mCompatUIConfiguration, mOnRestartButtonClicked);
+ mCompatUIConfiguration, mOnRestartButtonClicked, mDesktopState);
mLayout = (CompatUILayout)
LayoutInflater.from(mContext).inflate(R.layout.compat_ui_layout, null);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java
index 0562bb8..9cf6575 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java
@@ -58,6 +58,7 @@
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.compatui.CompatUIController.CompatUIHintsState;
import com.android.wm.shell.compatui.api.CompatUIEvent;
+import com.android.wm.shell.shared.desktopmode.FakeDesktopState;
import junit.framework.Assert;
@@ -95,10 +96,12 @@
private CompatUIWindowManager mWindowManager;
private TaskInfo mTaskInfo;
private DisplayLayout mDisplayLayout;
+ private FakeDesktopState mDesktopState;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
+ mDesktopState = new FakeDesktopState();
doReturn(100).when(mCompatUIConfiguration).getHideSizeCompatRestartButtonTolerance();
mTaskInfo = createTaskInfo(/* hasSizeCompat= */ false);
@@ -116,7 +119,7 @@
mDisplayLayout.setInsets(mContext.getResources(), insetsState);
mWindowManager = new CompatUIWindowManager(mContext, mTaskInfo, mSyncTransactionQueue,
mCallback, mTaskListener, mDisplayLayout, new CompatUIHintsState(),
- mCompatUIConfiguration, mOnRestartButtonClicked);
+ mCompatUIConfiguration, mOnRestartButtonClicked, mDesktopState);
spyOn(mWindowManager);
doReturn(mLayout).when(mWindowManager).inflateLayout();
@@ -401,7 +404,7 @@
doReturn(85).when(mCompatUIConfiguration).getHideSizeCompatRestartButtonTolerance();
mWindowManager = new CompatUIWindowManager(mContext, mTaskInfo, mSyncTransactionQueue,
mCallback, mTaskListener, mDisplayLayout, new CompatUIHintsState(),
- mCompatUIConfiguration, mOnRestartButtonClicked);
+ mCompatUIConfiguration, mOnRestartButtonClicked, mDesktopState);
// Simulate rotation of activity in square display
TaskInfo taskInfo = createTaskInfo(true);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxInputControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxInputControllerTest.kt
index 8c39aa0..e524c75 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxInputControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxInputControllerTest.kt
@@ -194,7 +194,6 @@
fun checkUpdateSessionRegion(times: Int = 1, displayId: Int = DISPLAY_ID, region: Region) {
verify(windowSession, times(times)).updateInputChannel(
any(),
- anyOrNull(),
eq(displayId),
any(),
any(),
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/crashhandling/ShellCrashHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/crashhandling/ShellCrashHandlerTest.kt
index 5c77f78..3868a63 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/crashhandling/ShellCrashHandlerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/crashhandling/ShellCrashHandlerTest.kt
@@ -34,7 +34,7 @@
import com.android.wm.shell.ShellTestCase
import com.android.wm.shell.common.HomeIntentProvider
import com.android.wm.shell.common.ShellExecutor
-import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
+import com.android.wm.shell.shared.desktopmode.FakeDesktopState
import com.android.wm.shell.sysui.ShellInit
import com.google.common.truth.Truth.assertThat
import kotlin.test.Test
@@ -53,13 +53,13 @@
@Rule
val extendedMockitoRule =
ExtendedMockitoRule.Builder(this)
- .mockStatic(DesktopModeStatus::class.java)
.mockStatic(PendingIntent::class.java)
.build()!!
private val testExecutor = mock<ShellExecutor>()
private val context = mock<Context>()
private val shellTaskOrganizer = mock<ShellTaskOrganizer>()
+ private val desktopState = FakeDesktopState()
private lateinit var homeIntentProvider: HomeIntentProvider
private lateinit var crashHandler: ShellCrashHandler
@@ -68,13 +68,13 @@
@Before
fun setup() {
- whenever(DesktopModeStatus.canEnterDesktopMode(any())).thenReturn(true)
+ desktopState.canEnterDesktopMode = true
whenever(PendingIntent.getActivity(any(), any(), any(), any(), any())).thenReturn(mock())
shellInit = spy(ShellInit(testExecutor))
homeIntentProvider = HomeIntentProvider(context)
- crashHandler = ShellCrashHandler(context, shellTaskOrganizer, homeIntentProvider, shellInit)
+ crashHandler = ShellCrashHandler(shellTaskOrganizer, homeIntentProvider, desktopState, shellInit)
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandlerTest.kt
index cbe4ecb..27c729b 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandlerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandlerTest.kt
@@ -19,14 +19,20 @@
import android.platform.test.annotations.EnableFlags
import android.testing.AndroidTestingRunner
import android.view.Display.DEFAULT_DISPLAY
+import android.window.DisplayAreaInfo
import androidx.test.filters.SmallTest
import com.android.server.display.feature.flags.Flags as DisplayFlags
import com.android.window.flags.Flags
import com.android.wm.shell.RootTaskDisplayAreaOrganizer
+import com.android.wm.shell.RootTaskDisplayAreaOrganizer.RootTaskDisplayAreaListener
import com.android.wm.shell.ShellTestCase
import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.common.DisplayController.OnDisplaysChangedListener
import com.android.wm.shell.common.ShellExecutor
+import com.android.wm.shell.desktopmode.desktopfirst.DESKTOP_FIRST_DISPLAY_WINDOWING_MODE
+import com.android.wm.shell.desktopmode.desktopfirst.DesktopDisplayModeController
+import com.android.wm.shell.desktopmode.desktopfirst.TOUCH_FIRST_DISPLAY_WINDOWING_MODE
+import com.android.wm.shell.desktopmode.multidesks.DesksOrganizer
import com.android.wm.shell.desktopmode.multidesks.DesksTransitionObserver
import com.android.wm.shell.desktopmode.persistence.DesktopRepositoryInitializer
import com.android.wm.shell.shared.desktopmode.FakeDesktopState
@@ -47,8 +53,10 @@
import org.mockito.Mockito.spy
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
+import org.mockito.kotlin.any
import org.mockito.kotlin.argumentCaptor
import org.mockito.kotlin.clearInvocations
+import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
import org.mockito.kotlin.never
import org.mockito.kotlin.whenever
@@ -66,10 +74,11 @@
@Mock lateinit var displayController: DisplayController
@Mock private lateinit var mockShellController: ShellController
@Mock private lateinit var mockRootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer
+ @Mock private lateinit var mockDesksOrganizer: DesksOrganizer
@Mock private lateinit var mockDesktopUserRepositories: DesktopUserRepositories
@Mock private lateinit var mockDesktopRepository: DesktopRepository
@Mock private lateinit var mockDesktopTasksController: DesktopTasksController
- @Mock private lateinit var desktopDisplayModeController: DesktopDisplayModeController
+ @Mock private lateinit var mockDesktopDisplayModeController: DesktopDisplayModeController
@Mock private lateinit var mockDesksTransitionObserver: DesksTransitionObserver
private val desktopRepositoryInitializer = FakeDesktopRepositoryInitializer()
private val testScope = TestScope()
@@ -79,12 +88,14 @@
private lateinit var handler: DesktopDisplayEventHandler
private val onDisplaysChangedListenerCaptor = argumentCaptor<OnDisplaysChangedListener>()
+ private val onRootTdaListenerCaptor = argumentCaptor<RootTaskDisplayAreaListener>()
private val externalDisplayId = 100
@Before
fun setUp() {
shellInit = spy(ShellInit(testExecutor))
whenever(mockDesktopUserRepositories.current).thenReturn(mockDesktopRepository)
+ whenever(mockDesktopRepository.userId).thenReturn(PRIMARY_USER_ID)
handler =
DesktopDisplayEventHandler(
shellInit,
@@ -92,10 +103,11 @@
mockShellController,
displayController,
mockRootTaskDisplayAreaOrganizer,
+ mockDesksOrganizer,
desktopRepositoryInitializer,
mockDesktopUserRepositories,
mockDesktopTasksController,
- desktopDisplayModeController,
+ mockDesktopDisplayModeController,
mockDesksTransitionObserver,
desktopState,
)
@@ -111,89 +123,211 @@
@Test
@EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
- fun testDisplayAdded_supportsDesks_desktopRepositoryInitialized_createsDesk() =
+ fun testDisplayAdded_registersTdaListener() =
testScope.runTest {
- desktopState.canEnterDesktopMode = true
+ onDisplaysChangedListenerCaptor.lastValue.onDisplayAdded(SECOND_DISPLAY)
- onDisplaysChangedListenerCaptor.lastValue.onDisplayAdded(DEFAULT_DISPLAY)
- desktopRepositoryInitializer.initialize(mockDesktopUserRepositories)
- runCurrent()
-
- verify(mockDesktopTasksController).createDesk(DEFAULT_DISPLAY)
+ verify(mockRootTaskDisplayAreaOrganizer).registerListener(eq(SECOND_DISPLAY), any())
}
@Test
@EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
- fun testDisplayAdded_supportsDesks_desktopRepositoryNotInitialized_doesNotCreateDesk() =
+ fun testDisplayRemoved_unregistersTdaListener() =
testScope.runTest {
- desktopState.canEnterDesktopMode = true
+ onDisplaysChangedListenerCaptor.lastValue.onDisplayRemoved(SECOND_DISPLAY)
- onDisplaysChangedListenerCaptor.lastValue.onDisplayAdded(DEFAULT_DISPLAY)
- runCurrent()
-
- verify(mockDesktopTasksController, never()).createDesk(DEFAULT_DISPLAY)
+ verify(mockRootTaskDisplayAreaOrganizer).unregisterListener(eq(SECOND_DISPLAY), any())
}
@Test
@EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
- fun testDisplayAdded_supportsDesks_desktopRepositoryInitializedTwice_createsDeskOnce() =
+ fun testDisplayAreaAppeared_desktopRepositoryInitialized_desktopFirst_createsDesk() =
testScope.runTest {
- desktopState.canEnterDesktopMode = true
+ setUpDisplayDesktopSupport(SECOND_DISPLAY, desktopFirst = true)
- onDisplaysChangedListenerCaptor.lastValue.onDisplayAdded(DEFAULT_DISPLAY)
- desktopRepositoryInitializer.initialize(mockDesktopUserRepositories)
+ addDisplay(SECOND_DISPLAY, withTda = true)
desktopRepositoryInitializer.initialize(mockDesktopUserRepositories)
runCurrent()
- verify(mockDesktopTasksController, times(1)).createDesk(DEFAULT_DISPLAY)
+ verify(mockDesktopTasksController)
+ .createDesk(eq(SECOND_DISPLAY), eq(PRIMARY_USER_ID), any(), any())
}
@Test
@EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
- fun testDisplayAdded_supportsDesks_desktopRepositoryInitialized_deskExists_doesNotCreateDesk() =
+ fun testDisplayAreaAppeared_desktopRepositoryInitialized_touchFirst_warmsUpDesk() =
testScope.runTest {
- desktopState.canEnterDesktopMode = true
+ setUpDisplayDesktopSupport(DEFAULT_DISPLAY, desktopFirst = false)
+
+ addDisplay(DEFAULT_DISPLAY, withTda = true)
+ desktopRepositoryInitializer.initialize(mockDesktopUserRepositories)
+ runCurrent()
+
+ verify(mockDesksOrganizer)
+ .warmUpDefaultDesk(DEFAULT_DISPLAY, mockDesktopRepository.userId)
+ }
+
+ @Test
+ @EnableFlags(
+ Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND,
+ Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_DEFAULT_ACTIVATION_IN_DESKTOP_FIRST_DISPLAYS,
+ )
+ fun testDisplayAreaAppeared_desktopRepositoryInitialized_desktopFirst_createsAndActivatesDesk() =
+ testScope.runTest {
+ setUpDisplayDesktopSupport(displayId = SECOND_DISPLAY, desktopFirst = true)
+
+ addDisplay(SECOND_DISPLAY, withTda = true)
+ desktopRepositoryInitializer.initialize(mockDesktopUserRepositories)
+ runCurrent()
+
+ verify(mockDesktopTasksController)
+ .createDesk(
+ displayId = eq(SECOND_DISPLAY),
+ userId = eq(PRIMARY_USER_ID),
+ activateDesk = eq(true),
+ onResult = any(),
+ )
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun testDisplayAreaAppeared_desktopRepositoryNotInitialized_doesNotCreateDesk() =
+ testScope.runTest {
+ setUpDisplayDesktopSupport(DEFAULT_DISPLAY, supportsDesktop = true)
+
+ addDisplay(DEFAULT_DISPLAY, withTda = true)
+ runCurrent()
+
+ verify(mockDesktopTasksController, never())
+ .createDesk(eq(DEFAULT_DISPLAY), any(), any(), any())
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun testDisplayAreaAppeared_desktopRepositoryInitializedTwice_desktopFirst_createsDeskOnce() =
+ testScope.runTest {
+ setUpDisplayDesktopSupport(SECOND_DISPLAY, desktopFirst = true)
+
+ addDisplay(SECOND_DISPLAY, withTda = true)
+ desktopRepositoryInitializer.initialize(mockDesktopUserRepositories)
+ desktopRepositoryInitializer.initialize(mockDesktopUserRepositories)
+ runCurrent()
+
+ verify(mockDesktopTasksController, times(1))
+ .createDesk(eq(SECOND_DISPLAY), eq(PRIMARY_USER_ID), any(), any())
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun testDisplayAreaAppeared_desktopRepositoryInitializedTwice_touchFirst_warmsUpDeskOnce() =
+ testScope.runTest {
+ setUpDisplayDesktopSupport(DEFAULT_DISPLAY, desktopFirst = false)
+
+ addDisplay(DEFAULT_DISPLAY, withTda = true)
+ desktopRepositoryInitializer.initialize(mockDesktopUserRepositories)
+ desktopRepositoryInitializer.initialize(mockDesktopUserRepositories)
+ runCurrent()
+
+ verify(mockDesksOrganizer, times(1))
+ .warmUpDefaultDesk(DEFAULT_DISPLAY, mockDesktopRepository.userId)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun testDisplayAreaAppeared_desktopRepositoryInitialized_deskExists_doesNotCreateDeskOrWarmsUp() =
+ testScope.runTest {
+ setUpDisplayDesktopSupport(DEFAULT_DISPLAY)
whenever(mockDesktopRepository.getNumberOfDesks(DEFAULT_DISPLAY)).thenReturn(1)
- onDisplaysChangedListenerCaptor.lastValue.onDisplayAdded(DEFAULT_DISPLAY)
+ addDisplay(DEFAULT_DISPLAY, withTda = true)
desktopRepositoryInitializer.initialize(mockDesktopUserRepositories)
runCurrent()
- verify(mockDesktopTasksController, never()).createDesk(DEFAULT_DISPLAY)
+ verify(mockDesktopTasksController, never())
+ .createDesk(eq(DEFAULT_DISPLAY), any(), any(), any())
+ verify(mockDesksOrganizer, never())
+ .warmUpDefaultDesk(DEFAULT_DISPLAY, mockDesktopRepository.userId)
}
@Test
@EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
- fun testDisplayAdded_cannotEnterDesktopMode_doesNotCreateDesk() =
+ fun testDisplayAreaAppeared_desktopNotSupported_doesNotCreateDeskOrWarmsUp() =
testScope.runTest {
- desktopState.canEnterDesktopMode = false
+ setUpDisplayDesktopSupport(DEFAULT_DISPLAY, supportsDesktop = false)
desktopRepositoryInitializer.initialize(mockDesktopUserRepositories)
- onDisplaysChangedListenerCaptor.lastValue.onDisplayAdded(DEFAULT_DISPLAY)
+ addDisplay(DEFAULT_DISPLAY, withTda = true)
runCurrent()
- verify(mockDesktopTasksController, never()).createDesk(DEFAULT_DISPLAY)
+ verify(mockDesktopTasksController, never())
+ .createDesk(eq(DEFAULT_DISPLAY), any(), any(), any())
+ verify(mockDesksOrganizer, never())
+ .warmUpDefaultDesk(DEFAULT_DISPLAY, mockDesktopRepository.userId)
}
@Test
@EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
- fun testDeskRemoved_noDesksRemain_createsDesk() =
+ fun testDeskRemoved_noDesksRemain_desktopFirst_createsDesk() =
testScope.runTest {
- desktopState.canEnterDesktopMode = true
+ setUpDisplayDesktopSupport(SECOND_DISPLAY, desktopFirst = true)
+ whenever(mockDesktopRepository.getNumberOfDesks(SECOND_DISPLAY)).thenReturn(0)
+ desktopRepositoryInitializer.initialize(mockDesktopUserRepositories)
+
+ handler.onDeskRemoved(SECOND_DISPLAY, deskId = 1)
+ runCurrent()
+
+ verify(mockDesktopTasksController)
+ .createDesk(
+ eq(SECOND_DISPLAY),
+ eq(PRIMARY_USER_ID),
+ activateDesk = any(),
+ onResult = any(),
+ )
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun testDeskRemoved_noDesksRemain_touchFirst_warmsUpDesk() =
+ testScope.runTest {
+ setUpDisplayDesktopSupport(DEFAULT_DISPLAY, desktopFirst = false)
whenever(mockDesktopRepository.getNumberOfDesks(DEFAULT_DISPLAY)).thenReturn(0)
desktopRepositoryInitializer.initialize(mockDesktopUserRepositories)
handler.onDeskRemoved(DEFAULT_DISPLAY, deskId = 1)
runCurrent()
- verify(mockDesktopTasksController).createDesk(DEFAULT_DISPLAY)
+ verify(mockDesksOrganizer)
+ .warmUpDefaultDesk(DEFAULT_DISPLAY, mockDesktopRepository.userId)
+ }
+
+ @Test
+ @EnableFlags(
+ Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND,
+ Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_DEFAULT_ACTIVATION_IN_DESKTOP_FIRST_DISPLAYS,
+ )
+ fun testDeskRemoved_noDesksRemain_desktopFirstDisplay_createsAndActivatesDesk() =
+ testScope.runTest {
+ setUpDisplayDesktopSupport(SECOND_DISPLAY, desktopFirst = true)
+ whenever(mockDesktopRepository.getNumberOfDesks(SECOND_DISPLAY)).thenReturn(0)
+ desktopRepositoryInitializer.initialize(mockDesktopUserRepositories)
+
+ handler.onDeskRemoved(SECOND_DISPLAY, deskId = 1)
+ runCurrent()
+
+ verify(mockDesktopTasksController)
+ .createDesk(
+ displayId = eq(SECOND_DISPLAY),
+ userId = eq(PRIMARY_USER_ID),
+ activateDesk = eq(true),
+ onResult = any(),
+ )
}
@Test
@EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
- fun testDeskRemoved_desksRemain_doesNotCreateDesk() =
+ fun testDeskRemoved_desksRemain_doesNotCreateDeskOrWarmsUpDesk() =
testScope.runTest {
- desktopState.canEnterDesktopMode = true
+ setUpDisplayDesktopSupport(DEFAULT_DISPLAY, desktopFirst = false)
whenever(mockDesktopRepository.getNumberOfDesks(DEFAULT_DISPLAY)).thenReturn(1)
desktopRepositoryInitializer.initialize(mockDesktopUserRepositories)
@@ -201,58 +335,120 @@
runCurrent()
verify(mockDesktopTasksController, never()).createDesk(DEFAULT_DISPLAY)
+ verify(mockDesksOrganizer, never())
+ .warmUpDefaultDesk(DEFAULT_DISPLAY, mockDesktopRepository.userId)
}
@Test
@EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
- fun testUserChanged_createsDeskWhenNeeded() =
+ fun testUserChanged_createsOrWarmsUpDeskWhenNeeded() =
testScope.runTest {
val userId = 11
- desktopState.canEnterDesktopMode = true
+ setUpDisplayDesktopSupport(DEFAULT_DISPLAY, supportsDesktop = false)
+ setUpDisplayDesktopSupport(SECOND_DISPLAY, desktopFirst = true)
+ setUpDisplayDesktopSupport(3, desktopFirst = true)
+ setUpDisplayDesktopSupport(4, desktopFirst = true)
val userChangeListenerCaptor = argumentCaptor<UserChangeListener>()
verify(mockShellController).addUserChangeListener(userChangeListenerCaptor.capture())
val mockRepository = mock<DesktopRepository>()
+ whenever(mockRepository.userId).thenReturn(userId)
whenever(mockDesktopUserRepositories.getProfile(userId)).thenReturn(mockRepository)
- whenever(mockRepository.getNumberOfDesks(displayId = 2)).thenReturn(0)
+ whenever(mockRepository.getNumberOfDesks(displayId = DEFAULT_DISPLAY)).thenReturn(0)
+ whenever(mockRepository.getNumberOfDesks(displayId = SECOND_DISPLAY)).thenReturn(0)
whenever(mockRepository.getNumberOfDesks(displayId = 3)).thenReturn(0)
whenever(mockRepository.getNumberOfDesks(displayId = 4)).thenReturn(1)
- whenever(mockRootTaskDisplayAreaOrganizer.displayIds).thenReturn(intArrayOf(2, 3, 4))
+ whenever(mockRootTaskDisplayAreaOrganizer.displayIds)
+ .thenReturn(intArrayOf(DEFAULT_DISPLAY, SECOND_DISPLAY, 3, 4))
desktopRepositoryInitializer.initialize(mockDesktopUserRepositories)
- handler.onDisplayAdded(displayId = 2)
- handler.onDisplayAdded(displayId = 3)
- handler.onDisplayAdded(displayId = 4)
+ addDisplay(displayId = DEFAULT_DISPLAY, withTda = false)
+ addDisplay(displayId = SECOND_DISPLAY, withTda = false)
+ addDisplay(displayId = 3, withTda = false)
+ addDisplay(displayId = 4, withTda = false)
runCurrent()
clearInvocations(mockDesktopTasksController)
userChangeListenerCaptor.lastValue.onUserChanged(userId, context)
runCurrent()
- verify(mockDesktopTasksController).createDesk(displayId = 2, activateDesk = true)
- verify(mockDesktopTasksController).createDesk(displayId = 3, activateDesk = true)
- verify(mockDesktopTasksController, never()).createDesk(displayId = 4)
+ verify(mockDesktopTasksController)
+ .createDesk(
+ displayId = eq(2),
+ userId = eq(userId),
+ activateDesk = any(),
+ onResult = any(),
+ )
+ verify(mockDesktopTasksController)
+ .createDesk(
+ displayId = eq(3),
+ userId = eq(userId),
+ activateDesk = any(),
+ onResult = any(),
+ )
+ verify(mockDesktopTasksController, never())
+ .createDesk(
+ displayId = eq(4),
+ userId = any(),
+ activateDesk = any(),
+ onResult = any(),
+ )
}
@Test
fun testConnectExternalDisplay() {
onDisplaysChangedListenerCaptor.lastValue.onDisplayAdded(externalDisplayId)
- verify(desktopDisplayModeController).updateExternalDisplayWindowingMode(externalDisplayId)
- verify(desktopDisplayModeController).updateDefaultDisplayWindowingMode()
+ verify(mockDesktopDisplayModeController)
+ .updateExternalDisplayWindowingMode(externalDisplayId)
+ verify(mockDesktopDisplayModeController).updateDefaultDisplayWindowingMode()
}
@Test
fun testDisconnectExternalDisplay() {
onDisplaysChangedListenerCaptor.lastValue.onDisplayRemoved(externalDisplayId)
- verify(desktopDisplayModeController).updateDefaultDisplayWindowingMode()
+ verify(mockDesktopDisplayModeController).updateDefaultDisplayWindowingMode()
}
@Test
@EnableFlags(DisplayFlags.FLAG_ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT)
fun testDesktopModeEligibleChanged() {
onDisplaysChangedListenerCaptor.lastValue.onDesktopModeEligibleChanged(externalDisplayId)
- verify(desktopDisplayModeController).updateExternalDisplayWindowingMode(externalDisplayId)
- verify(desktopDisplayModeController).updateDefaultDisplayWindowingMode()
+ verify(mockDesktopDisplayModeController)
+ .updateExternalDisplayWindowingMode(externalDisplayId)
+ verify(mockDesktopDisplayModeController).updateDefaultDisplayWindowingMode()
}
+ private fun addDisplay(displayId: Int, withTda: Boolean = false) {
+ onDisplaysChangedListenerCaptor.lastValue.onDisplayAdded(displayId)
+ if (withTda) {
+ verify(mockRootTaskDisplayAreaOrganizer)
+ .registerListener(eq(displayId), onRootTdaListenerCaptor.capture())
+ onRootTdaListenerCaptor.lastValue.onDisplayAreaAppeared(
+ createDisplayAreaInfo(displayId)
+ )
+ }
+ }
+
+ private fun setUpDisplayDesktopSupport(
+ displayId: Int,
+ supportsDesktop: Boolean = true,
+ desktopFirst: Boolean = false,
+ ) {
+ desktopState.overrideDesktopModeSupportPerDisplay[displayId] = supportsDesktop
+ whenever(mockRootTaskDisplayAreaOrganizer.getDisplayAreaInfo(displayId))
+ .thenReturn(
+ createDisplayAreaInfo(displayId).apply {
+ configuration.windowConfiguration.windowingMode =
+ if (desktopFirst) {
+ DESKTOP_FIRST_DISPLAY_WINDOWING_MODE
+ } else {
+ TOUCH_FIRST_DISPLAY_WINDOWING_MODE
+ }
+ }
+ )
+ }
+
+ private fun createDisplayAreaInfo(displayId: Int) =
+ DisplayAreaInfo(/* token= */ mock(), displayId, /* featureId= */ 0)
+
private class FakeDesktopRepositoryInitializer : DesktopRepositoryInitializer {
override var deskRecreationFactory: DesktopRepositoryInitializer.DeskRecreationFactory =
DesktopRepositoryInitializer.DeskRecreationFactory { _, _, deskId -> deskId }
@@ -263,4 +459,9 @@
isInitialized.value = true
}
}
+
+ companion object {
+ private const val SECOND_DISPLAY = 2
+ private const val PRIMARY_USER_ID = 10
+ }
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopImeHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopImeHandlerTest.kt
index 9b3f772..38df754 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopImeHandlerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopImeHandlerTest.kt
@@ -20,10 +20,14 @@
import android.app.WindowConfiguration
import android.content.Context
import android.graphics.Rect
+import android.os.IBinder
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.view.Display.DEFAULT_DISPLAY
import android.view.WindowManager.TRANSIT_CHANGE
+import android.view.WindowManager.TRANSIT_TO_BACK
+import android.window.TransitionInfo
+import android.window.TransitionInfo.Change
import android.window.WindowContainerTransaction
import com.android.window.flags.Flags
import com.android.wm.shell.ShellTaskOrganizer
@@ -40,6 +44,7 @@
import kotlin.test.Test
import org.junit.Before
import org.mockito.ArgumentCaptor
+import org.mockito.Mockito
import org.mockito.kotlin.any
import org.mockito.kotlin.anyOrNull
import org.mockito.kotlin.eq
@@ -344,6 +349,62 @@
Flags.FLAG_ENABLE_DESKTOP_IME_BUGFIX,
Flags.FLAG_ENABLE_DISPLAY_FOCUS_IN_SHELL_TRANSITIONS,
)
+ fun onImeStartPositioning_newTransitionOnTask_doesNotRestorePreImeBounds() {
+ setUpLandscapeDisplay()
+ val wct = ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
+ val taskBounds = Rect(0, 400, 500, 1600)
+ val expectedBounds =
+ Rect(
+ taskBounds.left,
+ STATUS_BAR_HEIGHT,
+ taskBounds.right,
+ STATUS_BAR_HEIGHT + taskBounds.height(),
+ )
+ var freeformTask = createFreeformTask(DEFAULT_DISPLAY, taskBounds)
+ freeformTask.isFocused = true
+ whenever(focusTransitionObserver.globallyFocusedTaskId).thenReturn(freeformTask.taskId)
+ whenever(shellTaskOrganizer.getRunningTaskInfo(freeformTask.taskId))
+ .thenReturn(freeformTask)
+
+ imeHandler.onImeStartPositioning(
+ DEFAULT_DISPLAY,
+ hiddenTop = DISPLAY_DIMENSION_SHORT,
+ shownTop = IME_HEIGHT,
+ showing = true,
+ isFloating = false,
+ t = mock(),
+ )
+
+ // Moves the task up to the top of stable bounds
+ verify(transitions).startTransition(eq(TRANSIT_CHANGE), wct.capture(), anyOrNull())
+ assertThat(findBoundsChange(wct.value, freeformTask)).isEqualTo(expectedBounds)
+
+ // Create a transition that affects the freeform task we modified previously
+ imeHandler.onTransitionReady(
+ transition = Mockito.mock(IBinder::class.java),
+ info = createToBackTransition(freeformTask),
+ )
+
+ // This should be no op.
+ imeHandler.onImeStartPositioning(
+ DEFAULT_DISPLAY,
+ hiddenTop = DISPLAY_DIMENSION_SHORT,
+ shownTop = IME_HEIGHT,
+ showing = false,
+ isFloating = false,
+ t = mock(),
+ )
+
+ // Task is not moved back to original position with a new transition.
+ verify(transitions, times(1))
+ .startTransition(eq(TRANSIT_CHANGE), wct.capture(), anyOrNull())
+ }
+
+ @Test
+ @EnableFlags(
+ Flags.FLAG_ENABLE_DESKTOP_IME_BUGFIX,
+ Flags.FLAG_ENABLE_DISPLAY_FOCUS_IN_SHELL_TRANSITIONS,
+ )
fun onImeStartPositioning_taskAboveIme_noOp() {
setUpLandscapeDisplay()
val wct = ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
@@ -393,6 +454,18 @@
}
}
+ private fun createToBackTransition(task: RunningTaskInfo?) =
+ TransitionInfo(TRANSIT_TO_BACK, /* flags= */ 0).apply {
+ addChange(
+ Change(mock(), mock()).apply {
+ mode = TRANSIT_TO_BACK
+ parent = null
+ taskInfo = task
+ flags = flags
+ }
+ )
+ }
+
private companion object {
private const val DISPLAY_DIMENSION_SHORT = 1600
private const val DISPLAY_DIMENSION_LONG = 2560
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandlerTest.kt
index 0c585b3..daef64ee 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandlerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandlerTest.kt
@@ -47,6 +47,7 @@
import com.android.wm.shell.ShellTestCase
import com.android.wm.shell.TestRunningTaskInfoBuilder
import com.android.wm.shell.desktopmode.DesktopMixedTransitionHandler.PendingMixedTransition
+import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_DESKTOP_MODE_TASK_LIMIT_MINIMIZE
import com.android.wm.shell.freeform.FreeformTaskTransitionHandler
import com.android.wm.shell.sysui.ShellInit
import com.android.wm.shell.transition.Transitions
@@ -62,6 +63,7 @@
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Mock
import org.mockito.Mockito
+import org.mockito.Mockito.never
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.kotlin.any
@@ -100,6 +102,16 @@
fun setUp() {
whenever(userRepositories.current).thenReturn(desktopRepository)
whenever(userRepositories.getProfile(Mockito.anyInt())).thenReturn(desktopRepository)
+ whenever(
+ desktopMinimizationTransitionHandler.startAnimation(
+ any(),
+ any(),
+ any(),
+ any(),
+ any(),
+ )
+ )
+ .thenReturn(true)
mixedHandler =
DesktopMixedTransitionHandler(
context,
@@ -398,7 +410,7 @@
SurfaceControl.Transaction(),
) {}
- verify(rootTaskDisplayAreaOrganizer, times(0)).reparentToDisplayArea(anyInt(), any(), any())
+ verify(rootTaskDisplayAreaOrganizer, never()).reparentToDisplayArea(anyInt(), any(), any())
}
@Test
@@ -587,16 +599,6 @@
transitions.startTransition(eq(Transitions.TRANSIT_MINIMIZE), eq(wct), anyOrNull())
)
.thenReturn(transition)
- whenever(
- desktopMinimizationTransitionHandler.startAnimation(
- any(),
- any(),
- any(),
- any(),
- any(),
- )
- )
- .thenReturn(true)
mixedHandler.startMinimizedModeTransition(
wct = wct,
@@ -638,16 +640,6 @@
transitions.startTransition(eq(Transitions.TRANSIT_MINIMIZE), eq(wct), anyOrNull())
)
.thenReturn(transition)
- whenever(
- desktopMinimizationTransitionHandler.startAnimation(
- any(),
- any(),
- any(),
- any(),
- any(),
- )
- )
- .thenReturn(true)
mixedHandler.startMinimizedModeTransition(
wct = wct,
@@ -678,6 +670,88 @@
}
@Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_TASK_LIMIT_SEPARATE_TRANSITION)
+ fun startTaskLimitMinimizeTransition_taskLimitFlagEnabled_callsMinimizationHandler() {
+ val wct = WindowContainerTransaction()
+ val minimizingTask = createTask(WINDOWING_MODE_FREEFORM)
+ val minimizingTaskChange = createChange(minimizingTask)
+ val transition = Binder()
+ whenever(
+ transitions.startTransition(
+ eq(TRANSIT_DESKTOP_MODE_TASK_LIMIT_MINIMIZE),
+ eq(wct),
+ anyOrNull(),
+ )
+ )
+ .thenReturn(transition)
+
+ mixedHandler.startTaskLimitMinimizeTransition(wct = wct, taskId = minimizingTask.taskId)
+ val started =
+ mixedHandler.startAnimation(
+ transition = transition,
+ info =
+ createCloseTransitionInfo(
+ TRANSIT_DESKTOP_MODE_TASK_LIMIT_MINIMIZE,
+ listOf(minimizingTaskChange),
+ ),
+ startTransaction = mock(),
+ finishTransaction = mock(),
+ finishCallback = {},
+ )
+
+ assertTrue("Should delegate animation to minimization transition handler", started)
+ verify(desktopMinimizationTransitionHandler)
+ .startAnimation(
+ eq(transition),
+ argThat { info -> info.changes.contains(minimizingTaskChange) },
+ any(),
+ any(),
+ any(),
+ )
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_TASK_LIMIT_SEPARATE_TRANSITION)
+ fun startTaskLimitMinimizeTransition_taskLimitFlagDisabled_doesNotCallMinimizationHandler() {
+ val wct = WindowContainerTransaction()
+ val minimizingTask = createTask(WINDOWING_MODE_FREEFORM)
+ val minimizingTaskChange = createChange(minimizingTask)
+ val transition = Binder()
+ whenever(
+ transitions.startTransition(
+ eq(TRANSIT_DESKTOP_MODE_TASK_LIMIT_MINIMIZE),
+ eq(wct),
+ anyOrNull(),
+ )
+ )
+ .thenReturn(transition)
+
+ mixedHandler.startTaskLimitMinimizeTransition(wct = wct, taskId = minimizingTask.taskId)
+ val started =
+ mixedHandler.startAnimation(
+ transition = transition,
+ info =
+ createCloseTransitionInfo(
+ TRANSIT_DESKTOP_MODE_TASK_LIMIT_MINIMIZE,
+ listOf(minimizingTaskChange),
+ ),
+ startTransaction = mock(),
+ finishTransaction = mock(),
+ finishCallback = {},
+ )
+
+ assertFalse("Should not delegate animation to minimization transition handler", started)
+ verify(desktopMinimizationTransitionHandler, never())
+ .startAnimation(
+ eq(transition),
+ argThat { info -> info.changes.contains(minimizingTaskChange) },
+ any(),
+ any(),
+ any(),
+ )
+ }
+
+ @Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS_BUGFIX)
fun addPendingAndAnimateLaunchTransition_noMinimizeChange_doesNotReparentMinimizeChange() {
val wct = WindowContainerTransaction()
@@ -702,7 +776,7 @@
SurfaceControl.Transaction(),
) {}
- verify(rootTaskDisplayAreaOrganizer, times(0)).reparentToDisplayArea(anyInt(), any(), any())
+ verify(rootTaskDisplayAreaOrganizer, never()).reparentToDisplayArea(anyInt(), any(), any())
}
@Test
@@ -792,16 +866,6 @@
val minimizingTask = createTask(WINDOWING_MODE_FREEFORM)
val transition = Binder()
whenever(desktopRepository.getExpandedTaskCount(any())).thenReturn(2)
- whenever(
- desktopMinimizationTransitionHandler.startAnimation(
- any(),
- any(),
- any(),
- any(),
- any(),
- )
- )
- .thenReturn(true)
mixedHandler.addPendingMixedTransition(
PendingMixedTransition.Minimize(
transition = transition,
@@ -837,16 +901,6 @@
val minimizingTask = createTask(WINDOWING_MODE_FREEFORM)
val transition = Binder()
whenever(desktopRepository.getExpandedTaskCount(any())).thenReturn(2)
- whenever(
- desktopMinimizationTransitionHandler.startAnimation(
- any(),
- any(),
- any(),
- any(),
- any(),
- )
- )
- .thenReturn(true)
mixedHandler.addPendingMixedTransition(
PendingMixedTransition.Minimize(
transition = transition,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt
index 7520069..4a5431c 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt
@@ -51,6 +51,7 @@
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.TaskUpdate
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.UnminimizeReason
import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_DESKTOP_MODE_END_DRAG_TO_DESKTOP
+import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_DESKTOP_MODE_TASK_LIMIT_MINIMIZE
import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_ENTER_DESKTOP_FROM_APP_FROM_OVERVIEW
import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_ENTER_DESKTOP_FROM_APP_HANDLE_MENU_BUTTON
import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_ENTER_DESKTOP_FROM_KEYBOARD_SHORTCUT
@@ -860,6 +861,33 @@
)
}
+ @Test
+ fun onTransitionReady_taskLimitMinimizeTransitionType_logsTaskMinimized() {
+ val taskInfo2 = createTaskInfo(WINDOWING_MODE_FREEFORM, id = 2)
+ transitionObserver.apply {
+ isSessionActive = true
+ addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM, id = 1))
+ addTaskInfosToCachedMap(taskInfo2)
+ }
+ val transitionInfo =
+ TransitionInfoBuilder(TRANSIT_DESKTOP_MODE_TASK_LIMIT_MINIMIZE, /* flags= */ 0)
+ .addChange(createChange(TRANSIT_TO_BACK, taskInfo2))
+ .build()
+
+ callOnTransitionReady(transitionInfo)
+
+ verify(desktopModeEventLogger, times(1))
+ .logTaskRemoved(
+ eq(
+ DEFAULT_TASK_UPDATE.copy(
+ instanceId = 2,
+ visibleTaskCount = 1,
+ minimizeReason = MinimizeReason.TASK_LIMIT,
+ )
+ )
+ )
+ }
+
/** Simulate calling the onTransitionReady() method */
private fun callOnTransitionReady(transitionInfo: TransitionInfo) {
val transition = mock<IBinder>()
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListenerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListenerTest.kt
index 4ace1b2..1b86f00 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListenerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListenerTest.kt
@@ -21,13 +21,17 @@
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION
+import com.android.window.flags.Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND
import com.android.wm.shell.ShellTestCase
import com.android.wm.shell.desktopmode.DesktopTestHelpers.createFreeformTask
import com.android.wm.shell.desktopmode.DesktopTestHelpers.createFullscreenTask
+import com.android.wm.shell.shared.desktopmode.FakeDesktopState
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.kotlin.any
+import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
import org.mockito.kotlin.never
import org.mockito.kotlin.verify
@@ -46,10 +50,15 @@
private val desktopUserRepositories = mock<DesktopUserRepositories>()
private val desktopRepository = mock<DesktopRepository>()
+ private val desktopState =
+ FakeDesktopState().apply {
+ canEnterDesktopMode = true
+ overrideDesktopModeSupportPerDisplay[UNSUPPORTED_DISPLAY_ID] = false
+ }
@Before
fun setUp() {
- desktopTaskChangeListener = DesktopTaskChangeListener(desktopUserRepositories)
+ desktopTaskChangeListener = DesktopTaskChangeListener(desktopUserRepositories, desktopState)
whenever(desktopUserRepositories.current).thenReturn(desktopRepository)
whenever(desktopUserRepositories.getProfile(anyInt())).thenReturn(desktopRepository)
@@ -99,6 +108,18 @@
}
@Test
+ @EnableFlags(FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun onTaskOpening_desktopModeNotSupportedInDisplay_noOp() {
+ val task = createFreeformTask(UNSUPPORTED_DISPLAY_ID)
+ whenever(desktopUserRepositories.current.isActiveTask(task.taskId)).thenReturn(false)
+
+ desktopTaskChangeListener.onTaskOpening(task)
+
+ verify(desktopUserRepositories.current, never())
+ .addTask(displayId = eq(UNSUPPORTED_DISPLAY_ID), taskId = any(), isVisible = any())
+ }
+
+ @Test
fun onTaskChanging_fullscreenTask_activeInDesktopRepository_removesTaskFromRepo() {
val task = createFullscreenTask().apply { isVisible = true }
whenever(desktopUserRepositories.current.isActiveTask(task.taskId)).thenReturn(true)
@@ -128,6 +149,17 @@
}
@Test
+ @EnableFlags(FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun onTaskChanging_desktopModeNotSupportedInDisplay_noOp() {
+ val task = createFreeformTask(UNSUPPORTED_DISPLAY_ID)
+
+ desktopTaskChangeListener.onTaskChanging(task)
+
+ verify(desktopUserRepositories.current, never())
+ .addTask(displayId = eq(UNSUPPORTED_DISPLAY_ID), taskId = any(), isVisible = any())
+ }
+
+ @Test
fun onTaskMovingToFront_fullscreenTask_activeTaskInDesktopRepo_removesTaskFromRepo() {
val task = createFullscreenTask().apply { isVisible = true }
whenever(desktopUserRepositories.current.isActiveTask(task.taskId)).thenReturn(true)
@@ -157,6 +189,17 @@
}
@Test
+ @EnableFlags(FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun onTaskMovingToFront_desktopModeNotSupportedInDisplay_noOp() {
+ val task = createFreeformTask(UNSUPPORTED_DISPLAY_ID).apply { isVisible = true }
+
+ desktopTaskChangeListener.onTaskMovingToFront(task)
+
+ verify(desktopUserRepositories.current, never())
+ .addTask(displayId = eq(UNSUPPORTED_DISPLAY_ID), taskId = any(), isVisible = any())
+ }
+
+ @Test
fun onTaskMovingToBack_activeTaskInRepo_updatesTask() {
val task = createFreeformTask().apply { isVisible = true }
whenever(desktopUserRepositories.current.isActiveTask(task.taskId)).thenReturn(true)
@@ -179,6 +222,18 @@
}
@Test
+ @EnableFlags(FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun onTaskMovingToBack_desktopModeNotSupportedInDisplay_noOp() {
+ val task = createFreeformTask(UNSUPPORTED_DISPLAY_ID)
+ whenever(desktopUserRepositories.current.isActiveTask(task.taskId)).thenReturn(true)
+
+ desktopTaskChangeListener.onTaskMovingToBack(task)
+
+ verify(desktopUserRepositories.current, never())
+ .updateTask(displayId = eq(UNSUPPORTED_DISPLAY_ID), taskId = any(), isVisible = any())
+ }
+
+ @Test
@EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION)
fun onTaskClosing_backNavEnabled_nonClosingTask_minimizesTaskInRepo() {
val task = createFreeformTask().apply { isVisible = true }
@@ -218,4 +273,24 @@
verify(desktopUserRepositories.current).removeClosingTask(task.taskId)
verify(desktopUserRepositories.current).removeTask(task.displayId, task.taskId)
}
+
+ @Test
+ @EnableFlags(
+ FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION,
+ FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND,
+ )
+ fun onTaskClosing_desktopModeNotSupportedInDisplay_noOp() {
+ val task = createFreeformTask(UNSUPPORTED_DISPLAY_ID).apply { isVisible = true }
+ whenever(desktopUserRepositories.current.isActiveTask(task.taskId)).thenReturn(true)
+ whenever(desktopUserRepositories.current.isClosingTask(task.taskId)).thenReturn(true)
+
+ desktopTaskChangeListener.onTaskClosing(task)
+
+ verify(desktopUserRepositories.current, never()).removeClosingTask(task.taskId)
+ verify(desktopUserRepositories.current, never()).removeTask(task.displayId, task.taskId)
+ }
+
+ companion object {
+ private const val UNSUPPORTED_DISPLAY_ID = 3
+ }
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
index 0ae5a35..3624adb 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
@@ -19,6 +19,7 @@
import android.app.ActivityManager.RecentTaskInfo
import android.app.ActivityManager.RunningTaskInfo
import android.app.ActivityOptions
+import android.app.AppOpsManager
import android.app.KeyguardManager
import android.app.PendingIntent
import android.app.PictureInPictureParams
@@ -143,6 +144,11 @@
import com.android.wm.shell.recents.RecentsTransitionStateListener.TRANSITION_STATE_ANIMATING
import com.android.wm.shell.recents.RecentsTransitionStateListener.TRANSITION_STATE_REQUESTED
import com.android.wm.shell.shared.desktopmode.DesktopModeCompatPolicy
+import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource.ADB_COMMAND
+import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource.APP_FROM_OVERVIEW
+import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource.APP_HANDLE_MENU_BUTTON
+import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource.KEYBOARD_SHORTCUT
+import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource.TASK_DRAG
import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource.UNKNOWN
import com.android.wm.shell.shared.desktopmode.FakeDesktopConfig
import com.android.wm.shell.shared.desktopmode.FakeDesktopState
@@ -169,14 +175,12 @@
import kotlin.test.assertIs
import kotlin.test.assertNotNull
import kotlin.test.assertNull
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancel
import kotlinx.coroutines.runBlocking
-import kotlinx.coroutines.test.StandardTestDispatcher
-import kotlinx.coroutines.test.setMain
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
import org.junit.After
import org.junit.Assume.assumeTrue
import org.junit.Before
@@ -273,6 +277,8 @@
@Mock private lateinit var dragToDisplayTransitionHandler: DragToDisplayTransitionHandler
@Mock
private lateinit var moveToDisplayTransitionHandler: DesktopModeMoveToDisplayTransitionHandler
+ @Mock private lateinit var mockAppOpsManager: AppOpsManager
+ @Mock private lateinit var visualIndicatorUpdateScheduler: VisualIndicatorUpdateScheduler
private lateinit var controller: DesktopTasksController
private lateinit var shellInit: ShellInit
@@ -280,7 +286,6 @@
private lateinit var userRepositories: DesktopUserRepositories
private lateinit var desktopTasksLimiter: DesktopTasksLimiter
private lateinit var recentsTransitionStateListener: RecentsTransitionStateListener
- private lateinit var testScope: CoroutineScope
private lateinit var desktopModeCompatPolicy: DesktopModeCompatPolicy
private lateinit var spyContext: TestableContext
private lateinit var homeIntentProvider: HomeIntentProvider
@@ -289,6 +294,7 @@
private val shellExecutor = TestShellExecutor()
private val bgExecutor = TestShellExecutor()
+ private val testScope = TestScope()
// Mock running tasks are registered here so we can get the list from mock shell task organizer
private val runningTasks = mutableListOf<RunningTaskInfo>()
@@ -313,7 +319,6 @@
@Before
fun setUp() {
- Dispatchers.setMain(StandardTestDispatcher())
mockitoSession =
mockitoSession()
.strictness(Strictness.LENIENT)
@@ -324,7 +329,6 @@
desktopConfig = FakeDesktopConfig()
desktopState.canEnterDesktopMode = true
- testScope = CoroutineScope(Dispatchers.Unconfined + SupervisorJob())
spyContext = spy(mContext)
shellInit = spy(ShellInit(testExecutor))
userRepositories =
@@ -333,7 +337,7 @@
shellController,
persistentRepository,
repositoryInitializer,
- testScope,
+ testScope.backgroundScope,
userManager,
desktopState,
)
@@ -343,6 +347,7 @@
userRepositories,
shellTaskOrganizer,
desksOrganizer,
+ desktopMixedTransitionHandler,
MAX_TASK_LIMIT,
)
desktopModeCompatPolicy = spy(DesktopModeCompatPolicy(spyContext))
@@ -455,6 +460,7 @@
recentsTransitionHandler,
multiInstanceHelper,
shellExecutor,
+ testScope.backgroundScope,
bgExecutor,
Optional.of(desktopTasksLimiter),
recentTasksController,
@@ -475,6 +481,7 @@
homeIntentProvider,
desktopState,
desktopConfig,
+ visualIndicatorUpdateScheduler,
)
@After
@@ -1243,6 +1250,31 @@
}
@Test
+ @EnableFlags(Flags.FLAG_ENABLE_CASCADING_WINDOWS, Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun handleRequest_newFreeformTaskLaunch_newDesk_desksCascadeIndependently() {
+ setUpLandscapeDisplay()
+ val stableBounds = Rect()
+ displayLayout.getStableBoundsForDesktopMode(stableBounds)
+
+ // Launch freeform tasks in default desk.
+ setUpFreeformTask(bounds = DEFAULT_LANDSCAPE_BOUNDS)
+ val freeformTask = setUpFreeformTask(bounds = DEFAULT_LANDSCAPE_BOUNDS, active = false)
+ controller.handleRequest(Binder(), createTransition(freeformTask))
+
+ // Create new active desk and launch new task.
+ taskRepository.addDesk(DEFAULT_DISPLAY, deskId = 2)
+ taskRepository.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = 2)
+ val newDeskTask = setUpFullscreenTask(displayId = DEFAULT_DISPLAY)
+ val wct = controller.handleRequest(Binder(), createTransition(newDeskTask))
+
+ // New task should be cascaded independently of tasks in other desks.
+ assertNotNull(wct, "should handle request")
+ val finalBounds = findBoundsChange(wct, newDeskTask)
+ assertThat(stableBounds.getDesktopTaskPosition(finalBounds!!))
+ .isEqualTo(DesktopTaskPosition.Center)
+ }
+
+ @Test
@EnableFlags(Flags.FLAG_ENABLE_CASCADING_WINDOWS)
fun handleRequest_freeformTaskAlreadyExistsInDesktopMode_cascadeNotApplied() {
setUpLandscapeDisplay()
@@ -1511,6 +1543,7 @@
}
@Test
+ @EnableFlags(Flags.FLAG_ENABLE_DISPLAY_WINDOWING_MODE_SWITCHING)
fun launchIntent_taskInDesktopMode_onSecondaryDisplay_transitionStarted() {
setUpLandscapeDisplay()
taskRepository.addDesk(SECOND_DISPLAY, deskId = 2)
@@ -1619,6 +1652,64 @@
}
@Test
+ @EnableFlags(Flags.FLAG_ENABLE_PROJECTED_DISPLAY_DESKTOP_MODE)
+ @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun moveToDesktop_displayNotSupported_withOverButtonOrAdb_movesToDesk() {
+ val spyController = spy(controller)
+ desktopState.overrideDesktopModeSupportPerDisplay[DEFAULT_DISPLAY] = false
+ val task = setUpFullscreenTask()
+ spyController.moveTaskToDefaultDeskAndActivate(task.taskId, transitionSource = ADB_COMMAND)
+ verify(spyController, times(1))
+ .moveTaskToDesk(anyInt(), anyInt(), any(), eq(ADB_COMMAND), eq(null), eq(null))
+
+ clearInvocations(desksOrganizer)
+
+ spyController.moveTaskToDefaultDeskAndActivate(
+ task.taskId,
+ transitionSource = APP_FROM_OVERVIEW,
+ )
+ verify(spyController, times(1))
+ .moveTaskToDesk(anyInt(), anyInt(), any(), eq(APP_FROM_OVERVIEW), eq(null), eq(null))
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_PROJECTED_DISPLAY_DESKTOP_MODE)
+ @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun moveToDesktop_displayNotSupported_doesNothing() {
+ val spyController = spy(controller)
+ desktopState.overrideDesktopModeSupportPerDisplay[DEFAULT_DISPLAY] = false
+ val task = setUpFullscreenTask()
+ spyController.moveTaskToDefaultDeskAndActivate(task.taskId, transitionSource = UNKNOWN)
+ verify(spyController, times(0))
+ .moveTaskToDesk(anyInt(), anyInt(), any(), eq(UNKNOWN), eq(null), eq(null))
+
+ spyController.moveTaskToDefaultDeskAndActivate(
+ task.taskId,
+ transitionSource = KEYBOARD_SHORTCUT,
+ )
+ verify(spyController, times(0))
+ .moveTaskToDesk(anyInt(), anyInt(), any(), eq(KEYBOARD_SHORTCUT), eq(null), eq(null))
+
+ spyController.moveTaskToDefaultDeskAndActivate(
+ task.taskId,
+ transitionSource = APP_HANDLE_MENU_BUTTON,
+ )
+ verify(spyController, times(0))
+ .moveTaskToDesk(
+ anyInt(),
+ anyInt(),
+ any(),
+ eq(APP_HANDLE_MENU_BUTTON),
+ eq(null),
+ eq(null),
+ )
+
+ spyController.moveTaskToDefaultDeskAndActivate(task.taskId, transitionSource = TASK_DRAG)
+ verify(spyController, times(0))
+ .moveTaskToDesk(anyInt(), anyInt(), any(), eq(TASK_DRAG), eq(null), eq(null))
+ }
+
+ @Test
@DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
fun moveToDesktop_tdaFullscreen_windowingModeSetToFreeform() {
val task = setUpFullscreenTask()
@@ -1648,36 +1739,42 @@
@Test
@EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
- fun moveRunningTaskToDesktop_movesTaskToDefaultDesk() {
- val task = setUpFullscreenTask(displayId = DEFAULT_DISPLAY)
+ fun moveRunningTaskToDesktop_movesTaskToDefaultDesk() =
+ testScope.runTest {
+ val task = setUpFullscreenTask(displayId = DEFAULT_DISPLAY)
- controller.moveTaskToDefaultDeskAndActivate(task.taskId, transitionSource = UNKNOWN)
+ controller.moveTaskToDefaultDeskAndActivate(task.taskId, transitionSource = UNKNOWN)
+ runCurrent()
- val wct = getLatestEnterDesktopWct()
- verify(desksOrganizer).moveTaskToDesk(wct, deskId = 0, task)
- }
+ val wct = getLatestEnterDesktopWct()
+ verify(desksOrganizer).moveTaskToDesk(wct, deskId = 0, task)
+ }
@Test
@EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
- fun moveRunningTaskToDesktop_activatesDesk() {
- val task = setUpFullscreenTask(displayId = DEFAULT_DISPLAY)
+ fun moveRunningTaskToDesktop_activatesDesk() =
+ testScope.runTest {
+ val task = setUpFullscreenTask(displayId = DEFAULT_DISPLAY)
- controller.moveTaskToDefaultDeskAndActivate(task.taskId, transitionSource = UNKNOWN)
+ controller.moveTaskToDefaultDeskAndActivate(task.taskId, transitionSource = UNKNOWN)
+ runCurrent()
- val wct = getLatestEnterDesktopWct()
- verify(desksOrganizer).activateDesk(wct, deskId = 0)
- }
+ val wct = getLatestEnterDesktopWct()
+ verify(desksOrganizer).activateDesk(wct, deskId = 0)
+ }
@Test
@EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
- fun moveRunningTaskToDesktop_triggersEnterDesktopListener() {
- val task = setUpFullscreenTask(displayId = DEFAULT_DISPLAY)
+ fun moveRunningTaskToDesktop_triggersEnterDesktopListener() =
+ testScope.runTest {
+ val task = setUpFullscreenTask(displayId = DEFAULT_DISPLAY)
- controller.moveTaskToDefaultDeskAndActivate(task.taskId, transitionSource = UNKNOWN)
+ controller.moveTaskToDefaultDeskAndActivate(task.taskId, transitionSource = UNKNOWN)
+ runCurrent()
- verify(desktopModeEnterExitTransitionListener)
- .onEnterDesktopModeTransitionStarted(FREEFORM_ANIMATION_DURATION)
- }
+ verify(desktopModeEnterExitTransitionListener)
+ .onEnterDesktopModeTransitionStarted(FREEFORM_ANIMATION_DURATION)
+ }
@Test
@EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
@@ -1723,56 +1820,68 @@
@Test
@DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
- fun moveTaskToDesktop_desktopWallpaperDisabled_nonRunningTask_launchesInFreeform() {
- val task = createRecentTaskInfo(1)
- whenever(shellTaskOrganizer.getRunningTaskInfo(anyInt())).thenReturn(null)
- whenever(recentTasksController.findTaskInBackground(anyInt())).thenReturn(task)
+ fun moveTaskToDesktop_desktopWallpaperDisabled_nonRunningTask_launchesInFreeform() =
+ testScope.runTest {
+ val task = createRecentTaskInfo(1)
+ whenever(shellTaskOrganizer.getRunningTaskInfo(anyInt())).thenReturn(null)
+ whenever(recentTasksController.findTaskInBackground(anyInt())).thenReturn(task)
- controller.moveTaskToDefaultDeskAndActivate(task.taskId, transitionSource = UNKNOWN)
+ controller.moveTaskToDefaultDeskAndActivate(task.taskId, transitionSource = UNKNOWN)
+ runCurrent()
- with(getLatestEnterDesktopWct()) {
- assertLaunchTaskAt(0, task.taskId, WINDOWING_MODE_FREEFORM)
+ with(getLatestEnterDesktopWct()) {
+ assertLaunchTaskAt(0, task.taskId, WINDOWING_MODE_FREEFORM)
+ }
}
- }
@Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
- fun moveTaskToDesktop_desktopWallpaperEnabled_nonRunningTask_launchesInFreeform() {
- whenever(desktopWallpaperActivityTokenProvider.getToken()).thenReturn(null)
- val task = createRecentTaskInfo(1)
- whenever(shellTaskOrganizer.getRunningTaskInfo(anyInt())).thenReturn(null)
- whenever(recentTasksController.findTaskInBackground(anyInt())).thenReturn(task)
+ fun moveTaskToDesktop_desktopWallpaperEnabled_nonRunningTask_launchesInFreeform() =
+ testScope.runTest {
+ whenever(desktopWallpaperActivityTokenProvider.getToken()).thenReturn(null)
+ val task = createRecentTaskInfo(1)
+ whenever(shellTaskOrganizer.getRunningTaskInfo(anyInt())).thenReturn(null)
+ whenever(recentTasksController.findTaskInBackground(anyInt())).thenReturn(task)
- controller.moveTaskToDefaultDeskAndActivate(task.taskId, transitionSource = UNKNOWN)
+ controller.moveTaskToDefaultDeskAndActivate(task.taskId, transitionSource = UNKNOWN)
+ runCurrent()
- with(getLatestEnterDesktopWct()) {
- // Add desktop wallpaper activity
- assertPendingIntentAt(index = 0, desktopWallpaperIntent)
- // Launch task
- assertLaunchTaskAt(index = 1, task.taskId, WINDOWING_MODE_FREEFORM)
+ with(getLatestEnterDesktopWct()) {
+ // Add desktop wallpaper activity
+ assertPendingIntentAt(index = 0, desktopWallpaperIntent)
+ // Launch task
+ assertLaunchTaskAt(index = 1, task.taskId, WINDOWING_MODE_FREEFORM)
+ }
}
- }
@Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
- fun moveBackgroundTaskToDesktop_remoteTransition_usesOneShotHandler() {
- val transitionHandlerArgCaptor = argumentCaptor<TransitionHandler>()
- whenever(transitions.startTransition(anyInt(), any(), transitionHandlerArgCaptor.capture()))
- .thenReturn(Binder())
+ fun moveBackgroundTaskToDesktop_remoteTransition_usesOneShotHandler() =
+ testScope.runTest {
+ val transitionHandlerArgCaptor = argumentCaptor<TransitionHandler>()
+ whenever(
+ transitions.startTransition(
+ anyInt(),
+ any(),
+ transitionHandlerArgCaptor.capture(),
+ )
+ )
+ .thenReturn(Binder())
- val task = createRecentTaskInfo(1)
- whenever(shellTaskOrganizer.getRunningTaskInfo(anyInt())).thenReturn(null)
- whenever(recentTasksController.findTaskInBackground(anyInt())).thenReturn(task)
- controller.moveTaskToDefaultDeskAndActivate(
- taskId = task.taskId,
- transitionSource = UNKNOWN,
- remoteTransition = RemoteTransition(spy(TestRemoteTransition())),
- )
+ val task = createRecentTaskInfo(1)
+ whenever(shellTaskOrganizer.getRunningTaskInfo(anyInt())).thenReturn(null)
+ whenever(recentTasksController.findTaskInBackground(anyInt())).thenReturn(task)
+ controller.moveTaskToDefaultDeskAndActivate(
+ taskId = task.taskId,
+ transitionSource = UNKNOWN,
+ remoteTransition = RemoteTransition(spy(TestRemoteTransition())),
+ )
+ runCurrent()
- verify(desktopModeEnterExitTransitionListener)
- .onEnterDesktopModeTransitionStarted(FREEFORM_ANIMATION_DURATION)
- assertIs<OneShotRemoteHandler>(transitionHandlerArgCaptor.firstValue)
- }
+ verify(desktopModeEnterExitTransitionListener)
+ .onEnterDesktopModeTransitionStarted(FREEFORM_ANIMATION_DURATION)
+ assertIs<OneShotRemoteHandler>(transitionHandlerArgCaptor.firstValue)
+ }
@Test
@EnableFlags(
@@ -1780,45 +1889,151 @@
Flags.FLAG_ENABLE_PER_DISPLAY_DESKTOP_WALLPAPER_ACTIVITY,
Flags.FLAG_ENABLE_DESKTOP_WALLPAPER_ACTIVITY_FOR_SYSTEM_USER,
)
- fun moveBackgroundTaskToDesktop_nonDefaultDisplay_reordersHomeAndWallpaperOfNonDefaultDisplay() {
- val homeTask = setUpHomeTask(displayId = SECOND_DISPLAY)
- val wallpaperToken = MockToken().token()
- whenever(desktopWallpaperActivityTokenProvider.getToken(SECOND_DISPLAY))
- .thenReturn(wallpaperToken)
- taskRepository.addDesk(SECOND_DISPLAY, deskId = 2)
- val task = setUpFreeformTask(displayId = SECOND_DISPLAY, deskId = 2, background = true)
+ fun moveBackgroundTaskToDesktop_nonDefaultDisplay_reordersHomeAndWallpaperOfNonDefaultDisplay() =
+ testScope.runTest {
+ val homeTask = setUpHomeTask(displayId = SECOND_DISPLAY)
+ val wallpaperToken = MockToken().token()
+ whenever(desktopWallpaperActivityTokenProvider.getToken(SECOND_DISPLAY))
+ .thenReturn(wallpaperToken)
+ taskRepository.addDesk(SECOND_DISPLAY, deskId = 2)
+ val task = setUpFreeformTask(displayId = SECOND_DISPLAY, deskId = 2, background = true)
- controller.moveTaskToDefaultDeskAndActivate(
- taskId = task.taskId,
- transitionSource = UNKNOWN,
- remoteTransition = RemoteTransition(spy(TestRemoteTransition())),
- )
+ controller.moveTaskToDefaultDeskAndActivate(
+ taskId = task.taskId,
+ transitionSource = UNKNOWN,
+ remoteTransition = RemoteTransition(spy(TestRemoteTransition())),
+ )
+ runCurrent()
- val wct = getLatestTransition()
- val homeReorderIndex = wct.indexOfReorder(homeTask, toTop = true)
- val wallpaperReorderIndex = wct.indexOfReorder(wallpaperToken, toTop = true)
- assertThat(homeReorderIndex).isNotEqualTo(-1)
- assertThat(wallpaperReorderIndex).isNotEqualTo(-1)
- // Wallpaper last, to be in front of Home.
- assertThat(wallpaperReorderIndex).isGreaterThan(homeReorderIndex)
- }
+ val wct = getLatestTransition()
+ val homeReorderIndex = wct.indexOfReorder(homeTask, toTop = true)
+ val wallpaperReorderIndex = wct.indexOfReorder(wallpaperToken, toTop = true)
+ assertThat(homeReorderIndex).isNotEqualTo(-1)
+ assertThat(wallpaperReorderIndex).isNotEqualTo(-1)
+ // Wallpaper last, to be in front of Home.
+ assertThat(wallpaperReorderIndex).isGreaterThan(homeReorderIndex)
+ }
@Test
- fun moveRunningTaskToDesktop_remoteTransition_usesOneShotHandler() {
- val transitionHandlerArgCaptor = argumentCaptor<TransitionHandler>()
- whenever(transitions.startTransition(anyInt(), any(), transitionHandlerArgCaptor.capture()))
- .thenReturn(Binder())
+ fun moveBackgroundTaskToDesktop_invalidDisplay_invalidFocusedDisplay_reordersHomeAndWallpaperInDefaultDisplay() =
+ testScope.runTest {
+ val task = createRecentTaskInfo(1, INVALID_DISPLAY)
+ val homeTask = setUpHomeTask(displayId = DEFAULT_DISPLAY)
+ val wallpaperToken = MockToken().token()
+ whenever(focusTransitionObserver.globallyFocusedDisplayId).thenReturn(INVALID_DISPLAY)
+ whenever(shellTaskOrganizer.getRunningTaskInfo(anyInt())).thenReturn(null)
+ whenever(recentTasksController.findTaskInBackground(anyInt())).thenReturn(task)
+ whenever(desktopWallpaperActivityTokenProvider.getToken(DEFAULT_DISPLAY))
+ .thenReturn(wallpaperToken)
- controller.moveTaskToDefaultDeskAndActivate(
- taskId = setUpFullscreenTask().taskId,
- transitionSource = UNKNOWN,
- remoteTransition = RemoteTransition(spy(TestRemoteTransition())),
- )
+ controller.moveTaskToDefaultDeskAndActivate(
+ taskId = task.taskId,
+ transitionSource = UNKNOWN,
+ remoteTransition = RemoteTransition(TestRemoteTransition()),
+ )
+ runCurrent()
- verify(desktopModeEnterExitTransitionListener)
- .onEnterDesktopModeTransitionStarted(FREEFORM_ANIMATION_DURATION)
- assertIs<OneShotRemoteHandler>(transitionHandlerArgCaptor.firstValue)
- }
+ val wct = getLatestTransition()
+ wct.assertReorder(homeTask)
+ wct.assertReorder(wallpaperToken)
+ }
+
+ @Test
+ fun moveBackgroundTaskToDesktop_invalidDisplay_validFocusedDisplay_reordersHomeAndWallpaperInFocusedDisplay() =
+ testScope.runTest {
+ val task = createRecentTaskInfo(1, INVALID_DISPLAY)
+ val focusedDisplayId = 5
+ val homeTask = setUpHomeTask(displayId = focusedDisplayId)
+ val wallpaperToken = MockToken().token()
+ taskRepository.addDesk(displayId = focusedDisplayId, deskId = 5)
+ whenever(focusTransitionObserver.globallyFocusedDisplayId).thenReturn(focusedDisplayId)
+ whenever(shellTaskOrganizer.getRunningTaskInfo(anyInt())).thenReturn(null)
+ whenever(recentTasksController.findTaskInBackground(anyInt())).thenReturn(task)
+ whenever(desktopWallpaperActivityTokenProvider.getToken(focusedDisplayId))
+ .thenReturn(wallpaperToken)
+
+ controller.moveTaskToDefaultDeskAndActivate(
+ taskId = task.taskId,
+ transitionSource = UNKNOWN,
+ remoteTransition = RemoteTransition(TestRemoteTransition()),
+ )
+ runCurrent()
+
+ val wct = getLatestTransition()
+ wct.assertReorder(homeTask)
+ wct.assertReorder(wallpaperToken)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun moveBackgroundTaskToDesktop_invalidDisplay_invalidFocusedDisplay_activatesDeskInDefaultDisplay() =
+ testScope.runTest {
+ val task = createRecentTaskInfo(1, INVALID_DISPLAY)
+ val deskId = 2
+ whenever(focusTransitionObserver.globallyFocusedDisplayId).thenReturn(INVALID_DISPLAY)
+ whenever(shellTaskOrganizer.getRunningTaskInfo(anyInt())).thenReturn(null)
+ whenever(recentTasksController.findTaskInBackground(anyInt())).thenReturn(task)
+ taskRepository.addDesk(displayId = DEFAULT_DISPLAY, deskId = deskId)
+ taskRepository.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = deskId)
+
+ controller.moveTaskToDefaultDeskAndActivate(
+ taskId = task.taskId,
+ transitionSource = UNKNOWN,
+ remoteTransition = RemoteTransition(TestRemoteTransition()),
+ )
+ runCurrent()
+
+ val wct = getLatestTransition()
+ verify(desksOrganizer).activateDesk(wct, deskId = deskId)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun moveBackgroundTaskToDesktop_invalidDisplay_validFocusedDisplay_activatesDeskInFocusedDisplay() =
+ testScope.runTest {
+ val task = createRecentTaskInfo(1, INVALID_DISPLAY)
+ val focusedDisplayId = 5
+ val deskId = 2
+ whenever(focusTransitionObserver.globallyFocusedDisplayId).thenReturn(focusedDisplayId)
+ whenever(shellTaskOrganizer.getRunningTaskInfo(anyInt())).thenReturn(null)
+ whenever(recentTasksController.findTaskInBackground(anyInt())).thenReturn(task)
+ taskRepository.addDesk(displayId = focusedDisplayId, deskId = deskId)
+
+ controller.moveTaskToDefaultDeskAndActivate(
+ taskId = task.taskId,
+ transitionSource = UNKNOWN,
+ remoteTransition = RemoteTransition(TestRemoteTransition()),
+ )
+ runCurrent()
+
+ val wct = getLatestTransition()
+ verify(desksOrganizer).activateDesk(wct, deskId = deskId)
+ }
+
+ @Test
+ fun moveRunningTaskToDesktop_remoteTransition_usesOneShotHandler() =
+ testScope.runTest {
+ val transitionHandlerArgCaptor = argumentCaptor<TransitionHandler>()
+ whenever(
+ transitions.startTransition(
+ anyInt(),
+ any(),
+ transitionHandlerArgCaptor.capture(),
+ )
+ )
+ .thenReturn(Binder())
+
+ controller.moveTaskToDefaultDeskAndActivate(
+ taskId = setUpFullscreenTask().taskId,
+ transitionSource = UNKNOWN,
+ remoteTransition = RemoteTransition(spy(TestRemoteTransition())),
+ )
+ runCurrent()
+
+ verify(desktopModeEnterExitTransitionListener)
+ .onEnterDesktopModeTransitionStarted(FREEFORM_ANIMATION_DURATION)
+ assertIs<OneShotRemoteHandler>(transitionHandlerArgCaptor.firstValue)
+ }
@Test
@DisableFlags(
@@ -1880,23 +2095,25 @@
Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND,
Flags.FLAG_ENABLE_DESKTOP_WALLPAPER_ACTIVITY_FOR_SYSTEM_USER,
)
- fun moveRunningTaskToDesktop_desktopWallpaperEnabled_multiDesksEnabled() {
- val freeformTask = setUpFreeformTask()
- val fullscreenTask = setUpFullscreenTask()
- markTaskHidden(freeformTask)
+ fun moveRunningTaskToDesktop_desktopWallpaperEnabled_multiDesksEnabled() =
+ testScope.runTest {
+ val freeformTask = setUpFreeformTask()
+ val fullscreenTask = setUpFullscreenTask()
+ markTaskHidden(freeformTask)
- controller.moveTaskToDefaultDeskAndActivate(
- fullscreenTask.taskId,
- transitionSource = UNKNOWN,
- )
+ controller.moveTaskToDefaultDeskAndActivate(
+ fullscreenTask.taskId,
+ transitionSource = UNKNOWN,
+ )
+ runCurrent()
- val wct = getLatestEnterDesktopWct()
- wct.assertReorderAt(index = 0, wallpaperToken)
- verify(desksOrganizer).moveTaskToDesk(wct, deskId = 0, fullscreenTask)
- verify(desksOrganizer).activateDesk(wct, deskId = 0)
- verify(desktopModeEnterExitTransitionListener)
- .onEnterDesktopModeTransitionStarted(FREEFORM_ANIMATION_DURATION)
- }
+ val wct = getLatestEnterDesktopWct()
+ wct.assertReorderAt(index = 0, wallpaperToken)
+ verify(desksOrganizer).moveTaskToDesk(wct, deskId = 0, fullscreenTask)
+ verify(desksOrganizer).activateDesk(wct, deskId = 0)
+ verify(desktopModeEnterExitTransitionListener)
+ .onEnterDesktopModeTransitionStarted(FREEFORM_ANIMATION_DURATION)
+ }
@Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
@@ -1913,87 +2130,102 @@
}
@Test
- fun moveRunningTaskToDesktop_onlyFreeformTasksFromCurrentDisplayBroughtToFront() {
- setUpHomeTask(displayId = DEFAULT_DISPLAY)
- val freeformTaskDefault = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
- val fullscreenTaskDefault = setUpFullscreenTask(displayId = DEFAULT_DISPLAY)
- markTaskHidden(freeformTaskDefault)
+ fun moveRunningTaskToDesktop_onlyFreeformTasksFromCurrentDisplayBroughtToFront() =
+ testScope.runTest {
+ setUpHomeTask(displayId = DEFAULT_DISPLAY)
+ val freeformTaskDefault = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
+ val fullscreenTaskDefault = setUpFullscreenTask(displayId = DEFAULT_DISPLAY)
+ markTaskHidden(freeformTaskDefault)
- taskRepository.addDesk(displayId = SECOND_DISPLAY, deskId = SECOND_DISPLAY)
- val homeTaskSecond = setUpHomeTask(displayId = SECOND_DISPLAY)
- val freeformTaskSecond = setUpFreeformTask(displayId = SECOND_DISPLAY)
- markTaskHidden(freeformTaskSecond)
+ taskRepository.addDesk(displayId = SECOND_DISPLAY, deskId = SECOND_DISPLAY)
+ val homeTaskSecond = setUpHomeTask(displayId = SECOND_DISPLAY)
+ val freeformTaskSecond = setUpFreeformTask(displayId = SECOND_DISPLAY)
+ markTaskHidden(freeformTaskSecond)
- controller.moveTaskToDefaultDeskAndActivate(
- fullscreenTaskDefault.taskId,
- transitionSource = UNKNOWN,
- )
+ controller.moveTaskToDefaultDeskAndActivate(
+ fullscreenTaskDefault.taskId,
+ transitionSource = UNKNOWN,
+ )
+ runCurrent()
- with(getLatestEnterDesktopWct()) {
- // Check that hierarchy operations do not include tasks from second display
- assertThat(hierarchyOps.map { it.container })
- .doesNotContain(homeTaskSecond.token.asBinder())
- assertThat(hierarchyOps.map { it.container })
- .doesNotContain(freeformTaskSecond.token.asBinder())
+ with(getLatestEnterDesktopWct()) {
+ // Check that hierarchy operations do not include tasks from second display
+ assertThat(hierarchyOps.map { it.container })
+ .doesNotContain(homeTaskSecond.token.asBinder())
+ assertThat(hierarchyOps.map { it.container })
+ .doesNotContain(freeformTaskSecond.token.asBinder())
+ }
+ verify(desktopModeEnterExitTransitionListener)
+ .onEnterDesktopModeTransitionStarted(FREEFORM_ANIMATION_DURATION)
}
- verify(desktopModeEnterExitTransitionListener)
- .onEnterDesktopModeTransitionStarted(FREEFORM_ANIMATION_DURATION)
- }
@Test
@DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
- fun moveRunningTaskToDesktop_splitTaskExitsSplit_multiDesksDisabled() {
- val task = setUpSplitScreenTask()
- controller.moveTaskToDefaultDeskAndActivate(task.taskId, transitionSource = UNKNOWN)
- val wct = getLatestEnterDesktopWct()
- assertThat(wct.changes[task.token.asBinder()]?.windowingMode)
- .isEqualTo(WINDOWING_MODE_FREEFORM)
- verify(desktopModeEnterExitTransitionListener)
- .onEnterDesktopModeTransitionStarted(FREEFORM_ANIMATION_DURATION)
- verify(splitScreenController)
- .prepareExitSplitScreen(
- any(),
- anyInt(),
- eq(SplitScreenController.EXIT_REASON_DESKTOP_MODE),
- )
- }
+ fun moveRunningTaskToDesktop_splitTaskExitsSplit_multiDesksDisabled() =
+ testScope.runTest {
+ val task = setUpSplitScreenTask()
+
+ controller.moveTaskToDefaultDeskAndActivate(task.taskId, transitionSource = UNKNOWN)
+ runCurrent()
+
+ val wct = getLatestEnterDesktopWct()
+ assertThat(wct.changes[task.token.asBinder()]?.windowingMode)
+ .isEqualTo(WINDOWING_MODE_FREEFORM)
+ verify(desktopModeEnterExitTransitionListener)
+ .onEnterDesktopModeTransitionStarted(FREEFORM_ANIMATION_DURATION)
+ verify(splitScreenController)
+ .prepareExitSplitScreen(
+ any(),
+ anyInt(),
+ eq(SplitScreenController.EXIT_REASON_DESKTOP_MODE),
+ )
+ }
@Test
@EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
- fun moveRunningTaskToDesktop_splitTaskExitsSplit_multiDesksEnabled() {
- val task = setUpSplitScreenTask()
- controller.moveTaskToDefaultDeskAndActivate(task.taskId, transitionSource = UNKNOWN)
- val wct = getLatestEnterDesktopWct()
- verify(desksOrganizer).moveTaskToDesk(wct, deskId = 0, task)
- verify(desktopModeEnterExitTransitionListener)
- .onEnterDesktopModeTransitionStarted(FREEFORM_ANIMATION_DURATION)
- verify(splitScreenController)
- .prepareExitSplitScreen(
- any(),
- anyInt(),
- eq(SplitScreenController.EXIT_REASON_DESKTOP_MODE),
- )
- }
+ fun moveRunningTaskToDesktop_splitTaskExitsSplit_multiDesksEnabled() =
+ testScope.runTest {
+ val task = setUpSplitScreenTask()
+
+ controller.moveTaskToDefaultDeskAndActivate(task.taskId, transitionSource = UNKNOWN)
+ runCurrent()
+
+ val wct = getLatestEnterDesktopWct()
+ verify(desksOrganizer).moveTaskToDesk(wct, deskId = 0, task)
+ verify(desktopModeEnterExitTransitionListener)
+ .onEnterDesktopModeTransitionStarted(FREEFORM_ANIMATION_DURATION)
+ verify(splitScreenController)
+ .prepareExitSplitScreen(
+ any(),
+ anyInt(),
+ eq(SplitScreenController.EXIT_REASON_DESKTOP_MODE),
+ )
+ }
@Test
- fun moveRunningTaskToDesktop_fullscreenTaskDoesNotExitSplit() {
- val task = setUpFullscreenTask()
- controller.moveTaskToDefaultDeskAndActivate(task.taskId, transitionSource = UNKNOWN)
- val wct = getLatestEnterDesktopWct()
- verify(desktopModeEnterExitTransitionListener)
- .onEnterDesktopModeTransitionStarted(FREEFORM_ANIMATION_DURATION)
- verify(splitScreenController, never())
- .prepareExitSplitScreen(
- any(),
- anyInt(),
- eq(SplitScreenController.EXIT_REASON_DESKTOP_MODE),
- )
- }
+ fun moveRunningTaskToDesktop_fullscreenTaskDoesNotExitSplit() =
+ testScope.runTest {
+ val task = setUpFullscreenTask()
+
+ controller.moveTaskToDefaultDeskAndActivate(task.taskId, transitionSource = UNKNOWN)
+ runCurrent()
+
+ val wct = getLatestEnterDesktopWct()
+ verify(desktopModeEnterExitTransitionListener)
+ .onEnterDesktopModeTransitionStarted(FREEFORM_ANIMATION_DURATION)
+ verify(splitScreenController, never())
+ .prepareExitSplitScreen(
+ any(),
+ anyInt(),
+ eq(SplitScreenController.EXIT_REASON_DESKTOP_MODE),
+ )
+ }
@Test
@DisableFlags(
Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY,
Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND,
+ Flags.FLAG_ENABLE_DESKTOP_TASK_LIMIT_SEPARATE_TRANSITION,
)
fun moveRunningTaskToDesktop_desktopWallpaperDisabled_bringsTasksOver_dontShowBackTask() {
val freeformTasks = (1..MAX_TASK_LIMIT).map { _ -> setUpFreeformTask() }
@@ -2016,7 +2248,10 @@
@Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
- @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ @DisableFlags(
+ Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND,
+ Flags.FLAG_ENABLE_DESKTOP_TASK_LIMIT_SEPARATE_TRANSITION,
+ )
fun moveRunningTaskToDesktop_desktopWallpaperEnabled_bringsTasksOverLimit_dontShowBackTask() {
whenever(desktopWallpaperActivityTokenProvider.getToken()).thenReturn(null)
val freeformTasks = (1..MAX_TASK_LIMIT).map { _ -> setUpFreeformTask() }
@@ -2375,6 +2610,37 @@
@Test
@EnableFlags(com.android.launcher3.Flags.FLAG_ENABLE_ALT_TAB_KQS_FLATENNING)
+ @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun moveToFullscreen_enforceDesktopWithMultipleDesktopDisabled_taskReorderToTop() {
+ desktopState.enterDesktopByDefaultOnFreeformDisplay = true
+ val transitionHandlerArgCaptor = argumentCaptor<TransitionHandler>()
+ whenever(transitions.startTransition(anyInt(), any(), transitionHandlerArgCaptor.capture()))
+ .thenReturn(Binder())
+
+ val task = setUpFullscreenTask()
+ assertNotNull(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY))
+ .configuration
+ .windowConfiguration
+ .windowingMode = WINDOWING_MODE_FREEFORM
+
+ controller.moveToFullscreen(
+ task.taskId,
+ transitionSource = UNKNOWN,
+ remoteTransition = RemoteTransition(spy(TestRemoteTransition())),
+ )
+
+ val wct =
+ getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java)
+ assertThat(wct.changes[task.token.asBinder()]?.windowingMode)
+ .isEqualTo(WINDOWING_MODE_FULLSCREEN)
+ wct.assertReorderAt(index = 0, task)
+ verify(desktopModeEnterExitTransitionListener, never())
+ .onEnterDesktopModeTransitionStarted(anyInt())
+ assertIs<OneShotRemoteHandler>(transitionHandlerArgCaptor.firstValue)
+ }
+
+ @Test
+ @EnableFlags(com.android.launcher3.Flags.FLAG_ENABLE_ALT_TAB_KQS_FLATENNING)
fun moveToFullscreen_fullscreenTaskWithRemoteTransition_transitToFrontUsesRemoteTransition() {
val transitionHandlerArgCaptor = argumentCaptor<TransitionHandler>()
whenever(transitions.startTransition(anyInt(), any(), transitionHandlerArgCaptor.capture()))
@@ -2471,7 +2737,7 @@
@Test
@EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
- fun moveTaskToFront_reordersToFront() {
+ fun moveTaskToFront_desktopTask_reordersToFront() {
val task1 = setUpFreeformTask(displayId = DEFAULT_DISPLAY, deskId = 0)
setUpFreeformTask(displayId = DEFAULT_DISPLAY, deskId = 0)
whenever(
@@ -2491,7 +2757,54 @@
}
@Test
- @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun moveTaskToFront_nonDesktopTask_reordersToFront() {
+ val task = setUpFullscreenTask(displayId = DEFAULT_DISPLAY)
+ whenever(
+ desktopMixedTransitionHandler.startLaunchTransition(
+ eq(TRANSIT_TO_FRONT),
+ any(),
+ eq(task.taskId),
+ anyOrNull(),
+ anyOrNull(),
+ )
+ )
+ .thenReturn(Binder())
+
+ controller.moveTaskToFront(task, remoteTransition = null)
+
+ val wct = getLatestDesktopMixedTaskWct(type = TRANSIT_TO_FRONT)
+ assertNotNull(wct)
+ wct.assertReorder(task = task, toTop = true, includingParents = true)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun moveTaskToFront_nonDesktopTask_doesNotActivateDesk() {
+ val task = setUpFullscreenTask(displayId = DEFAULT_DISPLAY)
+ whenever(
+ desktopMixedTransitionHandler.startLaunchTransition(
+ eq(TRANSIT_TO_FRONT),
+ any(),
+ eq(task.taskId),
+ anyOrNull(),
+ anyOrNull(),
+ )
+ )
+ .thenReturn(Binder())
+
+ controller.moveTaskToFront(task, remoteTransition = null)
+
+ val wct = getLatestDesktopMixedTaskWct(type = TRANSIT_TO_FRONT)
+ assertNotNull(wct)
+ verify(desksOrganizer, never()).activateDesk(eq(wct), any())
+ }
+
+ @Test
+ @DisableFlags(
+ Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND,
+ Flags.FLAG_ENABLE_DESKTOP_TASK_LIMIT_SEPARATE_TRANSITION,
+ )
fun moveTaskToFront_bringsTasksOverLimit_multiDesksDisabled_minimizesBackTask() {
setUpHomeTask()
val freeformTasks =
@@ -2519,6 +2832,7 @@
@Test
@EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_TASK_LIMIT_SEPARATE_TRANSITION)
fun moveTaskToFront_bringsTasksOverLimit_multiDesksEnabled_minimizesBackTask() {
val deskId = 0
setUpHomeTask()
@@ -2544,6 +2858,34 @@
}
@Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_TASK_LIMIT_SEPARATE_TRANSITION)
+ fun moveTaskToFront_bringsTasksOverLimit_separateTaskLimitTransition_minimizeSeparately() {
+ val deskId = 0
+ setUpHomeTask()
+ val freeformTasks =
+ (1..MAX_TASK_LIMIT + 1).map { _ ->
+ setUpFreeformTask(displayId = DEFAULT_DISPLAY, deskId = deskId)
+ }
+ val transition = Binder()
+ whenever(
+ desktopMixedTransitionHandler.startLaunchTransition(
+ eq(TRANSIT_TO_FRONT),
+ any(),
+ eq(freeformTasks[0].taskId),
+ anyOrNull(),
+ anyOrNull(),
+ )
+ )
+ .thenReturn(transition)
+
+ controller.moveTaskToFront(freeformTasks[0], remoteTransition = null)
+
+ verify(desksOrganizer, never()).minimizeTask(any(), any(), any())
+ assertThat(desktopTasksLimiter.getMinimizingTask(transition)).isNull()
+ assertThat(desktopTasksLimiter.hasTaskLimitTransitionForTesting(transition)).isTrue()
+ }
+
+ @Test
fun moveTaskToFront_minimizedTask_marksTaskAsUnminimized() {
val transition = Binder()
val freeformTask = setUpFreeformTask()
@@ -2597,6 +2939,7 @@
}
@Test
+ @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_TASK_LIMIT_SEPARATE_TRANSITION)
fun moveTaskToFront_bringsTasksOverLimit_remoteTransition_usesWindowLimitHandler() {
setUpHomeTask()
val freeformTasks = List(MAX_TASK_LIMIT + 1) { setUpFreeformTask() }
@@ -2632,7 +2975,10 @@
}
@Test
- @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ @DisableFlags(
+ Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND,
+ Flags.FLAG_ENABLE_DESKTOP_TASK_LIMIT_SEPARATE_TRANSITION,
+ )
fun moveTaskToFront_backgroundTaskBringsTasksOverLimit_multiDesksDisabled_minimizesBackTask() {
val deskId = 0
val freeformTasks =
@@ -2661,7 +3007,39 @@
}
@Test
+ @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_TASK_LIMIT_SEPARATE_TRANSITION)
+ fun moveTaskToFront_backgroundTaskBringsTasksOverLimit_multiDesksDisabled_separateMinimize() {
+ val deskId = 0
+ val freeformTasks =
+ (1..MAX_TASK_LIMIT).map { _ ->
+ setUpFreeformTask(displayId = DEFAULT_DISPLAY, deskId = deskId)
+ }
+ val task = createRecentTaskInfo(1001)
+ whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(null)
+ val transition = Binder()
+ whenever(
+ desktopMixedTransitionHandler.startLaunchTransition(
+ eq(TRANSIT_OPEN),
+ any(),
+ eq(task.taskId),
+ anyOrNull(),
+ anyOrNull(),
+ )
+ )
+ .thenReturn(transition)
+
+ controller.moveTaskToFront(task.taskId, unminimizeReason = UnminimizeReason.UNKNOWN)
+
+ val wct = getLatestDesktopMixedTaskWct(type = TRANSIT_OPEN)
+ assertThat(wct.hierarchyOps.size).isEqualTo(1)
+ wct.assertLaunchTaskAt(0, task.taskId, WINDOWING_MODE_FREEFORM)
+ assertThat(desktopTasksLimiter.hasTaskLimitTransitionForTesting(transition)).isTrue()
+ }
+
+ @Test
@EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_TASK_LIMIT_SEPARATE_TRANSITION)
fun moveTaskToFront_backgroundTaskBringsTasksOverLimit_multiDesksEnabled_minimizesBackTask() {
val deskId = 0
val freeformTasks =
@@ -3056,6 +3434,7 @@
@Test
@EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_TASK_LIMIT_SEPARATE_TRANSITION)
fun moveToNextDisplay_toDesktopInOtherDisplay_appliesTaskLimit() {
val transition = Binder()
val sourceDeskId = 0
@@ -3082,6 +3461,36 @@
@Test
@EnableFlags(
+ Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND,
+ Flags.FLAG_ENABLE_DESKTOP_TASK_LIMIT_SEPARATE_TRANSITION,
+ )
+ fun moveToNextDisplay_toDesktopInOtherDisplay_appliesTaskLimitSeparate() {
+ val transition = Binder()
+ val sourceDeskId = 0
+ val targetDeskId = 2
+ taskRepository.addDesk(displayId = SECOND_DISPLAY, deskId = targetDeskId)
+ taskRepository.setDeskInactive(deskId = targetDeskId)
+ val targetDeskTasks =
+ (1..MAX_TASK_LIMIT + 1).map { _ ->
+ setUpFreeformTask(displayId = SECOND_DISPLAY, deskId = targetDeskId)
+ }
+ // Set up two display ids
+ whenever(rootTaskDisplayAreaOrganizer.displayIds)
+ .thenReturn(intArrayOf(DEFAULT_DISPLAY, SECOND_DISPLAY))
+ whenever(transitions.startTransition(eq(TRANSIT_CHANGE), any(), anyOrNull()))
+ .thenReturn(transition)
+ val task = setUpFreeformTask(displayId = DEFAULT_DISPLAY, deskId = sourceDeskId)
+
+ controller.moveToNextDisplay(task.taskId)
+
+ val wct = getLatestTransition()
+ assertNotNull(wct)
+ assertThat(desktopTasksLimiter.hasTaskLimitTransitionForTesting(transition)).isTrue()
+ verify(desksOrganizer, never()).minimizeTask(wct, targetDeskId, targetDeskTasks[0])
+ }
+
+ @Test
+ @EnableFlags(
Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY,
Flags.FLAG_ENABLE_PER_DISPLAY_DESKTOP_WALLPAPER_ACTIVITY,
Flags.FLAG_ENABLE_DESKTOP_WALLPAPER_ACTIVITY_FOR_SYSTEM_USER,
@@ -3465,10 +3874,23 @@
.addPendingTransition(DeskTransition.DeactivateDesk(token = transition, deskId = 0))
}
- private fun minimizePipTask(task: RunningTaskInfo) {
+ private fun minimizePipTask(task: RunningTaskInfo, appOpsAllowed: Boolean = true) {
val handler = mock(TransitionHandler::class.java)
whenever(transitions.dispatchRequest(any(), any(), anyOrNull()))
.thenReturn(android.util.Pair(handler, WindowContainerTransaction()))
+ mContext.addMockSystemService(Context.APP_OPS_SERVICE, mockAppOpsManager)
+ mContext.setMockPackageManager(packageManager)
+
+ whenever(
+ mockAppOpsManager.checkOpNoThrow(
+ eq(AppOpsManager.OP_PICTURE_IN_PICTURE),
+ any(),
+ any(),
+ )
+ )
+ .thenReturn(
+ if (appOpsAllowed) AppOpsManager.MODE_ALLOWED else AppOpsManager.MODE_IGNORED
+ )
controller.minimizeTask(task, MinimizeReason.MINIMIZE_BUTTON)
}
@@ -3507,6 +3929,34 @@
@Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_PIP)
+ fun onPipTaskMinimize_pipNotAllowedInAppOps_startMinimizeTransition() {
+ val task = setUpPipTask(autoEnterEnabled = true)
+ whenever(
+ freeformTaskTransitionStarter.startMinimizedModeTransition(
+ any(),
+ anyInt(),
+ anyBoolean(),
+ )
+ )
+ .thenReturn(Binder())
+ whenever(
+ mockAppOpsManager.checkOpNoThrow(
+ eq(AppOpsManager.OP_PICTURE_IN_PICTURE),
+ any(),
+ any(),
+ )
+ )
+ .thenReturn(AppOpsManager.MODE_IGNORED)
+
+ minimizePipTask(task, appOpsAllowed = false)
+
+ verify(freeformTaskTransitionStarter)
+ .startMinimizedModeTransition(any(), eq(task.taskId), anyBoolean())
+ verify(freeformTaskTransitionStarter, never()).startPipTransition(any())
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_PIP)
fun onPipTaskMinimize_autoEnterEnabled_sendsTaskbarRoundingUpdate() {
val task = setUpPipTask(autoEnterEnabled = true)
@@ -3937,7 +4387,10 @@
}
@Test
- @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ @DisableFlags(
+ Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND,
+ Flags.FLAG_ENABLE_DESKTOP_TASK_LIMIT_SEPARATE_TRANSITION,
+ )
fun handleRequest_fullscreenTaskToDesk_bringsTasksOverLimit_multiDesksDisabled_otherTaskIsMinimized() {
val freeformTasks = (1..MAX_TASK_LIMIT).map { _ -> setUpFreeformTask() }
freeformTasks.forEach { markTaskVisible(it) }
@@ -3952,7 +4405,25 @@
}
@Test
+ @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_TASK_LIMIT_SEPARATE_TRANSITION)
+ fun handleRequest_fullscreenTaskToDesk_bringsTasksOverLimit_multiDesksDisabled_separateMinimize() {
+ val freeformTasks = (1..MAX_TASK_LIMIT).map { _ -> setUpFreeformTask() }
+ freeformTasks.forEach { markTaskVisible(it) }
+ val fullscreenTask = createFullscreenTask()
+ val transition = Binder()
+
+ val wct = controller.handleRequest(transition, createTransition(fullscreenTask))
+
+ // Make sure we reorder the new task to top, and the back task to the bottom
+ assertThat(wct!!.hierarchyOps.size).isEqualTo(1)
+ wct.assertReorderAt(0, fullscreenTask, toTop = true)
+ assertThat(desktopTasksLimiter.hasTaskLimitTransitionForTesting(transition)).isTrue()
+ }
+
+ @Test
@EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_TASK_LIMIT_SEPARATE_TRANSITION)
fun handleRequest_fullscreenTaskToDesk_bringsTasksOverLimit_multiDesksEnabled_otherTaskIsMinimized() {
val deskId = 5
taskRepository.addDesk(displayId = DEFAULT_DISPLAY, deskId = deskId)
@@ -3978,7 +4449,10 @@
}
@Test
- @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ @DisableFlags(
+ Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND,
+ Flags.FLAG_ENABLE_DESKTOP_TASK_LIMIT_SEPARATE_TRANSITION,
+ )
fun handleRequest_fullscreenTaskWithTaskOnHome_bringsTasksOverLimit_multiDesksDisabled_otherTaskIsMinimized() {
val freeformTasks = (1..MAX_TASK_LIMIT).map { _ -> setUpFreeformTask() }
freeformTasks.forEach { markTaskVisible(it) }
@@ -4001,6 +4475,7 @@
@Test
@EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_TASK_LIMIT_SEPARATE_TRANSITION)
fun handleRequest_fullscreenTaskWithTaskOnHome_bringsTasksOverLimit_multiDesksEnabled_otherTaskIsMinimized() {
val deskId = 5
taskRepository.addDesk(displayId = DEFAULT_DISPLAY, deskId = deskId)
@@ -4023,7 +4498,24 @@
}
@Test
- @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_TASK_LIMIT_SEPARATE_TRANSITION)
+ fun handleRequest_fullscreenTaskToDesk_bringsTasksOverLimit_separateMinimizeFlagEnabled_minimizeSeparately() {
+ val freeformTasks = (1..MAX_TASK_LIMIT).map { _ -> setUpFreeformTask() }
+ freeformTasks.forEach { markTaskVisible(it) }
+ val fullscreenTask = createFullscreenTask()
+ val transition = Binder()
+
+ val wct = controller.handleRequest(transition, createTransition(fullscreenTask))
+
+ assertThat(wct!!.hierarchyOps.size).isAtMost(1)
+ assertThat(desktopTasksLimiter.hasTaskLimitTransitionForTesting(transition)).isTrue()
+ }
+
+ @Test
+ @DisableFlags(
+ Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND,
+ Flags.FLAG_ENABLE_DESKTOP_TASK_LIMIT_SEPARATE_TRANSITION,
+ )
fun handleRequest_fullscreenTaskWithTaskOnHome_beyondLimit_multiDesksDisabled_existingAndNewTasksAreMinimized() {
val minimizedTask = setUpFreeformTask()
taskRepository.minimizeTask(displayId = DEFAULT_DISPLAY, taskId = minimizedTask.taskId)
@@ -4050,7 +4542,32 @@
}
@Test
+ @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_TASK_LIMIT_SEPARATE_TRANSITION)
+ fun handleRequest_fullscreenTaskWithTaskOnHome_beyondLimit_separateMinFlagEnabled_minimizeSeparately() {
+ val minimizedTask = setUpFreeformTask()
+ taskRepository.minimizeTask(displayId = DEFAULT_DISPLAY, taskId = minimizedTask.taskId)
+ val freeformTasks = (1..MAX_TASK_LIMIT).map { _ -> setUpFreeformTask() }
+ freeformTasks.forEach { markTaskVisible(it) }
+ val homeTask = setUpHomeTask()
+ val fullscreenTask = createFullscreenTask()
+ fullscreenTask.baseIntent.setFlags(Intent.FLAG_ACTIVITY_TASK_ON_HOME)
+
+ val wct = controller.handleRequest(Binder(), createTransition(fullscreenTask))
+
+ assertThat(wct!!.hierarchyOps.size).isEqualTo(10)
+ wct.assertReorderAt(0, fullscreenTask, toTop = true)
+ // Make sure we reorder the home task to the top, desktop tasks to top of them and minimized
+ // task is under the home task.
+ wct.assertReorderAt(1, homeTask, toTop = true)
+ // Oldest task that needs to minimized is never reordered to top over Home.
+ val taskToMinimize = freeformTasks[0]
+ wct.assertReorder(taskToMinimize.token, toTop = true)
+ }
+
+ @Test
@EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_TASK_LIMIT_SEPARATE_TRANSITION)
fun handleRequest_fullscreenTaskWithTaskOnHome_beyondLimit_multiDesksEnabled_existingAndNewTasksAreMinimized() {
// A desk with a minimized tasks, and non-minimized tasks already at the task limit.
val deskId = 5
@@ -4258,7 +4775,10 @@
}
@Test
- @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ @DisableFlags(
+ Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND,
+ Flags.FLAG_ENABLE_DESKTOP_TASK_LIMIT_SEPARATE_TRANSITION,
+ )
fun handleRequest_freeformTask_freeformVisible_aboveTaskLimit_multiDesksDisabled_minimize() {
val deskId = 0
val freeformTasks =
@@ -4277,6 +4797,7 @@
@Test
@EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_TASK_LIMIT_SEPARATE_TRANSITION)
fun handleRequest_freeformTask_freeformVisible_aboveTaskLimit_multiDesksEnabled_minimize() {
val deskId = 0
val freeformTasks =
@@ -4294,6 +4815,26 @@
}
@Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_TASK_LIMIT_SEPARATE_TRANSITION)
+ fun handleRequest_freeform_aboveTaskLimit_separateMinimizeFlagEnabled_minimizeSeparately() {
+ val deskId = 0
+ val freeformTasks =
+ (1..MAX_TASK_LIMIT).map { _ ->
+ setUpFreeformTask(displayId = DEFAULT_DISPLAY, deskId = deskId)
+ }
+ freeformTasks.forEach { markTaskVisible(it) }
+ val newFreeformTask = createFreeformTask()
+ val transition = Binder()
+
+ val wct =
+ controller.handleRequest(transition, createTransition(newFreeformTask, TRANSIT_OPEN))
+
+ assertNotNull(wct)
+ assertThat(wct.hierarchyOps).isEmpty()
+ assertThat(desktopTasksLimiter.hasTaskLimitTransitionForTesting(transition)).isTrue()
+ }
+
+ @Test
@EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
fun handleRequest_freeformTaskFromInactiveDesk_tracksDeskDeactivation() {
val deskId = 0
@@ -5337,20 +5878,22 @@
@Test
@EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
- fun moveFocusedTaskToDesktop_fullscreenTaskIsMovedToDesktop_multiDesksEnabled() {
- val task1 = setUpFullscreenTask()
- val task2 = setUpFullscreenTask()
- val task3 = setUpFullscreenTask()
+ fun moveFocusedTaskToDesktop_fullscreenTaskIsMovedToDesktop_multiDesksEnabled() =
+ testScope.runTest {
+ val task1 = setUpFullscreenTask()
+ val task2 = setUpFullscreenTask()
+ val task3 = setUpFullscreenTask()
- task1.isFocused = true
- task2.isFocused = false
- task3.isFocused = false
+ task1.isFocused = true
+ task2.isFocused = false
+ task3.isFocused = false
- controller.moveFocusedTaskToDesktop(DEFAULT_DISPLAY, transitionSource = UNKNOWN)
+ controller.moveFocusedTaskToDesktop(DEFAULT_DISPLAY, transitionSource = UNKNOWN)
+ runCurrent()
- val wct = getLatestEnterDesktopWct()
- verify(desksOrganizer).moveTaskToDesk(wct, deskId = 0, task1)
- }
+ val wct = getLatestEnterDesktopWct()
+ verify(desksOrganizer).moveTaskToDesk(wct, deskId = 0, task1)
+ }
@Test
@DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
@@ -5382,30 +5925,32 @@
@Test
@EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
- fun moveFocusedTaskToDesktop_splitScreenTaskIsMovedToDesktop_multiDesksEnabled() {
- val task1 = setUpSplitScreenTask()
- val task2 = setUpFullscreenTask()
- val task3 = setUpFullscreenTask()
- val task4 = setUpSplitScreenTask()
+ fun moveFocusedTaskToDesktop_splitScreenTaskIsMovedToDesktop_multiDesksEnabled() =
+ testScope.runTest {
+ val task1 = setUpSplitScreenTask()
+ val task2 = setUpFullscreenTask()
+ val task3 = setUpFullscreenTask()
+ val task4 = setUpSplitScreenTask()
- task1.isFocused = true
- task2.isFocused = false
- task3.isFocused = false
- task4.isFocused = true
+ task1.isFocused = true
+ task2.isFocused = false
+ task3.isFocused = false
+ task4.isFocused = true
- task4.parentTaskId = task1.taskId
+ task4.parentTaskId = task1.taskId
- controller.moveFocusedTaskToDesktop(DEFAULT_DISPLAY, transitionSource = UNKNOWN)
+ controller.moveFocusedTaskToDesktop(DEFAULT_DISPLAY, transitionSource = UNKNOWN)
+ runCurrent()
- val wct = getLatestEnterDesktopWct()
- verify(desksOrganizer).moveTaskToDesk(wct, deskId = 0, task4)
- verify(splitScreenController)
- .prepareExitSplitScreen(
- any(),
- anyInt(),
- eq(SplitScreenController.EXIT_REASON_DESKTOP_MODE),
- )
- }
+ val wct = getLatestEnterDesktopWct()
+ verify(desksOrganizer).moveTaskToDesk(wct, deskId = 0, task4)
+ verify(splitScreenController)
+ .prepareExitSplitScreen(
+ any(),
+ anyInt(),
+ eq(SplitScreenController.EXIT_REASON_DESKTOP_MODE),
+ )
+ }
@Test
fun moveFocusedTaskToFullscreen() {
@@ -6117,6 +6662,46 @@
}
@Test
+ @EnableFlags(Flags.FLAG_ENABLE_CONNECTED_DISPLAYS_WINDOW_DRAG)
+ fun onDesktopDragMove_callVisualIndicatorUpdateScheduler() {
+ val task = setUpFreeformTask()
+ val spyController = spy(controller)
+ val mockSurface = mock(SurfaceControl::class.java)
+ val mockDisplayLayout = mock(DisplayLayout::class.java)
+ val indicatorType = DesktopModeVisualIndicator.IndicatorType.NO_INDICATOR
+ whenever(displayController.getDisplayLayout(task.displayId)).thenReturn(mockDisplayLayout)
+ whenever(mockDisplayLayout.stableInsets()).thenReturn(Rect(0, 100, 2000, 2000))
+ val inputX = 200f
+ val inputY = 10f
+ val bounds = Rect(100, -100, 500, 1000)
+ doReturn(desktopModeVisualIndicator)
+ .whenever(spyController)
+ .getOrCreateVisualIndicator(
+ eq(task),
+ eq(mockSurface),
+ eq(DesktopModeVisualIndicator.DragStartState.FROM_FREEFORM),
+ )
+ whenever(
+ desktopModeVisualIndicator.calculateIndicatorType(
+ eq(PointF(inputX, bounds.top.toFloat()))
+ )
+ )
+ .thenReturn(indicatorType)
+
+ spyController.onDragPositioningMove(task, mockSurface, inputX, inputY, bounds)
+
+ verify(visualIndicatorUpdateScheduler)
+ .schedule(
+ eq(task.displayId),
+ eq(indicatorType),
+ eq(inputX),
+ eq(inputY),
+ eq(bounds),
+ any(),
+ )
+ }
+
+ @Test
fun onDesktopDragMove_endsOutsideValidDragArea_snapsToValidBounds() {
val task = setUpFreeformTask()
val spyController = spy(controller)
@@ -6124,7 +6709,13 @@
val mockDisplayLayout = mock(DisplayLayout::class.java)
whenever(displayController.getDisplayLayout(task.displayId)).thenReturn(mockDisplayLayout)
whenever(mockDisplayLayout.stableInsets()).thenReturn(Rect(0, 100, 2000, 2000))
- spyController.onDragPositioningMove(task, mockSurface, 200f, Rect(100, -100, 500, 1000))
+ spyController.onDragPositioningMove(
+ task,
+ mockSurface,
+ 200f,
+ 10f,
+ Rect(100, -100, 500, 1000),
+ )
whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull()))
@@ -6159,7 +6750,7 @@
val mockDisplayLayout = mock(DisplayLayout::class.java)
whenever(displayController.getDisplayLayout(task.displayId)).thenReturn(mockDisplayLayout)
whenever(mockDisplayLayout.stableInsets()).thenReturn(Rect(0, 100, 2000, 2000))
- spyController.onDragPositioningMove(task, mockSurface, 200f, Rect(100, 200, 500, 1000))
+ spyController.onDragPositioningMove(task, mockSurface, 200f, 10f, Rect(100, 200, 500, 1000))
val currentDragBounds = Rect(100, 200, 500, 1000)
whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
@@ -6198,7 +6789,7 @@
val mockDisplayLayout = mock(DisplayLayout::class.java)
whenever(displayController.getDisplayLayout(task.displayId)).thenReturn(mockDisplayLayout)
whenever(mockDisplayLayout.stableInsets()).thenReturn(Rect(0, 100, 2000, 2000))
- spyController.onDragPositioningMove(task, mockSurface, 200f, Rect(100, 200, 500, 1000))
+ spyController.onDragPositioningMove(task, mockSurface, 200f, 10f, Rect(100, 200, 500, 1000))
val currentDragBounds = Rect(100, 200, 500, 1000)
whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
@@ -6245,7 +6836,7 @@
.thenReturn(DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR)
// Drag move the task to the top edge
- spyController.onDragPositioningMove(task, mockSurface, 200f, Rect(100, 200, 500, 1000))
+ spyController.onDragPositioningMove(task, mockSurface, 200f, 10f, Rect(100, 200, 500, 1000))
spyController.onDragPositioningEnd(
task,
mockSurface,
@@ -6263,7 +6854,8 @@
}
@Test
- fun onDesktopDragEnd_fullscreenIndicator_dragToMaximize() {
+ @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_FIRST_BASED_DRAG_TO_MAXIMIZE)
+ fun onDesktopDragEnd_fullscreenIndicator_dragToMaximize_desktopFirstDisabled() {
val task = setUpFreeformTask(bounds = Rect(0, 0, 100, 100))
val spyController = spy(controller)
val mockSurface = mock(SurfaceControl::class.java)
@@ -6282,7 +6874,7 @@
// Drag move the task to the top edge
val currentDragBounds = Rect(100, 50, 500, 1000)
- spyController.onDragPositioningMove(task, mockSurface, 200f, Rect(100, 200, 500, 1000))
+ spyController.onDragPositioningMove(task, mockSurface, 200f, 10f, Rect(100, 200, 500, 1000))
spyController.onDragPositioningEnd(
task,
mockSurface,
@@ -6318,7 +6910,8 @@
}
@Test
- fun onDesktopDragEnd_fullscreenIndicator_dragToMaximize_noBoundsChange() {
+ @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_FIRST_BASED_DRAG_TO_MAXIMIZE)
+ fun onDesktopDragEnd_fullscreenIndicator_dragToMaximize_alreadyMaximized_noBoundsChange_desktopFirstDisabled() {
val task = setUpFreeformTask(bounds = STABLE_BOUNDS)
val spyController = spy(controller)
val mockSurface = mock(SurfaceControl::class.java)
@@ -6337,7 +6930,114 @@
// Drag move the task to the top edge
val currentDragBounds = Rect(100, 50, 500, 1000)
- spyController.onDragPositioningMove(task, mockSurface, 200f, Rect(100, 200, 500, 1000))
+ spyController.onDragPositioningMove(task, mockSurface, 200f, 10f, Rect(100, 200, 500, 1000))
+ spyController.onDragPositioningEnd(
+ task,
+ mockSurface,
+ inputCoordinate = PointF(200f, 300f),
+ currentDragBounds = currentDragBounds,
+ validDragArea = Rect(0, 50, 2000, 2000),
+ dragStartBounds = Rect(),
+ motionEvent,
+ )
+
+ // Assert that task is NOT updated via WCT
+ verify(toggleResizeDesktopTaskTransitionHandler, never())
+ .startTransition(any(), any(), any())
+ // Assert that task leash is updated via Surface Animations
+ verify(mReturnToDragStartAnimator)
+ .start(
+ eq(task.taskId),
+ eq(mockSurface),
+ eq(currentDragBounds),
+ eq(STABLE_BOUNDS),
+ anyOrNull(),
+ )
+ // Assert no event is logged
+ verify(desktopModeEventLogger, never())
+ .logTaskResizingStarted(any(), any(), any(), any(), any(), any(), any())
+ verify(desktopModeEventLogger, never())
+ .logTaskResizingEnded(any(), any(), any(), any(), any(), any(), any())
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_FIRST_BASED_DRAG_TO_MAXIMIZE)
+ fun onDesktopDragEnd_fullscreenIndicator_dragToMaximize() {
+ val task = setUpFreeformTask(bounds = Rect(0, 0, 100, 100))
+ val spyController = spy(controller)
+ val mockSurface = mock(SurfaceControl::class.java)
+ val mockDisplayLayout = mock(DisplayLayout::class.java)
+ val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!!
+ tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FREEFORM
+ whenever(displayController.getDisplayLayout(task.displayId)).thenReturn(mockDisplayLayout)
+ whenever(mockDisplayLayout.stableInsets()).thenReturn(Rect(0, 100, 2000, 2000))
+ whenever(mockDisplayLayout.getStableBounds(any())).thenAnswer { i ->
+ (i.arguments.first() as Rect).set(STABLE_BOUNDS)
+ }
+ whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
+ whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull()))
+ .thenReturn(DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR)
+ whenever(motionEvent.displayId).thenReturn(DEFAULT_DISPLAY)
+
+ // Drag move the task to the top edge
+ val currentDragBounds = Rect(100, 50, 500, 1000)
+ spyController.onDragPositioningMove(task, mockSurface, 200f, 10f, Rect(100, 200, 500, 1000))
+ spyController.onDragPositioningEnd(
+ task,
+ mockSurface,
+ inputCoordinate = PointF(200f, 300f),
+ currentDragBounds,
+ validDragArea = Rect(0, 50, 2000, 2000),
+ dragStartBounds = Rect(),
+ motionEvent,
+ )
+
+ // Assert bounds set to stable bounds
+ val wct = getLatestToggleResizeDesktopTaskWct(currentDragBounds)
+ assertThat(findBoundsChange(wct, task)).isEqualTo(STABLE_BOUNDS)
+ // Assert event is properly logged
+ verify(desktopModeEventLogger, times(1))
+ .logTaskResizingStarted(
+ ResizeTrigger.DRAG_TO_TOP_RESIZE_TRIGGER,
+ InputMethod.UNKNOWN_INPUT_METHOD,
+ task,
+ task.configuration.windowConfiguration.bounds.width(),
+ task.configuration.windowConfiguration.bounds.height(),
+ displayController,
+ )
+ verify(desktopModeEventLogger, times(1))
+ .logTaskResizingEnded(
+ ResizeTrigger.DRAG_TO_TOP_RESIZE_TRIGGER,
+ InputMethod.UNKNOWN_INPUT_METHOD,
+ task,
+ STABLE_BOUNDS.width(),
+ STABLE_BOUNDS.height(),
+ displayController,
+ )
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_FIRST_BASED_DRAG_TO_MAXIMIZE)
+ fun onDesktopDragEnd_fullscreenIndicator_dragToMaximize_alreadyMaximized_noBoundsChange() {
+ val task = setUpFreeformTask(bounds = STABLE_BOUNDS)
+ val spyController = spy(controller)
+ val mockSurface = mock(SurfaceControl::class.java)
+ val mockDisplayLayout = mock(DisplayLayout::class.java)
+ val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!!
+ tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FREEFORM
+ whenever(displayController.getDisplayLayout(task.displayId)).thenReturn(mockDisplayLayout)
+ whenever(mockDisplayLayout.stableInsets()).thenReturn(Rect(0, 100, 2000, 2000))
+ whenever(mockDisplayLayout.getStableBounds(any())).thenAnswer { i ->
+ (i.arguments.first() as Rect).set(STABLE_BOUNDS)
+ }
+ whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
+ whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull()))
+ .thenReturn(DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR)
+ whenever(motionEvent.displayId).thenReturn(DEFAULT_DISPLAY)
+
+ // Drag move the task to the top edge
+ val currentDragBounds = Rect(100, 50, 500, 1000)
+ spyController.onDragPositioningMove(task, mockSurface, 200f, 10f, Rect(100, 200, 500, 1000))
spyController.onDragPositioningEnd(
task,
mockSurface,
@@ -6675,7 +7375,10 @@
@Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MULTI_INSTANCE_FEATURES)
- @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ @DisableFlags(
+ Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND,
+ Flags.FLAG_ENABLE_DESKTOP_TASK_LIMIT_SEPARATE_TRANSITION,
+ )
fun openInstance_fromFreeform_multiDesksDisabled_minimizesIfNeeded() {
setUpLandscapeDisplay()
val deskId = 0
@@ -6712,6 +7415,7 @@
Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MULTI_INSTANCE_FEATURES,
Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND,
)
+ @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_TASK_LIMIT_SEPARATE_TRANSITION)
fun openInstance_fromFreeform_multiDesksEnabled_minimizesIfNeeded() {
setUpLandscapeDisplay()
val deskId = 0
@@ -6742,6 +7446,42 @@
}
@Test
+ @EnableFlags(
+ Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MULTI_INSTANCE_FEATURES,
+ Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND,
+ Flags.FLAG_ENABLE_DESKTOP_TASK_LIMIT_SEPARATE_TRANSITION,
+ )
+ fun openInstance_fromFreeform_minimizesSeparately() {
+ setUpLandscapeDisplay()
+ val deskId = 0
+ val freeformTasks =
+ (1..MAX_TASK_LIMIT + 1).map { _ ->
+ setUpFreeformTask(displayId = DEFAULT_DISPLAY, deskId = deskId)
+ }
+ val oldestTask = freeformTasks.first()
+ val newestTask = freeformTasks.last()
+
+ val transition = Binder()
+ val wctCaptor = argumentCaptor<WindowContainerTransaction>()
+ whenever(
+ desktopMixedTransitionHandler.startLaunchTransition(
+ anyInt(),
+ wctCaptor.capture(),
+ anyInt(),
+ anyOrNull(),
+ anyOrNull(),
+ )
+ )
+ .thenReturn(transition)
+
+ runOpenInstance(newestTask, freeformTasks[1].taskId)
+
+ val wct = wctCaptor.firstValue
+ verify(desksOrganizer, never()).minimizeTask(wct, deskId, oldestTask)
+ assertThat(desktopTasksLimiter.hasTaskLimitTransitionForTesting(transition)).isTrue()
+ }
+
+ @Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MULTI_INSTANCE_FEATURES)
fun openInstance_fromFreeform_exitsImmersiveIfNeeded() {
setUpLandscapeDisplay()
@@ -7412,92 +8152,106 @@
}
@Test
- fun moveTaskToDesktop_background_attemptsImmersiveExit() {
- val task = setUpFreeformTask(background = true)
- val wct = WindowContainerTransaction()
- val runOnStartTransit = RunOnStartTransitionCallback()
- val transition = Binder()
- whenever(
- mMockDesktopImmersiveController.exitImmersiveIfApplicable(
- eq(wct),
- eq(task.displayId),
- eq(task.taskId),
- any(),
+ fun moveTaskToDesktop_background_attemptsImmersiveExit() =
+ testScope.runTest {
+ val task = setUpFreeformTask(background = true)
+ val wct = WindowContainerTransaction()
+ val runOnStartTransit = RunOnStartTransitionCallback()
+ val transition = Binder()
+ whenever(
+ mMockDesktopImmersiveController.exitImmersiveIfApplicable(
+ eq(wct),
+ eq(task.displayId),
+ eq(task.taskId),
+ any(),
+ )
)
+ .thenReturn(
+ ExitResult.Exit(exitingTask = 5, runOnTransitionStart = runOnStartTransit)
+ )
+ whenever(enterDesktopTransitionHandler.moveToDesktop(wct, UNKNOWN))
+ .thenReturn(transition)
+
+ controller.moveTaskToDefaultDeskAndActivate(
+ taskId = task.taskId,
+ wct = wct,
+ transitionSource = UNKNOWN,
)
- .thenReturn(ExitResult.Exit(exitingTask = 5, runOnTransitionStart = runOnStartTransit))
- whenever(enterDesktopTransitionHandler.moveToDesktop(wct, UNKNOWN)).thenReturn(transition)
+ runCurrent()
- controller.moveTaskToDefaultDeskAndActivate(
- taskId = task.taskId,
- wct = wct,
- transitionSource = UNKNOWN,
- )
-
- verify(mMockDesktopImmersiveController)
- .exitImmersiveIfApplicable(eq(wct), eq(task.displayId), eq(task.taskId), any())
- runOnStartTransit.assertOnlyInvocation(transition)
- }
+ verify(mMockDesktopImmersiveController)
+ .exitImmersiveIfApplicable(eq(wct), eq(task.displayId), eq(task.taskId), any())
+ runOnStartTransit.assertOnlyInvocation(transition)
+ }
@Test
- fun moveTaskToDesktop_foreground_attemptsImmersiveExit() {
- val task = setUpFreeformTask(background = false)
- val wct = WindowContainerTransaction()
- val runOnStartTransit = RunOnStartTransitionCallback()
- val transition = Binder()
- whenever(
- mMockDesktopImmersiveController.exitImmersiveIfApplicable(
- eq(wct),
- eq(task.displayId),
- eq(task.taskId),
- any(),
+ fun moveTaskToDesktop_foreground_attemptsImmersiveExit() =
+ testScope.runTest {
+ val task = setUpFreeformTask(background = false)
+ val wct = WindowContainerTransaction()
+ val runOnStartTransit = RunOnStartTransitionCallback()
+ val transition = Binder()
+ whenever(
+ mMockDesktopImmersiveController.exitImmersiveIfApplicable(
+ eq(wct),
+ eq(task.displayId),
+ eq(task.taskId),
+ any(),
+ )
)
+ .thenReturn(
+ ExitResult.Exit(exitingTask = 5, runOnTransitionStart = runOnStartTransit)
+ )
+ whenever(enterDesktopTransitionHandler.moveToDesktop(wct, UNKNOWN))
+ .thenReturn(transition)
+
+ controller.moveTaskToDefaultDeskAndActivate(
+ taskId = task.taskId,
+ wct = wct,
+ transitionSource = UNKNOWN,
)
- .thenReturn(ExitResult.Exit(exitingTask = 5, runOnTransitionStart = runOnStartTransit))
- whenever(enterDesktopTransitionHandler.moveToDesktop(wct, UNKNOWN)).thenReturn(transition)
+ runCurrent()
- controller.moveTaskToDefaultDeskAndActivate(
- taskId = task.taskId,
- wct = wct,
- transitionSource = UNKNOWN,
- )
-
- verify(mMockDesktopImmersiveController)
- .exitImmersiveIfApplicable(eq(wct), eq(task.displayId), eq(task.taskId), any())
- runOnStartTransit.assertOnlyInvocation(transition)
- }
+ verify(mMockDesktopImmersiveController)
+ .exitImmersiveIfApplicable(eq(wct), eq(task.displayId), eq(task.taskId), any())
+ runOnStartTransit.assertOnlyInvocation(transition)
+ }
@Test
- fun moveTaskToFront_background_attemptsImmersiveExit() {
- val task = setUpFreeformTask(background = true)
- val runOnStartTransit = RunOnStartTransitionCallback()
- val transition = Binder()
- whenever(
- mMockDesktopImmersiveController.exitImmersiveIfApplicable(
- any(),
- eq(task.displayId),
- eq(task.taskId),
- any(),
+ fun moveTaskToFront_background_attemptsImmersiveExit() =
+ testScope.runTest {
+ val task = setUpFreeformTask(background = true)
+ val runOnStartTransit = RunOnStartTransitionCallback()
+ val transition = Binder()
+ whenever(
+ mMockDesktopImmersiveController.exitImmersiveIfApplicable(
+ any(),
+ eq(task.displayId),
+ eq(task.taskId),
+ any(),
+ )
)
- )
- .thenReturn(ExitResult.Exit(exitingTask = 5, runOnTransitionStart = runOnStartTransit))
- whenever(
- desktopMixedTransitionHandler.startLaunchTransition(
- any(),
- any(),
- anyInt(),
- anyOrNull(),
- anyOrNull(),
+ .thenReturn(
+ ExitResult.Exit(exitingTask = 5, runOnTransitionStart = runOnStartTransit)
)
- )
- .thenReturn(transition)
+ whenever(
+ desktopMixedTransitionHandler.startLaunchTransition(
+ any(),
+ any(),
+ anyInt(),
+ anyOrNull(),
+ anyOrNull(),
+ )
+ )
+ .thenReturn(transition)
- controller.moveTaskToFront(task.taskId, unminimizeReason = UnminimizeReason.UNKNOWN)
+ controller.moveTaskToFront(task.taskId, unminimizeReason = UnminimizeReason.UNKNOWN)
+ runCurrent()
- verify(mMockDesktopImmersiveController)
- .exitImmersiveIfApplicable(any(), eq(task.displayId), eq(task.taskId), any())
- runOnStartTransit.assertOnlyInvocation(transition)
- }
+ verify(mMockDesktopImmersiveController)
+ .exitImmersiveIfApplicable(any(), eq(task.displayId), eq(task.taskId), any())
+ runOnStartTransit.assertOnlyInvocation(transition)
+ }
@Test
fun moveTaskToFront_foreground_attemptsImmersiveExit() {
@@ -7557,6 +8311,44 @@
}
@Test
+ @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun handleRequest_homeTask_notHandled() {
+ val home = createHomeTask(DEFAULT_DISPLAY)
+
+ val transition = Binder()
+ val result = controller.handleRequest(transition, createTransition(home))
+
+ assertNull(result)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun handleRequest_homeTask_activeDesk_deactivates() {
+ taskRepository.setActiveDesk(DEFAULT_DISPLAY, deskId = 0)
+ val home = createHomeTask(DEFAULT_DISPLAY)
+
+ val transition = Binder()
+ val result = controller.handleRequest(transition, createTransition(home))
+
+ assertNotNull(result)
+ verify(desksOrganizer).deactivateDesk(result, deskId = 0)
+ verify(desksTransitionsObserver)
+ .addPendingTransition(DeskTransition.DeactivateDesk(token = transition, deskId = 0))
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun handleRequest_homeTask_closing_notHandled() {
+ taskRepository.setActiveDesk(DEFAULT_DISPLAY, deskId = 0)
+ val home = createHomeTask(DEFAULT_DISPLAY)
+
+ val transition = Binder()
+ val result = controller.handleRequest(transition, createTransition(home, TRANSIT_CLOSE))
+
+ assertNull(result)
+ }
+
+ @Test
@EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
fun shouldPlayDesktopAnimation_notShowingDesktop_doesNotPlay() {
taskRepository.setDeskInactive(deskId = 0)
@@ -7797,6 +8589,7 @@
@EnableFlags(
Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY,
Flags.FLAG_ENABLE_DESKTOP_WALLPAPER_ACTIVITY_FOR_SYSTEM_USER,
+ Flags.FLAG_ENABLE_DISPLAY_WINDOWING_MODE_SWITCHING,
)
fun startLaunchTransition_desktopNotShowing_movesWallpaperToFront() {
taskRepository.setDeskInactive(deskId = 0)
@@ -7928,6 +8721,22 @@
}
@Test
+ @EnableFlags(Flags.FLAG_SHOW_HOME_BEHIND_DESKTOP)
+ fun showHomeBehindDesktop_wallpaperNotPresent() {
+ desktopState.shouldShowHomeBehindDesktop = true
+ val homeTask = setUpHomeTask()
+ val task1 = setUpFreeformTask()
+
+ controller.activateDesk(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition()))
+
+ val wct =
+ getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java)
+ val wallpaperReorderIndex = wct.indexOfReorder(wallpaperToken, toTop = true)
+
+ // There should be no wallpaper present to reorder.
+ assertThat(wallpaperReorderIndex).isEqualTo(-1)
+ }
+
@EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
fun onRecentsInDesktopAnimationFinishing_returningToApp_noDeskDeactivation() {
val deskId = 0
@@ -8194,11 +9003,17 @@
autoEnterEnabled: Boolean,
displayId: Int = DEFAULT_DISPLAY,
deskId: Int = DEFAULT_DISPLAY,
- ): RunningTaskInfo =
- setUpFreeformTask(displayId = displayId, deskId = deskId).apply {
- pictureInPictureParams =
- PictureInPictureParams.Builder().setAutoEnterEnabled(autoEnterEnabled).build()
- }
+ ): RunningTaskInfo {
+ val task =
+ setUpFreeformTask(displayId = displayId, deskId = deskId).apply {
+ pictureInPictureParams =
+ PictureInPictureParams.Builder().setAutoEnterEnabled(autoEnterEnabled).build()
+ baseActivity = ComponentName("com.test.dummypackage", "TestClass")
+ }
+ whenever(packageManager.getApplicationInfoAsUser(any(), anyInt(), anyInt()))
+ .thenReturn(task.topActivityInfo?.applicationInfo)
+ return task
+ }
private fun setUpHomeTask(displayId: Int = DEFAULT_DISPLAY): RunningTaskInfo {
val task = createHomeTask(displayId)
@@ -8276,12 +9091,7 @@
whenever(displayLayout.width()).thenReturn(DISPLAY_DIMENSION_LONG)
whenever(displayLayout.height()).thenReturn(DISPLAY_DIMENSION_SHORT)
val stableBounds =
- Rect(
- 0,
- 0,
- DISPLAY_DIMENSION_LONG,
- DISPLAY_DIMENSION_SHORT - Companion.TASKBAR_FRAME_HEIGHT,
- )
+ Rect(0, 0, DISPLAY_DIMENSION_LONG, DISPLAY_DIMENSION_SHORT - TASKBAR_FRAME_HEIGHT)
whenever(displayLayout.getStableBoundsForDesktopMode(any())).thenAnswer { i ->
(i.arguments.first() as Rect).set(stableBounds)
}
@@ -8291,12 +9101,7 @@
whenever(displayLayout.width()).thenReturn(DISPLAY_DIMENSION_SHORT)
whenever(displayLayout.height()).thenReturn(DISPLAY_DIMENSION_LONG)
val stableBounds =
- Rect(
- 0,
- 0,
- DISPLAY_DIMENSION_SHORT,
- DISPLAY_DIMENSION_LONG - Companion.TASKBAR_FRAME_HEIGHT,
- )
+ Rect(0, 0, DISPLAY_DIMENSION_SHORT, DISPLAY_DIMENSION_LONG - TASKBAR_FRAME_HEIGHT)
whenever(displayLayout.getStableBoundsForDesktopMode(any())).thenAnswer { i ->
(i.arguments.first() as Rect).set(stableBounds)
}
@@ -8416,7 +9221,10 @@
@JvmStatic
@Parameters(name = "{0}")
fun getParams(): List<FlagsParameterization> =
- FlagsParameterization.allCombinationsOf(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ FlagsParameterization.allCombinationsOf(
+ Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND,
+ Flags.FLAG_DEFAULT_DESK_WITHOUT_WARMUP_MIGRATION,
+ )
}
}
@@ -8453,11 +9261,15 @@
return indexOfReorder(task.token, toTop)
}
-private class ReorderPredicate(val token: WindowContainerToken, val toTop: Boolean? = null) :
- ((WindowContainerTransaction.HierarchyOp) -> Boolean) {
+private class ReorderPredicate(
+ val token: WindowContainerToken,
+ val toTop: Boolean? = null,
+ val includingParents: Boolean? = null,
+) : ((WindowContainerTransaction.HierarchyOp) -> Boolean) {
override fun invoke(hop: WindowContainerTransaction.HierarchyOp): Boolean =
hop.type == HIERARCHY_OP_TYPE_REORDER &&
(toTop == null || hop.toTop == toTop) &&
+ (includingParents == null || hop.includingParents() == includingParents) &&
hop.container == token.asBinder()
}
@@ -8476,15 +9288,17 @@
private fun WindowContainerTransaction.assertReorder(
task: RunningTaskInfo,
toTop: Boolean? = null,
+ includingParents: Boolean? = null,
) {
- assertReorder(task.token, toTop)
+ assertReorder(task.token, toTop, includingParents)
}
private fun WindowContainerTransaction.assertReorder(
token: WindowContainerToken,
toTop: Boolean? = null,
+ includingParents: Boolean? = null,
) {
- assertHop(ReorderPredicate(token, toTop))
+ assertHop(ReorderPredicate(token, toTop, includingParents))
}
private fun WindowContainerTransaction.assertReorderAt(
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt
index 7dc5b0f..9633dc0 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt
@@ -63,14 +63,18 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Mock
import org.mockito.Mockito.mock
import org.mockito.Mockito.spy
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when`
import org.mockito.kotlin.any
+import org.mockito.kotlin.argumentCaptor
import org.mockito.kotlin.eq
import org.mockito.kotlin.never
+import org.mockito.kotlin.whenever
/**
* Test class for {@link DesktopTasksLimiter}
@@ -90,6 +94,7 @@
@Mock lateinit var repositoryInitializer: DesktopRepositoryInitializer
@Mock lateinit var userManager: UserManager
@Mock lateinit var shellController: ShellController
+ @Mock lateinit var desktopMixedTransitionHandler: DesktopMixedTransitionHandler
private lateinit var desktopTasksLimiter: DesktopTasksLimiter
private lateinit var userRepositories: DesktopUserRepositories
@@ -122,6 +127,7 @@
userRepositories,
shellTaskOrganizer,
desksOrganizer,
+ desktopMixedTransitionHandler,
MAX_TASK_LIMIT,
)
}
@@ -139,6 +145,7 @@
userRepositories,
shellTaskOrganizer,
desksOrganizer,
+ desktopMixedTransitionHandler,
0,
)
}
@@ -152,6 +159,7 @@
userRepositories,
shellTaskOrganizer,
desksOrganizer,
+ desktopMixedTransitionHandler,
-5,
)
}
@@ -165,6 +173,7 @@
userRepositories,
shellTaskOrganizer,
desksOrganizer,
+ desktopMixedTransitionHandler,
maxTasksLimit = null,
)
}
@@ -532,6 +541,7 @@
userRepositories,
shellTaskOrganizer,
desksOrganizer,
+ desktopMixedTransitionHandler,
MAX_TASK_LIMIT2,
)
val tasks = (1..MAX_TASK_LIMIT2 + 1).map { setUpFreeformTask() }
@@ -680,6 +690,78 @@
)
}
+ @Test
+ fun onTransitionReady_taskLimitTransition_tasksOverLimit_startsMinimizeTransitionInRunOnIdle() {
+ desktopTaskRepo.addDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
+ desktopTaskRepo.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
+ val transition = Binder()
+ val minimizeTransition = Binder()
+ val existingTasks = (1..MAX_TASK_LIMIT).map { setUpFreeformTask() }
+ val launchTask = setUpFreeformTask()
+ desktopTasksLimiter.addPendingTaskLimitTransition(
+ transition,
+ deskId = 0,
+ taskId = launchTask.taskId,
+ )
+ val transitionInfo =
+ TransitionInfoBuilder(TRANSIT_OPEN).addChange(TRANSIT_TO_BACK, launchTask).build()
+ whenever(desktopMixedTransitionHandler.startTaskLimitMinimizeTransition(any(), anyInt()))
+ .thenReturn(minimizeTransition)
+
+ callOnTransitionReady(transition, transitionInfo)
+
+ val onIdleArgumentCaptor = argumentCaptor<Runnable>()
+ verify(transitions).runOnIdle(onIdleArgumentCaptor.capture())
+ onIdleArgumentCaptor.lastValue.run()
+ verify(desktopMixedTransitionHandler).startTaskLimitMinimizeTransition(any(), any())
+ assertThat(desktopTasksLimiter.getMinimizingTask(minimizeTransition)?.taskId)
+ .isEqualTo(existingTasks.first().taskId)
+ }
+
+ @Test
+ fun onTransitionReady_noPendingTaskLimitTransition_doesntTriggerOnIdle() {
+ desktopTaskRepo.addDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
+ desktopTaskRepo.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
+ val transition = Binder()
+ (1..MAX_TASK_LIMIT).map { setUpFreeformTask() }
+ val launchTask = setUpFreeformTask()
+ val transitionInfo =
+ TransitionInfoBuilder(TRANSIT_OPEN).addChange(TRANSIT_TO_BACK, launchTask).build()
+ whenever(desktopMixedTransitionHandler.startTaskLimitMinimizeTransition(any(), anyInt()))
+ .thenReturn(Binder())
+
+ callOnTransitionReady(transition, transitionInfo)
+
+ verify(transitions, never()).runOnIdle(any())
+ verify(desktopMixedTransitionHandler, never())
+ .startTaskLimitMinimizeTransition(any(), any())
+ }
+
+ @Test
+ fun onTransitionReady_taskLimitTransition_tasksWithinLimit_doesntStartMinimizeTransition() {
+ desktopTaskRepo.addDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
+ desktopTaskRepo.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
+ val transition = Binder()
+ val task = setUpFreeformTask()
+ desktopTasksLimiter.addPendingTaskLimitTransition(
+ transition,
+ deskId = 0,
+ taskId = task.taskId,
+ )
+ val transitionInfo =
+ TransitionInfoBuilder(TRANSIT_OPEN).addChange(TRANSIT_TO_BACK, task).build()
+ whenever(desktopMixedTransitionHandler.startTaskLimitMinimizeTransition(any(), anyInt()))
+ .thenReturn(Binder())
+
+ callOnTransitionReady(transition, transitionInfo)
+
+ val onIdleArgumentCaptor = ArgumentCaptor.forClass(Runnable::class.java)
+ verify(transitions).runOnIdle(onIdleArgumentCaptor.capture())
+ onIdleArgumentCaptor.value.run()
+ verify(desktopMixedTransitionHandler, never())
+ .startTaskLimitMinimizeTransition(any(), any())
+ }
+
private fun setUpFreeformTask(displayId: Int = DEFAULT_DISPLAY): RunningTaskInfo {
val task = createFreeformTask(displayId)
`when`(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/VisualIndicatorUpdateSchedulerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/VisualIndicatorUpdateSchedulerTest.kt
new file mode 100644
index 0000000..5667bd5
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/VisualIndicatorUpdateSchedulerTest.kt
@@ -0,0 +1,368 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.desktopmode
+
+import android.content.res.Configuration
+import android.graphics.Rect
+import android.hardware.display.DisplayTopology
+import android.hardware.display.DisplayTopologyGraph
+import android.hardware.display.DisplayTopologyGraph.AdjacentDisplay
+import android.hardware.display.DisplayTopologyGraph.DisplayNode
+import android.platform.test.annotations.EnableFlags
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
+import com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE
+import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.TestShellExecutor
+import com.android.wm.shell.common.DisplayController
+import com.android.wm.shell.common.MultiDisplayTestUtil.TestDisplay
+import com.android.wm.shell.sysui.ShellInit
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.cancel
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestCoroutineScheduler
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.advanceTimeBy
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import kotlinx.coroutines.test.setMain
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.Captor
+import org.mockito.Mockito.times
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.never
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.verifyNoInteractions
+import org.mockito.kotlin.whenever
+
+/**
+ * Test class for [VisualIndicatorUpdateScheduler]
+ *
+ * Usage: atest WMShellUnitTests:VisualIndicatorUpdateSchedulerTest
+ */
+@OptIn(ExperimentalCoroutinesApi::class)
+@RunWithLooper
+@RunWith(AndroidTestingRunner::class)
+@EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
+class VisualIndicatorUpdateSchedulerTest : ShellTestCase() {
+ private var mockDisplayController = mock<DisplayController>()
+ private var mockIndicator = mock<DesktopModeVisualIndicator>()
+ private var mockDisplayTopology = mock<DisplayTopology>()
+ private var mockDisplayTopologyGraph = mock<DisplayTopologyGraph>()
+
+ @Captor
+ private lateinit var displayListenerCaptor:
+ ArgumentCaptor<DisplayController.OnDisplaysChangedListener>
+
+ private lateinit var scheduler: VisualIndicatorUpdateScheduler
+ private val testScheduler = TestCoroutineScheduler()
+ private val testDispatcher = StandardTestDispatcher(testScheduler)
+ private val testScope = TestScope(testDispatcher)
+ private lateinit var shellInit: ShellInit
+
+ private val delayMillis = 800L // Match the constant in the class
+ private val displayId0 = 0
+ private val displayId1 = 1
+ private val displayId3 = 3
+
+ private val taskBounds = Rect(100, 100, 500, 500)
+ private val differentTaskBounds = Rect(110, 110, 510, 510)
+ private val similarTaskBounds = Rect(102, 102, 502, 502)
+
+ // With the test data from MultiDisplayTestUtil, we are using three displays here:
+ // +---+
+ // | 1 |
+ // +-+---+-+---+-+
+ // | 0 | 3 |
+ // +-------+ |
+ // +---+
+
+ private val adjacentDisplay1To0 =
+ AdjacentDisplay(displayId1, DisplayTopology.TreeNode.POSITION_TOP, 100f)
+ private val adjacentDisplay3To0 =
+ AdjacentDisplay(displayId3, DisplayTopology.TreeNode.POSITION_RIGHT, 0f)
+
+ private val adjacentDisplay0To1 =
+ AdjacentDisplay(displayId0, DisplayTopology.TreeNode.POSITION_BOTTOM, -200f)
+
+ private val adjacentDisplay0To3 =
+ AdjacentDisplay(displayId0, DisplayTopology.TreeNode.POSITION_LEFT, 0f)
+
+ private val displayNodes: Array<DisplayNode> =
+ arrayOf(
+ DisplayNode(
+ displayId0,
+ TestDisplay.DISPLAY_0.dpi,
+ arrayOf(adjacentDisplay1To0, adjacentDisplay3To0),
+ ),
+ DisplayNode(displayId1, TestDisplay.DISPLAY_1.dpi, arrayOf(adjacentDisplay0To1)),
+ DisplayNode(displayId3, TestDisplay.DISPLAY_3.dpi, arrayOf(adjacentDisplay0To3)),
+ )
+
+ @Before
+ fun setUp() {
+ Dispatchers.setMain(testDispatcher)
+ shellInit = ShellInit(TestShellExecutor())
+
+ scheduler =
+ VisualIndicatorUpdateScheduler(
+ shellInit = shellInit,
+ mainDispatcher = testDispatcher,
+ bgScope = testScope,
+ displayController = mockDisplayController,
+ )
+
+ val resources = mContext.getOrCreateTestableResources()
+ val resourceConfiguration = Configuration()
+ resourceConfiguration.uiMode = 0
+ resources.overrideConfiguration(resourceConfiguration)
+ val spyDisplayLayout0 = TestDisplay.DISPLAY_0.getSpyDisplayLayout(resources.resources)
+ val spyDisplayLayout1 = TestDisplay.DISPLAY_1.getSpyDisplayLayout(resources.resources)
+ val spyDisplayLayout3 = TestDisplay.DISPLAY_3.getSpyDisplayLayout(resources.resources)
+
+ shellInit.init()
+ verify(mockDisplayController).addDisplayWindowListener(displayListenerCaptor.capture())
+
+ whenever(mockDisplayController.getDisplayLayout(displayId0)).thenReturn(spyDisplayLayout0)
+ whenever(mockDisplayController.getDisplayLayout(displayId1)).thenReturn(spyDisplayLayout1)
+ whenever(mockDisplayController.getDisplayLayout(displayId3)).thenReturn(spyDisplayLayout3)
+ whenever(mockDisplayTopology.getGraph()).thenReturn(mockDisplayTopologyGraph)
+ whenever(mockDisplayTopologyGraph.displayNodes).thenReturn(displayNodes)
+ displayListenerCaptor.value.onTopologyChanged(mockDisplayTopology)
+ }
+
+ @After
+ fun tearDown() {
+ testScope.cancel()
+ }
+
+ @Test
+ fun init_registersDisplayListener() {
+ assert(displayListenerCaptor.value != null)
+ }
+
+ @Test
+ fun onTopologyChanged_validTopology_updatesGraph() {
+ val newTopology = mock<DisplayTopology>()
+ val newGraph = mock<DisplayTopologyGraph>()
+ whenever(newTopology.getGraph()).thenReturn(newGraph)
+
+ displayListenerCaptor.value.onTopologyChanged(newTopology)
+
+ verify(newTopology).getGraph()
+ }
+
+ @Test
+ fun scheduleVisualIndicator_indicatorUpdateTypeNotCrossDisplay_updatesImmediately() =
+ runTest(testDispatcher) {
+ val type = DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR
+ scheduler.schedule(
+ displayId = displayId0,
+ indicatorType = type,
+ inputX = 10f,
+ inputY = 10f,
+ taskBounds = taskBounds,
+ visualIndicator = mockIndicator,
+ )
+
+ runCurrent()
+
+ verify(mockIndicator).updateIndicatorWithType(type)
+ assert(testScope.coroutineContext[Job]?.children?.count() == 0)
+ }
+
+ @Test
+ fun scheduleVisualIndicatorUpdate_notNearAdjacentBorder_updatesImmediately() =
+ runTest(testDispatcher) {
+ val type = DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR
+ scheduler.schedule(
+ displayId = displayId0,
+ indicatorType = type,
+ // Display#1's bottom edge aligns with Display#0's top edge along Display#0's
+ // X-coordinates [100, 1100]px.
+ inputX = 50f,
+ inputY = 10f,
+ taskBounds = taskBounds,
+ visualIndicator = mockIndicator,
+ )
+
+ runCurrent()
+
+ verify(mockIndicator).updateIndicatorWithType(type)
+ assert(testScope.coroutineContext[Job]?.children?.count() == 0)
+ }
+
+ @Test
+ fun scheduleVisualIndicatorUpdateOnExternalDisplay_notNearAdjacentBorder_updatesImmediately() =
+ runTest(testDispatcher) {
+ val type = DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_LEFT_INDICATOR
+
+ scheduler.schedule(
+ displayId = displayId3,
+ indicatorType = type,
+ inputX = 200f,
+ // Display#3's left edge aligns with Display#0's right edge along Display#3's
+ // Y-coordinates [0 , 200]px.
+ inputY = 250f,
+ taskBounds = taskBounds,
+ visualIndicator = mockIndicator,
+ )
+
+ runCurrent()
+
+ verify(mockIndicator).updateIndicatorWithType(type)
+ assert(testScope.coroutineContext[Job]?.children?.count() == 0)
+ }
+
+ @Test
+ fun scheduleUpdateVisualIndicator_potentialCrossDisplayDrag_boundsNotChanged_continuePreviousJob() =
+ runTest(testDispatcher) {
+ val mockIndicator1 = mock<DesktopModeVisualIndicator>()
+ val mockIndicator2 = mock<DesktopModeVisualIndicator>()
+ val type = DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR
+
+ scheduler.schedule(
+ displayId = displayId0,
+ indicatorType = type,
+ // Display#1's bottom edge aligns with Display#0's top edge along Display#0's
+ // X-coordinates [100, 1100]px.
+ inputX = 150f,
+ inputY = 10f,
+ taskBounds = taskBounds,
+ visualIndicator = mockIndicator1,
+ )
+
+ advanceTimeBy(delayMillis / 2)
+ runCurrent()
+
+ assert(testScope.coroutineContext[Job]?.children?.count() == 1)
+ verifyNoInteractions(mockIndicator1)
+
+ scheduler.schedule(
+ displayId = displayId0,
+ indicatorType = type,
+ // Display#1's bottom edge aligns with Display#0's top edge along Display#0's
+ // X-coordinates [100, 1100]px.
+ inputX = 150f,
+ inputY = 10f,
+ taskBounds = similarTaskBounds,
+ visualIndicator = mockIndicator2,
+ )
+
+ advanceTimeBy(delayMillis * 2)
+ runCurrent()
+
+ verify(mockIndicator1, times(1)).updateIndicatorWithType(type)
+ verify(mockIndicator2, never()).updateIndicatorWithType(type)
+ assert(testScope.coroutineContext[Job]?.children?.count() == 0)
+ }
+
+ @Test
+ fun scheduleUpdateVisualIndicator_potentialCrossDisplayDrag_boundsChanged_cancelsPreviousJob() =
+ runTest(testDispatcher) {
+ val mockIndicator1 = mock<DesktopModeVisualIndicator>()
+ val mockIndicator2 = mock<DesktopModeVisualIndicator>()
+ val type = DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR
+
+ scheduler.schedule(
+ displayId = displayId0,
+ indicatorType = type,
+ // Display#1's bottom edge aligns with Display#0's top edge along Display#0's
+ // X-coordinates [100, 1100]px.
+ inputX = 150f,
+ inputY = 10f,
+ taskBounds = taskBounds,
+ visualIndicator = mockIndicator1,
+ )
+
+ advanceTimeBy(delayMillis / 2)
+ runCurrent()
+
+ assert(testScope.coroutineContext[Job]?.children?.count() == 1)
+ val firstJob = testScope.coroutineContext[Job]?.children?.first()
+ verifyNoInteractions(mockIndicator1)
+
+ scheduler.schedule(
+ displayId = displayId0,
+ indicatorType = type,
+ // Display#1's bottom edge aligns with Display#0's top edge along Display#0's
+ // X-coordinates [100, 1100]px.
+ inputX = 150f,
+ inputY = 10f,
+ taskBounds = differentTaskBounds, // Significant change
+ visualIndicator = mockIndicator2,
+ )
+
+ assert(firstJob?.isCancelled == true)
+ assert(testScope.coroutineContext[Job]?.children?.count() == 1)
+
+ advanceTimeBy(delayMillis)
+ runCurrent()
+
+ verify(mockIndicator1, never()).updateIndicatorWithType(type)
+ verify(mockIndicator2, times(1)).updateIndicatorWithType(type)
+ assert(testScope.coroutineContext[Job]?.children?.count() == 0)
+ }
+
+ @Test
+ fun scheduleUpdateVisualIndicator_potentialCrossDisplayDrag_indicatorTypeChange_cancelsPreviousJob() =
+ runTest(testDispatcher) {
+ val mockIndicator1 = mock<DesktopModeVisualIndicator>()
+ val mockIndicator2 = mock<DesktopModeVisualIndicator>()
+ val type1 = DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR
+ val type2 = DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_LEFT_INDICATOR
+
+ scheduler.schedule(
+ displayId = displayId0,
+ indicatorType = type1,
+ // Display#1's bottom edge aligns with Display#0's top edge along Display#0's
+ // X-coordinates [100, 1100]px.
+ inputX = 150f,
+ inputY = 10f,
+ taskBounds = taskBounds,
+ visualIndicator = mockIndicator1,
+ )
+ assert(testScope.coroutineContext[Job]?.children?.count() == 1)
+ val firstJob = testScope.coroutineContext[Job]?.children?.first()
+
+ scheduler.schedule(
+ displayId = displayId3,
+ indicatorType = type2,
+ inputX = 200f,
+ // Display#3's left edge aligns with Display#0's right edge along Display#3's
+ // Y-coordinates [0, 200]px.
+ inputY = 150f,
+ taskBounds = taskBounds,
+ visualIndicator = mockIndicator2,
+ )
+
+ assert(firstJob?.isCancelled == true)
+ assert(testScope.coroutineContext[Job]?.children?.count() == 1)
+ advanceTimeBy(delayMillis)
+ runCurrent()
+
+ verify(mockIndicator1, never()).updateIndicatorWithType(type1)
+ verify(mockIndicator2, times(1)).updateIndicatorWithType(type2)
+ assert(testScope.coroutineContext[Job]?.children?.count() == 0)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayModeControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/desktopfirst/DesktopDisplayModeControllerTest.kt
similarity index 94%
rename from libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayModeControllerTest.kt
rename to libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/desktopfirst/DesktopDisplayModeControllerTest.kt
index 13f5916..4d462b1 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayModeControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/desktopfirst/DesktopDisplayModeControllerTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.desktopmode
+package com.android.wm.shell.desktopmode.desktopfirst
import android.app.ActivityManager.RunningTaskInfo
import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
@@ -56,6 +56,7 @@
import com.google.testing.junit.testparameterinjector.TestParameterInjector
import com.google.testing.junit.testparameterinjector.TestParameterValuesProvider
import org.junit.After
+import org.junit.Assert.assertNull
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -258,7 +259,8 @@
@Test
@EnableFlags(Flags.FLAG_ENABLE_DISPLAY_WINDOWING_MODE_SWITCHING)
- fun displayWindowingModeSwitch_existingTasksOnConnected() {
+ @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun displayWindowingModeSwitch_existingTasksOnConnected_multidesk_disabled() {
defaultTDA.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN
whenever(mockWindowManager.getWindowingMode(anyInt())).thenReturn(WINDOWING_MODE_FULLSCREEN)
setExtendedMode(true)
@@ -275,7 +277,8 @@
@Test
@EnableFlags(Flags.FLAG_ENABLE_DISPLAY_WINDOWING_MODE_SWITCHING)
- fun displayWindowingModeSwitch_existingTasksOnDisconnected() {
+ @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun displayWindowingModeSwitch_existingTasksOnDisconnected_multidesk_disabled() {
defaultTDA.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FREEFORM
whenever(mockWindowManager.getWindowingMode(anyInt())).thenAnswer {
WINDOWING_MODE_FULLSCREEN
@@ -293,6 +296,46 @@
}
@Test
+ @EnableFlags(
+ Flags.FLAG_ENABLE_DISPLAY_WINDOWING_MODE_SWITCHING,
+ Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND,
+ )
+ fun displayWindowingModeSwitch_existingTasksOnConnected() {
+ defaultTDA.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN
+ whenever(mockWindowManager.getWindowingMode(anyInt())).thenReturn(WINDOWING_MODE_FULLSCREEN)
+ setExtendedMode(true)
+
+ connectExternalDisplay()
+
+ val arg = argumentCaptor<WindowContainerTransaction>()
+ verify(transitions, times(1)).startTransition(eq(TRANSIT_CHANGE), arg.capture(), isNull())
+ assertNull(arg.firstValue.changes[freeformTask.token.asBinder()])
+ assertThat(arg.firstValue.changes[fullscreenTask.token.asBinder()]?.windowingMode)
+ .isEqualTo(WINDOWING_MODE_FULLSCREEN)
+ }
+
+ @Test
+ @EnableFlags(
+ Flags.FLAG_ENABLE_DISPLAY_WINDOWING_MODE_SWITCHING,
+ Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND,
+ )
+ fun displayWindowingModeSwitch_existingTasksOnDisconnected() {
+ defaultTDA.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FREEFORM
+ whenever(mockWindowManager.getWindowingMode(anyInt())).thenAnswer {
+ WINDOWING_MODE_FULLSCREEN
+ }
+ setExtendedMode(true)
+
+ disconnectExternalDisplay()
+
+ val arg = argumentCaptor<WindowContainerTransaction>()
+ verify(transitions, times(1)).startTransition(eq(TRANSIT_CHANGE), arg.capture(), isNull())
+ assertNull(arg.firstValue.changes[freeformTask.token.asBinder()])
+ assertThat(arg.firstValue.changes[fullscreenTask.token.asBinder()]?.windowingMode)
+ .isEqualTo(WINDOWING_MODE_UNDEFINED)
+ }
+
+ @Test
@EnableFlags(DisplayFlags.FLAG_ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT)
fun externalDisplayWindowingMode() {
externalTDA.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN
@@ -364,6 +407,7 @@
return FlagsParameterization.allCombinationsOf(
Flags.FLAG_FORM_FACTOR_BASED_DESKTOP_FIRST_SWITCH,
DisplayFlags.FLAG_ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT,
+ Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND,
)
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/desktopfirst/DesktopFirstUtilsTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/desktopfirst/DesktopFirstUtilsTest.kt
new file mode 100644
index 0000000..f4c5741
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/desktopfirst/DesktopFirstUtilsTest.kt
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.desktopmode.desktopfirst
+
+import android.testing.AndroidTestingRunner
+import android.window.DisplayAreaInfo
+import androidx.test.filters.SmallTest
+import com.android.wm.shell.MockToken
+import com.android.wm.shell.RootTaskDisplayAreaOrganizer
+import com.android.wm.shell.ShellTestCase
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
+
+/**
+ * Test class for [DesktopFirstUtils]
+ *
+ * Usage: atest WMShellUnitTests:DesktopFirstUtilsTest
+ */
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class DesktopFirstUtilsTest : ShellTestCase() {
+ private val rootTaskDisplayAreaOrganizer = mock<RootTaskDisplayAreaOrganizer>()
+ private val desktopFirstTDA = DisplayAreaInfo(MockToken().token(), DESKTOP_FIRST_DISPLAY_ID, 0)
+ private val touchFirstTDA = DisplayAreaInfo(MockToken().token(), TOUCH_FIRST_DISPLAY_ID, 0)
+
+ @Before
+ fun setUp() {
+ desktopFirstTDA.configuration.windowConfiguration.windowingMode =
+ DESKTOP_FIRST_DISPLAY_WINDOWING_MODE
+ touchFirstTDA.configuration.windowConfiguration.windowingMode =
+ TOUCH_FIRST_DISPLAY_WINDOWING_MODE
+ whenever(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DESKTOP_FIRST_DISPLAY_ID))
+ .thenReturn(desktopFirstTDA)
+ whenever(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(TOUCH_FIRST_DISPLAY_ID))
+ .thenReturn(touchFirstTDA)
+ whenever(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(UNKNOWN_DISPLAY_ID))
+ .thenReturn(null)
+ }
+
+ @Test
+ fun isDisplayDesktopFirst() {
+ assertTrue(rootTaskDisplayAreaOrganizer.isDisplayDesktopFirst(DESKTOP_FIRST_DISPLAY_ID))
+ assertFalse(rootTaskDisplayAreaOrganizer.isDisplayDesktopFirst(TOUCH_FIRST_DISPLAY_ID))
+ assertFalse(rootTaskDisplayAreaOrganizer.isDisplayDesktopFirst(UNKNOWN_DISPLAY_ID))
+ }
+
+ companion object {
+ const val DESKTOP_FIRST_DISPLAY_ID = 100
+ const val TOUCH_FIRST_DISPLAY_ID = 200
+ const val UNKNOWN_DISPLAY_ID = 999
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/multidesks/RootTaskDesksOrganizerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/multidesks/RootTaskDesksOrganizerTest.kt
index 3939c95..c8ce47f 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/multidesks/RootTaskDesksOrganizerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/multidesks/RootTaskDesksOrganizerTest.kt
@@ -29,6 +29,7 @@
import android.window.WindowContainerTransaction.HierarchyOp
import android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER
import android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT
+import androidx.core.util.valueIterator
import androidx.test.filters.SmallTest
import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.ShellTaskOrganizer.TaskListener
@@ -50,6 +51,7 @@
import org.junit.runner.RunWith
import org.mockito.Mockito
import org.mockito.Mockito.never
+import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.kotlin.any
import org.mockito.kotlin.argThat
@@ -117,6 +119,130 @@
}
@Test
+ fun testWarmUpDefaultDesk_deskCreated() = runTest {
+ warmUpDefaultDesk(userId = PRIMARY_USER_ID, displayId = DEFAULT_DISPLAY)
+
+ assertThat(organizer.deskRootsByDeskId.size()).isEqualTo(1)
+ val root = organizer.deskRootsByDeskId.valueAt(0)
+ assertThat(root.users.isEmpty()).isTrue()
+ }
+
+ @Test
+ fun testWarmUpDefaultDesk_deskAlreadyExists_noDeskCreated() = runTest {
+ createDeskSuspending(userId = PRIMARY_USER_ID, displayId = DEFAULT_DISPLAY)
+
+ warmUpDefaultDesk(userId = PRIMARY_USER_ID, displayId = DEFAULT_DISPLAY)
+
+ // Only the one that already existed is present.
+ assertThat(organizer.deskRootsByDeskId.size()).isEqualTo(1)
+ }
+
+ @Test
+ fun testWarmUpDefaultDesk_createRequestInProgress_noDeskCreated() = runTest {
+ organizer.createDesk(displayId = DEFAULT_DISPLAY, userId = PRIMARY_USER_ID) {}
+
+ warmUpDefaultDesk(userId = PRIMARY_USER_ID, displayId = DEFAULT_DISPLAY)
+
+ assertThat(organizer.deskRootsByDeskId.size()).isEqualTo(0)
+ }
+
+ @Test
+ fun testWarmUpDefaultDesk_deskAlreadyExists_forOtherDisplayOnly_deskCreated() = runTest {
+ createDeskSuspending(userId = PRIMARY_USER_ID, displayId = SECOND_DISPLAY)
+
+ warmUpDefaultDesk(userId = PRIMARY_USER_ID, displayId = DEFAULT_DISPLAY)
+
+ assertThat(
+ organizer.deskRootsByDeskId.valueIterator().asSequence().singleOrNull { root ->
+ root.taskInfo.displayId == DEFAULT_DISPLAY && root.users.isEmpty()
+ }
+ )
+ .isNotNull()
+ }
+
+ @Test
+ fun testWarmUpDefaultDesk_deskAlreadyExists_removalInProgress_deskCreated() = runTest {
+ val desk = createDeskSuspending(userId = PRIMARY_USER_ID, displayId = DEFAULT_DISPLAY)
+ organizer.removeDesk(WindowContainerTransaction(), desk.deskRoot.deskId, PRIMARY_USER_ID)
+
+ warmUpDefaultDesk(userId = PRIMARY_USER_ID, displayId = DEFAULT_DISPLAY)
+
+ // A desk different than the one getting removed should exist.
+ assertThat(
+ organizer.deskRootsByDeskId.valueIterator().asSequence().singleOrNull { root ->
+ root.taskInfo.displayId == DEFAULT_DISPLAY &&
+ root.users.isEmpty() &&
+ root.deskId != desk.deskRoot.deskId
+ }
+ )
+ .isNotNull()
+ }
+
+ @Test
+ fun testCreateDeskImmediate() = runTest {
+ warmUpDefaultDesk(userId = PRIMARY_USER_ID, displayId = DEFAULT_DISPLAY)
+
+ val deskId =
+ organizer.createDeskImmediate(userId = PRIMARY_USER_ID, displayId = DEFAULT_DISPLAY)
+
+ assertThat(deskId).isNotNull()
+ }
+
+ @Test
+ fun testCreateDeskImmediate_rootExistsForOtherUser_pendingDeletion_doesNotReuseRoot() =
+ runTest {
+ val desk = createDeskSuspending(userId = PRIMARY_USER_ID, displayId = DEFAULT_DISPLAY)
+ organizer.removeDesk(
+ WindowContainerTransaction(),
+ desk.deskRoot.deskId,
+ PRIMARY_USER_ID,
+ )
+
+ val desk2 =
+ organizer.createDeskImmediate(
+ userId = SECONDARY_USER_ID,
+ displayId = DEFAULT_DISPLAY,
+ )
+
+ assertThat(desk2).isNull()
+ }
+
+ @Test
+ fun testCreateDeskImmediate_rootExistsForOtherUser_inOtherDisplay_doesNotReuseRoot() = runTest {
+ createDeskSuspending(userId = PRIMARY_USER_ID, displayId = DEFAULT_DISPLAY)
+
+ val desk2 =
+ organizer.createDeskImmediate(userId = SECONDARY_USER_ID, displayId = SECOND_DISPLAY)
+
+ assertThat(desk2).isNull()
+ }
+
+ @Test
+ fun testCreateDesk_warmUpInProgress_usesWarmedUpDesk() = runTest {
+ val displayId = DEFAULT_DISPLAY
+ organizer.warmUpDefaultDesk(userId = PRIMARY_USER_ID, displayId = DEFAULT_DISPLAY)
+
+ organizer.createDesk(userId = PRIMARY_USER_ID, displayId = DEFAULT_DISPLAY) {}
+
+ // Only one desk attempt.
+ verify(mockShellTaskOrganizer, times(1))
+ .createRootTask(displayId, WINDOWING_MODE_FREEFORM, organizer, true)
+ }
+
+ @Test
+ fun testCreateDesk_twice_warmUpInProgress_usesWarmedUpDeskAndCreatesOne() = runTest {
+ val displayId = DEFAULT_DISPLAY
+ organizer.warmUpDefaultDesk(userId = PRIMARY_USER_ID, displayId = DEFAULT_DISPLAY)
+
+ organizer.createDesk(userId = PRIMARY_USER_ID, displayId = DEFAULT_DISPLAY) {}
+ organizer.createDesk(userId = PRIMARY_USER_ID, displayId = DEFAULT_DISPLAY) {}
+
+ // One for the warmup/first desk and one for the second desk.
+ verify(mockShellTaskOrganizer, times(2))
+ .createRootTask(displayId, WINDOWING_MODE_FREEFORM, organizer, true)
+ }
+
+ @Test
fun testCreateMinimizationRoot_marksHidden() = runTest {
val desk = createDeskSuspending()
@@ -807,6 +933,37 @@
return DeskRoots(deskRoot, minimizationRoot)
}
+ private fun warmUpDefaultDesk(userId: Int = PRIMARY_USER_ID, displayId: Int = DEFAULT_DISPLAY) {
+ val freeformRootTask =
+ createFreeformTask().apply {
+ parentTaskId = -1
+ this.displayId = displayId
+ }
+ val minimizationRootTask =
+ createFreeformTask().apply {
+ parentTaskId = -1
+ this.displayId = displayId
+ }
+ Mockito.reset(mockShellTaskOrganizer)
+ whenever(
+ mockShellTaskOrganizer.createRootTask(
+ displayId,
+ WINDOWING_MODE_FREEFORM,
+ organizer,
+ true,
+ )
+ )
+ .thenAnswer { invocation ->
+ val listener = (invocation.arguments[2] as TaskListener)
+ listener.onTaskAppeared(freeformRootTask, SurfaceControl())
+ }
+ .thenAnswer { invocation ->
+ val listener = (invocation.arguments[2] as TaskListener)
+ listener.onTaskAppeared(minimizationRootTask, SurfaceControl())
+ }
+ organizer.warmUpDefaultDesk(displayId, userId)
+ }
+
private fun WindowContainerTransaction.hasMinimizationHops(
desk: DeskRoots,
task: WindowContainerToken,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropControllerTest.java
index 06dcd88..cd37780 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropControllerTest.java
@@ -50,19 +50,20 @@
import com.android.wm.shell.bubbles.bar.BubbleBarDragListener;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.shared.desktopmode.FakeDesktopState;
import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellController;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.Transitions;
+import dagger.Lazy;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import dagger.Lazy;
-
/**
* Tests for the drag and drop controller.
*/
@@ -96,16 +97,18 @@
private GlobalDragListener mGlobalDragListener;
@Mock
private Lazy<BubbleBarDragListener> mBubbleBarDragControllerLazy;
+ private FakeDesktopState mDesktopState;
private DragAndDropController mController;
@Before
public void setUp() throws RemoteException {
+ mDesktopState = new FakeDesktopState();
MockitoAnnotations.initMocks(this);
mController = new DragAndDropController(mContext, mShellInit, mShellController,
mShellCommandHandler, mShellTaskOrganizer, mDisplayController, mUiEventLogger,
mIconProvider, mGlobalDragListener, mTransitions, mBubbleBarDragControllerLazy,
- mMainExecutor);
+ mMainExecutor, mDesktopState);
mController.onInit();
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskListenerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskListenerTests.java
index 52c5ad1..a361f41 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskListenerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskListenerTests.java
@@ -20,8 +20,6 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.view.Display.INVALID_DISPLAY;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
import static com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION;
import static com.android.window.flags.Flags.FLAG_ENABLE_WINDOWING_TRANSITION_HANDLERS_OBSERVERS;
import static com.android.window.flags.Flags.FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION;
@@ -40,7 +38,6 @@
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
-import com.android.dx.mockito.inline.extended.StaticMockitoSession;
import com.android.window.flags.Flags;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.ShellTestCase;
@@ -50,7 +47,7 @@
import com.android.wm.shell.desktopmode.DesktopRepository;
import com.android.wm.shell.desktopmode.DesktopTasksController;
import com.android.wm.shell.desktopmode.DesktopUserRepositories;
-import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
+import com.android.wm.shell.shared.desktopmode.FakeDesktopState;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.windowdecor.WindowDecorViewModel;
@@ -59,7 +56,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
-import org.mockito.quality.Strictness;
+import org.mockito.MockitoAnnotations;
import java.util.Optional;
@@ -93,17 +90,16 @@
private TaskChangeListener mTaskChangeListener;
private FreeformTaskListener mFreeformTaskListener;
- private StaticMockitoSession mMockitoSession;
+ private AutoCloseable mMocksInit = null;
@Before
public void setup() {
- mMockitoSession =
- mockitoSession()
- .initMocks(this)
- .strictness(Strictness.LENIENT)
- .mockStatic(DesktopModeStatus.class)
- .startMocking();
- doReturn(true).when(() -> DesktopModeStatus.canEnterDesktopMode(any()));
+ mMocksInit = MockitoAnnotations.openMocks(this);
+
+ var desktopState = new FakeDesktopState();
+ desktopState.setCanEnterDesktopMode(true);
+ desktopState.setFreeformEnabled(true);
+
when(mDesktopUserRepositories.getCurrent()).thenReturn(mDesktopRepository);
when(mDesktopUserRepositories.getProfile(anyInt())).thenReturn(mDesktopRepository);
mFreeformTaskListener =
@@ -116,7 +112,16 @@
mDesktopModeLoggerTransitionObserver,
mLaunchAdjacentController,
mWindowDecorViewModel,
- Optional.of(mTaskChangeListener));
+ Optional.of(mTaskChangeListener),
+ desktopState);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ if (mMocksInit != null) {
+ mMocksInit.close();
+ mMocksInit = null;
+ }
}
@Test
@@ -339,9 +344,4 @@
verify(mDesktopUserRepositories.getCurrent(), never())
.updateTask(task.displayId, task.taskId, task.isVisible);
}
-
- @After
- public void tearDown() {
- mMockitoSession.finishMocking();
- }
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserverTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserverTest.java
index 69a4216..e3c3d0e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserverTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserverTest.java
@@ -17,12 +17,15 @@
package com.android.wm.shell.freeform;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
+import static com.android.wm.shell.transition.Transitions.TRANSIT_START_RECENTS_TRANSITION;
+
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -43,8 +46,10 @@
import com.android.window.flags.Flags;
import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.desktopmode.DesktopImeHandler;
import com.android.wm.shell.desktopmode.DesktopImmersiveController;
import com.android.wm.shell.desktopmode.multidesks.DesksTransitionObserver;
+import com.android.wm.shell.shared.desktopmode.FakeDesktopState;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.FocusTransitionObserver;
import com.android.wm.shell.transition.TransitionInfoBuilder;
@@ -52,6 +57,7 @@
import com.android.wm.shell.util.StubTransaction;
import com.android.wm.shell.windowdecor.WindowDecorViewModel;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
@@ -71,12 +77,17 @@
@Mock private TaskChangeListener mTaskChangeListener;
@Mock private FocusTransitionObserver mFocusTransitionObserver;
@Mock private DesksTransitionObserver mDesksTransitionObserver;
-
+ @Mock private DesktopImeHandler mDesktopImeHandler;
+ private FakeDesktopState mDesktopState;
private FreeformTaskTransitionObserver mTransitionObserver;
+ private AutoCloseable mMocksInits = null;
@Before
public void setUp() {
- MockitoAnnotations.initMocks(this);
+ mMocksInits = MockitoAnnotations.openMocks(this);
+
+ mDesktopState = new FakeDesktopState();
+ mDesktopState.setFreeformEnabled(true);
PackageManager pm = mock(PackageManager.class);
doReturn(true).when(pm).hasSystemFeature(PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT);
@@ -85,20 +96,29 @@
mTransitionObserver =
new FreeformTaskTransitionObserver(
- context,
mShellInit,
mTransitions,
Optional.of(mDesktopImmersiveController),
mWindowDecorViewModel,
Optional.of(mTaskChangeListener),
mFocusTransitionObserver,
- Optional.of(mDesksTransitionObserver));
+ Optional.of(mDesksTransitionObserver),
+ mDesktopState,
+ Optional.of(mDesktopImeHandler));
final ArgumentCaptor<Runnable> initRunnableCaptor = ArgumentCaptor.forClass(Runnable.class);
verify(mShellInit).addInitCallback(initRunnableCaptor.capture(), same(mTransitionObserver));
initRunnableCaptor.getValue().run();
}
+ @After
+ public void tearDown() throws Exception {
+ if (mMocksInits != null) {
+ mMocksInits.close();
+ mMocksInits = null;
+ }
+ }
+
@Test
public void init_registersObserver() {
verify(mTransitions).registerObserver(same(mTransitionObserver));
@@ -172,6 +192,30 @@
}
@Test
+ public void recentsTransition_onTransitionFinished_notifiesOnTaskMovingToBack() {
+ final TransitionInfo.Change change =
+ createChange(TRANSIT_TO_BACK, /* taskId= */ 1, WINDOWING_MODE_FREEFORM);
+ final TransitionInfo.Change homeChange =
+ createChange(TRANSIT_TO_FRONT, /* taskId= */ 2, WINDOWING_MODE_FULLSCREEN);
+ final TransitionInfo info =
+ new TransitionInfoBuilder(TRANSIT_START_RECENTS_TRANSITION, /* flags= */ 0)
+ .addChange(homeChange)
+ .addChange(change)
+ .build();
+
+ final IBinder transition = mock(IBinder.class);
+ final SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class);
+ final SurfaceControl.Transaction finishT = mock(SurfaceControl.Transaction.class);
+ mTransitionObserver.onTransitionReady(transition, info, startT, finishT);
+ mTransitionObserver.onTransitionStarting(transition);
+
+ verify(mTaskChangeListener, never()).onTaskMovingToBack(change.getTaskInfo());
+
+ mTransitionObserver.onTransitionFinished(transition, false);
+ verify(mTaskChangeListener).onTaskMovingToBack(change.getTaskInfo());
+ }
+
+ @Test
public void changeTransition_notifiesOnTaskChange() {
final TransitionInfo.Change change =
createChange(TRANSIT_CHANGE, /* taskId= */ 1, WINDOWING_MODE_FREEFORM);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipAlphaAnimatorTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipAlphaAnimatorTest.java
index 2bd9afc..50f558d 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipAlphaAnimatorTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipAlphaAnimatorTest.java
@@ -96,7 +96,8 @@
@Test
public void setAnimationStartCallback_fadeInAnimator_callbackStartCallback() {
- mPipAlphaAnimator = new PipAlphaAnimator(mMockContext, mTestLeash, mMockStartTransaction,
+ mPipAlphaAnimator = new PipAlphaAnimator(mMockContext,
+ new PipSurfaceTransactionHelper(mMockContext), mTestLeash, mMockStartTransaction,
mMockFinishTransaction, PipAlphaAnimator.FADE_IN);
mPipAlphaAnimator.setAnimationStartCallback(mMockStartCallback);
@@ -112,7 +113,8 @@
@Test
public void setAnimationEndCallback_fadeInAnimator_callbackStartAndEndCallback() {
- mPipAlphaAnimator = new PipAlphaAnimator(mMockContext, mTestLeash, mMockStartTransaction,
+ mPipAlphaAnimator = new PipAlphaAnimator(mMockContext,
+ new PipSurfaceTransactionHelper(mMockContext), mTestLeash, mMockStartTransaction,
mMockFinishTransaction, PipAlphaAnimator.FADE_IN);
mPipAlphaAnimator.setAnimationStartCallback(mMockStartCallback);
@@ -128,7 +130,8 @@
@Test
public void onAnimationStart_fadeInAnimator_setCornerAndShadowRadii() {
- mPipAlphaAnimator = new PipAlphaAnimator(mMockContext, mTestLeash, mMockStartTransaction,
+ mPipAlphaAnimator = new PipAlphaAnimator(mMockContext,
+ new PipSurfaceTransactionHelper(mMockContext), mTestLeash, mMockStartTransaction,
mMockFinishTransaction, PipAlphaAnimator.FADE_IN);
mPipAlphaAnimator.setSurfaceControlTransactionFactory(mMockFactory);
@@ -145,7 +148,8 @@
@Test
public void onAnimationStart_fadeOutAnimator_setCornerNoShadowRadii() {
- mPipAlphaAnimator = new PipAlphaAnimator(mMockContext, mTestLeash, mMockStartTransaction,
+ mPipAlphaAnimator = new PipAlphaAnimator(mMockContext,
+ new PipSurfaceTransactionHelper(mMockContext), mTestLeash, mMockStartTransaction,
mMockFinishTransaction, PipAlphaAnimator.FADE_OUT);
mPipAlphaAnimator.setSurfaceControlTransactionFactory(mMockFactory);
@@ -164,7 +168,8 @@
@Test
public void onAnimationUpdate_fadeInAnimator_setCornerAndShadowRadii() {
- mPipAlphaAnimator = new PipAlphaAnimator(mMockContext, mTestLeash, mMockStartTransaction,
+ mPipAlphaAnimator = new PipAlphaAnimator(mMockContext,
+ new PipSurfaceTransactionHelper(mMockContext), mTestLeash, mMockStartTransaction,
mMockFinishTransaction, PipAlphaAnimator.FADE_IN);
mPipAlphaAnimator.setSurfaceControlTransactionFactory(mMockFactory);
@@ -181,7 +186,8 @@
@Test
public void onAnimationUpdate_fadeOutAnimator_setCornerNoShadowRadii() {
- mPipAlphaAnimator = new PipAlphaAnimator(mMockContext, mTestLeash, mMockStartTransaction,
+ mPipAlphaAnimator = new PipAlphaAnimator(mMockContext,
+ new PipSurfaceTransactionHelper(mMockContext), mTestLeash, mMockStartTransaction,
mMockFinishTransaction, PipAlphaAnimator.FADE_OUT);
mPipAlphaAnimator.setSurfaceControlTransactionFactory(mMockFactory);
@@ -200,7 +206,8 @@
@Test
public void onAnimationEnd_fadeInAnimator_setCornerAndShadowRadii() {
- mPipAlphaAnimator = new PipAlphaAnimator(mMockContext, mTestLeash, mMockStartTransaction,
+ mPipAlphaAnimator = new PipAlphaAnimator(mMockContext,
+ new PipSurfaceTransactionHelper(mMockContext), mTestLeash, mMockStartTransaction,
mMockFinishTransaction, PipAlphaAnimator.FADE_IN);
mPipAlphaAnimator.setSurfaceControlTransactionFactory(mMockFactory);
@@ -217,7 +224,8 @@
@Test
public void onAnimationEnd_fadeOutAnimator_setCornerNoShadowRadii() {
- mPipAlphaAnimator = new PipAlphaAnimator(mMockContext, mTestLeash, mMockStartTransaction,
+ mPipAlphaAnimator = new PipAlphaAnimator(mMockContext,
+ new PipSurfaceTransactionHelper(mMockContext), mTestLeash, mMockStartTransaction,
mMockFinishTransaction, PipAlphaAnimator.FADE_OUT);
mPipAlphaAnimator.setSurfaceControlTransactionFactory(mMockFactory);
@@ -236,7 +244,8 @@
@Test
public void onAnimationEnd_fadeInAnimator_leashVisibleAtEnd() {
- mPipAlphaAnimator = new PipAlphaAnimator(mMockContext, mTestLeash, mMockStartTransaction,
+ mPipAlphaAnimator = new PipAlphaAnimator(mMockContext,
+ new PipSurfaceTransactionHelper(mMockContext), mTestLeash, mMockStartTransaction,
mMockFinishTransaction, PipAlphaAnimator.FADE_IN);
mPipAlphaAnimator.setSurfaceControlTransactionFactory(mMockFactory);
@@ -251,7 +260,8 @@
@Test
public void onAnimationEnd_fadeOutAnimator_leashInvisibleAtEnd() {
- mPipAlphaAnimator = new PipAlphaAnimator(mMockContext, mTestLeash, mMockStartTransaction,
+ mPipAlphaAnimator = new PipAlphaAnimator(mMockContext,
+ new PipSurfaceTransactionHelper(mMockContext), mTestLeash, mMockStartTransaction,
mMockFinishTransaction, PipAlphaAnimator.FADE_OUT);
mPipAlphaAnimator.setSurfaceControlTransactionFactory(mMockFactory);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipEnterAnimatorTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipEnterAnimatorTest.java
index fa7ab952..fd10265 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipEnterAnimatorTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipEnterAnimatorTest.java
@@ -104,7 +104,8 @@
public void setAnimationStartCallback_enter_callbackStartCallback() {
mRotation = Surface.ROTATION_0;
mEndBounds = new Rect(100, 100, 500, 500);
- mPipEnterAnimator = new PipEnterAnimator(mMockContext, mTestLeash,
+ mPipEnterAnimator = new PipEnterAnimator(mMockContext,
+ new PipSurfaceTransactionHelper(mMockContext), mTestLeash,
mMockStartTransaction, mMockFinishTransaction,
mEndBounds, mRotation);
mPipEnterAnimator.setSurfaceControlTransactionFactory(mMockFactory);
@@ -130,7 +131,8 @@
public void setAnimationEndCallback_enter_callbackStartAndEndCallback() {
mRotation = Surface.ROTATION_0;
mEndBounds = new Rect(100, 100, 500, 500);
- mPipEnterAnimator = new PipEnterAnimator(mMockContext, mTestLeash,
+ mPipEnterAnimator = new PipEnterAnimator(mMockContext,
+ new PipSurfaceTransactionHelper(mMockContext), mTestLeash,
mMockStartTransaction, mMockFinishTransaction,
mEndBounds, mRotation);
mPipEnterAnimator.setSurfaceControlTransactionFactory(mMockFactory);
@@ -156,7 +158,8 @@
public void setAppIconContentOverlay_thenGetContentOverlayLeash_returnOverlayLeash() {
mRotation = Surface.ROTATION_0;
mEndBounds = new Rect(100, 100, 500, 500);
- mPipEnterAnimator = new PipEnterAnimator(mMockContext, mTestLeash,
+ mPipEnterAnimator = new PipEnterAnimator(mMockContext,
+ new PipSurfaceTransactionHelper(mMockContext), mTestLeash,
mMockStartTransaction, mMockFinishTransaction,
mEndBounds, mRotation);
mPipEnterAnimator.setSurfaceControlTransactionFactory(mMockFactory);
@@ -173,7 +176,8 @@
public void setAppIconContentOverlay_thenClearAppIconOverlay_returnNullLeash() {
mRotation = Surface.ROTATION_0;
mEndBounds = new Rect(100, 100, 500, 500);
- mPipEnterAnimator = new PipEnterAnimator(mMockContext, mTestLeash,
+ mPipEnterAnimator = new PipEnterAnimator(mMockContext,
+ new PipSurfaceTransactionHelper(mMockContext), mTestLeash,
mMockStartTransaction, mMockFinishTransaction,
mEndBounds, mRotation);
mPipEnterAnimator.setSurfaceControlTransactionFactory(mMockFactory);
@@ -191,7 +195,8 @@
public void onEnterAnimationUpdate_withContentOverlay_animateOverlay() {
mRotation = Surface.ROTATION_0;
mEndBounds = new Rect(100, 100, 500, 500);
- mPipEnterAnimator = new PipEnterAnimator(mMockContext, mTestLeash,
+ mPipEnterAnimator = new PipEnterAnimator(mMockContext,
+ new PipSurfaceTransactionHelper(mMockContext), mTestLeash,
mMockStartTransaction, mMockFinishTransaction,
mEndBounds, mRotation);
mPipEnterAnimator.setSurfaceControlTransactionFactory(mMockFactory);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipExpandAnimatorTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipExpandAnimatorTest.java
index c873b7d..f18574e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipExpandAnimatorTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipExpandAnimatorTest.java
@@ -131,7 +131,8 @@
mBaseBounds = new Rect(0, 0, 1_000, 2_000);
mStartBounds = new Rect(500, 1_000, 1_000, 2_000);
mEndBounds = new Rect(mBaseBounds);
- mPipExpandAnimator = new PipExpandAnimator(mMockContext, mTestLeash,
+ mPipExpandAnimator = new PipExpandAnimator(mMockContext,
+ new PipSurfaceTransactionHelper(mMockContext), mTestLeash,
mMockStartTransaction, mMockFinishTransaction,
mBaseBounds, mStartBounds, mEndBounds, mSourceRectHint,
mRotation, false /* isPipInDesktopMode */);
@@ -154,7 +155,8 @@
mBaseBounds = new Rect(0, 0, 1_000, 2_000);
mStartBounds = new Rect(500, 1_000, 1_000, 2_000);
mEndBounds = new Rect(mBaseBounds);
- mPipExpandAnimator = new PipExpandAnimator(mMockContext, mTestLeash,
+ mPipExpandAnimator = new PipExpandAnimator(mMockContext,
+ new PipSurfaceTransactionHelper(mMockContext), mTestLeash,
mMockStartTransaction, mMockFinishTransaction,
mBaseBounds, mStartBounds, mEndBounds, mSourceRectHint,
mRotation, false /* isPipInDesktopMode */);
@@ -177,7 +179,8 @@
mBaseBounds = new Rect(0, 0, 1_000, 2_000);
mStartBounds = new Rect(500, 1_000, 1_000, 2_000);
mEndBounds = new Rect(mBaseBounds);
- mPipExpandAnimator = new PipExpandAnimator(mMockContext, mTestLeash,
+ mPipExpandAnimator = new PipExpandAnimator(mMockContext,
+ new PipSurfaceTransactionHelper(mMockContext), mTestLeash,
mMockStartTransaction, mMockFinishTransaction,
mBaseBounds, mStartBounds, mEndBounds, mSourceRectHint,
mRotation, false /* isPipInDesktopMode */);
@@ -202,7 +205,8 @@
mBaseBounds = new Rect(0, 0, 1_000, 2_000);
mStartBounds = new Rect(500, 1_000, 1_000, 2_000);
mEndBounds = new Rect(mBaseBounds);
- mPipExpandAnimator = new PipExpandAnimator(mMockContext, mTestLeash,
+ mPipExpandAnimator = new PipExpandAnimator(mMockContext,
+ new PipSurfaceTransactionHelper(mMockContext), mTestLeash,
mMockStartTransaction, mMockFinishTransaction,
mBaseBounds, mStartBounds, mEndBounds, mSourceRectHint,
mRotation, true /* isPipInDesktopMode */);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipResizeAnimatorTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipResizeAnimatorTest.java
index c99ca6d..b4e9eda 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipResizeAnimatorTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipResizeAnimatorTest.java
@@ -105,7 +105,8 @@
mEndBounds = new Rect(mBaseBounds);
final int duration = 10;
final float delta = 0;
- mPipResizeAnimator = new PipResizeAnimator(mMockContext, mTestLeash,
+ mPipResizeAnimator = new PipResizeAnimator(mMockContext,
+ new PipSurfaceTransactionHelper(mMockContext), mTestLeash,
mMockStartTransaction, mMockFinishTransaction,
mBaseBounds, mStartBounds, mEndBounds,
duration, delta);
@@ -128,7 +129,8 @@
mEndBounds = new Rect(mBaseBounds);
final int duration = 10;
final float delta = 0;
- mPipResizeAnimator = new PipResizeAnimator(mMockContext, mTestLeash,
+ mPipResizeAnimator = new PipResizeAnimator(mMockContext,
+ new PipSurfaceTransactionHelper(mMockContext), mTestLeash,
mMockStartTransaction, mMockFinishTransaction,
mBaseBounds, mStartBounds, mEndBounds,
duration, delta);
@@ -153,7 +155,8 @@
final int duration = 10;
final float delta = 0;
final float[] matrix = new float[9];
- mPipResizeAnimator = new PipResizeAnimator(mMockContext, mTestLeash,
+ mPipResizeAnimator = new PipResizeAnimator(mMockContext,
+ new PipSurfaceTransactionHelper(mMockContext), mTestLeash,
mMockStartTransaction, mMockFinishTransaction,
mBaseBounds, mStartBounds, mEndBounds,
duration, delta);
@@ -209,7 +212,8 @@
final int duration = 10;
final float delta = 0;
final float[] matrix = new float[9];
- mPipResizeAnimator = new PipResizeAnimator(mMockContext, mTestLeash,
+ mPipResizeAnimator = new PipResizeAnimator(mMockContext,
+ new PipSurfaceTransactionHelper(mMockContext), mTestLeash,
mMockStartTransaction, mMockFinishTransaction,
mBaseBounds, mStartBounds, mEndBounds,
duration, delta);
@@ -264,7 +268,8 @@
final int duration = 10;
final float delta = 45;
final float[] matrix = new float[9];
- mPipResizeAnimator = new PipResizeAnimator(mMockContext, mTestLeash,
+ mPipResizeAnimator = new PipResizeAnimator(mMockContext,
+ new PipSurfaceTransactionHelper(mMockContext), mTestLeash,
mMockStartTransaction, mMockFinishTransaction,
mBaseBounds, mStartBounds, mEndBounds,
duration, delta);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipDisplayTransferHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipDisplayTransferHandlerTest.kt
index c18c502..d880781 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipDisplayTransferHandlerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipDisplayTransferHandlerTest.kt
@@ -25,6 +25,8 @@
import com.android.window.flags.Flags.FLAG_ENABLE_DRAGGING_PIP_ACROSS_DISPLAYS
import com.android.wm.shell.RootTaskDisplayAreaOrganizer
import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.common.DisplayController
+import com.android.wm.shell.common.pip.PipBoundsState
import com.android.wm.shell.common.pip.PipDisplayLayoutState
import com.android.wm.shell.desktopmode.DesktopRepository
import com.android.wm.shell.desktopmode.DesktopUserRepositories
@@ -37,15 +39,31 @@
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever
import org.mockito.kotlin.verify
+import android.content.res.Configuration
+import android.content.res.Resources
+import android.graphics.PointF
import android.graphics.Rect
import android.os.Bundle
+import android.testing.TestableResources
+import android.util.ArrayMap
+import android.view.Display
import android.view.SurfaceControl
+import com.android.modules.utils.testing.ExtendedMockitoRule
+import com.android.wm.shell.pip2.PipSurfaceTransactionHelper
import com.android.wm.shell.pip2.phone.PipDisplayTransferHandler.ORIGIN_DISPLAY_ID_KEY
import com.android.wm.shell.pip2.phone.PipDisplayTransferHandler.TARGET_DISPLAY_ID_KEY
import com.android.wm.shell.pip2.phone.PipTransition.PIP_DESTINATION_BOUNDS
import com.android.wm.shell.pip2.phone.PipTransition.PIP_START_TX
import com.android.wm.shell.pip2.phone.PipTransitionState.CHANGING_PIP_BOUNDS
import org.mockito.kotlin.eq
+import org.mockito.kotlin.times
+import com.google.common.truth.Truth.assertThat
+import com.android.wm.shell.R
+import com.android.wm.shell.common.DisplayLayout
+import com.android.wm.shell.common.MultiDisplayDragMoveBoundsCalculator
+import com.android.wm.shell.common.MultiDisplayTestUtil.TestDisplay
+import org.junit.Rule
+import org.mockito.kotlin.never
/**
* Unit test against [PipDisplayTransferHandler].
@@ -61,24 +79,77 @@
private val mockPipTransitionState = mock<PipTransitionState>()
private val mockPipScheduler = mock<PipScheduler>()
private val mockRootTaskDisplayAreaOrganizer = mock<RootTaskDisplayAreaOrganizer>()
+ private val mockPipBoundsState = mock<PipBoundsState>()
private val mockTaskInfo = mock<ActivityManager.RunningTaskInfo>()
+ private val mockDisplayController = mock<DisplayController>()
+ private val mockTransaction = mock<SurfaceControl.Transaction>()
+ private val mockLeash = mock<SurfaceControl>()
+ private val mockFactory = mock<PipSurfaceTransactionHelper.SurfaceControlTransactionFactory>()
+ private val mockSurfaceTransactionHelper = mock<PipSurfaceTransactionHelper>()
+
+ private lateinit var testableResources: TestableResources
+ private lateinit var resources: Resources
private lateinit var defaultTda: DisplayAreaInfo
private lateinit var pipDisplayTransferHandler: PipDisplayTransferHandler
+ private val displayIds = intArrayOf(ORIGIN_DISPLAY_ID, TARGET_DISPLAY_ID, SECONDARY_DISPLAY_ID)
+ private val displayLayouts = ArrayMap<Int, DisplayLayout>()
+ private val mockBounds = Rect()
+
+ @JvmField
+ @Rule
+ val extendedMockitoRule =
+ ExtendedMockitoRule.Builder(this)
+ .mockStatic(SurfaceControl::class.java)
+ .build()!!
+
@Before
fun setUp() {
+ testableResources = mContext.getOrCreateTestableResources()
+ val resourceConfiguration = Configuration()
+ resourceConfiguration.uiMode = 0
+ testableResources.overrideConfiguration(resourceConfiguration)
+ resources = testableResources.resources
+
+ whenever(resources.getDimensionPixelSize(R.dimen.pip_corner_radius)).thenReturn(
+ TEST_CORNER_RADIUS
+ )
+ whenever(resources.getDimensionPixelSize(R.dimen.pip_shadow_radius)).thenReturn(
+ TEST_SHADOW_RADIUS
+ )
+ whenever(SurfaceControl.mirrorSurface(any())).thenReturn(mockLeash)
+ whenever(mockPipTransitionState.pinnedTaskLeash).thenReturn(mockLeash)
whenever(mockDesktopUserRepositories.current).thenReturn(mockDesktopRepository)
whenever(mockTaskInfo.getDisplayId()).thenReturn(ORIGIN_DISPLAY_ID)
whenever(mockPipDisplayLayoutState.displayId).thenReturn(ORIGIN_DISPLAY_ID)
+ whenever(mockTransaction.remove(any())).thenReturn(mockTransaction)
+ whenever(mockTransaction.show(any())).thenReturn(mockTransaction)
+ whenever(mockFactory.transaction).thenReturn(mockTransaction)
+ whenever(mockPipBoundsState.bounds).thenReturn(mockBounds)
defaultTda =
DisplayAreaInfo(mock<WindowContainerToken>(), ORIGIN_DISPLAY_ID, /* featureId = */ 0)
whenever(mockRootTaskDisplayAreaOrganizer.getDisplayAreaInfo(ORIGIN_DISPLAY_ID)).thenReturn(
defaultTda
)
+ whenever(mockRootTaskDisplayAreaOrganizer.displayIds).thenReturn(displayIds)
+
+ for (id in displayIds) {
+ val display = mock<Display>()
+ whenever(mockDisplayController.getDisplay(id)).thenReturn(display)
+ val displayLayout =
+ TestDisplay.entries.find { it.id == id }?.getSpyDisplayLayout(resources)
+ displayLayouts.put(id, displayLayout)
+ whenever(mockDisplayController.getDisplayLayout(id)).thenReturn(displayLayout)
+ }
pipDisplayTransferHandler =
- PipDisplayTransferHandler(mockPipTransitionState, mockPipScheduler)
+ PipDisplayTransferHandler(
+ mContext, mockPipTransitionState, mockPipScheduler,
+ mockRootTaskDisplayAreaOrganizer, mockPipBoundsState, mockDisplayController
+ )
+ pipDisplayTransferHandler.setSurfaceControlTransactionFactory(mockFactory)
+ pipDisplayTransferHandler.setSurfaceTransactionHelper(mockSurfaceTransactionHelper)
}
@Test
@@ -119,8 +190,121 @@
verify(mockPipScheduler).scheduleFinishPipBoundsChange(eq(destinationBounds))
}
+ @Test
+ fun showDragMirrorOnConnectedDisplays_hasNotLeftOriginDisplay_shouldNotCreateMirrors() {
+ val globalDpBounds = MultiDisplayDragMoveBoundsCalculator.calculateGlobalDpBoundsForDrag(
+ displayLayouts.get(ORIGIN_DISPLAY_ID)!!, START_DRAG_COORDINATES,
+ PIP_BOUNDS, displayLayouts.get(ORIGIN_DISPLAY_ID)!!, 150f, 150f)
+ pipDisplayTransferHandler.showDragMirrorOnConnectedDisplays(
+ ORIGIN_DISPLAY_ID, globalDpBounds
+ )
+
+ verify(mockRootTaskDisplayAreaOrganizer, never()).reparentToDisplayArea(
+ eq(TARGET_DISPLAY_ID),
+ any(),
+ any()
+ )
+ assertThat(pipDisplayTransferHandler.mOnDragMirrorPerDisplayId.isEmpty()).isTrue()
+ verify(mockSurfaceTransactionHelper, never()).setPipTransformations(
+ any(),
+ any(),
+ any(),
+ any(),
+ any()
+ )
+ verify(mockTransaction, never()).show(any())
+ verify(mockTransaction, times(1)).apply()
+ }
+
+ @Test
+ fun showDragMirrorOnConnectedDisplays_movedToAnotherDisplay_createsOneMirror() {
+ val globalDpBounds = MultiDisplayDragMoveBoundsCalculator.calculateGlobalDpBoundsForDrag(
+ displayLayouts.get(ORIGIN_DISPLAY_ID)!!, START_DRAG_COORDINATES,
+ PIP_BOUNDS, displayLayouts.get(TARGET_DISPLAY_ID)!!,
+ TestDisplay.DISPLAY_1.bounds.centerX(), TestDisplay.DISPLAY_1.bounds.centerY())
+ pipDisplayTransferHandler.showDragMirrorOnConnectedDisplays(
+ ORIGIN_DISPLAY_ID, globalDpBounds
+ )
+
+ verify(mockRootTaskDisplayAreaOrganizer).reparentToDisplayArea(
+ eq(TARGET_DISPLAY_ID),
+ any(),
+ any()
+ )
+ assertThat(pipDisplayTransferHandler.mOnDragMirrorPerDisplayId.size).isEqualTo(1)
+ assertThat(pipDisplayTransferHandler.mOnDragMirrorPerDisplayId.containsKey(TARGET_DISPLAY_ID)).isTrue()
+ verify(mockSurfaceTransactionHelper).setPipTransformations(
+ any(),
+ any(),
+ any(),
+ any(),
+ any()
+ )
+ verify(mockTransaction, times(1)).show(any())
+ verify(mockTransaction, times(1)).apply()
+ }
+
+ @Test
+ fun showDragMirrorOnConnectedDisplays_inBetweenThreeDisplays_createsTwoMirrors() {
+ val globalDpBounds = MultiDisplayDragMoveBoundsCalculator.calculateGlobalDpBoundsForDrag(
+ displayLayouts.get(ORIGIN_DISPLAY_ID)!!, START_DRAG_COORDINATES,
+ PIP_BOUNDS, displayLayouts.get(TARGET_DISPLAY_ID)!!,
+ 1000f, -100f)
+ pipDisplayTransferHandler.showDragMirrorOnConnectedDisplays(
+ ORIGIN_DISPLAY_ID, globalDpBounds
+ )
+
+ verify(mockRootTaskDisplayAreaOrganizer).reparentToDisplayArea(
+ eq(SECONDARY_DISPLAY_ID),
+ any(),
+ any()
+ )
+ verify(mockRootTaskDisplayAreaOrganizer).reparentToDisplayArea(
+ eq(TARGET_DISPLAY_ID),
+ any(),
+ any()
+ )
+ assertThat(pipDisplayTransferHandler.mOnDragMirrorPerDisplayId.size).isEqualTo(2)
+ assertThat(
+ pipDisplayTransferHandler.mOnDragMirrorPerDisplayId.containsKey(
+ SECONDARY_DISPLAY_ID
+ )
+ ).isTrue()
+ assertThat(pipDisplayTransferHandler.mOnDragMirrorPerDisplayId.containsKey(TARGET_DISPLAY_ID)).isTrue()
+ verify(mockSurfaceTransactionHelper, times(2)).setPipTransformations(
+ any(),
+ any(),
+ any(),
+ any(),
+ any()
+ )
+ verify(mockTransaction, times(2)).show(any())
+ verify(mockTransaction, times(1)).apply()
+ }
+
+ @Test
+ fun removeMirrors_removesAllMirrorsAndAppliesTransactionOnce() {
+ pipDisplayTransferHandler.mOnDragMirrorPerDisplayId = ArrayMap()
+ pipDisplayTransferHandler.mOnDragMirrorPerDisplayId.apply {
+ put(0, mockLeash)
+ put(1, mockLeash)
+ put(2, mockLeash)
+ }
+
+ pipDisplayTransferHandler.removeMirrors()
+
+ verify(mockTransaction, times(3)).remove(any())
+ verify(mockTransaction, times(1)).apply()
+ assertThat(pipDisplayTransferHandler.mOnDragMirrorPerDisplayId.isEmpty()).isTrue()
+ }
+
companion object {
- private const val ORIGIN_DISPLAY_ID = 0
- private const val TARGET_DISPLAY_ID = 1
+ const val ORIGIN_DISPLAY_ID = 0
+ const val TARGET_DISPLAY_ID = 1
+ const val SECONDARY_DISPLAY_ID = 2
+ const val TEST_CORNER_RADIUS = 5
+ const val TEST_SHADOW_RADIUS = 5
+ val START_DRAG_COORDINATES = PointF(100f, 100f)
+ val PIP_BOUNDS = Rect(0, 0, 700, 700)
}
}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipSchedulerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipSchedulerTest.java
index 8b4242f..eaff5dc 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipSchedulerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipSchedulerTest.java
@@ -126,13 +126,16 @@
mMockRootTaskDisplayAreaOrganizer);
when(mMockDisplayLayout.densityDpi()).thenReturn(DEFAULT_DPI);
when(mDisplayController.getDisplayLayout(anyInt())).thenReturn(mMockDisplayLayout);
- mPipScheduler = new PipScheduler(mMockContext, mMockPipBoundsState, mMockMainExecutor,
+ mPipScheduler = new PipScheduler(mMockContext,
+ new PipSurfaceTransactionHelper(mMockContext), mMockPipBoundsState,
+ mMockMainExecutor,
mMockPipTransitionState, Optional.of(mMockSplitScreenController),
Optional.of(mMockDesktopPipTransitionController), mMockPipDesktopState,
mDisplayController);
mPipScheduler.setPipTransitionController(mMockPipTransitionController);
mPipScheduler.setSurfaceControlTransactionFactory(mMockFactory);
- mPipScheduler.setPipAlphaAnimatorSupplier((context, leash, startTx, finishTx, direction) ->
+ mPipScheduler.setPipAlphaAnimatorSupplier(
+ (context, pipSurfaceTransactionHelper, leash, startTx, finishTx, direction) ->
mMockAlphaAnimator);
final PictureInPictureParams params = new PictureInPictureParams.Builder().build();
mPipScheduler.setPipParamsSupplier(() -> params);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipTaskListenerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipTaskListenerTest.java
index a760890..a59d249 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipTaskListenerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipTaskListenerTest.java
@@ -100,6 +100,7 @@
MockitoAnnotations.initMocks(this);
mRemoteActionListCaptor = ArgumentCaptor.forClass(List.class);
when(mMockPipTransitionState.getPinnedTaskLeash()).thenReturn(mMockLeash);
+ when(mMockContext.getResources()).thenReturn(mock(Resources.class));
}
@Test
@@ -339,7 +340,8 @@
PipTransitionState.SCHEDULED_BOUNDS_CHANGE,
extras);
mPipTaskListener.setPipResizeAnimatorSupplier(
- (context, leash, startTx, finishTx, baseBounds, startBounds, endBounds,
+ (context, pipSurfaceTransactionHelper, leash, startTx, finishTx, baseBounds,
+ startBounds, endBounds,
duration, delta) -> mMockPipResizeAnimator);
mPipTaskListener.onPipTransitionStateChanged(
PipTransitionState.SCHEDULED_BOUNDS_CHANGE,
@@ -365,7 +367,8 @@
PipTransitionState.SCHEDULED_BOUNDS_CHANGE,
extras);
mPipTaskListener.setPipResizeAnimatorSupplier(
- (context, leash, startTx, finishTx, baseBounds, startBounds, endBounds,
+ (context, pipSurfaceTransactionHelper, leash, startTx, finishTx, baseBounds,
+ startBounds, endBounds,
duration, delta) -> mMockPipResizeAnimator);
mPipTaskListener.onPipTransitionStateChanged(
PipTransitionState.SCHEDULED_BOUNDS_CHANGE,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipTouchHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipTouchHandlerTest.kt
new file mode 100644
index 0000000..920e277
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipTouchHandlerTest.kt
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.wm.shell.pip2.phone
+
+import android.graphics.PointF
+import android.graphics.Rect
+import android.graphics.RectF
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
+import android.view.SurfaceControl
+import androidx.test.filters.SmallTest
+import com.android.dx.mockito.inline.extended.ExtendedMockito
+import com.android.modules.utils.testing.ExtendedMockitoRule
+import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.common.DisplayController
+import com.android.wm.shell.common.DisplayLayout
+import com.android.wm.shell.common.FloatingContentCoordinator
+import com.android.wm.shell.common.MultiDisplayDragMoveBoundsCalculator
+import com.android.wm.shell.common.ShellExecutor
+import com.android.wm.shell.common.pip.PipBoundsAlgorithm
+import com.android.wm.shell.common.pip.PipBoundsState
+import com.android.wm.shell.common.pip.PipDesktopState
+import com.android.wm.shell.common.pip.PipDisplayLayoutState
+import com.android.wm.shell.common.pip.PipPerfHintController
+import com.android.wm.shell.common.pip.PipUiEventLogger
+import com.android.wm.shell.common.pip.SizeSpecSource
+import com.android.wm.shell.pip2.PipSurfaceTransactionHelper
+import com.android.wm.shell.sysui.ShellCommandHandler
+import com.android.wm.shell.sysui.ShellInit
+import java.util.Optional
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.kotlin.any
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+
+/**
+ * Unit test against [PipTouchHandler].
+ *
+ * To run: atest WMShellUnitTests:com.android.wm.shell.pip2.phone.PipTouchHandlerTest
+ */
+@SmallTest
+@RunWithLooper
+@RunWith(AndroidTestingRunner::class)
+class PipTouchHandlerTest : ShellTestCase() {
+ private val mockShellInit = mock<ShellInit>()
+ private val mockShellCommandHandler = mock<ShellCommandHandler>()
+ private val mockMenuPhoneController = mock<PhonePipMenuController>()
+ private val mockPipBoundsAlgorithm = mock<PipBoundsAlgorithm>()
+ private val mockPipBoundsState = mock<PipBoundsState>()
+ private val mockPipTransitionState = mock<PipTransitionState>()
+ private val mockPipScheduler = mock<PipScheduler>()
+ private val mockSizeSpecSource = mock<SizeSpecSource>()
+ private val mockPipDisplayLayoutState = mock<PipDisplayLayoutState>()
+ private val mockPipDesktopState = mock<PipDesktopState>()
+ private val mockDisplayController = mock<DisplayController>()
+ private val mockPipMotionHelper = mock<PipMotionHelper>()
+ private val mockFloatingContentCoordinator = mock<FloatingContentCoordinator>()
+ private val mockPipUiEventLogger = mock<PipUiEventLogger>()
+ private val shellExecutor = mock<ShellExecutor>()
+ private val mockPipPerfHintController = mock<PipPerfHintController>()
+ private val mockPipDisplayTransferHandler = mock<PipDisplayTransferHandler>()
+ private val pipTransitionState = mock<PipTransitionState>()
+ private val pipTouchState = mock<PipTouchState>()
+ private val mockMotionBoundsState = mock<PipBoundsState.MotionBoundsState>()
+ private val mockLeash = mock<SurfaceControl>()
+ private val mockDisplayLayout = mock<DisplayLayout>()
+ private val mockTouchPosition = PointF()
+ private val mockPipSurfaceTransactionHelper = mock<PipSurfaceTransactionHelper>()
+
+ private lateinit var pipTouchHandler: PipTouchHandler
+ private lateinit var pipTouchGesture: PipTouchGesture
+
+ @JvmField
+ @Rule
+ val extendedMockitoRule =
+ ExtendedMockitoRule.Builder(this)
+ .mockStatic(MultiDisplayDragMoveBoundsCalculator::class.java)
+ .build()!!
+
+ @Before
+ fun setUp() {
+ pipTouchHandler = PipTouchHandler(
+ mContext, mockPipSurfaceTransactionHelper, mockShellInit, mockShellCommandHandler,
+ mockMenuPhoneController, mockPipBoundsAlgorithm, mockPipBoundsState,
+ mockPipTransitionState, mockPipScheduler, mockSizeSpecSource, mockPipDisplayLayoutState,
+ mockPipDesktopState, mockDisplayController, mockPipMotionHelper,
+ mockFloatingContentCoordinator, mockPipUiEventLogger, shellExecutor,
+ Optional.of(mockPipPerfHintController), mockPipDisplayTransferHandler
+ )
+ pipTouchGesture = pipTouchHandler.touchGesture
+ pipTouchHandler.setPipTouchState(pipTouchState)
+
+ whenever(pipTouchState.downTouchPosition).thenReturn(mockTouchPosition)
+ whenever(pipTouchState.velocity).thenReturn(mockTouchPosition)
+ whenever(pipTouchState.lastTouchPosition).thenReturn(mockTouchPosition)
+ whenever(pipTouchState.lastTouchDisplayId).thenReturn(ORIGIN_DISPLAY_ID)
+ whenever(pipTouchState.lastTouchDelta).thenReturn(mockTouchPosition)
+ whenever(pipTransitionState.pinnedTaskLeash).thenReturn(mockLeash)
+ whenever(mockPipBoundsState.movementBounds).thenReturn(PIP_BOUNDS)
+ whenever(mockPipBoundsState.motionBoundsState).thenReturn(mockMotionBoundsState)
+ whenever(pipTouchHandler.possiblyMotionBounds).thenReturn(PIP_BOUNDS)
+ whenever(mockDisplayController.getDisplayLayout(anyInt()))
+ .thenReturn(mockDisplayLayout)
+ whenever(
+ MultiDisplayDragMoveBoundsCalculator.convertGlobalDpToLocalPxForRect(any(), any())
+ ).thenReturn(PIP_BOUNDS)
+ whenever(
+ MultiDisplayDragMoveBoundsCalculator.calculateGlobalDpBoundsForDrag(
+ any(), any(), any(), any(), any(), any()
+ )
+ ).thenReturn(GLOBAL_BOUNDS)
+ }
+
+ @Test
+ fun pipTouchGesture_crossDisplayDragFlagEnabled_onMove_showsMirrors() {
+ whenever(mockPipDesktopState.isDraggingPipAcrossDisplaysEnabled()).thenReturn(true)
+ whenever(pipTouchState.isUserInteracting).thenReturn(true)
+ whenever(pipTouchState.isDragging).thenReturn(true)
+ // Initialize variables that are set on down
+ pipTouchGesture.onDown(pipTouchState)
+
+ pipTouchGesture.onMove(pipTouchState)
+
+ ExtendedMockito.verify {
+ MultiDisplayDragMoveBoundsCalculator.calculateGlobalDpBoundsForDrag(
+ any(), any(), any(), any(), any(), any()
+ )
+ }
+ ExtendedMockito.verify {
+ MultiDisplayDragMoveBoundsCalculator.convertGlobalDpToLocalPxForRect(
+ eq(GLOBAL_BOUNDS),
+ eq(mockDisplayLayout)
+ )
+ }
+ verify(mockPipDisplayTransferHandler).showDragMirrorOnConnectedDisplays(
+ eq(ORIGIN_DISPLAY_ID), eq(GLOBAL_BOUNDS))
+ verify(mockPipMotionHelper).movePip(eq(PIP_BOUNDS), eq(true))
+ }
+
+ @Test
+ fun pipTouchGesture_crossDisplayDragFlagEnabled_onUpOnADifferentDisplay_schedulesMovePip() {
+ whenever(mockPipDesktopState.isDraggingPipAcrossDisplaysEnabled()).thenReturn(true)
+ whenever(pipTouchState.isUserInteracting).thenReturn(true)
+ pipTouchGesture.onDown(pipTouchState)
+
+ pipTouchHandler.mEnableStash = false
+ whenever(pipTouchState.isDragging).thenReturn(true)
+ whenever(pipTouchState.lastTouchDisplayId).thenReturn(TARGET_DISPLAY_ID)
+ pipTouchGesture.onUp(pipTouchState)
+
+ verify(mockPipDisplayTransferHandler).scheduleMovePipToDisplay(
+ eq(ORIGIN_DISPLAY_ID),
+ eq(TARGET_DISPLAY_ID)
+ )
+ }
+
+ @Test
+ fun pipTouchGesture_crossDisplayDragFlagEnabled_onUp_removesMirrors() {
+ whenever(mockPipDesktopState.isDraggingPipAcrossDisplaysEnabled()).thenReturn(true)
+
+ pipTouchGesture.onUp(pipTouchState)
+
+ verify(mockPipDisplayTransferHandler).removeMirrors()
+ }
+
+ private companion object {
+ const val ORIGIN_DISPLAY_ID = 0
+ const val TARGET_DISPLAY_ID = 1
+ val PIP_BOUNDS = Rect(0, 0, 700, 700)
+ val GLOBAL_BOUNDS = RectF(0f, 0f, 400f, 400f)
+ }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/transition/PipExpandHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/transition/PipExpandHandlerTest.java
index e506e13..2da4156 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/transition/PipExpandHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/transition/PipExpandHandlerTest.java
@@ -36,6 +36,7 @@
import android.app.WindowConfiguration;
import android.content.Context;
import android.content.Intent;
+import android.content.res.Resources;
import android.graphics.Rect;
import android.os.IBinder;
import android.testing.AndroidTestingRunner;
@@ -55,6 +56,7 @@
import com.android.wm.shell.common.pip.PipBoundsState;
import com.android.wm.shell.common.pip.PipDesktopState;
import com.android.wm.shell.common.pip.PipDisplayLayoutState;
+import com.android.wm.shell.pip2.PipSurfaceTransactionHelper;
import com.android.wm.shell.pip2.animation.PipExpandAnimator;
import com.android.wm.shell.pip2.phone.PipInteractionHandler;
import com.android.wm.shell.pip2.phone.PipTransitionState;
@@ -115,12 +117,16 @@
when(mMockPipBoundsState.getBounds()).thenReturn(PIP_BOUNDS);
when(mMockPipBoundsAlgorithm.getSnapFraction(eq(PIP_BOUNDS))).thenReturn(SNAP_FRACTION);
when(mMockPipDisplayLayoutState.getRotation()).thenReturn(DISPLAY_ROTATION);
+ when(mMockContext.getResources()).thenReturn(mock(Resources.class));
- mPipExpandHandler = new PipExpandHandler(mMockContext, mMockPipBoundsState,
+ mPipExpandHandler = new PipExpandHandler(mMockContext,
+ new PipSurfaceTransactionHelper(mMockContext),
+ mMockPipBoundsState,
mMockPipBoundsAlgorithm, mMockPipTransitionState, mMockPipDisplayLayoutState,
mMockPipDesktopState, mMockPipInteractionHandler,
Optional.of(mMockSplitScreenController));
- mPipExpandHandler.setPipExpandAnimatorSupplier((context, leash, startTransaction,
+ mPipExpandHandler.setPipExpandAnimatorSupplier(
+ (context, pipSurfaceTransactionHelper, leash, startTransaction,
finishTransaction, baseBounds, startBounds, endBounds,
sourceRectHint, rotation, isPipInDesktopMode) -> mMockPipExpandAnimator);
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
index 2d3e6ab..08b4707 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
@@ -22,7 +22,6 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
import static com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_PERSISTENCE;
import static com.android.wm.shell.shared.GroupedTaskInfo.TYPE_DESK;
import static com.android.wm.shell.shared.GroupedTaskInfo.TYPE_FULLSCREEN;
@@ -69,8 +68,6 @@
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
-import com.android.dx.mockito.inline.extended.ExtendedMockito;
-import com.android.dx.mockito.inline.extended.StaticMockitoSession;
import com.android.window.flags.Flags;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.ShellTestCase;
@@ -81,7 +78,7 @@
import com.android.wm.shell.desktopmode.DesktopUserRepositories;
import com.android.wm.shell.desktopmode.DesktopWallpaperActivity;
import com.android.wm.shell.shared.GroupedTaskInfo;
-import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
+import com.android.wm.shell.shared.desktopmode.FakeDesktopState;
import com.android.wm.shell.shared.split.SplitBounds;
import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellController;
@@ -92,7 +89,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
-import org.mockito.quality.Strictness;
+import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
import java.util.Arrays;
@@ -130,6 +127,7 @@
private DesktopRepository mDesktopRepository;
@Mock
private UserManager mUserManager;
+ private FakeDesktopState mDesktopState;
private ShellTaskOrganizer mShellTaskOrganizer;
private RecentTasksController mRecentTasksController;
@@ -137,14 +135,14 @@
private ShellInit mShellInit;
private ShellController mShellController;
private TestShellExecutor mMainExecutor;
- private static StaticMockitoSession sMockitoSession;
+ private AutoCloseable mMocksInit = null;
@Before
public void setUp() {
- sMockitoSession = mockitoSession().initMocks(this).strictness(Strictness.LENIENT)
- .mockStatic(DesktopModeStatus.class).startMocking();
- ExtendedMockito.doReturn(true)
- .when(() -> DesktopModeStatus.canEnterDesktopMode(any()));
+ mDesktopState = new FakeDesktopState();
+ mDesktopState.setCanEnterDesktopMode(true);
+
+ mMocksInit = MockitoAnnotations.openMocks(this);
mMainExecutor = new TestShellExecutor();
when(mDesktopUserRepositories.getCurrent()).thenReturn(mDesktopRepository);
@@ -158,7 +156,7 @@
mRecentTasksControllerReal = new RecentTasksController(mContext, mShellInit,
mShellController, mShellCommandHandler, mTaskStackListener, mActivityTaskManager,
Optional.of(mDesktopUserRepositories), mTaskStackTransitionObserver,
- mMainExecutor);
+ mMainExecutor, mDesktopState);
mRecentTasksController = spy(mRecentTasksControllerReal);
mShellTaskOrganizer = new ShellTaskOrganizer(mShellInit, mShellCommandHandler,
null /* sizeCompatUI */, Optional.empty(), Optional.of(mRecentTasksController),
@@ -167,8 +165,11 @@
}
@After
- public void tearDown() {
- sMockitoSession.finishMocking();
+ public void tearDown() throws Exception {
+ if (mMocksInit != null) {
+ mMocksInit.close();
+ mMocksInit = null;
+ }
}
@Test
@@ -409,8 +410,7 @@
@Test
public void testGetRecentTasks_hasActiveDesktopTasks_proto2Disabled_doNotGroupFreeformTasks() {
- ExtendedMockito.doReturn(false)
- .when(() -> DesktopModeStatus.canEnterDesktopMode(any()));
+ mDesktopState.setCanEnterDesktopMode(false);
RecentTaskInfo t1 = makeTaskInfo(1);
RecentTaskInfo t2 = makeTaskInfo(2);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentsTransitionHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentsTransitionHandlerTest.java
index cc4ac72..6968346 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentsTransitionHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentsTransitionHandlerTest.java
@@ -23,7 +23,6 @@
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
import static com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_RECENTS_TRANSITIONS_CORNERS_BUGFIX;
import static com.android.wm.shell.Flags.FLAG_ENABLE_RECENTS_BOOKEND_TRANSITION;
import static com.android.wm.shell.recents.RecentsTransitionStateListener.TRANSITION_STATE_ANIMATING;
@@ -65,8 +64,6 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
-import com.android.dx.mockito.inline.extended.ExtendedMockito;
-import com.android.dx.mockito.inline.extended.StaticMockitoSession;
import com.android.internal.os.IResultReceiver;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.ShellTestCase;
@@ -77,7 +74,7 @@
import com.android.wm.shell.desktopmode.DesktopRepository;
import com.android.wm.shell.desktopmode.DesktopUserRepositories;
import com.android.wm.shell.shared.R;
-import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
+import com.android.wm.shell.shared.desktopmode.FakeDesktopState;
import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellController;
import com.android.wm.shell.sysui.ShellInit;
@@ -91,7 +88,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
-import org.mockito.quality.Strictness;
+import org.mockito.MockitoAnnotations;
import java.util.Optional;
@@ -136,14 +133,14 @@
private ShellInit mShellInit;
private ShellController mShellController;
private TestShellExecutor mMainExecutor;
- private static StaticMockitoSession sMockitoSession;
+ private AutoCloseable mMocksInit = null;
@Before
public void setUp() {
- sMockitoSession = mockitoSession().initMocks(this).strictness(Strictness.LENIENT)
- .mockStatic(DesktopModeStatus.class).startMocking();
- ExtendedMockito.doReturn(true)
- .when(() -> DesktopModeStatus.canEnterDesktopMode(any()));
+ var desktopState = new FakeDesktopState();
+ desktopState.setCanEnterDesktopMode(true);
+
+ mMocksInit = MockitoAnnotations.openMocks(this);
when(mDesktopUserRepositories.getCurrent()).thenReturn(mDesktopRepository);
mMainExecutor = new TestShellExecutor();
@@ -160,7 +157,7 @@
mRecentTasksControllerReal = new RecentTasksController(mContext, mShellInit,
mShellController, mShellCommandHandler, mTaskStackListener, mActivityTaskManager,
Optional.of(mDesktopUserRepositories), mTaskStackTransitionObserver,
- mMainExecutor);
+ mMainExecutor, desktopState);
mRecentTasksController = spy(mRecentTasksControllerReal);
mShellTaskOrganizer = new ShellTaskOrganizer(mShellInit, mShellCommandHandler,
null /* sizeCompatUI */, Optional.empty(), Optional.of(mRecentTasksController),
@@ -178,8 +175,11 @@
}
@After
- public void tearDown() {
- sMockitoSession.finishMocking();
+ public void tearDown() throws Exception {
+ if (mMocksInit != null) {
+ mMocksInit.close();
+ mMocksInit = null;
+ }
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatusTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatusTest.kt
index edf91fe..fd8321d 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatusTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatusTest.kt
@@ -16,7 +16,6 @@
package com.android.wm.shell.shared.desktopmode
-import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
import android.content.Context
import android.content.res.Resources
import android.platform.test.annotations.DisableFlags
@@ -28,7 +27,6 @@
import com.android.internal.R
import com.android.window.flags.Flags
import com.android.wm.shell.ShellTestCase
-import com.android.wm.shell.util.createTaskInfo
import com.google.common.truth.Truth.assertThat
import org.junit.After
import org.junit.Before
@@ -154,70 +152,6 @@
assertThat(DesktopModeStatus.canEnterDesktopMode(mockContext)).isTrue()
}
- @EnableFlags(
- Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
- Flags.FLAG_ENABLE_OPAQUE_BACKGROUND_FOR_TRANSPARENT_WINDOWS,
- )
- @Test
- fun shouldSetBackground_BTWFlagEnabled_freeformTask_returnsTrue() {
- val freeFormTaskInfo = createTaskInfo(deviceWindowingMode = WINDOWING_MODE_FREEFORM)
- assertThat(DesktopModeStatus.shouldSetBackground(freeFormTaskInfo)).isTrue()
- }
-
- @EnableFlags(
- Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
- Flags.FLAG_ENABLE_OPAQUE_BACKGROUND_FOR_TRANSPARENT_WINDOWS,
- )
- @Test
- fun shouldSetBackground_BTWFlagEnabled_notFreeformTask_returnsFalse() {
- val notFreeFormTaskInfo = createTaskInfo()
- assertThat(DesktopModeStatus.shouldSetBackground(notFreeFormTaskInfo)).isFalse()
- }
-
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
- @DisableFlags(Flags.FLAG_ENABLE_OPAQUE_BACKGROUND_FOR_TRANSPARENT_WINDOWS)
- @Test
- fun shouldSetBackground_BTWFlagDisabled_freeformTaskAndFluid_returnsTrue() {
- val freeFormTaskInfo = createTaskInfo(deviceWindowingMode = WINDOWING_MODE_FREEFORM)
-
- setIsVeiledResizeEnabled(false)
- assertThat(DesktopModeStatus.shouldSetBackground(freeFormTaskInfo)).isTrue()
- }
-
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
- @DisableFlags(Flags.FLAG_ENABLE_OPAQUE_BACKGROUND_FOR_TRANSPARENT_WINDOWS)
- @Test
- fun shouldSetBackground_BTWFlagDisabled_freeformTaskAndVeiled_returnsFalse() {
- val freeFormTaskInfo = createTaskInfo(deviceWindowingMode = WINDOWING_MODE_FREEFORM)
-
- setIsVeiledResizeEnabled(true)
- assertThat(DesktopModeStatus.shouldSetBackground(freeFormTaskInfo)).isFalse()
- }
-
- @EnableFlags(
- Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
- Flags.FLAG_ENABLE_OPAQUE_BACKGROUND_FOR_TRANSPARENT_WINDOWS,
- )
- @Test
- fun shouldSetBackground_BTWFlagEnabled_freeformTaskAndFluid_returnsTrue() {
- val freeFormTaskInfo = createTaskInfo(deviceWindowingMode = WINDOWING_MODE_FREEFORM)
-
- setIsVeiledResizeEnabled(false)
- assertThat(DesktopModeStatus.shouldSetBackground(freeFormTaskInfo)).isTrue()
- }
-
- @EnableFlags(
- Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
- Flags.FLAG_ENABLE_OPAQUE_BACKGROUND_FOR_TRANSPARENT_WINDOWS,
- )
- @Test
- fun shouldSetBackground_BTWFlagEnabled_windowModesTask_freeformTaskAndVeiled_returnsTrue() {
- val freeFormTaskInfo = createTaskInfo(deviceWindowingMode = WINDOWING_MODE_FREEFORM)
-
- setIsVeiledResizeEnabled(true)
- assertThat(DesktopModeStatus.shouldSetBackground(freeFormTaskInfo)).isTrue()
- }
-
@Test
fun isDeviceEligibleForDesktopMode_configDEModeOnAndIntDispHostsDesktop_returnsTrue() {
doReturn(true).whenever(mockResources).getBoolean(eq(R.bool.config_isDesktopModeSupported))
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/FakeDesktopState.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/FakeDesktopState.kt
index 0b3f801..2cf4826 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/FakeDesktopState.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/FakeDesktopState.kt
@@ -59,4 +59,6 @@
override var overridesShowAppHandle: Boolean = false
override var isFreeformEnabled: Boolean = false
+
+ override var shouldShowHomeBehindDesktop: Boolean = false
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
index ff000ca..b9f3cb5 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
@@ -46,6 +46,7 @@
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
+import android.app.IActivityTaskManager;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Intent;
@@ -125,6 +126,7 @@
@Mock SplitState mSplitState;
@Mock UserManager mUserManager;
@Mock RootDisplayAreaOrganizer mRootDisplayAreaOrganizer;
+ @Mock private IActivityTaskManager activityTaskManager;
@Captor ArgumentCaptor<Intent> mIntentCaptor;
private ShellController mShellController;
@@ -145,7 +147,7 @@
mIconProvider, Optional.of(mRecentTasks), mLaunchAdjacentController,
Optional.of(mWindowDecorViewModel), Optional.of(mDesktopTasksController),
mStageCoordinator, mMultiInstanceHelper, mSplitState, mMainExecutor, mMainHandler,
- mRootDisplayAreaOrganizer, mDesktopState));
+ mRootDisplayAreaOrganizer, mDesktopState, activityTaskManager));
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java
index 2a7fec1..e699c13e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java
@@ -23,6 +23,8 @@
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
+import android.app.ActivityManager;
+import android.app.IActivityTaskManager;
import android.content.Context;
import android.graphics.Rect;
import android.hardware.display.DisplayManager;
@@ -97,13 +99,14 @@
Optional<DesktopTasksController> desktopTasksController,
RootTaskDisplayAreaOrganizer rootTDAOrganizer,
RootDisplayAreaOrganizer rootDisplayAreaOrganizer,
- DesktopState desktopState) {
+ DesktopState desktopState,
+ IActivityTaskManager activityTaskManager) {
super(context, displayId, syncQueue, taskOrganizer, mainStage,
sideStage, displayController, imeController, insetsController, splitLayout,
transitions, transactionPool, mainExecutor, mainHandler, recentTasks,
launchAdjacentController, windowDecorViewModel, splitState,
desktopTasksController, rootTDAOrganizer, rootDisplayAreaOrganizer,
- desktopState);
+ desktopState, activityTaskManager);
// Prepare root task for testing.
mRootLeash = new SurfaceControl.Builder().setName("test").build();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
index e0640f1..ceb192e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
@@ -27,7 +27,9 @@
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER;
+import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_10_90;
import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_50_50;
+import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_90_10;
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_MAIN;
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_SIDE;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW;
@@ -52,9 +54,11 @@
import android.annotation.NonNull;
import android.app.ActivityManager;
+import android.app.IActivityTaskManager;
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
+import android.platform.test.annotations.EnableFlags;
import android.view.SurfaceControl;
import android.window.DisplayAreaInfo;
import android.window.IRemoteTransition;
@@ -88,6 +92,7 @@
import com.android.wm.shell.common.split.SplitState;
import com.android.wm.shell.shared.TransactionPool;
import com.android.wm.shell.shared.desktopmode.FakeDesktopState;
+import com.android.wm.shell.shared.split.SplitScreenConstants;
import com.android.wm.shell.transition.DefaultMixedHandler;
import com.android.wm.shell.transition.TestRemoteTransition;
import com.android.wm.shell.transition.TransitionInfoBuilder;
@@ -107,6 +112,9 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
public class SplitTransitionTests extends ShellTestCase {
+ private static final int MAIN_TASK_ID = 3;
+ private static final int SIDE_TASK_ID = 11;
+
@Mock private ShellTaskOrganizer mTaskOrganizer;
@Mock private SyncTransactionQueue mSyncQueue;
@Mock private RootTaskDisplayAreaOrganizer mRootTDAOrganizer;
@@ -125,6 +133,7 @@
@Mock private DefaultMixedHandler mMixedHandler;
@Mock private SplitScreen.SplitInvocationListener mInvocationListener;
private FakeDesktopState mDesktopState;
+ @Mock private IActivityTaskManager mActivityTaskManager;
private final TestShellExecutor mTestShellExecutor = new TestShellExecutor();
private SplitLayout mSplitLayout;
private StageTaskListener mMainStage;
@@ -162,7 +171,7 @@
mDisplayImeController, mDisplayInsetsController, mSplitLayout, mTransitions,
mTransactionPool, mMainExecutor, mMainHandler, Optional.empty(),
mLaunchAdjacentController, Optional.empty(), mSplitState, Optional.empty(),
- mRootTDAOrganizer, mRootDisplayAreaOrganizer, mDesktopState);
+ mRootTDAOrganizer, mRootDisplayAreaOrganizer, mDesktopState, mActivityTaskManager);
when(mRootTDAOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)).thenReturn(mDisplayAreaInfo);
mStageCoordinator.setMixedHandler(mMixedHandler);
@@ -171,8 +180,14 @@
.when(mTransitions).startTransition(anyInt(), any(), any());
mMainChild = new TestRunningTaskInfoBuilder()
+ .setTaskId(MAIN_TASK_ID)
+ .setVisible(true)
+ .setVisibleRequested(true)
.setParentTaskId(mMainStage.mRootTaskInfo.taskId).build();
mSideChild = new TestRunningTaskInfoBuilder()
+ .setTaskId(SIDE_TASK_ID)
+ .setVisible(true)
+ .setVisibleRequested(true)
.setParentTaskId(mSideStage.mRootTaskInfo.taskId).build();
doReturn(mock(SplitDecorManager.class)).when(mMainStage).getSplitDecorManager();
doReturn(mock(SplitDecorManager.class)).when(mSideStage).getSplitDecorManager();
@@ -521,6 +536,35 @@
assertFalse(mStageCoordinator.isSplitScreenVisible());
}
+ @Test
+ public void testRequestingFocusForDefaultLaunch() throws RemoteException {
+ enterSplit();
+ verify(mActivityTaskManager, times(0)).setFocusedTask(anyInt());
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_FLEXIBLE_TWO_APP_SPLIT)
+ public void testRequestingFocusFor9010Launch() throws RemoteException {
+ enterSplit(SNAP_TO_2_90_10);
+ mSplitScreenTransitions.mPendingEnter.mFinishedCallback.onFinished(
+ new WindowContainerTransaction(), new SurfaceControl.Transaction());
+ // We're assuming main stage is topLeft position, so we use that stage's taskId for focus
+ verify(mActivityTaskManager, times(1))
+ .setFocusedTask(mMainChild.getTaskId());
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_FLEXIBLE_TWO_APP_SPLIT)
+ public void testRequestingFocusFor1090Launch() throws RemoteException {
+ enterSplit(SNAP_TO_2_10_90);
+ mSplitScreenTransitions.mPendingEnter.mFinishedCallback.onFinished(
+ new WindowContainerTransaction(), new SurfaceControl.Transaction());
+ // We're assuming side stage is bottomRight position, so we use that stage's taskId for
+ // focus
+ verify(mActivityTaskManager, times(1))
+ .setFocusedTask(mSideChild.getTaskId());
+ }
+
private TransitionInfo createEnterPairInfo() {
return new TransitionInfoBuilder(TRANSIT_OPEN, 0)
.addChange(TRANSIT_OPEN, mMainChild)
@@ -529,11 +573,15 @@
}
private void enterSplit() {
+ enterSplit(SNAP_TO_2_50_50);
+ }
+
+ private void enterSplit(@SplitScreenConstants.PersistentSnapPosition int snapPosition) {
TransitionInfo enterInfo = createEnterPairInfo();
IBinder enterTransit = mSplitScreenTransitions.startEnterTransition(
TRANSIT_OPEN, new WindowContainerTransaction(),
new RemoteTransition(new TestRemoteTransition(), "Test"),
- mStageCoordinator, TRANSIT_SPLIT_SCREEN_PAIR_OPEN, false, SNAP_TO_2_50_50);
+ mStageCoordinator, TRANSIT_SPLIT_SCREEN_PAIR_OPEN, false, snapPosition);
mMainStage.onTaskAppeared(mMainChild, createMockSurface());
mSideStage.onTaskAppeared(mSideChild, createMockSurface());
mStageCoordinator.startAnimation(enterTransit, enterInfo,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
index c9a1d6e..4bd44ea 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
@@ -58,6 +58,7 @@
import android.app.ActivityManager;
import android.app.ActivityOptions;
+import android.app.IActivityTaskManager;
import android.app.PendingIntent;
import android.content.res.Configuration;
import android.graphics.Rect;
@@ -157,6 +158,7 @@
@Mock
private RootDisplayAreaOrganizer mRootDisplayAreaOrganizer;
private FakeDesktopState mDesktopState;
+ private IActivityTaskManager mIActivityTaskManager;
private final Rect mBounds1 = new Rect(10, 20, 30, 40);
private final Rect mBounds2 = new Rect(5, 10, 15, 20);
@@ -198,7 +200,7 @@
mDisplayInsetsController, mSplitLayout, transitions, mTransactionPool,
mMainExecutor, mMainHandler, Optional.empty(), mLaunchAdjacentController,
Optional.empty(), mSplitState, Optional.empty(), mRootTDAOrganizer,
- mRootDisplayAreaOrganizer, mDesktopState));
+ mRootDisplayAreaOrganizer, mDesktopState, mIActivityTaskManager));
mSplitScreenTransitions = spy(mStageCoordinator.getSplitTransitions());
mSplitScreenListener = mock(SplitScreenListener.class);
mStageCoordinator.setSplitTransitions(mSplitScreenTransitions);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java
index 4b01d84..03f2351 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java
@@ -234,12 +234,12 @@
}) {
when(TaskSnapshotWindow.create(eq(windowInfo), eq(mBinder), eq(snapshot), any(),
any())).thenReturn(mockSnapshotWindow);
- // Simulate a task snapshot window created with IME snapshot shown.
+ // Simulate a task snapshot window created with hasImeSurface.
mStartingSurfaceDrawer.makeTaskSnapshotWindow(windowInfo, snapshot);
waitHandlerIdle(mTestHandler);
- // Verify the task snapshot with IME snapshot will be removed when received the real IME
- // drawn callback.
+ // Verify the task snapshot with hasImeSurface will be removed when receiving the
+ // callback that the real IME was drawn.
// makeTaskSnapshotWindow shall call removeWindowSynced before there add a new
// StartingWindowRecord for the task.
mStartingSurfaceDrawer.onImeDrawnOnTask(1);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTransitionStartAnimationTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTransitionStartAnimationTest.java
index 215b8e4..4c183dd 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTransitionStartAnimationTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTransitionStartAnimationTest.java
@@ -22,7 +22,11 @@
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
+import static com.android.window.flags.Flags.enableHandlersDebuggingMode;
import static com.android.wm.shell.Flags.FLAG_TASK_VIEW_TRANSITIONS_REFACTOR;
+import static com.android.wm.shell.Flags.taskViewTransitionsRefactor;
+import static com.android.wm.shell.transition.TransitionDispatchState.CAPTURED_UNRELATED_CHANGE;
+import static com.android.wm.shell.transition.TransitionDispatchState.LOST_RELEVANT_CHANGE;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
@@ -44,6 +48,7 @@
import android.platform.test.annotations.UsesFlags;
import android.platform.test.flag.junit.FlagsParameterization;
import android.testing.TestableLooper;
+import android.util.Slog;
import android.view.SurfaceControl;
import android.window.TransitionInfo;
import android.window.WindowContainerToken;
@@ -55,6 +60,7 @@
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.shared.bubbles.BubbleAnythingFlagHelper;
+import com.android.wm.shell.transition.TransitionDispatchState;
import com.android.wm.shell.transition.TransitionInfoBuilder;
import com.android.wm.shell.transition.Transitions;
import com.android.wm.shell.util.StubTransaction;
@@ -73,10 +79,11 @@
/**
* Class to verify the behavior of startAnimation.
- * Verifies that changes behind FLAG_ENABLE_HANDLERS_DEBUGGING_MODE don't change the behavior
- * of startAnimation. The refactor tests' life span matches the flag's. Permanent tests are meant
- * to be added to TaskViewTransitionsTest.
- * Test failures that manifest only when the flag is on mean that the behavior diverged.
+ * 1. Verifies that startAnimation populates TransitionDispatchState correctly.
+ * 2. Verifies that changes behind FLAG_ENABLE_HANDLERS_DEBUGGING_MODE don't change the behavior
+ * of startAnimation. Refactor test's life span matches the flag's. Permanent tests are meant to
+ * be added to TaskViewTransitionsTest.
+ * Test failures that manifest only when the flag is on mean that the behavior diverged.
*/
@SmallTest
@RunWith(ParameterizedAndroidJunit4.class)
@@ -123,6 +130,8 @@
TaskViewTransitions.PendingTransition mPendingFront;
TaskViewTransitions.PendingTransition mPendingBack;
+ static final String TAG = "TVstartAnimTest";
+
public TaskViewTransitionStartAnimationTest(FlagsParameterization flags) {
mSetFlagsRule.setFlagsParameterization(flags);
}
@@ -147,6 +156,7 @@
mUnregisteredTaskInfo.token = mUnregisteredToken;
// Same id as the other to match pending info id
mUnregisteredTaskInfo.taskId = 314;
+ mUnregisteredTaskInfo.launchCookies.add(mock(IBinder.class));
mTaskInfo.taskDescription = mock(ActivityManager.TaskDescription.class);
mBounds = new Rect(0, 0, 100, 100);
@@ -209,6 +219,85 @@
}
/**
+ * Tests on TransitionDispatchState
+ */
+ @Test
+ public void taskView_dispatchStateFindsIncompatible_animationMode() {
+ assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS);
+ assumeTrue(enableHandlersDebuggingMode());
+ assumeTrue(taskViewTransitionsRefactor()); // To avoid running twice
+
+ TransitionInfo.Change showingTV = getTaskView(TRANSIT_TO_FRONT);
+ TransitionInfo.Change nonTV = getTask(TRANSIT_TO_BACK, false /* registered */);
+ TaskViewTransitions.PendingTransition pending =
+ setPendingTransaction(true /* visible*/, false /* opening */);
+ // Showing taskView + normal task.
+ // TaskView is accepted, but normal task is detected as error
+ final TransitionInfo info = new TransitionInfoBuilder(TRANSIT_TO_FRONT)
+ .addChange(showingTV)
+ .addChange(nonTV)
+ .build();
+
+ TransitionDispatchState dispatchState =
+ spy(new TransitionDispatchState(pending.mClaimed, info));
+
+ boolean handled = mTaskViewTransitions.startAnimation(pending.mClaimed, info,
+ dispatchState, mStartTransaction, mFinishTransaction, mFinishCallback);
+
+ Slog.v(TAG, "DispatchState:\n" + dispatchState.getDebugInfo());
+ // Has animated the taskView
+ assertWithMessage("Handler should play the transition")
+ .that(handled).isTrue();
+ ArgumentCaptor<WindowContainerTransaction> wctCaptor =
+ ArgumentCaptor.forClass(WindowContainerTransaction.class);
+ verify(mFinishCallback).onTransitionFinished(wctCaptor.capture());
+ assertWithMessage("Expected wct to be created and sent to callback")
+ .that(wctCaptor.getValue()).isNotNull();
+ verify(pending.mTaskView).notifyAppeared(eq(false));
+
+ // Non task-view spotted as intruder
+ verify(dispatchState).addError(eq(mTaskViewTransitions), eq(nonTV),
+ eq(CAPTURED_UNRELATED_CHANGE));
+ assertThat(dispatchState.hasErrors(mTaskViewTransitions)).isTrue();
+ }
+
+ @Test
+ public void taskView_dispatchStateFindsCompatible_dataCollectionMode() {
+ assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS);
+ assumeTrue(enableHandlersDebuggingMode());
+ assumeTrue(taskViewTransitionsRefactor()); // To avoid running twice
+
+ TransitionInfo.Change showingTV = getTaskView(TRANSIT_TO_FRONT);
+ TransitionInfo.Change nonTV = getTask(TRANSIT_TO_BACK, false /* registered */);
+ TaskViewTransitions.PendingTransition pending =
+ setPendingTransaction(true /* visible*/, false /* opening */);
+ // Showing taskView + normal task.
+ // TaskView is detected as change that could have played
+ final TransitionInfo info = new TransitionInfoBuilder(TRANSIT_TO_FRONT)
+ .addChange(showingTV)
+ .addChange(nonTV)
+ .build();
+
+ TransitionDispatchState dispatchState =
+ spy(new TransitionDispatchState(pending.mClaimed, info));
+
+ boolean handled = mTaskViewTransitions.startAnimation(pending.mClaimed, null,
+ dispatchState, mStartTransaction, mFinishTransaction, mFinishCallback);
+
+ Slog.v(TAG, "DispatchState:\n" + dispatchState.getDebugInfo());
+ // Has not animated the taskView
+ assertWithMessage("Handler should not play the transition")
+ .that(handled).isFalse();
+ verify(mFinishCallback, never()).onTransitionFinished(any());
+ verify(pending.mTaskView, never()).notifyAppeared(anyBoolean());
+
+ // Non task-view spotted as intruder
+ verify(dispatchState)
+ .addError(eq(mTaskViewTransitions), eq(showingTV), eq(LOST_RELEVANT_CHANGE));
+ assertThat(dispatchState.hasErrors(mTaskViewTransitions)).isTrue();
+ }
+
+ /**
* Refactor tests on taskViews
*/
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTransitionsTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTransitionsTest.java
index 3b6fd37..bf40a90 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTransitionsTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTransitionsTest.java
@@ -23,6 +23,7 @@
import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static com.android.window.flags.Flags.FLAG_EXCLUDE_TASK_FROM_RECENTS;
+import static com.android.wm.shell.Flags.FLAG_ENABLE_BUBBLE_ANYTHING;
import static com.android.wm.shell.Flags.FLAG_ENABLE_TASK_VIEW_CONTROLLER_CLEANUP;
import static com.google.common.truth.Truth.assertThat;
@@ -40,6 +41,7 @@
import android.app.ActivityManager;
import android.graphics.Rect;
+import android.os.Binder;
import android.os.IBinder;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.FlagsParameterization;
@@ -80,7 +82,8 @@
public static List<FlagsParameterization> getParams() {
return FlagsParameterization.allCombinationsOf(
FLAG_ENABLE_TASK_VIEW_CONTROLLER_CLEANUP,
- FLAG_EXCLUDE_TASK_FROM_RECENTS);
+ FLAG_EXCLUDE_TASK_FROM_RECENTS,
+ FLAG_ENABLE_BUBBLE_ANYTHING);
}
@Mock
@@ -110,6 +113,8 @@
doReturn(true).when(mTransitions).isRegistered();
}
+ when(mToken.asBinder()).thenReturn(new Binder());
+
mTaskInfo = new ActivityManager.RunningTaskInfo();
mTaskInfo.token = mToken;
mTaskInfo.taskId = 314;
@@ -169,9 +174,32 @@
assertThat(pending).isNotNull();
final WindowContainerTransaction wct = pending.mWct;
assertThat(wct).isNotNull();
- final WindowContainerTransaction.Change chg = wct.getChanges().get(mToken.asBinder());
- assertThat(chg).isNotNull();
- assertThat(chg.getForceExcludedFromRecents()).isFalse();
+
+ // Verify that the WCT has the task force exclude from recents.
+ final Map<IBinder, WindowContainerTransaction.Change> chgs = wct.getChanges();
+ final boolean hasForceExcludedFromRecents = chgs.entrySet().stream()
+ .filter((entry) -> entry.getKey().equals(mToken.asBinder()))
+ .anyMatch((entry) -> entry.getValue().getForceExcludedFromRecents());
+ assertThat(hasForceExcludedFromRecents).isFalse();
+ }
+
+ @Test
+ @EnableFlags(FLAG_ENABLE_BUBBLE_ANYTHING)
+ public void testMoveTaskViewToFullscreen_disallowFlagLaunchAdjacent() {
+ mTaskViewTransitions.moveTaskViewToFullscreen(mTaskViewTaskController);
+
+ final TaskViewTransitions.PendingTransition pending = mTaskViewTransitions.findPending(
+ mTaskViewTaskController, TRANSIT_CHANGE);
+ assertThat(pending).isNotNull();
+ final WindowContainerTransaction wct = pending.mWct;
+ assertThat(wct).isNotNull();
+
+ // Verify that the WCT has the disallow-launch-adjacent hierarchy op
+ final List<WindowContainerTransaction.HierarchyOp> ops = wct.getHierarchyOps();
+ final boolean hasLaunchAdjacentDisabled = ops.stream()
+ .filter((op) -> op.getContainer().equals(mToken.asBinder()))
+ .anyMatch((op) -> op.isLaunchAdjacentDisabled());
+ assertThat(hasLaunchAdjacentDisabled).isFalse();
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/DefaultTransitionHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/DefaultTransitionHandlerTest.java
index 2dab391..f9676fc 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/DefaultTransitionHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/DefaultTransitionHandlerTest.java
@@ -26,6 +26,8 @@
import static android.window.TransitionInfo.FLAG_TRANSLUCENT;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -39,6 +41,7 @@
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
+import android.util.Log;
import android.view.SurfaceControl;
import android.view.animation.AlphaAnimation;
import android.window.TransitionInfo;
@@ -90,6 +93,8 @@
private RootTaskDisplayAreaOrganizer mRootTaskDisplayAreaOrganizer;
private DefaultTransitionHandler mTransitionHandler;
+ static final String TAG = "DefaultHandlerTest";
+
@Before
public void setUp() {
mShellInit = new ShellInit(mMainExecutor);
@@ -269,6 +274,40 @@
}
@Test
+ public void startAnimation_neverFindsErrors_animationMode() {
+ final TransitionInfo info = mock(TransitionInfo.class);
+ final IBinder token = mock(Binder.class);
+
+ TransitionDispatchState dispatchState = new TransitionDispatchState(token, info);
+
+ boolean hasPlayed = mTransitionHandler
+ .startAnimation(token, info, dispatchState, MockTransactionPool.create(),
+ MockTransactionPool.create(),
+ mock(Transitions.TransitionFinishCallback.class));
+
+ Log.v(TAG, "dispatchState: \n" + dispatchState.getDebugInfo());
+ assertTrue(hasPlayed);
+ assertFalse(dispatchState.hasErrors(mTransitionHandler));
+ }
+
+ @Test
+ public void startAnimation_neverFindsErrors_dataCollectionMode() {
+ final TransitionInfo info = mock(TransitionInfo.class);
+ final IBinder token = mock(Binder.class);
+
+ TransitionDispatchState dispatchState = new TransitionDispatchState(token, info);
+
+ boolean hasPlayed = mTransitionHandler
+ .startAnimation(token, null, dispatchState, MockTransactionPool.create(),
+ MockTransactionPool.create(),
+ mock(Transitions.TransitionFinishCallback.class));
+
+ Log.v(TAG, "dispatchState: \n" + dispatchState.getDebugInfo());
+ assertFalse(hasPlayed);
+ assertFalse(dispatchState.hasErrors(mTransitionHandler));
+ }
+
+ @Test
public void startAnimation_freeform_minimizeAnimation_reparentsTask() {
final TransitionInfo.Change openChange = new ChangeBuilder(TRANSIT_OPEN)
.setTask(createTaskInfo(
@@ -316,4 +355,3 @@
return taskInfo;
}
}
-
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
index 88bafeb..7b5f6ce 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
@@ -63,6 +63,7 @@
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.InputMethod
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.MinimizeReason
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ResizeTrigger
+import com.android.wm.shell.desktopmode.DesktopTasksController
import com.android.wm.shell.desktopmode.DesktopTasksController.SnapPosition
import com.android.wm.shell.desktopmode.common.ToggleTaskSizeInteraction
import com.android.wm.shell.recents.RecentsTransitionStateListener
@@ -94,6 +95,8 @@
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
import org.mockito.quality.Strictness
+import org.mockito.kotlin.isNotNull
+import org.mockito.kotlin.isNull
/**
* Tests of [DesktopModeWindowDecorViewModel]
@@ -193,6 +196,28 @@
}
@Test
+ fun snapToHalfScreen_callsCorrectPersistenceFunction() {
+ val task = createTask(displayId = DEFAULT_DISPLAY, windowingMode = WINDOWING_MODE_FREEFORM)
+ desktopModeWindowDecorViewModel.snapToHalfScreen(
+ task,
+ INITIAL_BOUNDS,
+ DesktopTasksController.SnapPosition.LEFT,
+ )
+
+ verify(mockTilingWindowDecoration, times(1))
+ .snapToHalfScreen(any(), anyOrNull(), any(), any(), isNull())
+
+ desktopModeWindowDecorViewModel.snapPersistedTaskToHalfScreen(
+ task,
+ INITIAL_BOUNDS,
+ DesktopTasksController.SnapPosition.LEFT,
+ )
+
+ verify(mockTilingWindowDecoration, times(1))
+ .snapToHalfScreen(any(), anyOrNull(), any(), any(), isNotNull())
+ }
+
+ @Test
fun testBackEventHasRightDisplayId() {
val secondaryDisplay = createVirtualDisplay() ?: return
val secondaryDisplayId = secondaryDisplay.display.displayId
@@ -1118,6 +1143,24 @@
@Test
@EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
+ fun testImmersiveMenuOptionClick_exitsTiling() {
+ val onImmersiveClickCaptor = argumentCaptor<() -> Unit>()
+ val decor = createOpenTaskDecoration(
+ windowingMode = WINDOWING_MODE_FREEFORM,
+ onImmersiveOrRestoreListenerCaptor = onImmersiveClickCaptor,
+ requestingImmersive = true,
+ )
+ whenever(mockDesktopRepository.isTaskInFullImmersiveState(decor.mTaskInfo.taskId))
+ .thenReturn(false)
+
+ onImmersiveClickCaptor.firstValue()
+
+ verify(mockTilingWindowDecoration)
+ .removeTaskIfTiled(decor.mTaskInfo.displayId, decor.mTaskInfo.taskId)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
fun testImmersiveClick_closesMaximizeMenu() {
val onImmersiveClickCaptor = argumentCaptor<() -> Unit>()
val decor = createOpenTaskDecoration(
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt
index 4390cf1..7c85d9b 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt
@@ -355,7 +355,7 @@
mockDesktopModeWindowDecorFactory.create(
any(), any(), any(), any(), any(), any(), any(), eq(task), any(), any(), any(),
any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(),
- any(), any(), any(), any(), any(), any(), any())
+ any(), any(), any(), any(), any(), any(), any(), any())
).thenReturn(decoration)
decoration.mTaskInfo = task
whenever(decoration.user).thenReturn(mockUserHandle)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
index d85ca78..190e60d 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
@@ -25,6 +25,7 @@
import static android.view.WindowInsets.Type.captionBar;
import static android.view.WindowInsets.Type.statusBars;
import static android.view.WindowInsetsController.APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND;
+import static android.view.WindowManager.TRANSIT_CHANGE;
import static com.android.wm.shell.MockSurfaceControlHelper.createMockSurfaceControlTransaction;
import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
@@ -67,6 +68,7 @@
import android.graphics.Region;
import android.net.Uri;
import android.os.Handler;
+import android.os.Looper;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.testing.AndroidTestingRunner;
@@ -96,6 +98,7 @@
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.TestHandler;
import com.android.wm.shell.TestRunningTaskInfoBuilder;
import com.android.wm.shell.TestShellExecutor;
import com.android.wm.shell.apptoweb.AppToWebGenericLinksParser;
@@ -115,6 +118,7 @@
import com.android.wm.shell.shared.desktopmode.FakeDesktopConfig;
import com.android.wm.shell.shared.desktopmode.FakeDesktopState;
import com.android.wm.shell.splitscreen.SplitScreenController;
+import com.android.wm.shell.transition.Transitions;
import com.android.wm.shell.windowdecor.WindowDecoration.RelayoutParams;
import com.android.wm.shell.windowdecor.common.WindowDecorTaskResourceLoader;
import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHost;
@@ -257,6 +261,9 @@
private WindowDecorTaskResourceLoader mMockTaskResourceLoader;
@Mock
private DesktopModeCompatPolicy mDesktopModeCompatPolicy;
+ @Mock
+ private Transitions mTransitions;
+ private final TestHandler mTestHandler = new TestHandler(Looper.getMainLooper());
@Captor
private ArgumentCaptor<Function1<Boolean, Unit>> mOnMaxMenuHoverChangeListener;
@Captor
@@ -1226,6 +1233,39 @@
}
@Test
+ @EnableFlags({Flags.FLAG_ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION_ALWAYS,
+ Flags.FLAG_ENABLE_DESKTOP_WINDOWING_PIP})
+ public void relayout_shouldSetAppBounds_startsTransition() {
+ final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
+ final DesktopModeWindowDecoration spyWindowDecor =
+ spy(createWindowDecoration(taskInfo, new FakeMaximizeMenuFactory(), /* relayout= */
+ false, mTestHandler));
+ taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
+ when(mDesktopModeCompatPolicy.shouldExcludeCaptionFromAppBounds(taskInfo)).thenReturn(true);
+
+ spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */, mExclusionRegion);
+
+ verify(mTransitions).startTransition(eq(TRANSIT_CHANGE), any(), any());
+ }
+
+ @Test
+ @EnableFlags({Flags.FLAG_ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION_ALWAYS,
+ Flags.FLAG_ENABLE_DESKTOP_WINDOWING_PIP})
+ public void relayout_shouldNotSetAppBounds_doesNotStartTransition() {
+ final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
+ final DesktopModeWindowDecoration spyWindowDecor =
+ spy(createWindowDecoration(taskInfo, new FakeMaximizeMenuFactory(), /* relayout= */
+ false, mTestHandler));
+ taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
+ when(mDesktopModeCompatPolicy.shouldExcludeCaptionFromAppBounds(taskInfo)).thenReturn(
+ false);
+
+ spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */, mExclusionRegion);
+
+ verify(mTransitions, never()).startTransition(anyInt(), any(), any());
+ }
+
+ @Test
public void createMaximizeMenu_showsMenu() {
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(true /* visible */);
final MaximizeMenu menu = mock(MaximizeMenu.class);
@@ -1883,11 +1923,19 @@
ActivityManager.RunningTaskInfo taskInfo,
MaximizeMenuFactory maximizeMenuFactory,
boolean relayout) {
+ return createWindowDecoration(taskInfo, maximizeMenuFactory, relayout, mMockHandler);
+ }
+
+ private DesktopModeWindowDecoration createWindowDecoration(
+ ActivityManager.RunningTaskInfo taskInfo,
+ MaximizeMenuFactory maximizeMenuFactory,
+ boolean relayout,
+ Handler handler) {
final DesktopModeWindowDecoration windowDecor = new DesktopModeWindowDecoration(mContext,
mContext, mMockDisplayController, mMockTaskResourceLoader,
mMockSplitScreenController, mMockDesktopUserRepositories, mMockShellTaskOrganizer,
- taskInfo, mMockSurfaceControl, mMockHandler, mMainExecutor,
- mMockMainCoroutineDispatcher, mMockBgCoroutineScope, mBgExecutor,
+ taskInfo, mMockSurfaceControl, handler, mMainExecutor,
+ mMockMainCoroutineDispatcher, mMockBgCoroutineScope, mBgExecutor, mTransitions,
mMockChoreographer, mMockSyncQueue, mMockAppHeaderViewHolderFactory,
mMockAppHandleViewHolderFactory, mMockRootTaskDisplayAreaOrganizer,
mMockGenericLinksParser, mMockAssistContentRequester, SurfaceControl.Builder::new,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositionerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositionerTest.kt
index 0b62aa4..1b96664 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositionerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositionerTest.kt
@@ -42,7 +42,7 @@
import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.common.DisplayLayout
import com.android.wm.shell.common.MultiDisplayDragMoveIndicatorController
-import com.android.wm.shell.common.MultiDisplayTestUtil
+import com.android.wm.shell.common.MultiDisplayTestUtil.TestDisplay
import com.android.wm.shell.shared.desktopmode.FakeDesktopState
import com.android.wm.shell.transition.Transitions
import com.android.wm.shell.transition.Transitions.TransitionFinishCallback
@@ -118,18 +118,9 @@
val resourceConfiguration = Configuration()
resourceConfiguration.uiMode = 0
resources.overrideConfiguration(resourceConfiguration)
- spyDisplayLayout0 =
- MultiDisplayTestUtil.createSpyDisplayLayout(
- MultiDisplayTestUtil.DISPLAY_GLOBAL_BOUNDS_0,
- MultiDisplayTestUtil.DISPLAY_DPI_0,
- resources.resources,
- )
- spyDisplayLayout1 =
- MultiDisplayTestUtil.createSpyDisplayLayout(
- MultiDisplayTestUtil.DISPLAY_GLOBAL_BOUNDS_1,
- MultiDisplayTestUtil.DISPLAY_DPI_1,
- resources.resources,
- )
+ spyDisplayLayout0 = TestDisplay.DISPLAY_0.getSpyDisplayLayout(resources.resources)
+ spyDisplayLayout1 = TestDisplay.DISPLAY_1.getSpyDisplayLayout(resources.resources)
+
whenever(mockDisplayController.getDisplayLayout(DISPLAY_ID_0)).thenReturn(spyDisplayLayout0)
whenever(mockDisplayController.getDisplayLayout(DISPLAY_ID_1)).thenReturn(spyDisplayLayout1)
whenever(spyDisplayLayout0.densityDpi()).thenReturn(DENSITY_DPI)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDecorViewModelTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDecorViewModelTest.kt
index afd0473..8652a6b 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDecorViewModelTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDecorViewModelTest.kt
@@ -19,6 +19,7 @@
import android.content.res.Resources
import android.graphics.Rect
import android.testing.AndroidTestingRunner
+import android.util.SparseArray
import androidx.test.filters.SmallTest
import com.android.wm.shell.RootTaskDisplayAreaOrganizer
import com.android.wm.shell.ShellTaskOrganizer
@@ -36,6 +37,7 @@
import com.android.wm.shell.desktopmode.ToggleResizeDesktopTaskTransitionHandler
import com.android.wm.shell.recents.RecentsTransitionStateListener.TRANSITION_STATE_ANIMATING
import com.android.wm.shell.shared.desktopmode.FakeDesktopState
+import com.android.wm.shell.sysui.ShellInit
import com.android.wm.shell.transition.FocusTransitionObserver
import com.android.wm.shell.transition.Transitions
import com.android.wm.shell.windowdecor.DesktopModeWindowDecoration
@@ -46,7 +48,10 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.Captor
import org.mockito.kotlin.any
+import org.mockito.kotlin.capture
import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
import org.mockito.kotlin.times
@@ -80,7 +85,9 @@
private val focusTransitionObserver: FocusTransitionObserver = mock()
private val displayLayout: DisplayLayout = mock()
private val mainExecutor: ShellExecutor = mock()
+ private val shellInit: ShellInit = mock()
private lateinit var desktopTilingDecorViewModel: DesktopTilingDecorViewModel
+ @Captor private lateinit var callbackCaptor: ArgumentCaptor<Runnable>
@Before
fun setUp() {
@@ -103,6 +110,7 @@
focusTransitionObserver,
mainExecutor,
desktopState,
+ shellInit,
)
whenever(contextMock.createContextAsUser(any(), any())).thenReturn(contextMock)
whenever(displayControllerMock.getDisplayLayout(any())).thenReturn(displayLayout)
@@ -121,33 +129,83 @@
val task2 = createFreeformTask()
task1.displayId = 1
task2.displayId = 2
-
+ desktopTilingDecorViewModel.currentUserId = 1
desktopTilingDecorViewModel.snapToHalfScreen(
task1,
desktopModeWindowDecorationMock,
DesktopTasksController.SnapPosition.LEFT,
BOUNDS,
)
- assertThat(desktopTilingDecorViewModel.tilingTransitionHandlerByDisplayId.size())
- .isEqualTo(1)
+ assertThat(desktopTilingDecorViewModel.tilingHandlerByUserAndDisplayId.size()).isEqualTo(1)
desktopTilingDecorViewModel.snapToHalfScreen(
task2,
desktopModeWindowDecorationMock,
DesktopTasksController.SnapPosition.LEFT,
BOUNDS,
)
- assertThat(desktopTilingDecorViewModel.tilingTransitionHandlerByDisplayId.size())
+ assertThat(desktopTilingDecorViewModel.tilingHandlerByUserAndDisplayId.get(1).size())
.isEqualTo(2)
}
@Test
+ fun userSwitched_shouldCreateNewTilingHandlers() {
+ val task1 = createFreeformTask()
+ val task2 = createFreeformTask()
+ task1.displayId = 1
+ task2.displayId = 2
+ val currentUser = 1
+ desktopTilingDecorViewModel.currentUserId = currentUser
+
+ // Snap task 1 on display 1.
+ desktopTilingDecorViewModel.snapToHalfScreen(
+ task1,
+ desktopModeWindowDecorationMock,
+ DesktopTasksController.SnapPosition.LEFT,
+ BOUNDS,
+ )
+ // Assert only one user handler list is created.
+ assertThat(desktopTilingDecorViewModel.tilingHandlerByUserAndDisplayId.size()).isEqualTo(1)
+
+ // Snap task 2 on display 2.
+ desktopTilingDecorViewModel.snapToHalfScreen(
+ task2,
+ desktopModeWindowDecorationMock,
+ DesktopTasksController.SnapPosition.LEFT,
+ BOUNDS,
+ )
+
+ // Assert that two handlers, one for each display is created.
+ assertThat(
+ desktopTilingDecorViewModel.tilingHandlerByUserAndDisplayId.get(currentUser).size()
+ )
+ .isEqualTo(2)
+ // Assert only one user list exists.
+ assertThat(desktopTilingDecorViewModel.tilingHandlerByUserAndDisplayId.size()).isEqualTo(1)
+
+ val newUser = 2
+ // Notify tiling of user change.
+ desktopTilingDecorViewModel.onUserChange(newUser)
+ // Snap a new task after user change.
+ desktopTilingDecorViewModel.snapToHalfScreen(
+ task2,
+ desktopModeWindowDecorationMock,
+ DesktopTasksController.SnapPosition.LEFT,
+ BOUNDS,
+ )
+
+ // Assert two handler lists exist now, one for each user.
+ assertThat(desktopTilingDecorViewModel.tilingHandlerByUserAndDisplayId.size()).isEqualTo(2)
+ }
+
+ @Test
fun removeTile_shouldCreate_newTilingDecoration() {
+ val decorationByDisplayId = SparseArray<DesktopTilingWindowDecoration>()
+ decorationByDisplayId.put(1, desktopTilingDecoration)
val task1 = createFreeformTask()
task1.displayId = 1
- desktopTilingDecorViewModel.tilingTransitionHandlerByDisplayId.put(
- 1,
- desktopTilingDecoration,
- )
+ desktopTilingDecorViewModel.currentUserId = 1
+ desktopTilingDecorViewModel.tilingHandlerByUserAndDisplayId.put(1, decorationByDisplayId)
+
desktopTilingDecorViewModel.removeTaskIfTiled(task1.displayId, task1.taskId)
verify(desktopTilingDecoration, times(1)).removeTaskIfTiled(any(), any(), any())
@@ -155,13 +213,13 @@
@Test
fun moveTaskToFront_shouldRoute_toCorrectTilingDecoration() {
-
+ val decorationByDisplayId = SparseArray<DesktopTilingWindowDecoration>()
+ decorationByDisplayId.put(1, desktopTilingDecoration)
val task1 = createFreeformTask()
task1.displayId = 1
- desktopTilingDecorViewModel.tilingTransitionHandlerByDisplayId.put(
- 1,
- desktopTilingDecoration,
- )
+ desktopTilingDecorViewModel.tilingHandlerByUserAndDisplayId.put(1, decorationByDisplayId)
+ desktopTilingDecorViewModel.currentUserId = 1
+
desktopTilingDecorViewModel.moveTaskToFrontIfTiled(task1)
verify(desktopTilingDecoration, times(1))
@@ -169,50 +227,47 @@
}
@Test
- fun overviewAnimation_starting_ShouldNotifyAllDecorations() {
- desktopTilingDecorViewModel.tilingTransitionHandlerByDisplayId.put(
- 1,
- desktopTilingDecoration,
- )
- desktopTilingDecorViewModel.tilingTransitionHandlerByDisplayId.put(
- 2,
- desktopTilingDecoration,
- )
+ fun overviewAnimationStarting_shouldNotifyAllDecorations() {
+ val decorationByDisplayId = SparseArray<DesktopTilingWindowDecoration>()
+ decorationByDisplayId.put(1, desktopTilingDecoration)
+ decorationByDisplayId.put(2, desktopTilingDecoration)
+ desktopTilingDecorViewModel.currentUserId = 1
+ desktopTilingDecorViewModel.tilingHandlerByUserAndDisplayId.put(1, decorationByDisplayId)
+
desktopTilingDecorViewModel.onOverviewAnimationStateChange(TRANSITION_STATE_ANIMATING)
verify(desktopTilingDecoration, times(2)).onOverviewAnimationStateChange(any())
}
@Test
- fun onUserChange_allTilingSessionsShouldBeDestroyed() {
- desktopTilingDecorViewModel.tilingTransitionHandlerByDisplayId.put(
- 1,
- desktopTilingDecoration,
- )
- desktopTilingDecorViewModel.tilingTransitionHandlerByDisplayId.put(
- 2,
- desktopTilingDecoration,
- )
+ fun userChange_tilingDividerHidden() {
+ val decorationByDisplayId = SparseArray<DesktopTilingWindowDecoration>()
+ decorationByDisplayId.put(1, desktopTilingDecoration)
+ decorationByDisplayId.put(2, desktopTilingDecoration)
+ desktopTilingDecorViewModel.currentUserId = 1
+ desktopTilingDecorViewModel.tilingHandlerByUserAndDisplayId.put(1, decorationByDisplayId)
- desktopTilingDecorViewModel.onUserChange()
+ desktopTilingDecorViewModel.onUserChange(2)
- verify(desktopTilingDecoration, times(2)).resetTilingSession()
+ verify(desktopTilingDecoration, times(2)).hideDividerBar()
}
@Test
fun displayOrientationChange_tilingForDisplayShouldBeDestroyed() {
- desktopTilingDecorViewModel.tilingTransitionHandlerByDisplayId.put(
- 1,
- desktopTilingDecoration,
- )
- desktopTilingDecorViewModel.tilingTransitionHandlerByDisplayId.put(
- 2,
- desktopTilingDecoration,
- )
+ val decorationByDisplayId = SparseArray<DesktopTilingWindowDecoration>()
+ decorationByDisplayId.put(1, desktopTilingDecoration)
+ decorationByDisplayId.put(2, desktopTilingDecoration)
+ desktopTilingDecorViewModel.currentUserId = 1
+ desktopTilingDecorViewModel.tilingHandlerByUserAndDisplayId.put(1, decorationByDisplayId)
desktopTilingDecorViewModel.onDisplayChange(1, 1, 2, null, null)
verify(desktopTilingDecoration, times(1)).resetTilingSession()
+ verify(shellInit, times(1))
+ .addInitCallback(capture(callbackCaptor), eq(desktopTilingDecorViewModel))
+
+ callbackCaptor.value.run()
+
verify(displayControllerMock, times(1))
.addDisplayChangingController(eq(desktopTilingDecorViewModel))
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp
index 5087507..2f1e044 100644
--- a/libs/androidfw/AssetManager2.cpp
+++ b/libs/androidfw/AssetManager2.cpp
@@ -23,7 +23,6 @@
#include <map>
#include <set>
#include <span>
-#include <sstream>
#include <utility>
#include "android-base/logging.h"
@@ -238,9 +237,8 @@
// Add the package name -> build time ID mappings.
for (const DynamicPackageEntry& entry : package->GetDynamicPackageMap()) {
- String16 package_name(entry.package_name.c_str(), entry.package_name.size());
- package_group->dynamic_ref_table->mEntries.replaceValueFor(
- package_name, static_cast<uint8_t>(entry.package_id));
+ package_group->dynamic_ref_table->mEntries[entry.package_name] =
+ static_cast<uint8_t>(entry.package_id);
}
if (auto apk_assets_path = apk_assets->GetPath()) {
@@ -254,9 +252,8 @@
DynamicRefTable::AliasMap aliases;
for (const auto& group : package_groups_) {
const std::string& package_name = group.packages_[0].loaded_package_->GetPackageName();
- const auto name_16 = String16(package_name.c_str(), package_name.size());
for (auto&& inner_group : package_groups_) {
- inner_group.dynamic_ref_table->addMapping(name_16,
+ inner_group.dynamic_ref_table->addMapping(package_name,
group.dynamic_ref_table->mAssignedPackageId);
}
@@ -445,24 +442,6 @@
return false;
}
-static std::string ConfigVecToString(std::span<const ResTable_config> configurations) {
- std::stringstream ss;
- ss << "[";
- bool first = true;
- for (const auto& config : configurations) {
- if (!first) {
- ss << ",";
- }
- char out[RESTABLE_MAX_LOCALE_LEN] = {};
- config.getBcp47Locale(out);
- ss << out;
- first = false;
- }
- ss << "]";
- return ss.str();
-}
-
-
void AssetManager2::SetConfigurations(std::span<const ResTable_config> configurations,
bool force_refresh) {
int diff = 0;
@@ -478,16 +457,6 @@
}
}
- // Log the locale list change to investigate b/392255526
- if (diff & ConfigDescription::CONFIG_LOCALE) {
- auto oldstr = ConfigVecToString(configurations_);
- auto newstr = ConfigVecToString(configurations);
- if (oldstr != newstr) {
- LOG(INFO) << "AssetManager2(" << this << ") locale list changing from "
- << oldstr << " to " << newstr;
- }
- }
-
configurations_.clear();
for (auto&& config : configurations) {
configurations_.emplace_back(config);
@@ -499,24 +468,6 @@
}
void AssetManager2::SetDefaultLocale(std::optional<ResTable_config> default_locale) {
- int diff = 0;
- if (default_locale_ && default_locale) {
- diff = default_locale_->diff(default_locale.value());
- } else if (default_locale_ || default_locale) {
- diff = -1;
- }
- if (diff & ConfigDescription::CONFIG_LOCALE) {
- char old_loc[RESTABLE_MAX_LOCALE_LEN] = {};
- char new_loc[RESTABLE_MAX_LOCALE_LEN] = {};
- if (default_locale_) {
- default_locale_->getBcp47Locale(old_loc);
- }
- if (default_locale) {
- default_locale->getBcp47Locale(new_loc);
- }
- LOG(INFO) << "AssetManager2(" << this << ") default locale changing from '"
- << old_loc << "' to '" << new_loc << "'";
- }
default_locale_ = default_locale;
}
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 6ec605c..8d48663 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -39,8 +39,10 @@
#include <androidfw/ByteBucketArray.h>
#include <androidfw/ResourceTypes.h>
#include <androidfw/TypeWrappers.h>
+#include <androidfw/Util.h>
#include <cutils/atomic.h>
#include <utils/ByteOrder.h>
+#include <utils/KeyedVector.h>
#include <utils/Log.h>
#include <utils/String16.h>
#include <utils/String8.h>
@@ -2880,14 +2882,13 @@
}
}
- if (version || o.version) {
- if ((sdkVersion != o.sdkVersion) && requested->sdkVersion) {
+ if ((version || o.version) && requested->version) {
+ if (sdkVersion != o.sdkVersion) {
return (sdkVersion > o.sdkVersion);
}
- if ((minorVersion != o.minorVersion) &&
- requested->minorVersion) {
- return (minorVersion);
+ if (minorVersion != o.minorVersion) {
+ return (minorVersion > o.minorVersion);
}
}
@@ -3077,11 +3078,16 @@
return false;
}
}
- if (version != 0) {
- if (sdkVersion != 0 && sdkVersion > settings.sdkVersion) {
+ if (sdkVersion != 0 && sdkVersion > settings.sdkVersion) {
+ return false;
+ }
+ if (minorVersion != 0) {
+ if (sdkVersion == 0) {
+ // The version is 0.x. sdkVersion of 0 is not really valid. Therefore if it's 0,
+ // we shouldn't allow a minorVersion (unless it's also 0).
return false;
}
- if (minorVersion != 0 && minorVersion != settings.minorVersion) {
+ if (sdkVersion == settings.sdkVersion && minorVersion > settings.minorVersion) {
return false;
}
}
@@ -6956,7 +6962,7 @@
size_t N = mPackageGroups.size();
for (size_t i = 0; i < N; i++) {
mPackageGroups[i]->dynamicRefTable.addMapping(
- group->name, static_cast<uint8_t>(group->id));
+ std::string_view(String8(group->name)), static_cast<uint8_t>(group->id));
}
} else {
group = mPackageGroups.itemAt(idx - 1);
@@ -7154,7 +7160,8 @@
// Fill in the reference table with the entries we already know about.
size_t N = mPackageGroups.size();
for (size_t i = 0; i < N; i++) {
- group->dynamicRefTable.addMapping(mPackageGroups[i]->name, mPackageGroups[i]->id);
+ group->dynamicRefTable.addMapping(
+ std::string_view(String8(mPackageGroups[i]->name)), mPackageGroups[i]->id);
}
} else {
ALOGW("Found multiple library tables, ignoring...");
@@ -7201,19 +7208,20 @@
const ResTable_lib_entry* entry = (const ResTable_lib_entry*)(((uint8_t*) header) +
dtohl(header->header.headerSize));
+ std::string package_name;
for (uint32_t entryIndex = 0; entryIndex < entryCount; entryIndex++) {
uint32_t packageId = dtohl(entry->packageId);
- char16_t tmpName[sizeof(entry->packageName) / sizeof(char16_t)];
- strcpy16_dtoh(tmpName, entry->packageName, sizeof(entry->packageName) / sizeof(char16_t));
+ util::ReadUtf16StringFromDevice(entry->packageName, std::size(entry->packageName),
+ &package_name);
if (kDebugLibNoisy) {
- ALOGV("Found lib entry %s with id %d\n", String8(tmpName).c_str(),
+ ALOGV("Found lib entry %s with id %d\n", package_name.c_str(),
dtohl(entry->packageId));
}
if (packageId >= 256) {
ALOGE("Bad package id 0x%08x", packageId);
return UNKNOWN_ERROR;
}
- mEntries.replaceValueFor(String16(tmpName), (uint8_t) packageId);
+ mEntries[std::move(package_name)] = (uint8_t) packageId;
entry = entry + 1;
}
return NO_ERROR;
@@ -7224,15 +7232,10 @@
return UNKNOWN_ERROR;
}
- const size_t entryCount = other.mEntries.size();
- for (size_t i = 0; i < entryCount; i++) {
- ssize_t index = mEntries.indexOfKey(other.mEntries.keyAt(i));
- if (index < 0) {
- mEntries.add(String16(other.mEntries.keyAt(i)), other.mEntries[i]);
- } else {
- if (other.mEntries[i] != mEntries[index]) {
- return UNKNOWN_ERROR;
- }
+ for (auto [name, id] : other.mEntries) {
+ auto [it, inserted] = mEntries.emplace(name, id);
+ if (!inserted && id != it->second) {
+ return UNKNOWN_ERROR;
}
}
@@ -7250,13 +7253,12 @@
return NO_ERROR;
}
-status_t DynamicRefTable::addMapping(const String16& packageName, uint8_t packageId)
-{
- ssize_t index = mEntries.indexOfKey(packageName);
- if (index < 0) {
+status_t DynamicRefTable::addMapping(std::string_view packageName, uint8_t packageId) {
+ auto it = mEntries.find(packageName);
+ if (it == mEntries.end()) {
return UNKNOWN_ERROR;
}
- mLookupTable[mEntries.valueAt(index)] = packageId;
+ mLookupTable[it->second] = packageId;
return NO_ERROR;
}
@@ -7738,14 +7740,12 @@
(int)pgIndex, pg->id, (int)pg->packages.size(),
String8(pg->name).c_str());
- const KeyedVector<String16, uint8_t>& refEntries = pg->dynamicRefTable.entries();
+ const auto& refEntries = pg->dynamicRefTable.entries();
const size_t refEntryCount = refEntries.size();
if (refEntryCount > 0) {
printf(" DynamicRefTable entryCount=%d:\n", (int) refEntryCount);
- for (size_t refIndex = 0; refIndex < refEntryCount; refIndex++) {
- printf(" 0x%02x -> %s\n",
- refEntries.valueAt(refIndex),
- String8(refEntries.keyAt(refIndex)).c_str());
+ for (auto [refName, refId] : refEntries) {
+ printf(" 0x%02x -> %s\n", refId, refName.c_str());
}
printf("\n");
}
diff --git a/libs/androidfw/fuzz/resxmlparser_fuzzer/resxmlparser_fuzzer.cpp b/libs/androidfw/fuzz/resxmlparser_fuzzer/resxmlparser_fuzzer.cpp
index a218a1f..2d245ad 100644
--- a/libs/androidfw/fuzz/resxmlparser_fuzzer/resxmlparser_fuzzer.cpp
+++ b/libs/androidfw/fuzz/resxmlparser_fuzzer/resxmlparser_fuzzer.cpp
@@ -37,11 +37,8 @@
packageName.push_back(ch);
}
- // Convert std::string to String16 for compatibility
- android::String16 androidPackageName(packageName.c_str(), packageName.length());
-
// Add the mapping to the table
- table.addMapping(androidPackageName, packageId);
+ table.addMapping(packageName, packageId);
}
}
diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h
index bd72d37..cdff6b6 100644
--- a/libs/androidfw/include/androidfw/ResourceTypes.h
+++ b/libs/androidfw/include/androidfw/ResourceTypes.h
@@ -29,7 +29,6 @@
#include <androidfw/StringPiece.h>
#include <utils/ByteOrder.h>
#include <utils/Errors.h>
-#include <utils/KeyedVector.h>
#include <utils/String16.h>
#include <utils/Vector.h>
#include <utils/threads.h>
@@ -1143,8 +1142,9 @@
union {
struct {
uint16_t sdkVersion;
- // For now minorVersion must always be 0!!! Its meaning
- // is currently undefined.
+
+ // Until Baklava, this was always set to and assumed to be 0.
+ // After Baklava, this started to be used with minor SDK releases.
uint16_t minorVersion;
};
uint32_t version;
@@ -1962,7 +1962,7 @@
// Creates a mapping from build-time package ID to run-time package ID for
// the given package.
- status_t addMapping(const String16& packageName, uint8_t packageId);
+ status_t addMapping(std::string_view packageName, uint8_t packageId);
void addMapping(uint8_t buildPackageId, uint8_t runtimePackageId);
@@ -1979,7 +1979,8 @@
virtual status_t lookupResourceId(uint32_t* resId) const;
status_t lookupResourceValue(Res_value* value) const;
- inline const KeyedVector<String16, uint8_t>& entries() const {
+ using Entries = std::map<std::string, uint8_t, std::less<>>;
+ const Entries& entries() const {
return mEntries;
}
@@ -1987,7 +1988,7 @@
uint8_t mLookupTable[256];
uint8_t mAssignedPackageId;
bool mAppAsLib;
- KeyedVector<String16, uint8_t> mEntries;
+ Entries mEntries;
AliasMap mAliasId;
};
diff --git a/libs/androidfw/tests/Config_test.cpp b/libs/androidfw/tests/Config_test.cpp
index 5477621..77a552c 100644
--- a/libs/androidfw/tests/Config_test.cpp
+++ b/libs/androidfw/tests/Config_test.cpp
@@ -14,6 +14,9 @@
* limitations under the License.
*/
+#include <vector>
+
+#include "androidfw/ConfigDescription.h"
#include "androidfw/ResourceTypes.h"
#include "utils/Log.h"
@@ -23,15 +26,15 @@
#include "TestHelpers.h"
#include "gtest/gtest.h"
+using ::android::ConfigDescription;
+
namespace android {
static ResTable_config selectBest(const ResTable_config& target,
- const Vector<ResTable_config>& configs) {
+ const std::vector<ResTable_config>& configs) {
ResTable_config bestConfig;
memset(&bestConfig, 0, sizeof(bestConfig));
- const size_t configCount = configs.size();
- for (size_t i = 0; i < configCount; i++) {
- const ResTable_config& thisConfig = configs[i];
+ for (const auto& thisConfig : configs) {
if (!thisConfig.match(target)) {
continue;
}
@@ -57,34 +60,34 @@
deviceConfig.density = ResTable_config::DENSITY_XHIGH;
deviceConfig.sdkVersion = 21;
- Vector<ResTable_config> configs;
+ std::vector<ResTable_config> configs;
ResTable_config expectedBest =
buildDensityConfig(ResTable_config::DENSITY_HIGH);
- configs.add(expectedBest);
+ configs.push_back(expectedBest);
ASSERT_EQ(expectedBest, selectBest(deviceConfig, configs));
expectedBest = buildDensityConfig(ResTable_config::DENSITY_XXHIGH);
- configs.add(expectedBest);
+ configs.push_back(expectedBest);
ASSERT_EQ(expectedBest, selectBest(deviceConfig, configs));
expectedBest = buildDensityConfig(int(ResTable_config::DENSITY_XXHIGH) - 20);
- configs.add(expectedBest);
+ configs.push_back(expectedBest);
ASSERT_EQ(expectedBest, selectBest(deviceConfig, configs));
- configs.add(buildDensityConfig(int(ResTable_config::DENSITY_HIGH) + 20));
+ configs.push_back(buildDensityConfig(int(ResTable_config::DENSITY_HIGH) + 20));
ASSERT_EQ(expectedBest, selectBest(deviceConfig, configs));
- configs.add(buildDensityConfig(int(ResTable_config::DENSITY_XHIGH) - 1));
+ configs.push_back(buildDensityConfig(int(ResTable_config::DENSITY_XHIGH) - 1));
ASSERT_EQ(expectedBest, selectBest(deviceConfig, configs));
expectedBest = buildDensityConfig(ResTable_config::DENSITY_XHIGH);
- configs.add(expectedBest);
+ configs.push_back(expectedBest);
ASSERT_EQ(expectedBest, selectBest(deviceConfig, configs));
expectedBest = buildDensityConfig(ResTable_config::DENSITY_ANY);
expectedBest.sdkVersion = 21;
- configs.add(expectedBest);
+ configs.push_back(expectedBest);
ASSERT_EQ(expectedBest, selectBest(deviceConfig, configs));
}
@@ -93,16 +96,16 @@
memset(&deviceConfig, 0, sizeof(deviceConfig));
deviceConfig.sdkVersion = 21;
- Vector<ResTable_config> configs;
- configs.add(buildDensityConfig(ResTable_config::DENSITY_HIGH));
+ std::vector<ResTable_config> configs;
+ configs.push_back(buildDensityConfig(ResTable_config::DENSITY_HIGH));
ResTable_config expectedBest =
buildDensityConfig(ResTable_config::DENSITY_MEDIUM);
- configs.add(expectedBest);
+ configs.push_back(expectedBest);
ASSERT_EQ(expectedBest, selectBest(deviceConfig, configs));
expectedBest = buildDensityConfig(ResTable_config::DENSITY_ANY);
- configs.add(expectedBest);
+ configs.push_back(expectedBest);
ASSERT_EQ(expectedBest, selectBest(deviceConfig, configs));
}
@@ -219,4 +222,108 @@
EXPECT_EQ(masculine.diff(feminine), ResTable_config::CONFIG_GRAMMATICAL_GENDER);
}
+static ResTable_config Cfg(StringPiece str) {
+ ConfigDescription config = {};
+ // We're assuming str will successfully parse, to simplify writing tests
+ ConfigDescription::Parse(str, &config);
+ return config;
+}
+
+TEST(ConfigTest, SdkAndMinorVersion_Match) {
+ // Left is resource version, right is platform version
+ EXPECT_TRUE(Cfg("").match(Cfg("v41")));
+ EXPECT_TRUE(Cfg("").match(Cfg("v41.1")));
+
+ EXPECT_TRUE(Cfg("v41").match(Cfg("v41")));
+ EXPECT_TRUE(Cfg("v41").match(Cfg("v41.1")));
+ EXPECT_TRUE(Cfg("v41").match(Cfg("v41.2")));
+ EXPECT_TRUE(Cfg("v41").match(Cfg("v42")));
+ EXPECT_TRUE(Cfg("v41").match(Cfg("v42.1")));
+
+ EXPECT_FALSE(Cfg("v41.1").match(Cfg("v41")));
+ EXPECT_TRUE(Cfg("v41.1").match(Cfg("v41.1")));
+ EXPECT_TRUE(Cfg("v41.1").match(Cfg("v41.2")));
+ EXPECT_TRUE(Cfg("v41.1").match(Cfg("v42")));
+ EXPECT_TRUE(Cfg("v41.1").match(Cfg("v42.1")));
+
+ EXPECT_FALSE(Cfg("v41.2").match(Cfg("v41")));
+ EXPECT_FALSE(Cfg("v41.2").match(Cfg("v41.1")));
+ EXPECT_TRUE(Cfg("v41.2").match(Cfg("v41.2")));
+ EXPECT_TRUE(Cfg("v41.2").match(Cfg("v42")));
+ EXPECT_TRUE(Cfg("v41.2").match(Cfg("v42.1")));
+
+ EXPECT_FALSE(Cfg("v42").match(Cfg("v41")));
+ EXPECT_FALSE(Cfg("v42").match(Cfg("v41.1")));
+ EXPECT_FALSE(Cfg("v42").match(Cfg("v41.2")));
+ EXPECT_TRUE(Cfg("v42").match(Cfg("v42")));
+ EXPECT_TRUE(Cfg("v42").match(Cfg("v42.1")));
+
+ EXPECT_FALSE(Cfg("v42.1").match(Cfg("v41")));
+ EXPECT_FALSE(Cfg("v42.1").match(Cfg("v41.1")));
+ EXPECT_FALSE(Cfg("v42.1").match(Cfg("v41.2")));
+ EXPECT_FALSE(Cfg("v42.1").match(Cfg("v42")));
+ EXPECT_TRUE(Cfg("v42.1").match(Cfg("v42.1")));
+
+ // ConfigDescription::Parse doesn't allow "v0.3" so we have to create it manually to test
+ ResTable_config config = {};
+ config.sdkVersion = 0;
+ config.minorVersion = 3;
+ EXPECT_FALSE(config.match(Cfg("v41")));
+}
+
+TEST(ConfigTest, SdkAndMinorVersion_IsBetterThan) {
+ ResTable_config requested = Cfg("v45");
+ EXPECT_FALSE(Cfg("v40").isBetterThan(Cfg("v40"), &requested));
+ EXPECT_TRUE(Cfg("v41").isBetterThan(Cfg("v40"), &requested));
+ EXPECT_TRUE(Cfg("v41.1").isBetterThan(Cfg("v41"), &requested));
+ EXPECT_TRUE(Cfg("v41.2").isBetterThan(Cfg("v41.1"), &requested));
+ EXPECT_TRUE(Cfg("v42").isBetterThan(Cfg("v41.2"), &requested));
+ EXPECT_TRUE(Cfg("v43.1").isBetterThan(Cfg("v42"), &requested));
+
+ requested = Cfg("v45.9");
+ EXPECT_FALSE(Cfg("v40").isBetterThan(Cfg("v40"), &requested));
+ EXPECT_TRUE(Cfg("v41").isBetterThan(Cfg("v40"), &requested));
+ EXPECT_TRUE(Cfg("v41.1").isBetterThan(Cfg("v41"), &requested));
+ EXPECT_TRUE(Cfg("v41.2").isBetterThan(Cfg("v41.1"), &requested));
+ EXPECT_TRUE(Cfg("v42").isBetterThan(Cfg("v41.2"), &requested));
+ EXPECT_TRUE(Cfg("v43.1").isBetterThan(Cfg("v42"), &requested));
+
+ // isBetterThan defaults to isMoreSpecificThan if requested is null
+ EXPECT_FALSE(Cfg("v40").isBetterThan(Cfg("v40"), nullptr));
+ EXPECT_FALSE(Cfg("v41").isBetterThan(Cfg("v40"), nullptr));
+ EXPECT_TRUE(Cfg("v41.1").isBetterThan(Cfg("v41"), nullptr));
+ EXPECT_FALSE(Cfg("v41.2").isBetterThan(Cfg("v41.1"), nullptr));
+ EXPECT_FALSE(Cfg("v42").isBetterThan(Cfg("v41.2"), nullptr));
+ EXPECT_TRUE(Cfg("v43.1").isBetterThan(Cfg("v42"), nullptr));
+}
+
+TEST(ConfigTest, SdkAndMinorVersion_SelectBest) {
+ ResTable_config requested = Cfg("v45");
+ EXPECT_STREQ("v42", selectBest(requested, {Cfg("v40"), Cfg("v42"), Cfg("v41")}).toString());
+ EXPECT_STREQ("v42.3",
+ selectBest(requested, {Cfg("v40.5"), Cfg("v42.3"), Cfg("v41.2")}).toString());
+ EXPECT_STREQ("v42.5",
+ selectBest(requested, {Cfg("v42.5"), Cfg("v42.3"), Cfg("v41.2")}).toString());
+ EXPECT_STREQ("v42.5",
+ selectBest(requested, {Cfg("v42.3"), Cfg("v41.2"), Cfg("v42.5")}).toString());
+ EXPECT_STREQ("v42.5",
+ selectBest(requested, {Cfg("v42"), Cfg("v42.5"), Cfg("v41.2")}).toString());
+ EXPECT_STREQ("v44", selectBest(requested, {Cfg("v42.5"), Cfg("v42.3"), Cfg("v44")}).toString());
+
+ requested = Cfg("v45.9");
+ EXPECT_STREQ("v42", selectBest(requested, {Cfg("v40"), Cfg("v42"), Cfg("v41")}).toString());
+ EXPECT_STREQ("v42.3",
+ selectBest(requested, {Cfg("v40.5"), Cfg("v42.3"), Cfg("v41.2")}).toString());
+ EXPECT_STREQ("v42.5",
+ selectBest(requested, {Cfg("v42.5"), Cfg("v42.3"), Cfg("v41.2")}).toString());
+ EXPECT_STREQ("v42.5",
+ selectBest(requested, {Cfg("v42.3"), Cfg("v41.2"), Cfg("v42.5")}).toString());
+ EXPECT_STREQ("v42.5",
+ selectBest(requested, {Cfg("v42"), Cfg("v42.5"), Cfg("v41.2")}).toString());
+ EXPECT_STREQ("v44",
+ selectBest(requested, {Cfg("v42.5"), Cfg("v42.3"), Cfg("v44")}).toString());
+ EXPECT_STREQ("v45.6",
+ selectBest(requested, {Cfg("v45.3"), Cfg("45"), Cfg("v45.6")}).toString());
+}
+
} // namespace android.
diff --git a/libs/hostgraphics/include/gui/BufferItemConsumer.h b/libs/hostgraphics/include/gui/BufferItemConsumer.h
index b9ff0a77..df0aa5a 100644
--- a/libs/hostgraphics/include/gui/BufferItemConsumer.h
+++ b/libs/hostgraphics/include/gui/BufferItemConsumer.h
@@ -21,12 +21,33 @@
#include <gui/BufferQueue.h>
#include <gui/ConsumerBase.h>
#include <gui/IGraphicBufferConsumer.h>
+#include <gui/Surface.h>
#include <utils/RefBase.h>
namespace android {
class BufferItemConsumer : public ConsumerBase {
public:
+ enum { DEFAULT_MAX_BUFFERS = -1 };
+
+ static std::tuple<sp<BufferItemConsumer>, sp<Surface>> create(
+ uint64_t consumerUsage, int bufferCount = DEFAULT_MAX_BUFFERS,
+ bool controlledByApp = false, bool isConsumerSurfaceFlinger = false) {
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+ sp<BufferItemConsumer> bufferItemConsumer =
+ sp<BufferItemConsumer>::make(consumerUsage, bufferCount, controlledByApp,
+ isConsumerSurfaceFlinger);
+ return {bufferItemConsumer, bufferItemConsumer->getSurface()};
+#else
+ sp<IGraphicBufferProducer> igbp;
+ sp<IGraphicBufferConsumer> igbc;
+ BufferQueue::createBufferQueue(&igbp, &igbc, isConsumerSurfaceFlinger);
+ sp<BufferItemConsumer> bufferItemConsumer =
+ sp<BufferItemConsumer>::make(igbc, consumerUsage, bufferCount, controlledByApp);
+ return {bufferItemConsumer, sp<Surface>::make(igbp, controlledByApp)};
+#endif
+ }
+
BufferItemConsumer(const sp<IGraphicBufferConsumer>& consumer, uint64_t consumerUsage,
int bufferCount = -1, bool controlledByApp = false)
: mConsumer(consumer) {}
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 1bde5ff..f943cad 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -616,6 +616,7 @@
"Animator.cpp",
"AnimatorManager.cpp",
"CanvasTransform.cpp",
+ "ColorArea.cpp",
"DamageAccumulator.cpp",
"DeviceInfo.cpp",
"FrameInfo.cpp",
diff --git a/libs/hwui/CanvasTransform.cpp b/libs/hwui/CanvasTransform.cpp
index 6f60d01..fb5cac3 100644
--- a/libs/hwui/CanvasTransform.cpp
+++ b/libs/hwui/CanvasTransform.cpp
@@ -107,22 +107,6 @@
paint.setShader(SkGradientShader::MakeLinear(
info.fPoints, info.fColors, info.fColorOffsets, info.fColorCount,
info.fTileMode, info.fGradientFlags, nullptr));
- } else {
- if (transform == ColorTransform::Invert) {
- // Since we're trying to invert every thing around this draw call, we invert
- // the color of the draw call if we don't know what it is.
- auto filter = SkHighContrastFilter::Make(
- {/* grayscale= */ false,
- SkHighContrastConfig::InvertStyle::kInvertLightness,
- /* contrast= */ 0.0f});
-
- if (paint.getColorFilter()) {
- paint.setColorFilter(SkColorFilters::Compose(filter, paint.refColorFilter()));
- } else {
- paint.setColorFilter(filter);
- }
- return;
- }
}
}
@@ -143,7 +127,7 @@
return hsv[2] >= .5f ? BitmapPalette::Light : BitmapPalette::Dark;
}
-static BitmapPalette filterPalette(const SkPaint* paint, BitmapPalette palette) {
+BitmapPalette filterPalette(const SkPaint* paint, BitmapPalette palette) {
if (palette == BitmapPalette::Unknown || !paint || !paint->getColorFilter()) {
return palette;
}
diff --git a/libs/hwui/CanvasTransform.h b/libs/hwui/CanvasTransform.h
index 288dca4..de6cf0f 100644
--- a/libs/hwui/CanvasTransform.h
+++ b/libs/hwui/CanvasTransform.h
@@ -48,4 +48,7 @@
SkColor transformColor(ColorTransform transform, SkColor color);
SkColor transformColorInverse(ColorTransform transform, SkColor color);
+/** Returns a palette corrected in case it is tinted by the given paint's filter */
+BitmapPalette filterPalette(const SkPaint* paint, BitmapPalette palette);
+
} // namespace android::uirenderer
diff --git a/libs/hwui/ColorArea.cpp b/libs/hwui/ColorArea.cpp
new file mode 100644
index 0000000..1b0fc5d
--- /dev/null
+++ b/libs/hwui/ColorArea.cpp
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ColorArea.h"
+
+#include "CanvasTransform.h"
+#include "utils/MathUtils.h"
+
+namespace android::uirenderer {
+
+constexpr static int kMinimumAlphaToConsiderArea = 200;
+
+inline uint64_t calculateArea(int32_t width, int32_t height) {
+ // HWUI doesn't draw anything with negative width or height
+ if (width <= 0 || height <= 0) return 0;
+
+ return width * height;
+}
+
+void ColorArea::addArea(const SkRect& rect, const SkPaint* paint) {
+ addArea(rect.width(), rect.height(), paint);
+}
+
+void ColorArea::addArea(int32_t width, int32_t height, const SkPaint* paint) {
+ if (!paint) return;
+ addArea(calculateArea(width, height), *paint);
+}
+
+void ColorArea::addArea(uint64_t area, const SkPaint& paint) {
+ if (paint.getStyle() == SkPaint::Style::kStroke_Style) return;
+ if (CC_UNLIKELY(paint.nothingToDraw())) return;
+
+ if (paint.getShader()) {
+ // TODO(b/409395389): check if the shader is a gradient, and then slice up area into
+ // sections, determining polarity for each color stop of the gradient.
+ return;
+ }
+
+ addArea(area, paint.getColor());
+}
+
+void ColorArea::addArea(const SkRect& bounds, const SkPaint& paint,
+ android::BitmapPalette palette) {
+ palette = filterPalette(&paint, palette);
+ auto area = calculateArea(bounds.width(), bounds.height());
+ switch (palette) {
+ case android::BitmapPalette::Light:
+ addArea(area, Light);
+ break;
+ case android::BitmapPalette::Dark:
+ addArea(area, Dark);
+ break;
+ case android::BitmapPalette::Colorful:
+ case android::BitmapPalette::Unknown:
+ addArea(area, Unknown);
+ break;
+ }
+}
+
+void ColorArea::addArea(uint64_t area, SkColor color) {
+ if (CC_UNLIKELY(SkColorGetA(color) < kMinimumAlphaToConsiderArea)) return;
+
+ // TODO(b/381930266): optimize by detecting common black/white/grey colors and avoid converting
+ // also maybe cache colors or something?
+ Lab lab = sRGBToLab(color);
+ // TODO(b/372558459): add a case for a middle L that is grey, and don't count it?
+ addArea(area, lab.L > 50 ? Light : Dark);
+}
+
+void ColorArea::addArea(uint64_t area, Polarity polarity) {
+ // HWUI doesn't draw anything with negative width or height
+ if (area <= 0) return;
+
+ if (polarity == Light) {
+ mLight += area;
+ } else if (polarity == Dark) {
+ mDark += area;
+ }
+}
+
+Polarity ColorArea::getPolarity() const {
+ if (mLight == mDark) { // also covers the case if it was just reset()
+ return Polarity::Unknown;
+ }
+ if (mLight > mDark) {
+ return Polarity::Light;
+ } else {
+ return Polarity::Dark;
+ }
+}
+
+void ColorArea::reset() {
+ mParentHeight = -1;
+ mParentWidth = -1;
+ mLight = 0;
+ mDark = 0;
+}
+
+void ColorArea::merge(const ColorArea& source) {
+ mLight += source.mLight;
+ mDark += source.mDark;
+}
+
+int ColorArea::getParentWidth() const {
+ return mParentWidth;
+}
+
+void ColorArea::setParentWidth(int width) {
+ mParentWidth = width;
+}
+
+int ColorArea::getParentHeight() const {
+ return mParentHeight;
+}
+
+void ColorArea::setParentHeight(int height) {
+ mParentHeight = height;
+}
+
+} // namespace android::uirenderer
diff --git a/libs/hwui/ColorArea.h b/libs/hwui/ColorArea.h
new file mode 100644
index 0000000..e0ad074
--- /dev/null
+++ b/libs/hwui/ColorArea.h
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <SkCanvas.h>
+#include <SkPaintFilterCanvas.h>
+
+#include "hwui/Bitmap.h"
+#include "utils/Color.h"
+#include "utils/Macros.h"
+
+namespace android::uirenderer {
+
+/**
+ * The result of counting the color area.
+ */
+enum Polarity {
+ /** The result is too close to make a definite determination */
+ Unknown = 0,
+ /** Majority light fills */
+ Light,
+ /** Majority dark fills */
+ Dark
+};
+
+/**
+ * Tracks the app's overall polarity (i.e. dark or light theme) by counting the areas of backgrounds
+ * and their colors. This is used to determine if we should force invert the app, for instance if
+ * the user prefers dark theme but this app is mainly light.
+ *
+ * The idea is that we count the fill colors of any background-type draw calls: drawRect(),
+ * drawColor(), etc. If the area of light fills drawn to the screen is greater than the area of dark
+ * fills drawn to the screen, we can reasonably guess that the app is light theme, and vice-versa.
+ */
+class ColorArea {
+public:
+ ColorArea() {}
+ ~ColorArea() {}
+
+ /**
+ * Counts the given area of a draw call that is reasonably expected to draw a background:
+ * drawRect, drawColor, etc.
+ *
+ * @param area the total area of the draw call's fill (approximate)
+ * @param paint the paint used to fill the area. If the paint is not a fill, the area will not
+ * be added.
+ */
+ void addArea(uint64_t area, const SkPaint& paint);
+
+ /**
+ * See [addArea(uint64_t, SkPaint&)]
+ */
+ void addArea(const SkRect& rect, const SkPaint* paint);
+
+ /**
+ * See [addArea(uint64_t, SkPaint&)]
+ */
+ void addArea(const SkRect& rect, const SkPaint& paint, android::BitmapPalette palette);
+
+ /**
+ * See [addArea(long, SkPaint&)]
+ */
+ void addArea(int32_t width, int32_t height, const SkPaint* paint);
+
+ /**
+ * See [addArea(uint64_t, SkPaint&)]
+ */
+ void addArea(uint64_t area, SkColor color);
+
+ /**
+ * Prefer [addArea(uint64_t, SkPaint&)], unless the area you're measuring doesn't have a paint
+ * with measurable colors.
+ *
+ * @param area the total area of the draw call's fill (approximate)
+ * @param polarity whether the color of the given area is light or dark
+ */
+ void addArea(uint64_t area, Polarity polarity);
+
+ /**
+ * Adds the source's area to this area. This is so you can sum up the areas of a bunch of child
+ * nodes.
+ */
+ void merge(const ColorArea& source);
+
+ /** Resets the object back to the initial state */
+ void reset();
+
+ int getParentWidth() const;
+ void setParentWidth(int width);
+ int getParentHeight() const;
+ void setParentHeight(int height);
+
+ /** Returns the best guess of the polarity of this area */
+ Polarity getPolarity() const;
+
+private:
+ int mParentWidth = -1;
+ int mParentHeight = -1;
+
+ uint64_t mLight = 0;
+ uint64_t mDark = 0;
+};
+
+} // namespace android::uirenderer
diff --git a/libs/hwui/DisplayList.h b/libs/hwui/DisplayList.h
index b1c5bf4..894454d 100644
--- a/libs/hwui/DisplayList.h
+++ b/libs/hwui/DisplayList.h
@@ -16,12 +16,13 @@
#pragma once
-#include "pipeline/skia/SkiaDisplayList.h"
-#include "canvas/CanvasOpBuffer.h"
-
#include <memory>
#include <variant>
+#include "ColorArea.h"
+#include "canvas/CanvasOpBuffer.h"
+#include "pipeline/skia/SkiaDisplayList.h"
+
namespace android {
namespace uirenderer {
@@ -153,6 +154,13 @@
}
}
+ /** Adds all relevant draw calls with fills to the given ColorArea */
+ void findFillAreas(ColorArea& accumulator) {
+ if (mImpl) {
+ mImpl->findFillAreas(accumulator);
+ }
+ }
+
private:
std::unique_ptr<skiapipeline::SkiaDisplayList> mImpl;
};
diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp
index 0d09fb1..40d311d 100644
--- a/libs/hwui/RecordingCanvas.cpp
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -27,6 +27,7 @@
#include <experimental/type_traits>
#include <utility>
+#include "FeatureFlags.h"
#include "Mesh.h"
#include "SkAndroidFrameworkUtils.h"
#include "SkBlendMode.h"
@@ -801,6 +802,91 @@
return (value & (value - 1)) == 0;
}
+template <class T>
+using has_paint_helper = decltype(std::declval<T>().paint);
+
+template <class T>
+constexpr bool has_paint = std::experimental::is_detected_v<has_paint_helper, T>;
+
+template <class T>
+using has_palette_helper = decltype(std::declval<T>().palette);
+
+template <class T>
+constexpr bool has_palette = std::experimental::is_detected_v<has_palette_helper, T>;
+
+inline bool DisplayListData::shouldCountColorAreas() const {
+ return view_accessibility_flags::force_invert_color();
+}
+
+typedef void (*color_area_fn)(const void*, ColorArea*);
+
+template <class T>
+using has_bounds_helper = decltype(std::declval<T>().getConservativeBounds());
+
+template <class T>
+constexpr bool has_bounds = std::experimental::is_detected_v<has_bounds_helper, T>;
+
+template <class T>
+constexpr color_area_fn colorAreaForOp() {
+ if constexpr (has_palette<T> && has_bounds<T>) {
+ return [](const void* opRaw, ColorArea* accumulator) {
+ const T* op = reinterpret_cast<const T*>(opRaw);
+ const SkPaint* paint = &op->paint;
+ if (!paint) return;
+
+ auto rect = op->getConservativeBounds();
+ if (!rect.has_value()) return;
+
+ accumulator->addArea(*rect, *paint, op->palette);
+ };
+ } else if constexpr (has_paint<T> && has_bounds<T>) {
+ return [](const void* opRaw, ColorArea* accumulator) {
+ const T* op = reinterpret_cast<const T*>(opRaw);
+ const SkPaint* paint = &op->paint;
+ if (!paint) return;
+
+ auto rect = op->getConservativeBounds();
+ if (!rect.has_value()) return;
+
+ accumulator->addArea(*rect, paint);
+ };
+ } else {
+ return nullptr;
+ }
+}
+
+template <>
+constexpr color_area_fn colorAreaForOp<DrawBehind>() {
+ return [](const void* opRaw, ColorArea* accumulator) {
+ const DrawBehind* op = reinterpret_cast<const DrawBehind*>(opRaw);
+ const SkPaint* paint = &op->paint;
+
+ // drawColor() fills the entire canvas area / RenderNode. We are ignoring clipping for now,
+ // since usually people only slightly exceed their bounds.
+ accumulator->addArea(accumulator->getParentWidth() * accumulator->getParentHeight(),
+ *paint);
+ };
+}
+
+template <>
+constexpr color_area_fn colorAreaForOp<DrawPaint>() {
+ return [](const void* opRaw, ColorArea* accumulator) {
+ const DrawPaint* op = reinterpret_cast<const DrawPaint*>(opRaw);
+ const SkPaint* paint = &op->paint;
+
+ // drawColor() fills the entire canvas area / RenderNode. We are ignoring clipping for now,
+ // since usually people only slightly exceed their bounds.
+ accumulator->addArea(accumulator->getParentWidth() * accumulator->getParentHeight(),
+ *paint);
+ };
+}
+
+#define X(T) colorAreaForOp<T>(),
+static const color_area_fn color_area_fns[] = {
+#include "DisplayListOps.in"
+};
+#undef X
+
template <typename T>
constexpr bool doesPaintHaveFill(T& paint) {
using T1 = std::remove_cv_t<T>;
@@ -848,6 +934,12 @@
}
}
+ if (shouldCountColorAreas()) {
+ if (auto fn = color_area_fns[op->type]) {
+ fn(op, &mColorArea);
+ }
+ }
+
return op + 1;
}
@@ -1066,44 +1158,32 @@
// Leave fBytes and fReserved alone.
fUsed = 0;
+
+ // TODO(b/372558459): reset here only?
+ mColorArea.reset();
}
template <class T>
-using has_paint_helper = decltype(std::declval<T>().paint);
-
-template <class T>
-constexpr bool has_paint = std::experimental::is_detected_v<has_paint_helper, T>;
-
-template <class T>
-using has_palette_helper = decltype(std::declval<T>().palette);
-
-template <class T>
-constexpr bool has_palette = std::experimental::is_detected_v<has_palette_helper, T>;
-
-template <class T>
constexpr color_transform_fn colorTransformForOp() {
if
constexpr(has_paint<T> && has_palette<T>) {
- // It's a bitmap
- return [](const void* opRaw, ColorTransform transform) {
- // TODO: We should be const. Or not. Or just use a different map
- // Unclear, but this is the quick fix
- const T* op = reinterpret_cast<const T*>(opRaw);
- const SkPaint* paint = &op->paint;
- transformPaint(transform, const_cast<SkPaint*>(paint), op->palette);
- };
- }
- else if
- constexpr(has_paint<T>) {
- return [](const void* opRaw, ColorTransform transform) {
- // TODO: We should be const. Or not. Or just use a different map
- // Unclear, but this is the quick fix
- const T* op = reinterpret_cast<const T*>(opRaw);
- const SkPaint* paint = &op->paint;
- transformPaint(transform, const_cast<SkPaint*>(paint));
- };
- }
- else {
+ // It's a bitmap
+ return [](const void* opRaw, ColorTransform transform) {
+ // TODO: We should be const. Or not. Or just use a different map
+ // Unclear, but this is the quick fix
+ const T* op = reinterpret_cast<const T*>(opRaw);
+ const SkPaint* paint = &op->paint;
+ transformPaint(transform, const_cast<SkPaint*>(paint), op->palette);
+ };
+ } else if constexpr (has_paint<T>) {
+ return [](const void* opRaw, ColorTransform transform) {
+ // TODO: We should be const. Or not. Or just use a different map
+ // Unclear, but this is the quick fix
+ const T* op = reinterpret_cast<const T*>(opRaw);
+ const SkPaint* paint = &op->paint;
+ transformPaint(transform, const_cast<SkPaint*>(paint));
+ };
+ } else {
return nullptr;
}
}
@@ -1146,6 +1226,15 @@
this->map(color_transform_fns, transform);
}
+void DisplayListData::findFillAreas(ColorArea& accumulator) {
+ accumulator.merge(mColorArea);
+}
+
+void DisplayListData::setBounds(const SkIRect& bounds) {
+ mColorArea.setParentWidth(bounds.width());
+ mColorArea.setParentHeight(bounds.height());
+}
+
RecordingCanvas::RecordingCanvas() : INHERITED(1, 1), fDL(nullptr) {}
void RecordingCanvas::reset(DisplayListData* dl, const SkIRect& bounds) {
@@ -1153,6 +1242,9 @@
fDL = dl;
mClipMayBeComplex = false;
mSaveCount = mComplexSaveCount = 0;
+
+ // TODO(b/372558459) - Check if dl->reset() should be called here
+ dl->setBounds(bounds);
}
sk_sp<SkSurface> RecordingCanvas::onNewSurface(const SkImageInfo&, const SkSurfaceProps&) {
diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h
index f867852..a4ac8d4 100644
--- a/libs/hwui/RecordingCanvas.h
+++ b/libs/hwui/RecordingCanvas.h
@@ -32,6 +32,7 @@
#include <vector>
#include "CanvasTransform.h"
+#include "ColorArea.h"
#include "Gainmap.h"
#include "hwui/Bitmap.h"
#include "pipeline/skia/AnimatedDrawables.h"
@@ -105,15 +106,27 @@
void draw(SkCanvas* canvas) const;
void reset();
+
+ /** Resets the ColorArea counter with the new bounds */
+ void setBounds(const SkIRect& bounds);
+
bool empty() const { return fUsed == 0; }
void applyColorTransform(ColorTransform transform);
+ /** Adds all relevant draw calls with fills to the given ColorArea */
+ void findFillAreas(ColorArea& accumulator);
+
+ const ColorArea& getColorArea() const { return mColorArea; }
+
bool hasText() const { return mHasText; }
bool hasFill() const { return mHasFill; }
size_t usedSize() const { return fUsed; }
size_t allocatedSize() const { return fReserved; }
+ /** Returns true if this list should count ColorAreas as Ops are recorded. */
+ bool shouldCountColorAreas() const;
+
private:
friend class RecordingCanvas;
@@ -184,6 +197,8 @@
bool mHasText : 1;
bool mHasFill : 1;
+
+ ColorArea mColorArea;
};
class RecordingCanvas final : public SkCanvasVirtualEnforcer<SkNoDrawCanvas> {
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index 8b4e59a..d0d4b8b 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -37,6 +37,7 @@
#ifdef __ANDROID__
#include "include/gpu/ganesh/SkImageGanesh.h"
#endif
+#include "FeatureFlags.h"
#include "utils/ForceDark.h"
#include "utils/MathUtils.h"
#include "utils/StringUtils.h"
@@ -409,8 +410,10 @@
// Return true if the tree should use the force invert feature that inverts
// the entire tree to darken it.
inline bool RenderNode::isForceInvertDark(TreeInfo& info) {
- return CC_UNLIKELY(info.forceDarkType ==
- android::uirenderer::ForceDarkType::FORCE_INVERT_COLOR_DARK);
+ return CC_UNLIKELY(view_accessibility_flags::force_invert_color() &&
+ info.forceDarkType ==
+ android::uirenderer::ForceDarkType::FORCE_INVERT_COLOR_DARK &&
+ info.colorArea && info.colorArea->getPolarity() == Polarity::Light);
}
// Return true if the tree should use the force dark feature that selectively
@@ -419,12 +422,31 @@
return CC_UNLIKELY(info && !info->disableForceDark);
}
-void RenderNode::handleForceDark(TreeInfo *info) {
- if (CC_UNLIKELY(view_accessibility_flags::force_invert_color() && info &&
- isForceInvertDark(*info))) {
+void RenderNode::gatherColorAreasForSubtree(ColorArea& target, bool isModeFull) {
+ SkiaDisplayListWrapper* displayList = &mDisplayList;
+ if (isModeFull && mNeedsDisplayListSync) {
+ displayList = &mStagingDisplayList;
+ }
+
+ if (displayList && displayList->isValid() && !(displayList->isEmpty())) {
+ displayList->findFillAreas(target);
+ displayList->updateChildren([&target, &isModeFull](RenderNode* node) {
+ if (!node) return;
+
+ node->gatherColorAreasForSubtree(target, isModeFull);
+ });
+ }
+}
+
+void RenderNode::handleForceDark(android::uirenderer::TreeInfo* info) {
+ if (CC_UNLIKELY(info && isForceInvertDark(*info))) {
+ // TODO(b/391959649): what about apps who have opted in to force dark, but only partially?
+ // will this mess them up? e.g. if they set disableForceDark but only on a few nodes.
+ // The app is too bright, captain! Reverse the polarity!
mDisplayList.applyColorTransform(ColorTransform::Invert);
return;
}
+
if (!shouldEnableForceDark(info)) {
return;
}
diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h
index afbbce7..7ea919b 100644
--- a/libs/hwui/RenderNode.h
+++ b/libs/hwui/RenderNode.h
@@ -225,6 +225,9 @@
void markDrawStart(SkCanvas& canvas);
void markDrawEnd(SkCanvas& canvas);
+ /** Accumulates all the background color areas of all children into the given target */
+ void gatherColorAreasForSubtree(ColorArea& target, bool isModeFull);
+
private:
void computeOrderingImpl(RenderNodeOp* opState,
std::vector<RenderNodeOp*>* compositedChildrenOfProjectionSurface,
diff --git a/libs/hwui/TreeInfo.cpp b/libs/hwui/TreeInfo.cpp
index 717157c0..fa0af63 100644
--- a/libs/hwui/TreeInfo.cpp
+++ b/libs/hwui/TreeInfo.cpp
@@ -24,7 +24,7 @@
: mode(mode)
, prepareTextures(mode == MODE_FULL)
, canvasContext(canvasContext)
- , disableForceDark(canvasContext.getForceDarkType() == ForceDarkType::NONE ? 1 : 0)
+ , disableForceDark(canvasContext.getForceDarkType() == ForceDarkType::FORCE_DARK ? 0 : 1)
, forceDarkType(canvasContext.getForceDarkType())
, screenSize(canvasContext.getNextFrameSize()) {}
diff --git a/libs/hwui/TreeInfo.h b/libs/hwui/TreeInfo.h
index 88449f3..36152a66 100644
--- a/libs/hwui/TreeInfo.h
+++ b/libs/hwui/TreeInfo.h
@@ -21,6 +21,7 @@
#include <optional>
#include <string>
+#include "ColorArea.h"
#include "Properties.h"
#include "SkSize.h"
#include "SkippedFrameInfo.h"
@@ -92,6 +93,8 @@
DamageAccumulator* damageAccumulator = nullptr;
int64_t damageGenerationId = 0;
+ ColorArea* colorArea = nullptr;
+
LayerUpdateQueue* layerUpdateQueue = nullptr;
ErrorHandler* errorHandler = nullptr;
diff --git a/libs/hwui/aconfig/hwui_flags.aconfig b/libs/hwui/aconfig/hwui_flags.aconfig
index b3badd0..47d451a 100644
--- a/libs/hwui/aconfig/hwui_flags.aconfig
+++ b/libs/hwui/aconfig/hwui_flags.aconfig
@@ -210,4 +210,12 @@
namespace: "system_performance"
description: "Whether to parcel implicit copies of bitmaps to ashmem as immutable"
bug: "400807118"
+}
+
+flag {
+ name: "bitmap_use_memfd"
+ namespace: "system_performance"
+ description: "Whether to parcel implicit copies of bitmaps to memfd instead of ashmem"
+ bug: "400807118"
+ is_fixed_read_only: true
}
\ No newline at end of file
diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp
index 0650169..fbf934e 100644
--- a/libs/hwui/hwui/Bitmap.cpp
+++ b/libs/hwui/hwui/Bitmap.cpp
@@ -212,7 +212,14 @@
#ifdef __ANDROID__ // Layoutlib does not support hardware acceleration
return uirenderer::HardwareBitmapUploader::allocateHardwareBitmap(bitmap);
#else
- return Bitmap::allocateHeapBitmap(bitmap.info());
+ sk_sp<Bitmap> dest = Bitmap::allocateHeapBitmap(bitmap.info());
+
+ // HardwareBitmapUploader::allocateHardwareBitmap(SkBitmap&) copies Bitmap contents
+ // to a GL texture. To simulate this with an heap bitmap, we use memcpy.
+ auto destPM = dest->getSkBitmap().pixmap();
+ LOG_ALWAYS_FATAL_IF(!bitmap.pixmap().readPixels(destPM), "failed to copy pixels");
+
+ return dest;
#endif
}
diff --git a/libs/hwui/jni/Bitmap.cpp b/libs/hwui/jni/Bitmap.cpp
index 5e24161..5ca15e4 100644
--- a/libs/hwui/jni/Bitmap.cpp
+++ b/libs/hwui/jni/Bitmap.cpp
@@ -5,11 +5,15 @@
#ifdef __linux__
#include <com_android_graphics_hwui_flags.h>
#endif
+#include <fcntl.h>
#include <hwui/Bitmap.h>
#include <hwui/Paint.h>
#include <inttypes.h>
#include <renderthread/RenderProxy.h>
#include <string.h>
+#include <sys/mman.h>
+#include <sys/syscall.h>
+#include <unistd.h>
#include <memory>
@@ -675,10 +679,10 @@
return STATUS_OK;
}
-static binder_status_t writeBlob(AParcel* parcel, uint64_t bitmapId, const SkBitmap& bitmap) {
+static binder_status_t writeBlob(AParcel* parcel, uint64_t bitmapId, const SkBitmap& bitmap,
+ bool immutable) {
const size_t size = bitmap.computeByteSize();
const void* data = bitmap.getPixels();
- const bool immutable = bitmap.isImmutable();
if (size <= 0 || data == nullptr) {
return STATUS_NOT_ENOUGH_DATA;
@@ -688,23 +692,52 @@
// Create new ashmem region with read/write priv
auto ashmemId = Bitmap::getAshmemId("writeblob", bitmapId,
bitmap.width(), bitmap.height(), size);
- base::unique_fd fd(ashmem_create_region(ashmemId.c_str(), size));
- if (fd.get() < 0) {
- return STATUS_NO_MEMORY;
- }
+ base::unique_fd fd;
- {
- void* dest = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd.get(), 0);
- if (dest == MAP_FAILED) {
+ if (com::android::graphics::hwui::flags::bitmap_use_memfd()) {
+ fd.reset(syscall(__NR_memfd_create, ashmemId.c_str(), MFD_CLOEXEC | MFD_ALLOW_SEALING));
+ if (fd.get() < 0) {
return STATUS_NO_MEMORY;
}
- memcpy(dest, data, size);
- munmap(dest, size);
+
+ ssize_t written = write(fd.get(), data, size);
+ if (written != size) {
+ return STATUS_NO_MEMORY;
+ }
+
+ if (fcntl(fd, F_ADD_SEALS,
+ // Disallow growing / shrinking.
+ F_SEAL_GROW | F_SEAL_SHRINK
+ // If immutable, disallow writing.
+ // Use F_SEAL_FUTURE_WRITE instead of F_SEAL_WRITE to work around a bug in
+ // pre-6.7 kernels.
+ // There are no writable mappings made prior to this, so both seals are
+ // functionally equivalent.
+ // See: b/409846908#comment39
+ | (immutable ? F_SEAL_FUTURE_WRITE : 0))) {
+ return STATUS_UNKNOWN_ERROR;
+ }
+
+ } else {
+ fd.reset(ashmem_create_region(ashmemId.c_str(), size));
+ if (fd.get() < 0) {
+ return STATUS_NO_MEMORY;
+ }
+
+ {
+ void* dest = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd.get(), 0);
+ if (dest == MAP_FAILED) {
+ return STATUS_NO_MEMORY;
+ }
+ memcpy(dest, data, size);
+ munmap(dest, size);
+ }
+
+ if (immutable && ashmem_set_prot_region(fd.get(), PROT_READ) < 0) {
+ return STATUS_UNKNOWN_ERROR;
+ }
}
- if (immutable && ashmem_set_prot_region(fd.get(), PROT_READ) < 0) {
- return STATUS_UNKNOWN_ERROR;
- }
// Workaround b/149851140 in AParcel_writeParcelFileDescriptor
int rawFd = fd.release();
error = writeBlobFromFd(parcel, size, rawFd);
@@ -880,7 +913,8 @@
bitmapWrapper->getSkBitmap(&bitmap);
uint64_t id = bitmapWrapper->bitmap().getId();
- p.writeInt32(shouldParcelAsMutable(bitmap, p.get()));
+ const bool asMutable = shouldParcelAsMutable(bitmap, p.get());
+ p.writeInt32(asMutable);
p.writeInt32(bitmap.colorType());
p.writeInt32(bitmap.alphaType());
SkColorSpace* colorSpace = bitmap.colorSpace();
@@ -918,7 +952,7 @@
ALOGD("Bitmap.writeToParcel: copying bitmap into new blob (fds %s)",
p.allowFds() ? "allowed" : "forbidden");
#endif
- status = writeBlob(p.get(), id, bitmap);
+ status = writeBlob(p.get(), id, bitmap, !asMutable);
if (status) {
doThrowRE(env, "Could not copy bitmap to parcel blob.");
return JNI_FALSE;
diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.h b/libs/hwui/pipeline/skia/SkiaDisplayList.h
index 071a4e8..508a856 100644
--- a/libs/hwui/pipeline/skia/SkiaDisplayList.h
+++ b/libs/hwui/pipeline/skia/SkiaDisplayList.h
@@ -126,6 +126,9 @@
mDisplayList.applyColorTransform(transform);
}
+ /** Adds all relevant draw calls with fills to the given ColorArea */
+ void findFillAreas(ColorArea& accumulator) { mDisplayList.findFillAreas(accumulator); }
+
/**
* ONLY to be called by RenderNode::prepareTree in order to prepare this
* list while the UI thread is blocked. Here we can upload mutable bitmaps
diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
index e1de1e6..05020ab 100644
--- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
@@ -214,7 +214,11 @@
}
const SkM44& SkiaVulkanPipeline::getPixelSnapMatrix() const {
- return mVkSurface->getPixelSnapMatrix();
+ if (mVkSurface) {
+ return mVkSurface->getPixelSnapMatrix();
+ }
+ static const SkM44 sSnapMatrix = VulkanSurface::GetPixelSnapMatrix(SkISize::MakeEmpty(), 0);
+ return sSnapMatrix;
}
} /* namespace skiapipeline */
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index d5ac993..b8e61fd 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -36,6 +36,8 @@
#include "../Properties.h"
#include "AnimationContext.h"
+#include "ColorArea.h"
+#include "FeatureFlags.h"
#include "Frame.h"
#include "LayerUpdateQueue.h"
#include "Properties.h"
@@ -476,11 +478,17 @@
mCurrentFrameInfo->markSyncStart();
info.damageAccumulator = &mDamageAccumulator;
+ info.colorArea = &mColorArea;
info.layerUpdateQueue = &mLayerUpdateQueue;
info.damageGenerationId = mDamageId++;
info.out.skippedFrameReason = std::nullopt;
mAnimationContext->startFrame(info.mode);
+
+ if (target) {
+ determineColors(target);
+ }
+
for (const sp<RenderNode>& node : mRenderNodes) {
// Only the primary target node will be drawn full - all other nodes would get drawn in
// real time mode. In case of a window, the primary node is the window content and the other
@@ -1026,6 +1034,7 @@
ScopedActiveContext activeContext(this);
TreeInfo info(TreeInfo::MODE_FULL, *this);
info.damageAccumulator = &mDamageAccumulator;
+ info.colorArea = &mColorArea;
info.layerUpdateQueue = &mLayerUpdateQueue;
info.runAnimations = false;
node->prepareTree(info);
@@ -1165,6 +1174,20 @@
return windowDirty;
}
+void CanvasContext::determineColors(const RenderNode* target) {
+ if (CC_UNLIKELY(view_accessibility_flags::force_invert_color() &&
+ mForceDarkType == ForceDarkType::FORCE_INVERT_COLOR_DARK)) {
+ ATRACE_FORMAT("determineColors(): Color area calculation pre-pass");
+
+ // first pass: figure out if the app is light or dark mode
+ mColorArea.reset();
+
+ for (const sp<RenderNode>& node : mRenderNodes) {
+ node->gatherColorAreasForSubtree(mColorArea, target == node.get());
+ }
+ }
+}
+
CanvasContext* CanvasContext::getActiveContext() {
return ScopedActiveContext::getActiveContext();
}
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 655aeba..1e691ec 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -30,6 +30,7 @@
#include <utility>
#include <vector>
+#include "ColorArea.h"
#include "ColorMode.h"
#include "DamageAccumulator.h"
#include "FrameInfo.h"
@@ -207,6 +208,8 @@
SkISize getNextFrameSize() const;
+ const ColorArea& getColorArea() const { return mColorArea; }
+
// Returns the matrix to use to nudge non-AA'd points/lines towards the fragment center
const SkM44& getPixelSnapMatrix() const;
@@ -334,6 +337,7 @@
bool mHaveNewSurface = false;
DamageAccumulator mDamageAccumulator;
+ ColorArea mColorArea;
LayerUpdateQueue mLayerUpdateQueue;
std::unique_ptr<AnimationContext> mAnimationContext;
@@ -382,6 +386,15 @@
int64_t startTime;
};
std::optional<SkippedFrameInfo> mSkippedFrameInfo;
+
+ /**
+ * Does a pre-pass on all the nodes and populates mColorArea with color information. Used by
+ * force invert to determine if the app is light theme.
+ *
+ * @param target the target node to run in MODE_FULL. For this node, the staging displaylist is
+ * used. All other nodes are treated in MODE_RT, using their main displaylists.
+ */
+ void determineColors(const RenderNode* target);
};
} /* namespace renderthread */
diff --git a/libs/hwui/renderthread/VulkanSurface.cpp b/libs/hwui/renderthread/VulkanSurface.cpp
index 20c2b1a..af6cd76 100644
--- a/libs/hwui/renderthread/VulkanSurface.cpp
+++ b/libs/hwui/renderthread/VulkanSurface.cpp
@@ -65,7 +65,7 @@
return SkMatrix::I();
}
-static SkM44 GetPixelSnapMatrix(SkISize windowSize, int transform) {
+SkM44 VulkanSurface::GetPixelSnapMatrix(SkISize windowSize, int transform) {
// Small (~1/16th) nudge to ensure that pixel-aligned non-AA'd draws fill the
// desired fragment
static const SkScalar kOffset = 0.063f;
diff --git a/libs/hwui/renderthread/VulkanSurface.h b/libs/hwui/renderthread/VulkanSurface.h
index 116075c..a529f12 100644
--- a/libs/hwui/renderthread/VulkanSurface.h
+++ b/libs/hwui/renderthread/VulkanSurface.h
@@ -48,6 +48,7 @@
void setColorSpace(sk_sp<SkColorSpace> colorSpace);
const SkM44& getPixelSnapMatrix() const { return mWindowInfo.pixelSnapMatrix; }
+ static SkM44 GetPixelSnapMatrix(SkISize windowSize, int transform);
private:
/*
diff --git a/libs/hwui/tests/common/TestContext.cpp b/libs/hwui/tests/common/TestContext.cpp
index e427c97..715f533 100644
--- a/libs/hwui/tests/common/TestContext.cpp
+++ b/libs/hwui/tests/common/TestContext.cpp
@@ -19,6 +19,8 @@
#include <com_android_graphics_libgui_flags.h>
#include <cutils/trace.h>
+#include "gui/BufferItemConsumer.h"
+
namespace android {
namespace uirenderer {
namespace test {
@@ -102,24 +104,11 @@
}
void TestContext::createOffscreenSurface() {
-#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
- mConsumer = new BufferItemConsumer(GRALLOC_USAGE_HW_COMPOSER, 4);
+ std::tie(mConsumer, mSurface) = BufferItemConsumer::create(GRALLOC_USAGE_HW_COMPOSER, 4);
const ui::Size& resolution = getActiveDisplayResolution();
mConsumer->setDefaultBufferSize(resolution.getWidth(), resolution.getHeight());
- mSurface = mConsumer->getSurface();
mSurface->setMaxDequeuedBufferCount(3);
mSurface->setAsyncMode(true);
-#else
- sp<IGraphicBufferProducer> producer;
- sp<IGraphicBufferConsumer> consumer;
- BufferQueue::createBufferQueue(&producer, &consumer);
- producer->setMaxDequeuedBufferCount(3);
- producer->setAsyncMode(true);
- mConsumer = new BufferItemConsumer(consumer, GRALLOC_USAGE_HW_COMPOSER, 4);
- const ui::Size& resolution = getActiveDisplayResolution();
- mConsumer->setDefaultBufferSize(resolution.getWidth(), resolution.getHeight());
- mSurface = new Surface(producer);
-#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
}
void TestContext::waitForVsync() {
diff --git a/libs/hwui/tests/unit/CanvasContextTests.cpp b/libs/hwui/tests/unit/CanvasContextTests.cpp
index 47a4105..2f41e5e 100644
--- a/libs/hwui/tests/unit/CanvasContextTests.cpp
+++ b/libs/hwui/tests/unit/CanvasContextTests.cpp
@@ -17,6 +17,7 @@
#include <gtest/gtest.h>
#include "AnimationContext.h"
+#include "FeatureFlags.h"
#include "IContextFactory.h"
#include "renderthread/CanvasContext.h"
#include "renderthread/VulkanManager.h"
@@ -78,3 +79,95 @@
}
}
}
+
+RENDERTHREAD_TEST(CanvasContext, forceInvertColorArea_detectsLightTheme) {
+ if (!view_accessibility_flags::force_invert_color()) {
+ GTEST_SKIP() << "Test only applies when force_invert_colorarea_detector flag is enabled";
+ }
+
+ auto buttonNode =
+ TestUtils::createNode(0, 0, 50, 100, [](RenderProperties& props, Canvas& canvas) {
+ Paint paint;
+ paint.setStyle(SkPaint::Style::kFill_Style);
+ paint.setColor(0xFFEE11CC);
+
+ canvas.drawRoundRect(0, 0, 50, 100, 5, 5, paint);
+ });
+ auto panelNode = TestUtils::createNode(0, 0, 100, 200,
+ [buttonNode](RenderProperties& props, Canvas& canvas) {
+ Paint paint;
+ paint.setStyle(SkPaint::Style::kFill_Style);
+ paint.setColor(0xEE111111);
+
+ canvas.drawRect(0, 0, 100, 200, paint);
+ canvas.drawRenderNode(buttonNode.get());
+ });
+
+ auto node = TestUtils::createNode(0, 0, 200, 400,
+ [panelNode](RenderProperties& props, Canvas& canvas) {
+ canvas.drawColor(0xFFEEEEE1, SkBlendMode::kSrc);
+
+ canvas.drawRenderNode(panelNode.get());
+ });
+ node->mutateStagingProperties().mutateLayerProperties().setType(LayerType::RenderLayer);
+
+ auto& cacheManager = renderThread.cacheManager();
+ ContextFactory contextFactory;
+ std::unique_ptr<CanvasContext> canvasContext(
+ CanvasContext::create(renderThread, false, node.get(), &contextFactory, 0, 0));
+ canvasContext->setForceDark(android::uirenderer::ForceDarkType::FORCE_INVERT_COLOR_DARK);
+
+ EXPECT_EQ(canvasContext->getColorArea().getPolarity(), Unknown);
+
+ canvasContext->prepareAndDraw(node.get());
+
+ EXPECT_EQ(canvasContext->getColorArea().getPolarity(), Light);
+
+ renderThread.destroyRenderingContext();
+}
+
+RENDERTHREAD_TEST(CanvasContext, forceInvertColorArea_detectsDarkTheme) {
+ if (!view_accessibility_flags::force_invert_color()) {
+ GTEST_SKIP() << "Test only applies when force_invert_colorarea_detector flag is enabled";
+ }
+
+ auto buttonNode =
+ TestUtils::createNode(0, 0, 50, 100, [](RenderProperties& props, Canvas& canvas) {
+ Paint paint;
+ paint.setStyle(SkPaint::Style::kFill_Style);
+ paint.setColor(0xFFFF5566);
+
+ canvas.drawRoundRect(0, 0, 50, 100, 5, 5, paint);
+ });
+ auto panelNode = TestUtils::createNode(0, 0, 100, 200,
+ [buttonNode](RenderProperties& props, Canvas& canvas) {
+ Paint paint;
+ paint.setStyle(SkPaint::Style::kFill_Style);
+ paint.setColor(0xFFCCCCCC);
+
+ canvas.drawRect(0, 0, 100, 200, paint);
+ canvas.drawRenderNode(buttonNode.get());
+ });
+
+ auto node = TestUtils::createNode(0, 0, 200, 400,
+ [panelNode](RenderProperties& props, Canvas& canvas) {
+ canvas.drawColor(0xFF030102, SkBlendMode::kSrc);
+
+ canvas.drawRenderNode(panelNode.get());
+ });
+ node->mutateStagingProperties().mutateLayerProperties().setType(LayerType::RenderLayer);
+
+ auto& cacheManager = renderThread.cacheManager();
+ ContextFactory contextFactory;
+ std::unique_ptr<CanvasContext> canvasContext(
+ CanvasContext::create(renderThread, false, node.get(), &contextFactory, 0, 0));
+ canvasContext->setForceDark(android::uirenderer::ForceDarkType::FORCE_INVERT_COLOR_DARK);
+
+ EXPECT_EQ(canvasContext->getColorArea().getPolarity(), Unknown);
+
+ canvasContext->prepareAndDraw(node.get());
+
+ EXPECT_EQ(canvasContext->getColorArea().getPolarity(), Dark);
+
+ renderThread.destroyRenderingContext();
+}
diff --git a/location/java/android/location/LocationResult.java b/location/java/android/location/LocationResult.java
index 67f4775..c520e02 100644
--- a/location/java/android/location/LocationResult.java
+++ b/location/java/android/location/LocationResult.java
@@ -19,7 +19,6 @@
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.location.flags.Flags;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.SystemClock;
@@ -126,55 +125,44 @@
final int size = mLocations.size();
for (int i = 0; i < size; ++i) {
Location location = mLocations.get(i);
- if (Flags.locationValidation()) {
- if (location.getLatitude() < -90.0
- || location.getLatitude() > 90.0
- || location.getLongitude() < -180.0
- || location.getLongitude() > 180.0
- || Double.isNaN(location.getLatitude())
- || Double.isNaN(location.getLongitude())) {
- throw new BadLocationException("location must have valid lat/lng");
+ if (location.getLatitude() < -90.0
+ || location.getLatitude() > 90.0
+ || location.getLongitude() < -180.0
+ || location.getLongitude() > 180.0
+ || Double.isNaN(location.getLatitude())
+ || Double.isNaN(location.getLongitude())) {
+ throw new BadLocationException("location must have valid lat/lng");
+ }
+ if (!location.hasAccuracy()) {
+ throw new BadLocationException("location must have accuracy");
+ }
+ if (location.getAccuracy() < 0 || location.getAccuracy() > MAX_ACCURACY_M) {
+ throw new BadLocationException("location must have reasonable accuracy");
+ }
+ if (location.getTime() < 0) {
+ throw new BadLocationException("location must have valid time");
+ }
+ if (prevElapsedRealtimeNs > location.getElapsedRealtimeNanos()) {
+ throw new BadLocationException(
+ "location must have valid monotonically increasing realtime");
+ }
+ if (location.getElapsedRealtimeNanos()
+ > SystemClock.elapsedRealtimeNanos()) {
+ throw new BadLocationException("location must not have realtime in the future");
+ }
+ if (!location.isMock()) {
+ if (location.getProvider() == null) {
+ throw new BadLocationException("location must have valid provider");
}
- if (!location.hasAccuracy()) {
- throw new BadLocationException("location must have accuracy");
+ if (location.getLatitude() == 0 && location.getLongitude() == 0) {
+ throw new BadLocationException("location must not be at 0,0");
}
- if (location.getAccuracy() < 0 || location.getAccuracy() > MAX_ACCURACY_M) {
- throw new BadLocationException("location must have reasonable accuracy");
- }
- if (location.getTime() < 0) {
- throw new BadLocationException("location must have valid time");
- }
- if (prevElapsedRealtimeNs > location.getElapsedRealtimeNanos()) {
- throw new BadLocationException(
- "location must have valid monotonically increasing realtime");
- }
- if (location.getElapsedRealtimeNanos()
- > SystemClock.elapsedRealtimeNanos()) {
- throw new BadLocationException("location must not have realtime in the future");
- }
- if (!location.isMock()) {
- if (location.getProvider() == null) {
- throw new BadLocationException("location must have valid provider");
- }
- if (location.getLatitude() == 0 && location.getLongitude() == 0) {
- throw new BadLocationException("location must not be at 0,0");
- }
- }
+ }
- if (location.hasSpeed() && (location.getSpeed() < 0
- || location.getSpeed() > MAX_SPEED_MPS)) {
- Log.w(TAG, "removed bad location speed: " + location.getSpeed());
- location.removeSpeed();
- }
- } else {
- if (!location.isComplete()) {
- throw new IllegalArgumentException(
- "incomplete location at index " + i + ": " + mLocations);
- }
- if (location.getElapsedRealtimeNanos() < prevElapsedRealtimeNs) {
- throw new IllegalArgumentException(
- "incorrectly ordered location at index " + i + ": " + mLocations);
- }
+ if (location.hasSpeed() && (location.getSpeed() < 0
+ || location.getSpeed() > MAX_SPEED_MPS)) {
+ Log.w(TAG, "removed bad location speed: " + location.getSpeed());
+ location.removeSpeed();
}
prevElapsedRealtimeNs = location.getElapsedRealtimeNanos();
}
diff --git a/location/java/android/location/flags/location.aconfig b/location/java/android/location/flags/location.aconfig
index 4b460c6..427855a 100644
--- a/location/java/android/location/flags/location.aconfig
+++ b/location/java/android/location/flags/location.aconfig
@@ -78,47 +78,6 @@
}
flag {
- name: "release_supl_connection_on_timeout"
- namespace: "location"
- description: "Flag for releasing SUPL connection on timeout"
- bug: "315024652"
-}
-
-flag {
- name: "location_validation"
- namespace: "location"
- description: "Flag for location validation"
- bug: "314328533"
-}
-
-flag {
- name: "replace_future_elapsed_realtime_jni"
- namespace: "location"
- description: "Flag for replacing future elapsedRealtime in JNI"
- bug: "314328533"
-}
-
-flag {
- name: "use_legacy_ntp_time"
- namespace: "location"
- description: "Flag for switching to legacy NtpNetworkTimeHelper"
- bug: "368034558"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
- name: "subscriptions_changed_listener_thread"
- namespace: "location"
- description: "Flag for running onSubscriptionsChangedListener on FgThread"
- bug: "332451908"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
name: "gnss_configuration_from_resource"
namespace: "location"
description: "Flag for GNSS configuration from resource"
@@ -138,17 +97,6 @@
}
flag {
- name: "enable_ni_supl_message_injection_by_carrier_config"
- namespace: "location"
- description: "Flag for enabling NI SUPL message injection by carrier config"
- bug: "242105192"
- is_fixed_read_only: true
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
name: "population_density_provider"
namespace: "location"
description: "Flag for enabling the population density provider"
diff --git a/media/java/android/media/AudioDeviceVolumeManager.java b/media/java/android/media/AudioDeviceVolumeManager.java
index 311d64f..7c90da1 100644
--- a/media/java/android/media/AudioDeviceVolumeManager.java
+++ b/media/java/android/media/AudioDeviceVolumeManager.java
@@ -280,7 +280,7 @@
android.Manifest.permission.BLUETOOTH_PRIVILEGED })
public void register(boolean register, @NonNull AudioDeviceAttributes device,
@NonNull List<VolumeInfo> volumes, boolean handlesVolumeAdjustment,
- @AbsoluteDeviceVolumeBehavior int behavior) {
+ @DeviceVolumeBehaviorState int behavior) {
try {
getService().registerDeviceVolumeDispatcherForAbsoluteVolume(register,
this, mPackageName,
@@ -569,6 +569,29 @@
}
/**
+ * @hide
+ * Resets any set volume behavior to
+ * {@link AudioDeviceVolumeManager#DEVICE_VOLUME_BEHAVIOR_VARIABLE} for the given device
+ */
+ @TestApi
+ @RequiresPermission(anyOf = { android.Manifest.permission.MODIFY_AUDIO_ROUTING,
+ android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ android.Manifest.permission.BLUETOOTH_STACK})
+ @FlaggedApi(FLAG_UNIFY_ABSOLUTE_VOLUME_MANAGEMENT)
+ public void resetDeviceAbsoluteVolumeBehavior(@NonNull AudioDeviceAttributes device) {
+ synchronized (mDeviceVolumeListenerLock) {
+ if (mDeviceVolumeDispatcherStub == null) {
+ mDeviceVolumeDispatcherStub = new DeviceVolumeDispatcherStub();
+ }
+
+ mDeviceVolumeDispatcherStub.register(/*register=*/false, device,
+ new ArrayList<>(), /*handlesVolumeAdjustment=*/false,
+ DEVICE_VOLUME_BEHAVIOR_VARIABLE);
+ }
+ }
+
+ /**
* Base method for configuring a device to use absolute volume behavior, or one of its variants.
* See {@link AbsoluteDeviceVolumeBehavior} for a list of allowed behaviors.
*
@@ -683,6 +706,28 @@
/**
* @hide
+ * Notifies the audio framework that the volume on the given absolute volume device has changed.
+ * @param vi the volume information, only stream-based volumes are supported
+ * @param ada the device for which volume is to be modified. Must have been registered before
+ * as an absolute volume device
+ */
+ @SystemApi
+ @RequiresPermission(anyOf = {
+ Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED,
+ Manifest.permission.BLUETOOTH_PRIVILEGED
+ })
+ @FlaggedApi(FLAG_UNIFY_ABSOLUTE_VOLUME_MANAGEMENT)
+ public void notifyAbsoluteVolumeChanged(@NonNull VolumeInfo vi,
+ @NonNull AudioDeviceAttributes ada) {
+ try {
+ getService().notifyAbsoluteVolumeChanged(vi, ada, mPackageName);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * @hide
* Returns the volume on the given audio device for the given volume information.
* For instance if using a {@link VolumeInfo} configured for {@link AudioManager#STREAM_ALARM},
* it will return the alarm volume. When no volume index has ever been set for the given
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index a8071af..08da45e 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -5881,6 +5881,8 @@
final Message m = arci.mHandler.obtainMessage(
MSSG_PLAYBACK_CONFIG_CHANGE/*what*/,
new PlaybackConfigChangeCallbackData(arci.mCb, configs)/*obj*/);
+ // only the last config matters, discard unprocessed ones
+ arci.mHandler.removeMessages(MSSG_PLAYBACK_CONFIG_CHANGE);
arci.mHandler.sendMessage(m);
}
}
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 4262950..75a8f39 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -247,7 +247,8 @@
public static final int AUDIO_FORMAT_LC3 = 0x2B000000;
/** @hide */
public static final int AUDIO_FORMAT_OPUS = 0x08000000;
-
+ /** @hide */
+ public static final int AUDIO_FORMAT_OPUS_HI_RES = 0x08000001;
/** @hide */
@IntDef(flag = false, prefix = "AUDIO_FORMAT_", value = {
@@ -259,7 +260,8 @@
AUDIO_FORMAT_APTX_HD,
AUDIO_FORMAT_LDAC,
AUDIO_FORMAT_LC3,
- AUDIO_FORMAT_OPUS
+ AUDIO_FORMAT_OPUS,
+ AUDIO_FORMAT_OPUS_HI_RES,
}
)
@Retention(RetentionPolicy.SOURCE)
@@ -267,7 +269,10 @@
/** @hide */
@IntDef(flag = false, prefix = "AUDIO_FORMAT_", value = {
- AUDIO_FORMAT_LC3}
+ AUDIO_FORMAT_LC3,
+ AUDIO_FORMAT_OPUS,
+ AUDIO_FORMAT_OPUS_HI_RES,
+ }
)
@Retention(RetentionPolicy.SOURCE)
public @interface AudioFormatNativeEnumForBtLeAudioCodec {}
@@ -281,6 +286,8 @@
@Retention(RetentionPolicy.SOURCE)
public @interface BtOffloadDeviceType {}
+ //TODO b/396350294 : remove when BluetoothLeCodecConfig.SOURCE_CODEC_TYPE_OPUS_HI_RES is public
+ private static final int BLUETOOTH_LE_AUDIO_CODEC_CONFIG_SOURCE_CODEC_TYPE_OPUS_HI_RES = 2;
/**
* @hide
* Convert audio format enum values to Bluetooth codec values
@@ -310,6 +317,9 @@
@AudioFormatNativeEnumForBtLeAudioCodec int audioFormat) {
switch (audioFormat) {
case AUDIO_FORMAT_LC3: return BluetoothLeAudioCodecConfig.SOURCE_CODEC_TYPE_LC3;
+ case AUDIO_FORMAT_OPUS: return BluetoothLeAudioCodecConfig.SOURCE_CODEC_TYPE_OPUS;
+ case AUDIO_FORMAT_OPUS_HI_RES:
+ return BLUETOOTH_LE_AUDIO_CODEC_CONFIG_SOURCE_CODEC_TYPE_OPUS_HI_RES;
default:
Log.e(TAG, "Unknown audio format 0x" + Integer.toHexString(audioFormat)
+ " for conversion to BT LE audio codec");
@@ -358,6 +368,10 @@
switch (btCodec) {
case BluetoothLeAudioCodecConfig.SOURCE_CODEC_TYPE_LC3:
return AudioSystem.AUDIO_FORMAT_LC3;
+ case BluetoothLeAudioCodecConfig.SOURCE_CODEC_TYPE_OPUS:
+ return AudioSystem.AUDIO_FORMAT_OPUS;
+ case AudioSystem.BLUETOOTH_LE_AUDIO_CODEC_CONFIG_SOURCE_CODEC_TYPE_OPUS_HI_RES:
+ return AudioSystem.AUDIO_FORMAT_OPUS_HI_RES;
default:
Log.e(TAG, "Unknown LE Audio BT codec 0x" + Integer.toHexString(btCodec)
+ " for conversion to audio format");
@@ -392,6 +406,8 @@
return "AUDIO_FORMAT_VORBIS";
case /* AUDIO_FORMAT_OPUS */ 0x08000000:
return "AUDIO_FORMAT_OPUS";
+ case /* AUDIO_FORMAT_OPUS_HI_RES */ 0x08000001:
+ return "AUDIO_FORMAT_OPUS_HI_RES";
case /* AUDIO_FORMAT_AC3 */ 0x09000000:
return "AUDIO_FORMAT_AC3";
case /* AUDIO_FORMAT_E_AC3 */ 0x0A000000:
@@ -2830,4 +2846,10 @@
*/
public static native int unregisterAudioVolumeGroupCallback(
INativeAudioVolumeGroupCallback callback);
+
+ /**
+ * Enable or disable device connection simulation at the HAL level.
+ * @hide
+ */
+ public static native int setSimulateDeviceConnections(boolean enabled);
}
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index b97b943..349e3e7 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -122,6 +122,10 @@
void setDeviceVolume(in VolumeInfo vi, in AudioDeviceAttributes ada,
in String callingPackage);
+ @EnforcePermission(anyOf = {"MODIFY_AUDIO_SETTINGS_PRIVILEGED", "BLUETOOTH_PRIVILEGED"})
+ oneway void notifyAbsoluteVolumeChanged(in VolumeInfo vi, in AudioDeviceAttributes ada,
+ in String callingPackage);
+
@EnforcePermission(anyOf = {"MODIFY_AUDIO_ROUTING", "MODIFY_AUDIO_SETTINGS_PRIVILEGED"})
VolumeInfo getDeviceVolume(in VolumeInfo vi, in AudioDeviceAttributes ada,
in String callingPackage);
diff --git a/media/java/android/media/MediaMuxer.java b/media/java/android/media/MediaMuxer.java
index 4c5efc1..cbddfa4 100644
--- a/media/java/android/media/MediaMuxer.java
+++ b/media/java/android/media/MediaMuxer.java
@@ -602,70 +602,70 @@
* <tbody>
* <tr>
* <td>{@link MediaFormat#MIMETYPE_AUDIO_AAC AAC}</td>
- * <td>✓</td>
+ * <td>✓</td>
* <td></td>
* <td></td>
- * <td>17</td>
+ * <td>16</td>
* </tr>
* <tr>
* <td>{@link MediaFormat#MIMETYPE_AUDIO_AMR_NB NB-AMR}</td>
- * <td>✓</td>
+ * <td>✓</td>
* <td></td>
* <td></td>
- * <td>17</td>
+ * <td>16</td>
* </tr>
* <tr>
* <td>{@link MediaFormat#MIMETYPE_AUDIO_AMR_WB WB-AMR}</td>
- * <td>✓</td>
+ * <td>✓</td>
* <td></td>
* <td></td>
- * <td>17</td>
+ * <td>16</td>
* </tr>
* <tr>
* <td>{@link MediaFormat#MIMETYPE_VIDEO_H263 H.263}</td>
- * <td>✓</td>
+ * <td>✓</td>
* <td></td>
* <td></td>
- * <td>17</td>
+ * <td>16</td>
* </tr>
* <tr>
* <td>{@link MediaFormat#MIMETYPE_VIDEO_MPEG4 MPEG-4}</td>
- * <td>✓</td>
+ * <td>✓</td>
* <td></td>
* <td></td>
- * <td>17</td>
+ * <td>16</td>
* </tr>
* <tr>
* <td>{@link MediaFormat#MIMETYPE_VIDEO_AVC AVC} (H.264)</td>
- * <td>✓</td>
+ * <td>✓</td>
* <td></td>
* <td></td>
- * <td>17</td>
+ * <td>16</td>
* </tr>
* <tr>
* <td>{@link MediaFormat#MIMETYPE_AUDIO_VORBIS Vorbis}</td>
* <td></td>
- * <td>✓</td>
+ * <td>✓</td>
* <td></td>
* <td>21</td>
* </tr>
* <tr>
* <td>{@link MediaFormat#MIMETYPE_VIDEO_VP8 VP8}</td>
* <td></td>
- * <td>✓</td>
+ * <td>✓</td>
* <td></td>
* <td>21</td>
* </tr>
* <tr>
* <td>{@link MediaFormat#MIMETYPE_VIDEO_VP9 VP9}</td>
* <td></td>
- * <td>✓</td>
+ * <td>✓</td>
* <td></td>
* <td>24</td>
* </tr>
* <tr>
* <td>{@link MediaFormat#MIMETYPE_VIDEO_HEVC HEVC} (H.265)</td>
- * <td>✓</td>
+ * <td>✓</td>
* <td></td>
* <td></td>
* <td>24</td>
@@ -673,23 +673,30 @@
* <tr>
* <td>{@link MediaFormat#MIMETYPE_AUDIO_OPUS OPUS}</td>
* <td></td>
- * <td>✓</td>
- * <td>✓</td>
- * <td>26</td>
- * </tr>
- * <tr>
- * <td>{@link MediaFormat#MIMETYPE_VIDEO_AV1 AV1}</td>
- * <td>✓</td>
- * <td></td>
- * <td></td>
- * <td>31</td>
+ * <td>✓</td>
+ * <td>✓</td>
+ * <td>29</td>
* </tr>
* <tr>
* <td>{@link MediaFormat#MIMETYPE_VIDEO_DOLBY_VISION Dolby Vision}</td>
- * <td>✓</td>
+ * <td>✓</td>
* <td></td>
* <td></td>
- * <td>32</td>
+ * <td>33</td>
+ * </tr>
+ * <tr>
+ * <td>{@link MediaFormat#MIMETYPE_VIDEO_AV1 AV1}</td>
+ * <td>✓</td>
+ * <td></td>
+ * <td></td>
+ * <td>34</td>
+ * </tr>
+ * <tr>
+ * <td>{@link MediaFormat#MIMETYPE_VIDEO_APV APV}</td>
+ * <td>✓</td>
+ * <td></td>
+ * <td></td>
+ * <td>36</td>
* </tr>
* </tbody>
* </table>
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index 63a6224..b1634f7 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -1091,6 +1091,12 @@
@NonNull MediaRoute2Info route,
long managerRequestId) {
+ if (Flags.fixTransferFromUserRouteToUnselectedSystemRoute() && route.isSystemRoute()) {
+ notifyTransfer(controller, getSystemController());
+ controller.release();
+ return;
+ }
+
final int requestId = mNextRequestId.getAndIncrement();
ControllerCreationRequest request =
@@ -2380,7 +2386,14 @@
@Override
void releaseInternal(boolean shouldReleaseSession) {
- // Do nothing. SystemRoutingController will never be released
+ // SystemRoutingController will never be released. But in some cases, the session can be
+ // released, for example Bluetooth broadcast
+ if (Flags.enableOutputSwitcherPersonalAudioSharing()) {
+ mImpl.releaseSession(
+ shouldReleaseSession,
+ /* shouldNotifyStop= */ false,
+ /* controller= */ this);
+ }
}
}
@@ -2979,9 +2992,9 @@
return;
}
- // If this call is trying to transfer to a selected system route, we let them
- // through as a provider driven transfer in order to update the transfer reason and
- // initiator data.
+ // If this call is trying to transfer from an existing system route to a selected system
+ // route, we will handle the transfer through as a provider driven transfer in order to
+ // update the transfer reason and initiator data.
boolean isSystemRouteReselection =
Flags.enableBuiltInSpeakerRouteSuitabilityStatuses()
&& sessionInfo.isSystemSession()
@@ -2991,6 +3004,19 @@
|| isSystemRouteReselection) {
transferToRoute(sessionInfo, route, mClientUser, mClientPackageName);
} else {
+ RoutingSessionInfo systemSessionInfo = mSystemController.getRoutingSessionInfo();
+ boolean isTransferFromUserRouteToUnselectedSystemRoute =
+ Flags.fixTransferFromUserRouteToUnselectedSystemRoute()
+ && !sessionInfo.isSystemSession()
+ && route.isSystemRoute()
+ && !systemSessionInfo.getSelectedRoutes().contains(route.getId());
+ if (isTransferFromUserRouteToUnselectedSystemRoute) {
+ // During a transfer from a user route to an unselected system route, the system
+ // session must first be transferred to the target system route. Subsequently,
+ // the user route to system route transfer is processed by releasing the user
+ // route.
+ transferToRoute(systemSessionInfo, route, mClientUser, mClientPackageName);
+ }
requestCreateSession(sessionInfo, route);
}
}
diff --git a/media/java/android/media/RoutingSessionInfo.java b/media/java/android/media/RoutingSessionInfo.java
index d27d7fc..9b70526 100644
--- a/media/java/android/media/RoutingSessionInfo.java
+++ b/media/java/android/media/RoutingSessionInfo.java
@@ -22,6 +22,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.os.Binder;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
@@ -85,6 +86,36 @@
@Retention(RetentionPolicy.SOURCE)
public @interface TransferReason {}
+ /**
+ * Indicates release is unsupported on the session.
+ *
+ * @hide
+ */
+ public static final int RELEASE_UNSUPPORTED = 0;
+
+ /**
+ * Indicates the release type of the session is to stop casting.
+ *
+ * @hide
+ */
+ public static final int RELEASE_TYPE_CASTING = 1;
+
+ /**
+ * Indicates the release type of the session is to stop audio sharing.
+ *
+ * @hide
+ */
+ public static final int RELEASE_TYPE_SHARING = 2;
+
+ /**
+ * Indicates the release type of the session.
+ *
+ * @hide
+ */
+ @IntDef({RELEASE_UNSUPPORTED, RELEASE_TYPE_CASTING, RELEASE_TYPE_SHARING})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ReleaseType {}
+
@NonNull final String mOriginalId;
@Nullable
final CharSequence mName;
@@ -116,6 +147,8 @@
@Nullable final UserHandle mTransferInitiatorUserHandle;
@Nullable final String mTransferInitiatorPackageName;
+ @ReleaseType private final int mReleaseType;
+
RoutingSessionInfo(@NonNull Builder builder) {
Objects.requireNonNull(builder, "builder must not be null.");
@@ -144,6 +177,7 @@
mTransferReason = builder.mTransferReason;
mTransferInitiatorUserHandle = builder.mTransferInitiatorUserHandle;
mTransferInitiatorPackageName = builder.mTransferInitiatorPackageName;
+ mReleaseType = builder.mReleaseType;
}
RoutingSessionInfo(@NonNull Parcel src) {
@@ -171,6 +205,7 @@
mTransferReason = src.readInt();
mTransferInitiatorUserHandle = UserHandle.readFromParcel(src);
mTransferInitiatorPackageName = src.readString();
+ mReleaseType = src.readInt();
}
@Nullable
@@ -419,6 +454,16 @@
return mTransferInitiatorPackageName;
}
+ /**
+ * Returns the release type of the session.
+ *
+ * @hide
+ */
+ @ReleaseType
+ public int getReleaseType() {
+ return mReleaseType;
+ }
+
@Override
public int describeContents() {
return 0;
@@ -443,12 +488,13 @@
dest.writeInt(mTransferReason);
UserHandle.writeToParcel(mTransferInitiatorUserHandle, dest);
dest.writeString(mTransferInitiatorPackageName);
+ dest.writeInt(mReleaseType);
}
/**
* Dumps current state of the instance. Use with {@code dumpsys}.
*
- * See {@link android.os.Binder#dump(FileDescriptor, PrintWriter, String[])}.
+ * <p>See {@link Binder#dump(FileDescriptor, PrintWriter, String[])}.
*
* @hide
*/
@@ -472,6 +518,7 @@
pw.println(indent + "mTransferReason=" + mTransferReason);
pw.println(indent + "mtransferInitiatorUserHandle=" + mTransferInitiatorUserHandle);
pw.println(indent + "mtransferInitiatorPackageName=" + mTransferInitiatorPackageName);
+ pw.println(indent + "mReleaseType=" + getHumanReadableReleaseType(mReleaseType));
}
@Override
@@ -503,7 +550,8 @@
&& (mTransferReason == other.mTransferReason)
&& Objects.equals(mTransferInitiatorUserHandle, other.mTransferInitiatorUserHandle)
&& Objects.equals(
- mTransferInitiatorPackageName, other.mTransferInitiatorPackageName);
+ mTransferInitiatorPackageName, other.mTransferInitiatorPackageName)
+ && mReleaseType == other.mReleaseType;
}
@Override
@@ -523,7 +571,8 @@
mVolume,
mTransferReason,
mTransferInitiatorUserHandle,
- mTransferInitiatorPackageName);
+ mTransferInitiatorPackageName,
+ mReleaseType);
}
@Override
@@ -556,10 +605,22 @@
.append(getTransferInitiatorUserHandle())
.append(", transferInitiatorPackageName=")
.append(getTransferInitiatorPackageName())
+ .append(", releaseType=")
+ .append(getHumanReadableReleaseType(getReleaseType()))
.append(" }")
.toString();
}
+ @NonNull
+ private static String getHumanReadableReleaseType(@ReleaseType int releaseType) {
+ return switch (releaseType) {
+ case RELEASE_TYPE_SHARING -> "RELEASE_TYPE_SHARING";
+ case RELEASE_TYPE_CASTING -> "RELEASE_TYPE_CASTING";
+ case RELEASE_UNSUPPORTED -> "RELEASE_UNSUPPORTED";
+ default -> "";
+ };
+ }
+
/**
* Provides a new list with unique route IDs if {@link #mProviderId} is set, or the original IDs
* otherwise.
@@ -615,6 +676,7 @@
@TransferReason private int mTransferReason = TRANSFER_REASON_FALLBACK;
@Nullable private UserHandle mTransferInitiatorUserHandle;
@Nullable private String mTransferInitiatorPackageName;
+ @ReleaseType private int mReleaseType = RELEASE_TYPE_CASTING;
/**
* Constructor for builder to create {@link RoutingSessionInfo}.
@@ -688,6 +750,7 @@
mTransferReason = sessionInfo.mTransferReason;
mTransferInitiatorUserHandle = sessionInfo.mTransferInitiatorUserHandle;
mTransferInitiatorPackageName = sessionInfo.mTransferInitiatorPackageName;
+ mReleaseType = sessionInfo.mReleaseType;
}
/**
@@ -868,31 +931,25 @@
}
/**
- * Sets the session's volume handling.
- * {@link MediaRoute2Info#PLAYBACK_VOLUME_FIXED} or
+ * Sets the session's volume handling. {@link MediaRoute2Info#PLAYBACK_VOLUME_FIXED} or
* {@link MediaRoute2Info#PLAYBACK_VOLUME_VARIABLE}.
*/
@NonNull
- public RoutingSessionInfo.Builder setVolumeHandling(
- @MediaRoute2Info.PlaybackVolume int volumeHandling) {
+ public Builder setVolumeHandling(@MediaRoute2Info.PlaybackVolume int volumeHandling) {
mVolumeHandling = volumeHandling;
return this;
}
- /**
- * Sets the session's maximum volume, or 0 if unknown.
- */
+ /** Sets the session's maximum volume, or 0 if unknown. */
@NonNull
- public RoutingSessionInfo.Builder setVolumeMax(int volumeMax) {
+ public Builder setVolumeMax(int volumeMax) {
mVolumeMax = volumeMax;
return this;
}
- /**
- * Sets the session's current volume, or 0 if unknown.
- */
+ /** Sets the session's current volume, or 0 if unknown. */
@NonNull
- public RoutingSessionInfo.Builder setVolume(int volume) {
+ public Builder setVolume(int volume) {
mVolume = volume;
return this;
}
@@ -930,6 +987,19 @@
}
/**
+ * Gets the release type of the current session.
+ *
+ * <p>By default the release type is set to {@link RoutingSessionInfo#RELEASE_TYPE_CASTING}.
+ *
+ * @hide
+ */
+ @NonNull
+ public Builder setReleaseType(@ReleaseType int releaseType) {
+ mReleaseType = releaseType;
+ return this;
+ }
+
+ /**
* Sets the user handle and package name of the process that initiated the transfer.
*
* <p>By default the transfer initiation user handle and package name are set to {@code
diff --git a/media/java/android/media/audiofx/LoudnessEnhancer.java b/media/java/android/media/audiofx/LoudnessEnhancer.java
index 7dc4175..7c9fcfe 100644
--- a/media/java/android/media/audiofx/LoudnessEnhancer.java
+++ b/media/java/android/media/audiofx/LoudnessEnhancer.java
@@ -43,7 +43,7 @@
// These parameter constants must be synchronized with those in
// /system/media/audio_effects/include/audio_effects/effect_loudnessenhancer.h
/**
- * The maximum gain applied applied to the signal to process.
+ * The maximum gain applied to the signal to process.
* It is expressed in millibels (100mB = 1dB) where 0mB corresponds to no amplification.
*/
public static final int PARAM_TARGET_GAIN_MB = 0;
diff --git a/media/java/android/media/audiopolicy/AudioMix.java b/media/java/android/media/audiopolicy/AudioMix.java
index e4eaaa3..8134315 100644
--- a/media/java/android/media/audiopolicy/AudioMix.java
+++ b/media/java/android/media/audiopolicy/AudioMix.java
@@ -289,9 +289,7 @@
if (o == null || getClass() != o.getClass()) return false;
final AudioMix that = (AudioMix) o;
- boolean tokenMatch = android.media.audiopolicy.Flags.audioMixOwnership()
- ? Objects.equals(this.mToken, that.mToken)
- : true;
+ boolean tokenMatch = Objects.equals(this.mToken, that.mToken);
return Objects.equals(this.mRouteFlags, that.mRouteFlags)
&& Objects.equals(this.mRule, that.mRule)
&& Objects.equals(this.mMixType, that.mMixType)
@@ -302,10 +300,7 @@
/** @hide */
@Override
public int hashCode() {
- if (android.media.audiopolicy.Flags.audioMixOwnership()) {
- return Objects.hash(mRouteFlags, mRule, mMixType, mFormat, mToken);
- }
- return Objects.hash(mRouteFlags, mRule, mMixType, mFormat);
+ return Objects.hash(mRouteFlags, mRule, mMixType, mFormat, mToken);
}
@Override
diff --git a/media/java/android/media/flags/media_better_together.aconfig b/media/java/android/media/flags/media_better_together.aconfig
index 15e87f8..65523a5 100644
--- a/media/java/android/media/flags/media_better_together.aconfig
+++ b/media/java/android/media/flags/media_better_together.aconfig
@@ -187,16 +187,6 @@
}
flag {
- name: "enable_prevention_of_manager_scans_when_no_apps_scan"
- namespace: "media_solutions"
- description: "Prevents waking up route providers when no apps are scanning, even if SysUI or Settings are scanning."
- bug: "319604673"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
name: "enable_privileged_routing_for_media_routing_control"
is_exported: true
namespace: "media_solutions"
@@ -274,5 +264,15 @@
name: "enable_output_switcher_personal_audio_sharing"
namespace: "cross_device_experiences"
description: "Enables personal audio sharing in the output switcher."
- bug: "385672684"
-}
\ No newline at end of file
+ bug: "338479898"
+}
+
+flag {
+ name: "fix_transfer_from_user_route_to_unselected_system_route"
+ namespace: "media_better_together"
+ description: "Fixes a bug causing the failure of transferring to a builtin speaker."
+ bug: "398527989"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/media/java/android/media/flags/projection.aconfig b/media/java/android/media/flags/projection.aconfig
index 01e4a1a..9244f7b 100644
--- a/media/java/android/media/flags/projection.aconfig
+++ b/media/java/android/media/flags/projection.aconfig
@@ -55,3 +55,12 @@
bug: "406217983"
}
+flag {
+ namespace: "media_projection"
+ name: "recording_overlay"
+ description: "Allows overlay recordings via MediaProjection"
+ bug: "395054309"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/media/java/android/media/projection/IMediaProjection.aidl b/media/java/android/media/projection/IMediaProjection.aidl
index dacd1bd..f29d5a8 100644
--- a/media/java/android/media/projection/IMediaProjection.aidl
+++ b/media/java/android/media/projection/IMediaProjection.aidl
@@ -57,6 +57,14 @@
+ ".permission.MANAGE_MEDIA_PROJECTION)")
int getTaskId();
+ /**
+ * Returns whether the projection is functioning as a recording overlay. Can only be
+ * set by specific callers.
+ */
+ @EnforcePermission("MANAGE_MEDIA_PROJECTION")
+ @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest"
+ + ".permission.MANAGE_MEDIA_PROJECTION)")
+ boolean isRecordingOverlay();
/**
* Returns the displayId identifying the display to record. This only applies to full screen
@@ -80,6 +88,14 @@
+ ".permission.MANAGE_MEDIA_PROJECTION)")
void setTaskId(in int taskId);
+ /**
+ * Updates whether the projection should function as a recording overlay.
+ * Can only be set by specific callers.
+ */
+ @EnforcePermission("MANAGE_MEDIA_PROJECTION")
+ @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest"
+ + ".permission.MANAGE_MEDIA_PROJECTION)")
+ void setRecordingOverlay(in boolean isRecordingOverlay);
/**
* Returns {@code true} if this token is still valid. A token is valid as long as the token
diff --git a/media/java/android/media/quality/MediaQualityManager.java b/media/java/android/media/quality/MediaQualityManager.java
index 3f0ba31..6108a98 100644
--- a/media/java/android/media/quality/MediaQualityManager.java
+++ b/media/java/android/media/quality/MediaQualityManager.java
@@ -348,6 +348,26 @@
}
/**
+ * Get the default profile of the application.
+ *
+ * @return PictureProfile the default profile of the application.
+ *
+ * @see PictureProfile
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE)
+ @Nullable
+ public PictureProfile getDefaultPictureProfile() {
+ try {
+ return mService.getDefaultPictureProfile();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+
+ /**
* Sets preferred default picture profile.
*
* @param pictureProfileId the ID of the default profile. {@code null} to unset the default
diff --git a/media/java/android/media/quality/PictureProfile.java b/media/java/android/media/quality/PictureProfile.java
index 0121193..3fd1e5a 100644
--- a/media/java/android/media/quality/PictureProfile.java
+++ b/media/java/android/media/quality/PictureProfile.java
@@ -135,10 +135,100 @@
public static final String STATUS_SDR = "SDR";
/**
- * HDR status.
+ * HDR10 status.
* @hide
*/
- public static final String STATUS_HDR = "HDR";
+ public static final String STATUS_HDR10 = "HDR10";
+
+ /**
+ * DOLBY VISION status.
+ * @hide
+ */
+ public static final String STATUS_DOLBY_VISION = "DOLBY_VISION";
+
+ /**
+ * TCH status.
+ * @hide
+ */
+ public static final String STATUS_TCH = "TCH";
+
+ /**
+ * HLG status.
+ * @hide
+ */
+ public static final String STATUS_HLG = "HLG";
+
+ /**
+ * HDR10 PLUS status.
+ * @hide
+ */
+ public static final String STATUS_HDR10_PLUS = "HDR10_PLUS";
+
+ /**
+ * HDR VIVID status.
+ * @hide
+ */
+ public static final String STATUS_HDR_VIVID = "HDR_VIVID";
+
+ /**
+ * IMAX SDR status.
+ * @hide
+ */
+ public static final String STATUS_IMAX_SDR = " IMAX_SDR";
+
+ /**
+ * IMAX HDR10 status.
+ * @hide
+ */
+ public static final String STATUS_IMAX_HDR10 = "IMAX_HDR10";
+
+ /**
+ * IMAX HDR10 PLUS status.
+ * @hide
+ */
+ public static final String STATUS_IMAX_HDR10_PLUS = "IMAX_HDR10_PLUS";
+
+ /**
+ * FMM SDR status.
+ * @hide
+ */
+ public static final String STATUS_FMM_SDR = "FMM_SDR";
+
+ /**
+ * FMM HDR10 status.
+ * @hide
+ */
+ public static final String STATUS_FMM_HDR10 = "FMM_HDR10";
+
+ /**
+ * FMM HDR10 PLUS status.
+ * @hide
+ */
+ public static final String STATUS_FMM_HDR10_PLUS = "FMM_HDR10_PLUS";
+
+ /**
+ * FMM HLG status.
+ * @hide
+ */
+ public static final String STATUS_FMM_HLG = "FMM_HLG";
+
+ /**
+ * FMM DOLBY status.
+ * @hide
+ */
+ public static final String STATUS_FMM_DOLBY = "FMM_DOLBY";
+
+ /**
+ * FMM TCH status.
+ * @hide
+ */
+ public static final String STATUS_FMM_TCH = "FMM_TCH";
+
+ /**
+ * FMM HDR VIVID status.
+ * @hide
+ */
+ public static final String STATUS_FMM_HDR_VIVID = "FMM_HDR_VIVID";
/** @hide */
public static final String NAME_STANDARD = "standard";
diff --git a/media/java/android/media/quality/aidl/android/media/quality/IMediaQualityManager.aidl b/media/java/android/media/quality/aidl/android/media/quality/IMediaQualityManager.aidl
index ff1bf02..5001fe2 100644
--- a/media/java/android/media/quality/aidl/android/media/quality/IMediaQualityManager.aidl
+++ b/media/java/android/media/quality/aidl/android/media/quality/IMediaQualityManager.aidl
@@ -36,6 +36,7 @@
void createPictureProfile(in PictureProfile pp, int userId);
void updatePictureProfile(in String id, in PictureProfile pp, int userId);
void removePictureProfile(in String id, int userId);
+ PictureProfile getDefaultPictureProfile();
boolean setDefaultPictureProfile(in String id, int userId);
// TODO: use Bundle for includeParams
PictureProfile getPictureProfile(
diff --git a/media/java/android/media/tv/flags/media_tv.aconfig b/media/java/android/media/tv/flags/media_tv.aconfig
index 7670514..151cfdb 100644
--- a/media/java/android/media/tv/flags/media_tv.aconfig
+++ b/media/java/android/media/tv/flags/media_tv.aconfig
@@ -122,16 +122,6 @@
}
flag {
- name: "tif_extension_standardization_bugfix"
- namespace: "tv_os"
- description: "Bug fix flag for standardizing AIDL extension interface of TIS"
- bug: "389779152"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
name: "media_quality_fw_bugfix"
namespace: "tv_os"
description: "Bug fix flag for Media Quality framework"
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index c765766..76d67ad 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -15,12 +15,24 @@
],
}
+filegroup {
+ name: "jni_exports",
+ srcs: ["jni_exports.lds"],
+ visibility: [":__subpackages__"],
+}
+
+cc_defaults {
+ name: "media_jni_lib_defaults",
+ version_script: ":jni_exports",
+}
+
cc_library_shared {
name: "libmedia_jni",
defaults: [
"aconfig_lib_cc_static_link.defaults",
"libcodec2-internal-defaults",
+ "media_jni_lib_defaults",
],
min_sdk_version: "",
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index bf330da..8e816bb 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -2602,9 +2602,13 @@
jfieldID field = env->GetFieldID(clazz, "mCodeRates", "[I");
std::vector<FrontendInnerFec> v = s.get<FrontendStatus::Tag::codeRates>();
- ScopedLocalRef valObj(env, env->NewIntArray(v.size()));
- env->SetIntArrayRegion(valObj.get(), 0, v.size(), reinterpret_cast<jint *>(&v[0]));
-
+ //Short-term fix for frontend status coderates not retrieved correctly.
+ ScopedLocalRef<jintArray> valObj(env, env->NewIntArray(v.size()));
+ std::vector<jint> temp(v.size());
+ for (size_t i = 0; i < v.size(); i++) {
+ temp[i] = static_cast<jint>(v[i]);
+ }
+ env->SetIntArrayRegion(valObj.get(), 0, v.size(), temp.data());
env->SetObjectField(statusObj, field, valObj.get());
break;
}
diff --git a/media/jni/audioeffect/Android.bp b/media/jni/audioeffect/Android.bp
index 7caa9e4..aacf91e 100644
--- a/media/jni/audioeffect/Android.bp
+++ b/media/jni/audioeffect/Android.bp
@@ -10,6 +10,10 @@
cc_library_shared {
name: "libaudioeffect_jni",
+ defaults: [
+ "media_jni_lib_defaults",
+ ],
+
srcs: [
"android_media_AudioEffect.cpp",
"android_media_SourceDefaultEffect.cpp",
@@ -35,8 +39,6 @@
"framework-permission-aidl-cpp",
],
- version_script: "exports.lds",
-
cflags: [
"-Wall",
"-Werror",
diff --git a/media/jni/audioeffect/exports.lds b/media/jni/audioeffect/exports.lds
deleted file mode 100644
index 70491f4..0000000
--- a/media/jni/audioeffect/exports.lds
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- global:
- *;
- local:
- *android10Visualizer*;
-};
diff --git a/media/jni/jni_exports.lds b/media/jni/jni_exports.lds
new file mode 100644
index 0000000..c88e6ba
--- /dev/null
+++ b/media/jni/jni_exports.lds
@@ -0,0 +1,9 @@
+{
+ # Only export the primary JNI loading entrypoint.
+ global:
+ JNI_OnLoad;
+
+ # Hide everything else.
+ local:
+ *;
+};
diff --git a/media/tests/AudioPolicyTest/AndroidManifest.xml b/media/tests/AudioPolicyTest/AndroidManifest.xml
index 5c911b1..466da7e 100644
--- a/media/tests/AudioPolicyTest/AndroidManifest.xml
+++ b/media/tests/AudioPolicyTest/AndroidManifest.xml
@@ -19,6 +19,7 @@
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
+ <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_ROUTING" />
<uses-permission android:name="android.permission.CHANGE_ACCESSIBILITY_VOLUME" />
diff --git a/media/tests/projection/src/android/media/projection/FakeIMediaProjection.java b/media/tests/projection/src/android/media/projection/FakeIMediaProjection.java
index 58aa56b..f042949 100644
--- a/media/tests/projection/src/android/media/projection/FakeIMediaProjection.java
+++ b/media/tests/projection/src/android/media/projection/FakeIMediaProjection.java
@@ -34,6 +34,7 @@
LaunchCookie mLaunchCookie = null;
IMediaProjectionCallback mIMediaProjectionCallback = null;
int mDisplayId = Display.DEFAULT_DISPLAY;
+ boolean mRecordingOverlay = false;
FakeIMediaProjection(PermissionEnforcer enforcer) {
super(enforcer);
@@ -115,6 +116,20 @@
@Override
@EnforcePermission(MANAGE_MEDIA_PROJECTION)
+ public void setRecordingOverlay(boolean isRecordingOverlay) throws RemoteException {
+ setRecordingOverlay_enforcePermission();
+ mRecordingOverlay = isRecordingOverlay;
+ }
+
+ @Override
+ @EnforcePermission(MANAGE_MEDIA_PROJECTION)
+ public boolean isRecordingOverlay() throws RemoteException {
+ isRecordingOverlay_enforcePermission();
+ return mRecordingOverlay;
+ }
+
+ @Override
+ @EnforcePermission(MANAGE_MEDIA_PROJECTION)
public boolean isValid() throws RemoteException {
isValid_enforcePermission();
return true;
diff --git a/packages/CompanionDeviceManager/res/values-gu/strings.xml b/packages/CompanionDeviceManager/res/values-gu/strings.xml
index 8a576e5..097316d 100644
--- a/packages/CompanionDeviceManager/res/values-gu/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-gu/strings.xml
@@ -42,10 +42,8 @@
<string name="title_nearby_device_streaming" msgid="2727103756701741359">"શું <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>ને <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>ની ઍપને <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong> પર સ્ટ્રીમ કરવાની મંજૂરી આપીએ?"</string>
<string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g>ની પાસે એવી બધી બાબતોનો ઍક્સેસ રહેશે જે <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g> પર જોઈ શકાતી કે ચલાવી શકાતી હોય, જેમાં ઑડિયો, ફોટા, ચુકવણીની માહિતી, પાસવર્ડ અને મેસેજ શામેલ છે.<br/><br/><xliff:g id="APP_NAME_2">%1$s</xliff:g> ત્યાં સુધી ઍપને <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> પર સ્ટ્રીમ કરી શકશે, જ્યાં સુધી તમે આ પરવાનગીનો ઍક્સેસ કાઢી નહીં નાખો."</string>
<string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="APP_NAME">%1$s</xliff:g> તમારા <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>માંથી ઍપ સ્ટ્રીમ કરવા માટે <xliff:g id="DEVICE_NAME">%2$s</xliff:g> વતી પરવાનગીની વિનંતી કરી રહી છે"</string>
- <!-- no translation found for title_virtual_device (6556338763070329395) -->
- <skip />
- <!-- no translation found for summary_virtual_device (3502592544171322817) -->
- <skip />
+ <string name="title_virtual_device" msgid="6556338763070329395">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>ને તમારા <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> અને <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> વચ્ચે ઑડિયો અને સિસ્ટમની સુવિધાઓ સ્ટ્રીમ કરવાની મંજૂરી આપીએ?"</string>
+ <string name="summary_virtual_device" msgid="3502592544171322817">"<xliff:g id="DEVICE_NAME_1">%3$s</xliff:g> પર જે કંઈપણ ચલાવવામાં આવે, તેનો ઍક્સેસ <xliff:g id="APP_NAME_0">%2$s</xliff:g> પાસે રહેશે.<br/><br/><xliff:g id="APP_NAME_2">%2$s</xliff:g> જ્યાં સુધી તમે આ પરવાનગીનો ઍક્સેસ કાઢો નહીં, ત્યાં સુધી <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> પર ઑડિયો સ્ટ્રીમ કરી શકશે."</string>
<string name="profile_name_generic" msgid="6851028682723034988">"ડિવાઇસ"</string>
<string name="summary_generic" msgid="1761976003668044801">"આ ઍપ તમારા ફોન અને પસંદ કરેલા ડિવાઇસ વચ્ચે, કૉલ કરનાર કોઈ વ્યક્તિનું નામ જેવી માહિતી સિંક કરી શકશે"</string>
<string name="consent_yes" msgid="8344487259618762872">"મંજૂરી આપો"</string>
diff --git a/packages/CompanionDeviceManager/res/values-iw/strings.xml b/packages/CompanionDeviceManager/res/values-iw/strings.xml
index c1da37f..cc28385 100644
--- a/packages/CompanionDeviceManager/res/values-iw/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-iw/strings.xml
@@ -42,10 +42,8 @@
<string name="title_nearby_device_streaming" msgid="2727103756701741359">"לאשר לאפליקציית <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> לשדר את האפליקציות של ה<xliff:g id="DEVICE_TYPE">%2$s</xliff:g> ל-<strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>?"</string>
<string name="summary_nearby_device_streaming" msgid="70434958004946884">"לאפליקציה <xliff:g id="APP_NAME_0">%1$s</xliff:g> תהיה גישה לכל מה שרואים או מפעילים ב-<xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>, כולל אודיו, תמונות, פרטי תשלום, סיסמאות והודעות.<br/><br/>לאפליקציה <xliff:g id="APP_NAME_2">%1$s</xliff:g> תהיה אפשרות לשדר אפליקציות ל-<xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> עד שהגישה להרשאה הזו תוסר."</string>
<string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"האפליקציה <xliff:g id="APP_NAME">%1$s</xliff:g> מבקשת הרשאה ל-<xliff:g id="DEVICE_NAME">%2$s</xliff:g> כדי לשדר אפליקציות מה<xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string>
- <!-- no translation found for title_virtual_device (6556338763070329395) -->
- <skip />
- <!-- no translation found for summary_virtual_device (3502592544171322817) -->
- <skip />
+ <string name="title_virtual_device" msgid="6556338763070329395">"לאשר לאפליקציה <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> לשדר תכונות מערכת ואודיו בין ה<xliff:g id="DEVICE_TYPE">%3$s</xliff:g> שלך לבין <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string>
+ <string name="summary_virtual_device" msgid="3502592544171322817">"לאפליקציה <xliff:g id="APP_NAME_0">%2$s</xliff:g> תהיה גישה לכל מה שיופעל במכשיר <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>.<br/><br/>האפליקציה <xliff:g id="APP_NAME_2">%2$s</xliff:g> תוכל לשדר אודיו אל <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> עד שההרשאה הזו תוסר."</string>
<string name="profile_name_generic" msgid="6851028682723034988">"מכשיר"</string>
<string name="summary_generic" msgid="1761976003668044801">"האפליקציה הזו תוכל לסנכרן מידע, כמו השם של מי שמתקשר, בין הטלפון שלך למכשיר שבחרת"</string>
<string name="consent_yes" msgid="8344487259618762872">"יש אישור"</string>
diff --git a/packages/CompanionDeviceManager/res/values-pl/strings.xml b/packages/CompanionDeviceManager/res/values-pl/strings.xml
index 0a5070d..fc7b98d 100644
--- a/packages/CompanionDeviceManager/res/values-pl/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-pl/strings.xml
@@ -42,10 +42,8 @@
<string name="title_nearby_device_streaming" msgid="2727103756701741359">"Zezwolić aplikacji <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> na strumieniowanie aplikacji na <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> na urządzenie <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>?"</string>
<string name="summary_nearby_device_streaming" msgid="70434958004946884">"Aplikacja <xliff:g id="APP_NAME_0">%1$s</xliff:g> będzie miała dostęp do wszystkiego, co jest widoczne i odtwarzane na <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>, w tym do dźwięku, zdjęć, haseł i wiadomości.<br/><br/>Aplikacja <xliff:g id="APP_NAME_2">%1$s</xliff:g> będzie mogła strumieniować aplikacje na urządzenie <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g>, dopóki nie usuniesz dostępu do tego uprawnienia."</string>
<string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"Aplikacja <xliff:g id="APP_NAME">%1$s</xliff:g> prosi w imieniu urządzenia <xliff:g id="DEVICE_NAME">%2$s</xliff:g> o pozwolenie na strumieniowanie aplikacji z urządzenia <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string>
- <!-- no translation found for title_virtual_device (6556338763070329395) -->
- <skip />
- <!-- no translation found for summary_virtual_device (3502592544171322817) -->
- <skip />
+ <string name="title_virtual_device" msgid="6556338763070329395">"Zezwolić aplikacji <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> na strumieniowanie dźwięku i funkcji systemowych między urządzeniami <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> i <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string>
+ <string name="summary_virtual_device" msgid="3502592544171322817">"<xliff:g id="APP_NAME_0">%2$s</xliff:g> będzie mieć dostęp do wszystkiego, co jest odtwarzane na urządzeniu <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>.<br/><br/>Aplikacja <xliff:g id="APP_NAME_2">%2$s</xliff:g> będzie mogła odtwarzać dźwięk na urządzeniu <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g>, dopóki nie usuniesz dostępu do tego uprawnienia."</string>
<string name="profile_name_generic" msgid="6851028682723034988">"urządzenie"</string>
<string name="summary_generic" msgid="1761976003668044801">"Ta aplikacja może synchronizować informacje takie jak imię i nazwisko osoby dzwoniącej między Twoim telefonem i wybranym urządzeniem"</string>
<string name="consent_yes" msgid="8344487259618762872">"Zezwól"</string>
diff --git a/packages/CompanionDeviceManager/res/values-ur/strings.xml b/packages/CompanionDeviceManager/res/values-ur/strings.xml
index bbbc025..0732e73 100644
--- a/packages/CompanionDeviceManager/res/values-ur/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ur/strings.xml
@@ -42,8 +42,8 @@
<string name="title_nearby_device_streaming" msgid="2727103756701741359">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> کو آپ کے <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> کی ایپس کو <xliff:g id="DEVICE_NAME">%3$s</xliff:g> پر سلسلہ بندی کرنے کی اجازت دیں؟"</string>
<string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> کو <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g> پر دکھائی دینے والی یا چلائی جانے والی کسی بھی چیز تک رسائی حاصل ہوگی، بشمول آڈیو، تصاویر، ادائیگی کی معلومات، پاس ورڈز اور پیغامات۔<br/><br/><xliff:g id="APP_NAME_2">%1$s</xliff:g> <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> پر اس وقت تک ایپس کی سلسلہ بندی کر سکے گی جب تک آپ اس اجازت تک رسائی کو ہٹا نہیں دیتے۔"</string>
<string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="APP_NAME">%1$s</xliff:g> ایپ <xliff:g id="DEVICE_NAME">%2$s</xliff:g> کی جانب سے آپ کے <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> سے ایپس کی سلسلہ بندی کرنے کی اجازت کی درخواست کر رہی ہے"</string>
- <string name="title_virtual_device" msgid="6556338763070329395">"اپنے <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> اور <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> کے درمیان آڈیو اور سسٹم کی خصوصیات کی سلسلہ بندی کرنے کی <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> کو اجازت دیں؟"</string>
- <string name="summary_virtual_device" msgid="3502592544171322817">"<xliff:g id="APP_NAME_0">%2$s</xliff:g> کو آپ کے <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g> پر چلائی جانے والی کسی بھی چیز تک رسائی حاصل ہوگی۔<br/><br/><xliff:g id="APP_NAME_2">%2$s</xliff:g> <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> پر اس وقت تک آڈیو کی سلسلہ بندی کر سکے گی جب تک آپ اس اجازت تک رسائی کو ہٹا نہیں دیتے۔"</string>
+ <string name="title_virtual_device" msgid="6556338763070329395">"آپ کے <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> اور <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> کے درمیان آڈیو اور سسٹم کی خصوصیات کی سلسلہ بندی کرنے کی <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> کو اجازت دیں؟"</string>
+ <string name="summary_virtual_device" msgid="3502592544171322817">"<xliff:g id="APP_NAME_0">%2$s</xliff:g> کو آپ کے <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g> پر چلائی جانے والی کسی بھی چیز تک رسائی حاصل ہوگی۔<br/><br/><xliff:g id="APP_NAME_2">%2$s</xliff:g> <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> پر اس وقت تک آڈیو کی سلسلہ بندی کر سکے گی جب تک آپ اس اجازت تک رسائی کو ہٹا نہیں دیتے۔"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"آلہ"</string>
<string name="summary_generic" msgid="1761976003668044801">"یہ ایپ آپ کے فون اور منتخب کردہ آلے کے درمیان معلومات، جیسے کسی کال کرنے والے کے نام، کی مطابقت پذیری کر سکے گی"</string>
<string name="consent_yes" msgid="8344487259618762872">"اجازت دیں"</string>
diff --git a/packages/CredentialManager/Android.bp b/packages/CredentialManager/Android.bp
index c292b502..0b9a498 100644
--- a/packages/CredentialManager/Android.bp
+++ b/packages/CredentialManager/Android.bp
@@ -5,6 +5,7 @@
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
default_applicable_licenses: ["frameworks_base_license"],
+ default_team: "trendy_team_credential_manager",
}
android_library {
diff --git a/packages/CredentialManager/res/values-my/strings.xml b/packages/CredentialManager/res/values-my/strings.xml
index e0def18..c17ee51 100644
--- a/packages/CredentialManager/res/values-my/strings.xml
+++ b/packages/CredentialManager/res/values-my/strings.xml
@@ -21,7 +21,7 @@
<string name="string_cancel" msgid="6369133483981306063">"မလုပ်တော့"</string>
<string name="string_continue" msgid="1346732695941131882">"ရှေ့ဆက်ရန်"</string>
<string name="string_more_options" msgid="2763852250269945472">"အခြားနည်းသုံးရန်"</string>
- <string name="string_learn_more" msgid="4541600451688392447">"ပိုမိုလေ့လာရန်"</string>
+ <string name="string_learn_more" msgid="4541600451688392447">"ပိုလေ့လာရန်"</string>
<string name="content_description_show_password" msgid="3283502010388521607">"စကားဝှက်ကို ပြရန်"</string>
<string name="content_description_hide_password" msgid="6841375971631767996">"စကားဝှက်ကို ဖျောက်ရန်"</string>
<string name="passkey_creation_intro_title" msgid="4251037543787718844">"လျှို့ဝှက်ကီးများဖြင့် ပိုလုံခြုံသည်"</string>
diff --git a/packages/PackageInstaller/Android.bp b/packages/PackageInstaller/Android.bp
index 8b1828c..4779f6c 100644
--- a/packages/PackageInstaller/Android.bp
+++ b/packages/PackageInstaller/Android.bp
@@ -55,6 +55,7 @@
"androidx.leanback_leanback",
"androidx.lifecycle_lifecycle-extensions",
"androidx.lifecycle_lifecycle-livedata",
+ "com.google.android.material_material",
"kotlin-parcelize-runtime",
],
@@ -89,6 +90,7 @@
"androidx.leanback_leanback",
"androidx.lifecycle_lifecycle-extensions",
"androidx.lifecycle_lifecycle-livedata",
+ "com.google.android.material_material",
"kotlin-parcelize-runtime",
],
aaptflags: ["--product tablet"],
@@ -124,6 +126,7 @@
"androidx.leanback_leanback",
"androidx.lifecycle_lifecycle-extensions",
"androidx.lifecycle_lifecycle-livedata",
+ "com.google.android.material_material",
"kotlin-parcelize-runtime",
],
aaptflags: ["--product tv"],
diff --git a/packages/PackageInstaller/AndroidManifest.xml b/packages/PackageInstaller/AndroidManifest.xml
index bd96f7c..bd53af4 100644
--- a/packages/PackageInstaller/AndroidManifest.xml
+++ b/packages/PackageInstaller/AndroidManifest.xml
@@ -47,7 +47,8 @@
<activity android:name=".v2.ui.InstallLaunch"
android:configChanges="orientation|keyboardHidden|screenSize"
- android:exported="false"/>
+ android:exported="false"
+ android:theme="@style/Theme.PackageInstaller"/>
<activity android:name=".InstallStart"
android:exported="true"
@@ -120,7 +121,8 @@
android:configChanges="orientation|keyboardHidden|screenSize"
android:excludeFromRecents="true"
android:noHistory="true"
- android:exported="false">
+ android:exported="false"
+ android:theme="@style/Theme.PackageInstaller">
</activity>
<receiver android:name=".common.UninstallEventReceiver"
diff --git a/packages/PackageInstaller/res/drawable/background_colored_button.xml b/packages/PackageInstaller/res/drawable/background_colored_button.xml
new file mode 100644
index 0000000..789a7ef
--- /dev/null
+++ b/packages/PackageInstaller/res/drawable/background_colored_button.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2025 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ https://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ 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="40dp"/>
+ <solid android:color="?android:attr/colorAccent"/>
+ </shape>
+</inset>
diff --git a/packages/PackageInstaller/res/drawable/background_outlined_button.xml b/packages/PackageInstaller/res/drawable/background_outlined_button.xml
new file mode 100644
index 0000000..3399931
--- /dev/null
+++ b/packages/PackageInstaller/res/drawable/background_outlined_button.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2025 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ https://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT 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">
+ <corners android:radius="24dp" />
+ <stroke
+ android:width="1dp"
+ android:color="@color/colorButtonOutline"/>
+</shape>
diff --git a/packages/PackageInstaller/res/drawable/lock_24px.xml b/packages/PackageInstaller/res/drawable/lock_24px.xml
new file mode 100644
index 0000000..de90d634
--- /dev/null
+++ b/packages/PackageInstaller/res/drawable/lock_24px.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="960"
+ android:viewportHeight="960">
+ <path
+ android:fillColor="?android:attr/colorPrimary"
+ android:pathData="M240,880Q207,880 183.5,856.5Q160,833 160,800L160,400Q160,367 183.5,343.5Q207,320 240,320L280,320L280,240Q280,157 338.5,98.5Q397,40 480,40Q563,40 621.5,98.5Q680,157 680,240L680,320L720,320Q753,320 776.5,343.5Q800,367 800,400L800,800Q800,833 776.5,856.5Q753,880 720,880L240,880ZM240,800L720,800Q720,800 720,800Q720,800 720,800L720,400Q720,400 720,400Q720,400 720,400L240,400Q240,400 240,400Q240,400 240,400L240,800Q240,800 240,800Q240,800 240,800ZM480,680Q513,680 536.5,656.5Q560,633 560,600Q560,567 536.5,543.5Q513,520 480,520Q447,520 423.5,543.5Q400,567 400,600Q400,633 423.5,656.5Q447,680 480,680ZM360,320L600,320L600,240Q600,190 565,155Q530,120 480,120Q430,120 395,155Q360,190 360,240L360,320ZM240,800Q240,800 240,800Q240,800 240,800L240,400Q240,400 240,400Q240,400 240,400L240,400Q240,400 240,400Q240,400 240,400L240,800Q240,800 240,800Q240,800 240,800Z"/>
+</vector>
diff --git a/packages/PackageInstaller/res/layout/app_snippet_layout.xml b/packages/PackageInstaller/res/layout/app_snippet_layout.xml
new file mode 100644
index 0000000..03921e1a
--- /dev/null
+++ b/packages/PackageInstaller/res/layout/app_snippet_layout.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2025 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ https://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/app_snippet"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:visibility="gone"
+ style="@style/Widget.PackageInstaller.ViewGroup.AppSnippet">
+
+ <ImageView
+ android:id="@+id/app_icon"
+ android:layout_width="@dimen/app_snippet_icon_dimen"
+ android:layout_height="@dimen/app_snippet_icon_dimen"
+ style="@style/Widget.PackageInstaller.ImageView.AppIcon"/>
+
+ <TextView
+ android:id="@+id/app_label"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ style="@style/Widget.PackageInstaller.TextView.AppLabel"/>
+
+</LinearLayout>
diff --git a/packages/PackageInstaller/res/layout/install_blocked_custom_title_layout.xml b/packages/PackageInstaller/res/layout/install_blocked_custom_title_layout.xml
new file mode 100644
index 0000000..0018613
--- /dev/null
+++ b/packages/PackageInstaller/res/layout/install_blocked_custom_title_layout.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2025 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ https://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/customTitle"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ style="@style/Widget.PackageInstaller.ViewGroup.CustomTitle">
+
+ <ImageView
+ android:layout_width="@dimen/lock_icon_dimen"
+ android:layout_height="@dimen/lock_icon_dimen"
+ android:src="@drawable/lock_24px"/>
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/title_install_failed_blocked"
+ style="@style/Widget.PackageInstaller.WindowTitle.Custom"/>
+
+</LinearLayout>
diff --git a/packages/PackageInstaller/res/layout/install_fragment_layout.xml b/packages/PackageInstaller/res/layout/install_fragment_layout.xml
new file mode 100644
index 0000000..56fc569
--- /dev/null
+++ b/packages/PackageInstaller/res/layout/install_fragment_layout.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:orientation="vertical"
+ android:paddingHorizontal="?android:attr/dialogPreferredPadding"
+ android:paddingBottom="@dimen/alert_dialog_inner_padding">
+
+ <include layout="@layout/app_snippet_layout" />
+
+ <com.google.android.material.progressindicator.LinearProgressIndicator
+ style="@style/Widget.PackageInstaller.ProgressBar"
+ android:id="@+id/progress_bar"
+ android:indeterminate="true"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:visibility="gone" />
+
+ <TextView
+ style="@style/Widget.PackageInstaller.TextView.CustomMessage"
+ android:id="@+id/custom_message"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:visibility="gone" />
+
+ <CheckBox
+ style="@style/Widget.PackageInstaller.TextView.CustomMessage"
+ android:id="@+id/keep_data"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:visibility="gone" />
+
+</LinearLayout>
diff --git a/packages/PackageInstaller/res/values-iw/strings.xml b/packages/PackageInstaller/res/values-iw/strings.xml
index 1c7bd11..37ace29 100644
--- a/packages/PackageInstaller/res/values-iw/strings.xml
+++ b/packages/PackageInstaller/res/values-iw/strings.xml
@@ -98,7 +98,7 @@
<string name="untrusted_external_source_warning" product="tv" msgid="7057271609532508035">"לצורכי אבטחה, הטלוויזיה שלך חסומה להתקנת אפליקציות לא מוכרות מהמקור הזה. אפשר לשנות זאת ב\'הגדרות\'."</string>
<string name="untrusted_external_source_warning" product="watch" msgid="7195163388090818636">"לצורכי אבטחה, השעון שלך חסום להתקנת אפליקציות לא מוכרות מהמקור הזה. אפשר לשנות זאת ב\'הגדרות\'."</string>
<string name="untrusted_external_source_warning" product="default" msgid="8444191224459138919">"לצורכי אבטחה, הטלפון שלך חסום להתקנת אפליקציות לא מוכרות מהמקור הזה. אפשר לשנות זאת ב\'הגדרות\'."</string>
- <string name="anonymous_source_warning" product="default" msgid="2784902545920822500">"נתוני הטלפון והנתונים האישיים שלך חשופים יותר בפני התקפות על ידי אפליקציות ממקורות לא ידועים. התקנת האפליקציה הזו מהווה את הסכמתך לכך שהאחריות הבלעדית היא שלך במקרה של אובדן נתונים או גרימת נזק לטלפון שלך בעקבות השימוש באפליקציה."</string>
+ <string name="anonymous_source_warning" product="default" msgid="2784902545920822500">"נתוני הטלפון והנתונים האישיים שלך יהיו חשופים יותר להתקפות של אפליקציות לא מוכרות. התקנת האפליקציה הזו מהווה את הסכמתך לכך שהאחריות הבלעדית היא שלך במקרה של אובדן נתונים או גרימת נזק לטלפון שלך בעקבות השימוש באפליקציה."</string>
<string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"נתוני הטאבלט והנתונים האישיים שלך חשופים יותר בפני התקפות על ידי אפליקציות ממקורות לא ידועים. התקנת האפליקציה הזו מהווה את הסכמתך לכך שהאחריות הבלעדית היא שלך במקרה של אובדן נתונים או גרימת נזק לטאבלט בעקבות השימוש באפליקציה."</string>
<string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"נתוני הטלוויזיה והנתונים האישיים שלך חשופים יותר בפני התקפות על ידי אפליקציות ממקורות לא ידועים. התקנת האפליקציה הזו מהווה את הסכמתך לכך שהאחריות הבלעדית היא שלך במקרה של אובדן נתונים או גרימת נזק לטלוויזיה שלך בעקבות השימוש באפליקציה."</string>
<string name="cloned_app_label" msgid="7503612829833756160">"שכפול של <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
diff --git a/packages/PackageInstaller/res/values-night/colors.xml b/packages/PackageInstaller/res/values-night/colors.xml
new file mode 100644
index 0000000..df7de90
--- /dev/null
+++ b/packages/PackageInstaller/res/values-night/colors.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2025 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ https://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT 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>
+ <color name="colorButtonOutline">@android:color/system_outline_variant_dark</color>
+</resources>
diff --git a/packages/PackageInstaller/res/values-night/styles.xml b/packages/PackageInstaller/res/values-night/styles.xml
new file mode 100644
index 0000000..7a542c2
--- /dev/null
+++ b/packages/PackageInstaller/res/values-night/styles.xml
@@ -0,0 +1,24 @@
+<!--
+ ~ Copyright (C) 2025 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ https://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT 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="Theme.PackageInstaller.AlertDialog" parent="@android:style/Theme.DeviceDefault.Dialog.Alert">
+ <item name="android:buttonBarPositiveButtonStyle">@style/Widget.PackageInstaller.Button.Colored</item>
+ <item name="android:buttonBarNegativeButtonStyle">@style/Widget.PackageInstaller.Button.Outlined</item>
+ <item name="android:buttonBarStyle">@style/Widget.PackageInstaller.ButtonBar</item>
+ <item name="android:windowTitleStyle">@style/Widget.PackageInstaller.WindowTitle</item>
+ </style>
+</resources>
diff --git a/packages/PackageInstaller/res/values-night/themes.xml b/packages/PackageInstaller/res/values-night/themes.xml
index 37588ad..cda6b4a 100644
--- a/packages/PackageInstaller/res/values-night/themes.xml
+++ b/packages/PackageInstaller/res/values-night/themes.xml
@@ -24,4 +24,10 @@
<item name="android:windowAnimationStyle">@null</item>
</style>
+ <style name="Theme.PackageInstaller"
+ parent="@android:style/Theme.DeviceDefault.Dialog.Alert">
+ <item name="android:alertDialogTheme">@style/Theme.PackageInstaller.AlertDialog</item>
+ <item name="android:windowNoTitle">true</item>
+ </style>
+
</resources>
diff --git a/packages/PackageInstaller/res/values-te/strings.xml b/packages/PackageInstaller/res/values-te/strings.xml
index d91a9c7..2a4225d 100644
--- a/packages/PackageInstaller/res/values-te/strings.xml
+++ b/packages/PackageInstaller/res/values-te/strings.xml
@@ -17,7 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="7488448184431507488">"ప్యాకేజీ ఇన్స్టాలర్"</string>
- <string name="install" msgid="711829760615509273">"ఇన్స్టాల్ చేయి"</string>
+ <string name="install" msgid="711829760615509273">"ఇన్స్టాల్ చేయండి"</string>
<string name="update" msgid="3932142540719227615">"అప్డేట్ చేయి"</string>
<string name="done" msgid="6632441120016885253">"పూర్తయింది"</string>
<string name="cancel" msgid="1018267193425558088">"రద్దు చేయండి"</string>
diff --git a/packages/PackageInstaller/res/values/colors.xml b/packages/PackageInstaller/res/values/colors.xml
index 01a0c9a..42d6d0a 100644
--- a/packages/PackageInstaller/res/values/colors.xml
+++ b/packages/PackageInstaller/res/values/colors.xml
@@ -17,4 +17,5 @@
<resources>
<color name="bigIconColor">#C8CCCE</color>
+ <color name="colorButtonOutline">@android:color/system_outline_variant_light</color>
</resources>
diff --git a/packages/PackageInstaller/res/values/dimens.xml b/packages/PackageInstaller/res/values/dimens.xml
index bfea05e..795d2d26 100644
--- a/packages/PackageInstaller/res/values/dimens.xml
+++ b/packages/PackageInstaller/res/values/dimens.xml
@@ -53,4 +53,13 @@
<!-- Dialog padding minus control padding, used to fix alignment. -->
<dimen name="select_dialog_padding_start_material">20dp</dimen>
<dimen name="select_dialog_drawable_padding_start_material">20dp</dimen>
+
+ <dimen name="app_snippet_icon_dimen">40dp</dimen>
+ <dimen name="app_icon_margin">12dp</dimen>
+ <dimen name="lock_icon_dimen">32dp</dimen>
+ <dimen name="app_snippet_layout_vertical_margin">16dp</dimen>
+ <dimen name="app_label_padding">18dp</dimen>
+ <dimen name="dialog_inter_element_margin">16dp</dimen>
+ <dimen name="alert_dialog_inner_padding">24dp</dimen>
+ <dimen name="button_min_height">40dp</dimen>
</resources>
diff --git a/packages/PackageInstaller/res/values/strings_v2.xml b/packages/PackageInstaller/res/values/strings_v2.xml
new file mode 100644
index 0000000..897adb5
--- /dev/null
+++ b/packages/PackageInstaller/res/values/strings_v2.xml
@@ -0,0 +1,123 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2025 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ https://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- TODO(b/274120822): Add product configs for required strings -->
+
+ <!-- Dialog Titles -->
+ <string name="title_unknown_source_blocked">Permission needed</string>
+ <string name="title_anonymous_source_warning">Install from unknown source?</string>
+ <string name="title_install_blocked">Install blocked</string>
+
+ <string name="title_install_staging">Preparing to install app…</string>
+
+ <string name="title_install">Install this app?</string>
+ <string name="title_installing">Installing…</string>
+ <string name="title_installed">App installed</string>
+
+ <string name="title_reinstall">Reinstall this app?</string>
+ <string name="title_reinstalling">Reinstalling…</string>
+ <string name="title_reinstalled">App reinstalled</string>
+
+ <string name="title_update">Update this app?</string>
+ <string name="title_update_ownership_change">Update this app with <xliff:g id="new_update_owner">%1$s</xliff:g>?</string>
+ <string name="title_updating">Updating…</string>
+ <string name="title_updated">App updated</string>
+
+ <string name="title_cant_install_app">Can\'t install app</string>
+ <string name="title_install_failed_blocked">Install blocked</string>
+ <string name="title_install_failed_incompatible">App not compatible</string>
+ <string name="title_install_failed_invalid_package">Invalid package</string>
+ <string name="title_install_failed_less_storage">Not enough storage</string>
+ <string name="title_install_failed_not_installed">App not installed</string>
+
+ <!-- TODO: These strings are placeholders, until UX finalizes strings for Uninstall flow. -->
+ <string name="title_uninstall">Uninstall this app?</string>
+ <string name="title_uninstall_clone">Uninstall this app clone?</string>
+ <string name="title_uninstall_updates_system_app">Uninstall updates?</string>
+ <string name="title_uninstall_all_users">Uninstall this app for all users?</string>
+ <string name="title_uninstall_other_user">Uninstall this app for another user?</string>
+ <string name="title_uninstall_app_not_found">App not found</string>
+ <string name="title_uninstall_user_not_allowed">Can\'t uninstall this app</string>
+ <string name="title_uninstall_failed">App not uninstalled</string>
+
+ <string name="title_archive">Archive this app?</string>
+ <string name="title_archive_other_user">Archive this app for another user?</string>
+ <!-- End of placeholder strings -->
+ <!-- Dialog Titles end -->
+
+ <!-- Dialog Messages -->
+ <string name="message_update_owner_change">This app normally receives updates from <b><xliff:g id="existing_update_owner">%1$s</xliff:g></b>. By updating from a different source, you may receive future updates from any source on your tablet. App functionality may change.</string>
+ <string name="message_external_source_blocked">To protect your phone and data, <b><xliff:g id="installer_app_label">%1$s</xliff:g></b> needs permission to install unknown apps. You can change this in Settings.</string>
+ <string name="message_anonymous_source_warning"> Your phone and personal data are more vulnerable to attack by unknown apps. By installing this app, you agree that you are responsible for any damage to your phone or loss of data that may result from its use.</string>
+
+ <string name="message_install_failed_blocked">This app was blocked from being installed</string>
+ <string name="message_install_failed_conflict">This app conflicts with an app already on your device</string>
+ <string name="message_install_failed_incompatible">This app is not compatible with your device</string>
+ <string name="message_install_failed_invalid">There\'s a problem with this app\'s installation file</string>
+ <string name="message_install_failed_less_storage">To install this app, free up <b><xliff:g id="required_space" example="100MB">%1$s</xliff:g></b>, then try again</string>
+
+ <string name="message_parse_failed">There is a problem with the app file</string>
+
+ <string name="message_no_install_apps_restriction">Installing apps has been restricted</string>
+ <string name="message_no_install_unknown_apps_restriction">Installing unknown apps has been restricted</string>
+
+ <!-- TODO: These strings are placeholders, until UX finalizes strings for Uninstall flow. -->
+ <string name="message_uninstall_keep_data">Keep <xliff:g id="size" example="1.5MB">%1$s</xliff:g> of app data.</string>
+ <string name="message_uninstall_with_clone_instance">A clone of this app exists and will also be deleted</string>
+ <string name="message_delete_clone_app">This app is a clone of <xliff:g id="app_label" example="Example App">%1$s</xliff:g></string>
+ <string name="message_uninstall_activity"><xliff:g id="activity_name">%1$s</xliff:g> is part of the following app: <xliff:g id="app_name">%2$s</xliff:g></string>
+ <string name="message_uninstall_updates_system_app">This app will be replaced with the factory version and all app data will be removed</string>
+ <string name="message_uninstall_updates_system_app_all_users">This app will be replaced with the factory version and all app data will be removed. This will affect all users on the device, including those with work profiles.</string>
+ <string name="message_uninstall_all_users">This app and its data will be removed for all users on this device</string>
+ <string name="message_uninstall_other_user">This app will be uninstalled for user named \'<xliff:g id="user_name">%1$s</xliff:g>\'</string>
+ <string name="message_uninstall_work_profile">This app will be uninstalled from your work profile</string>
+ <string name="message_uninstall_private_space">This app will be uninstalled from your private space</string>
+ <string name="message_uninstall_app_not_found">This app wasn\'t found in the list of installed apps, or has been already uninstalled</string>
+ <string name="message_uninstall_user_not_allowed">The current user is not allowed to perform this uninstallation</string>
+ <string name="message_uninstall_failed">This app could not be uninstalled</string>
+
+ <string name="message_archive">Your app data will be saved</string>
+ <string name="message_archive_all_users">This app will be archived for all users. App data will be saved</string>
+ <string name="message_archive_other_user">This app will be archived for user named \'<xliff:g id="user_name">%1$s</xliff:g>\'. App data will be saved</string>
+ <string name="message_archive_work_profile">This app will be archived from your work profile. App data will be saved</string>
+ <string name="message_archive_private_space">This app will be archived from your private space. App data will be saved</string>
+ <!-- End of placeholder strings -->
+ <!-- Dialog Messages end -->
+
+ <!-- Dialog Buttons -->
+ <string name="button_install">Install</string>
+ <string name="button_update">Update</string>
+ <string name="button_update_anyway">Update anyway</string>
+ <string name="button_done">Done</string>
+ <string name="button_open">Open</string>
+ <string name="button_cancel">Cancel</string>
+ <string name="button_continue">Continue</string>
+ <string name="button_close">Close</string>
+ <string name="button_uninstall">Uninstall</string>
+ <string name="button_reinstall">Reinstall</string>
+ <string name="button_manage_apps">Manage apps</string>
+ <string name="button_archive">Archive</string>
+ <string name="button_uninstall_updates_system_app">Uninstall updates</string>
+ <!-- Dialog Buttons end -->
+
+ <!-- Miscellaneous -->
+ <string name="string_cloned_app_label"><xliff:g id="package_label">%1$s</xliff:g> Clone</string>
+ <!-- Miscellaneous end -->
+
+ <!-- Package Installer App V2 Strings section end -->
+</resources>
\ No newline at end of file
diff --git a/packages/PackageInstaller/res/values/styles.xml b/packages/PackageInstaller/res/values/styles.xml
index ca797e1..f073dd4 100644
--- a/packages/PackageInstaller/res/values/styles.xml
+++ b/packages/PackageInstaller/res/values/styles.xml
@@ -40,4 +40,88 @@
<style name="TextAppearance">
<item name="textSize">16sp</item>
</style>
-</resources>
\ No newline at end of file
+
+ <style name="Theme.PackageInstaller.AlertDialog" parent="@android:style/Theme.DeviceDefault.Light.Dialog.Alert">
+ <item name="android:buttonBarPositiveButtonStyle">@style/Widget.PackageInstaller.Button.Colored</item>
+ <item name="android:buttonBarNegativeButtonStyle">@style/Widget.PackageInstaller.Button.Outlined</item>
+ <item name="android:buttonBarStyle">@style/Widget.PackageInstaller.ButtonBar</item>
+ <item name="android:windowTitleStyle">@style/Widget.PackageInstaller.WindowTitle</item>
+ </style>
+
+ <style name="Theme.PackageInstaller.AlertDialog.Variant">
+ <item name="android:buttonBarPositiveButtonStyle">@style/Widget.PackageInstaller.Button.Outlined</item>
+ <item name="android:buttonBarNegativeButtonStyle">@style/Widget.PackageInstaller.Button</item>
+ </style>
+
+ <style name="Widget.PackageInstaller.Button" parent="android:Widget.DeviceDefault.Button">
+ <item name="android:textColor">?android:attr/colorAccent</item>
+ <item name="android:background">@null</item>
+ <item name="android:minHeight">@dimen/button_min_height</item>
+ <item name="android:layout_marginStart">8dp</item>
+ <item name="android:layout_marginLeft">8dp</item>
+
+ </style>
+
+ <style name="Widget.PackageInstaller.Button.Colored" parent="android:Widget.DeviceDefault.Button.Colored">
+ <item name="android:background">@drawable/background_colored_button</item>
+ <item name="android:minHeight">@dimen/button_min_height</item>
+ <item name="android:paddingHorizontal">24dp</item>
+ <item name="android:layout_marginStart">8dp</item>
+ <item name="android:layout_marginLeft">8dp</item>
+ </style>
+
+ <style name="Widget.PackageInstaller.Button.Outlined" parent="android:Widget.DeviceDefault.Button.Colored">
+ <item name="android:background">@drawable/background_outlined_button</item>
+ <item name="android:textColor">?android:attr/colorAccent</item>
+ <item name="android:minHeight">@dimen/button_min_height</item>
+ <item name="android:layout_marginStart">8dp</item>
+ <item name="android:layout_marginLeft">8dp</item>
+ </style>
+
+ <style name="Widget.PackageInstaller.ButtonBar" parent="">
+ <item name="android:paddingBottom">20dp</item>
+ <item name="android:paddingTop">4dp</item>
+ <item name="android:paddingHorizontal">12dp</item>
+ </style>
+
+ <style name="Widget.PackageInstaller.WindowTitle" parent="">
+ <item name="android:textAppearance">@android:style/TextAppearance.DeviceDefault.Headline</item>
+ <item name="android:layout_marginTop">4dp</item>
+ <item name="android:autoSizeTextType">uniform</item>
+ </style>
+
+ <style name="Widget.PackageInstaller.WindowTitle.Custom">
+ <item name="android:gravity">center</item>
+ <item name="android:layout_marginTop">16dp</item>
+ </style>
+
+ <style name="Widget.PackageInstaller.ViewGroup.AppSnippet" parent="">
+ <item name="android:layout_marginTop">@dimen/app_snippet_layout_vertical_margin</item>
+ <item name="android:gravity">center</item>
+ </style>
+
+ <style name="Widget.PackageInstaller.ViewGroup.CustomTitle" parent="">
+ <item name="android:paddingTop">@dimen/alert_dialog_inner_padding</item>
+ <item name="android:gravity">center</item>
+ </style>
+
+ <style name="Widget.PackageInstaller.ImageView.AppIcon" parent="">
+ <item name="android:layout_marginVertical">@dimen/app_icon_margin</item>
+ <item name="android:layout_marginRight">@dimen/app_icon_margin</item>
+ </style>
+
+ <style name="Widget.PackageInstaller.TextView.AppLabel" parent="@android:style/TextAppearance.DeviceDefault.SearchResult.Subtitle">
+ <item name="android:textFontWeight">600</item>
+ <item name="android:color">?android:attr/textColorPrimary</item>
+ </style>
+
+ <style name="Widget.PackageInstaller.TextView.CustomMessage" parent="@android:style/TextAppearance.DeviceDefault.Small">
+ <item name="android:textColor">?android:attr/textColorSecondary</item>
+ <item name="android:layout_marginTop">@dimen/dialog_inter_element_margin</item>
+ </style>
+
+ <style name="Widget.PackageInstaller.ProgressBar" parent="Widget.Material3.LinearProgressIndicator">
+ <item name="android:layout_marginTop">@dimen/dialog_inter_element_margin</item>
+ <item name="android:paddingVertical">4dp</item>
+ </style>
+</resources>
diff --git a/packages/PackageInstaller/res/values/themes.xml b/packages/PackageInstaller/res/values/themes.xml
index aa48712..c6e2d47 100644
--- a/packages/PackageInstaller/res/values/themes.xml
+++ b/packages/PackageInstaller/res/values/themes.xml
@@ -24,4 +24,10 @@
<item name="android:windowAnimationStyle">@null</item>
</style>
+ <style name="Theme.PackageInstaller"
+ parent="@android:style/Theme.DeviceDefault.Light.Dialog.Alert">
+ <item name="android:alertDialogTheme">@style/Theme.PackageInstaller.AlertDialog</item>
+ <item name="android:windowNoTitle">true</item>
+ </style>
+
</resources>
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java
index 44990f7..6826452 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java
@@ -21,6 +21,7 @@
import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
import static com.android.packageinstaller.PackageUtil.getMaxTargetSdkVersionForUid;
+import static com.android.packageinstaller.PackageUtil.getReasonForDebug;
import android.Manifest;
import android.app.Activity;
@@ -46,6 +47,7 @@
import android.os.Process;
import android.os.UserHandle;
import android.os.UserManager;
+import android.provider.Settings;
import android.util.Log;
import androidx.annotation.NonNull;
@@ -91,8 +93,12 @@
// be stale, if e.g. the app was uninstalled while the activity was destroyed.
super.onCreate(null);
- if (usePiaV2() && !isTv()) {
- Log.i(TAG, "Using Pia V2");
+ boolean testOverrideForPiaV2 = Settings.System.getInt(getContentResolver(),
+ "use_pia_v2", 0) == 1;
+ boolean usePiaV2aConfig = usePiaV2();
+
+ if ((usePiaV2aConfig || testOverrideForPiaV2) && !isTv()) {
+ Log.d(TAG, getReasonForDebug(usePiaV2aConfig, testOverrideForPiaV2));
boolean returnResult = getIntent().getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false);
Intent piaV2 = new Intent(getIntent());
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/InstallRepository.kt b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/InstallRepository.kt
index 3b0faf0..178e656 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/InstallRepository.kt
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/InstallRepository.kt
@@ -38,9 +38,10 @@
import android.text.TextUtils
import android.util.EventLog
import android.util.Log
+
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
-import com.android.packageinstaller.R
+
import com.android.packageinstaller.common.EventResultPersister
import com.android.packageinstaller.common.EventResultPersister.OutOfIdsException
import com.android.packageinstaller.common.InstallEventReceiver
@@ -52,6 +53,9 @@
import com.android.packageinstaller.v2.model.InstallUserActionRequired.Companion.USER_ACTION_REASON_ANONYMOUS_SOURCE
import com.android.packageinstaller.v2.model.InstallUserActionRequired.Companion.USER_ACTION_REASON_INSTALL_CONFIRMATION
import com.android.packageinstaller.v2.model.InstallUserActionRequired.Companion.USER_ACTION_REASON_UNKNOWN_SOURCE
+import com.android.packageinstaller.v2.model.PackageUtil.INSTALL_TYPE_NEW
+import com.android.packageinstaller.v2.model.PackageUtil.INSTALL_TYPE_REINSTALL
+import com.android.packageinstaller.v2.model.PackageUtil.INSTALL_TYPE_UPDATE
import com.android.packageinstaller.v2.model.PackageUtil.canPackageQuery
import com.android.packageinstaller.v2.model.PackageUtil.generateStubPackageInfo
import com.android.packageinstaller.v2.model.PackageUtil.getAppSnippet
@@ -61,13 +65,15 @@
import com.android.packageinstaller.v2.model.PackageUtil.isInstallPermissionGrantedOrRequested
import com.android.packageinstaller.v2.model.PackageUtil.isPermissionGranted
import com.android.packageinstaller.v2.model.PackageUtil.localLogv
-import java.io.File
-import java.io.IOException
+
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
+import java.io.File
+import java.io.IOException
+
class InstallRepository(private val context: Context) {
private val packageManager: PackageManager = context.packageManager
@@ -84,6 +90,7 @@
private val _installResult = MutableLiveData<InstallStage>()
val installResult: LiveData<InstallStage>
get() = _installResult
+ private var installType = INSTALL_TYPE_NEW
/**
* Session ID for a session created when caller uses PackageInstaller APIs
@@ -647,10 +654,13 @@
return InstallAborted(ABORT_REASON_INTERNAL_ERROR)
}
}
- return InstallUserActionRequired(
- USER_ACTION_REASON_INSTALL_CONFIRMATION, appSnippet, isAppUpdating(newPackageInfo!!),
- getUpdateMessage(newPackageInfo!!, userActionReason)
- )
+ installType = getInstallType(newPackageInfo)
+ val (existingUpdateOwner, requestedUpdateOwner) =
+ getUpdateOwners(newPackageInfo, userActionReason,
+ /* isAppUpdating= */ (installType != INSTALL_TYPE_NEW))
+
+ return InstallUserActionRequired(USER_ACTION_REASON_INSTALL_CONFIRMATION, appSnippet,
+ installType, existingUpdateOwner, requestedUpdateOwner)
}
/**
@@ -662,32 +672,39 @@
private fun processSessionInfo(sessionInfo: SessionInfo, userActionReason: Int): InstallStage {
newPackageInfo = generateStubPackageInfo(sessionInfo.getAppPackageName())
appSnippet = getAppSnippet(context, sessionInfo)
+ installType = getInstallType(newPackageInfo)
+ val (existingUpdateOwner, requestedUpdateOwner) =
+ getUpdateOwners(newPackageInfo, userActionReason,
+ /* isAppUpdating= */ (installType != INSTALL_TYPE_NEW))
- return InstallUserActionRequired(
- USER_ACTION_REASON_INSTALL_CONFIRMATION, appSnippet, isAppUpdating(newPackageInfo!!),
- getUpdateMessage(newPackageInfo!!, userActionReason)
-
- )
+ return InstallUserActionRequired(USER_ACTION_REASON_INSTALL_CONFIRMATION, appSnippet,
+ installType, existingUpdateOwner, requestedUpdateOwner)
}
- private fun getUpdateMessage(pkgInfo: PackageInfo, userActionReason: Int): String? {
- if (isAppUpdating(pkgInfo)) {
- val existingUpdateOwnerLabel = getExistingUpdateOwnerLabel(pkgInfo)
+ private fun getUpdateOwners(
+ pkgInfo: PackageInfo?,
+ userActionReason: Int,
+ isAppUpdating: Boolean
+ ): Pair<CharSequence?, CharSequence?> {
+ if (pkgInfo == null) {
+ return Pair(null, null)
+ }
+ val existingUpdateOwnerLabel = getExistingUpdateOwnerLabel(pkgInfo)
+
+ var requestedUpdateOwnerLabel: CharSequence? = if (
+ isAppUpdating &&
+ !TextUtils.isEmpty(existingUpdateOwnerLabel) &&
+ userActionReason == PackageInstaller.REASON_REMIND_OWNERSHIP
+ ) {
val originatingPackageName =
getPackageNameForUid(context, originatingUid, callingPackage)
- val requestedUpdateOwnerLabel = getApplicationLabel(originatingPackageName)
-
- if (!TextUtils.isEmpty(existingUpdateOwnerLabel)
- && userActionReason == PackageInstaller.REASON_REMIND_OWNERSHIP
- ) {
- return context.getString(
- R.string.install_confirm_question_update_owner_reminder,
- requestedUpdateOwnerLabel, existingUpdateOwnerLabel
- )
- }
+ getApplicationLabel(originatingPackageName)
+ } else {
+ null
}
- return null
+
+ return Pair(existingUpdateOwnerLabel, requestedUpdateOwnerLabel)
}
private fun getExistingUpdateOwnerLabel(pkgInfo: PackageInfo): CharSequence? {
@@ -717,7 +734,10 @@
}
}
- private fun isAppUpdating(newPkgInfo: PackageInfo): Boolean {
+ private fun getInstallType(newPkgInfo: PackageInfo?): Int {
+ if (newPkgInfo == null) {
+ return INSTALL_TYPE_NEW
+ }
var pkgName = newPkgInfo.packageName
// Check if there is already a package on the device with this name
// but it has been renamed to something else.
@@ -737,13 +757,25 @@
pkgName, PackageManager.MATCH_UNINSTALLED_PACKAGES
)
// If the package is archived, treat it as an update case.
- if (!appInfo.isArchived && appInfo.flags and ApplicationInfo.FLAG_INSTALLED == 0) {
- return false
+ if (appInfo.isArchived) {
+ return INSTALL_TYPE_UPDATE
+ } else if (appInfo.flags and ApplicationInfo.FLAG_INSTALLED == 0) {
+ return INSTALL_TYPE_NEW
+ }
+
+ val currentPkgInfo = packageManager.getPackageInfo(
+ pkgName, PackageManager.MATCH_UNINSTALLED_PACKAGES)
+ val currentVersionCode = currentPkgInfo.longVersionCode
+ var newVersionCode = newPkgInfo.longVersionCode
+
+ return if (currentVersionCode == newVersionCode) {
+ INSTALL_TYPE_REINSTALL
+ } else {
+ INSTALL_TYPE_UPDATE
}
} catch (e: PackageManager.NameNotFoundException) {
- return false
+ return INSTALL_TYPE_NEW
}
- return true
}
/**
@@ -809,7 +841,7 @@
val sourceAppSnippet = getAppSnippet(context, sourceInfo)
InstallUserActionRequired(
USER_ACTION_REASON_UNKNOWN_SOURCE, appSnippet = sourceAppSnippet,
- sourceApp = requestInfo.originatingPackage
+ unknownSourcePackageName = requestInfo.originatingPackage
)
} catch (e: PackageManager.NameNotFoundException) {
Log.e(LOG_TAG, "Did not find appInfo for " + requestInfo.originatingPackage)
@@ -869,7 +901,7 @@
}
val installId: Int
try {
- _installResult.value = InstallInstalling(appSnippet)
+ _installResult.value = InstallInstalling(appSnippet, installType)
installId = InstallEventReceiver.addObserver(
context, EventResultPersister.GENERATE_NEW_ID
) { statusCode: Int, legacyStatus: Int, message: String?, serviceId: Int ->
@@ -923,7 +955,8 @@
val intent = packageManager.getLaunchIntentForPackage(newPackageInfo!!.packageName)
if (isLauncherActivityEnabled(intent)) intent else null
}
- _installResult.setValue(InstallSuccess(appSnippet, shouldReturnResult, resultIntent))
+ _installResult.setValue(
+ InstallSuccess(appSnippet, shouldReturnResult, resultIntent, installType))
} else {
// TODO (b/346655018): Use INSTALL_FAILED_ABORTED legacyCode in the condition
// statusCode can be STATUS_FAILURE_ABORTED if:
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/InstallStages.kt b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/InstallStages.kt
index a8dad09..1c1db4a 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/InstallStages.kt
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/InstallStages.kt
@@ -21,6 +21,7 @@
import android.content.pm.PackageInstaller
import android.content.pm.PackageManager
import android.graphics.drawable.Drawable
+import com.android.packageinstaller.v2.model.PackageUtil.INSTALL_TYPE_NEW
sealed class InstallStage(val stageCode: Int) {
@@ -43,11 +44,10 @@
data class InstallUserActionRequired(
val actionReason: Int,
val appSnippet: PackageUtil.AppSnippet? = null,
- val isAppUpdating: Boolean = false,
- /**
- * This holds either a package name or the app label of the install source.
- */
- val sourceApp: String? = null,
+ @PackageUtil.InstallType val installType: Int = INSTALL_TYPE_NEW,
+ val existingUpdateOwnerLabel: CharSequence? = null,
+ val requestedUpdateOwnerLabel: CharSequence? = null,
+ val unknownSourcePackageName: String? = null,
) : InstallStage(STAGE_USER_ACTION_REQUIRED) {
val appIcon: Drawable?
@@ -63,7 +63,10 @@
}
}
-data class InstallInstalling(val appSnippet: PackageUtil.AppSnippet) :
+data class InstallInstalling(
+ val appSnippet: PackageUtil.AppSnippet,
+ @PackageUtil.InstallType val installType: Int
+) :
InstallStage(STAGE_INSTALLING) {
val appIcon: Drawable?
@@ -85,6 +88,7 @@
* the newly installed / updated app if a launchable activity exists.
*/
val resultIntent: Intent? = null,
+ @PackageUtil.InstallType val installType: Int,
) : InstallStage(STAGE_SUCCESS) {
val appIcon: Drawable?
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/PackageUtil.kt b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/PackageUtil.kt
index a6b8373..0466074 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/PackageUtil.kt
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/PackageUtil.kt
@@ -38,11 +38,15 @@
import android.os.UserHandle
import android.os.UserManager
import android.util.Log
+
+import androidx.annotation.IntDef
+
import com.android.packageinstaller.v2.model.PackageUtil.getAppSnippet
import java.io.ByteArrayOutputStream
import java.io.File
import java.nio.file.Files
import java.nio.file.Path
+
import kotlinx.parcelize.Parceler
import kotlinx.parcelize.Parcelize
@@ -59,23 +63,36 @@
const val ARGS_APP_DATA_SIZE: String = "app_data_size"
const val ARGS_APP_LABEL: String = "app_label"
const val ARGS_APP_SNIPPET: String = "app_snippet"
+ const val ARGS_BUTTON_TEXT: String = "button_text"
const val ARGS_ERROR_DIALOG_TYPE: String = "error_dialog_type"
+ const val ARGS_EXISTING_OWNER: String = "existing_owner"
const val ARGS_INSTALLER_LABEL: String = "installer_label"
const val ARGS_INSTALLER_PACKAGE: String = "installer_pkg"
- const val ARGS_IS_ARCHIVE: String = "is_archive"
- const val ARGS_IS_CLONE_USER: String = "clone_user"
- const val ARGS_IS_UPDATING: String = "is_updating"
const val ARGS_LEGACY_CODE: String = "legacy_code"
const val ARGS_MESSAGE: String = "message"
+ const val ARGS_INSTALL_TYPE: String = "new_app_state"
+ const val ARGS_NEW_OWNER: String = "new_owner"
const val ARGS_PENDING_INTENT: String = "pending_intent"
const val ARGS_REQUIRED_BYTES: String = "required_bytes"
const val ARGS_RESULT_INTENT: String = "result_intent"
const val ARGS_SHOULD_RETURN_RESULT: String = "should_return_result"
- const val ARGS_SOURCE_APP: String = "source_app"
+ const val ARGS_SOURCE_PKG: String = "source_pkg"
const val ARGS_STATUS_CODE: String = "status_code"
const val ARGS_TITLE: String = "title"
const val ARGS_UNARCHIVAL_STATUS: String = "unarchival_status"
+ @IntDef(
+ value = [
+ INSTALL_TYPE_NEW,
+ INSTALL_TYPE_UPDATE,
+ INSTALL_TYPE_REINSTALL,
+ ])
+ @Retention(AnnotationRetention.SOURCE)
+ annotation class InstallType
+ const val INSTALL_TYPE_NEW = 0
+ const val INSTALL_TYPE_UPDATE = 1
+ const val INSTALL_TYPE_REINSTALL = 2
+
/**
* Determines if the UID belongs to the system downloads provider and returns the
* [ApplicationInfo] of the provider
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/UninstallRepository.kt b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/UninstallRepository.kt
index 96525f6..3ad42f1 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/UninstallRepository.kt
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/UninstallRepository.kt
@@ -49,6 +49,7 @@
import com.android.packageinstaller.common.EventResultPersister
import com.android.packageinstaller.common.EventResultPersister.OutOfIdsException
import com.android.packageinstaller.common.UninstallEventReceiver
+import com.android.packageinstaller.v2.model.PackageUtil.getAppSnippet
import com.android.packageinstaller.v2.model.PackageUtil.getMaxTargetSdkVersionForUid
import com.android.packageinstaller.v2.model.PackageUtil.getPackageNameForUid
import com.android.packageinstaller.v2.model.PackageUtil.isPermissionGranted
@@ -211,6 +212,8 @@
fun generateUninstallDetails(): UninstallStage {
val messageBuilder = StringBuilder()
+ var dialogTitle = context.getString(R.string.title_uninstall)
+ var positiveButtonText = context.getString(R.string.button_uninstall)
targetAppLabel = targetAppInfo!!.loadSafeLabel(packageManager)
@@ -220,124 +223,137 @@
val activityLabel = targetActivityInfo!!.loadSafeLabel(packageManager)
if (!activityLabel.contentEquals(targetAppLabel)) {
messageBuilder.append(
- context.getString(R.string.uninstall_activity_text, activityLabel)
+ context.getString(
+ R.string.message_uninstall_activity,
+ activityLabel, targetAppLabel)
)
- messageBuilder.append(" ").append(targetAppLabel).append(".\n\n")
+ dialogTitle = context.getString(R.string.title_uninstall)
}
}
- val isUpdate = (targetAppInfo!!.flags and ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0
+ val isUpdatedSystemApp =
+ (targetAppInfo!!.flags and ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0
val isArchive =
PmFlags.archiving() && ((deleteFlags and PackageManager.DELETE_ARCHIVE) != 0)
val myUserHandle = Process.myUserHandle()
- val isSingleUser = isSingleUser()
+ val isSingleUserOnDevice = isSingleUserOnDevice()
- if (isUpdate) {
+ if (isUpdatedSystemApp) {
+ dialogTitle = context.getString(R.string.title_uninstall_updates_system_app)
+ positiveButtonText = context.getString(R.string.button_uninstall_updates_system_app)
messageBuilder.append(
context.getString(
- if (isSingleUser) {
- R.string.uninstall_update_text
+ if (isSingleUserOnDevice) {
+ R.string.message_uninstall_updates_system_app
} else {
- R.string.uninstall_update_text_multiuser
+ R.string.message_uninstall_updates_system_app_all_users
}
)
)
- } else if (uninstallFromAllUsers && !isSingleUser) {
- val messageString = if (isArchive) {
- context.getString(R.string.archive_application_text_all_users)
+ } else if (uninstallFromAllUsers && !isSingleUserOnDevice) {
+ var messageString: String
+ if (isArchive) {
+ messageString = context.getString(R.string.message_archive_all_users)
+ dialogTitle = context.getString(R.string.title_archive)
} else {
- context.getString(R.string.uninstall_application_text_all_users)
+ messageString = context.getString(R.string.message_uninstall_all_users)
+ dialogTitle = context.getString(R.string.title_uninstall_all_users)
}
messageBuilder.append(messageString)
- } else if (uninstalledUser != myUserHandle) {
- // Uninstalling user is issuing uninstall for another user
- val customUserManager = context.createContextAsUser(uninstalledUser!!, 0)
- .getSystemService(UserManager::class.java)
- val userName = customUserManager!!.userName
-
- var messageString = if (isArchive) {
- context.getString(R.string.archive_application_text_user, userName)
- } else {
- context.getString(R.string.uninstall_application_text_user, userName)
- }
-
- if (userManager!!.isSameProfileGroup(myUserHandle, uninstalledUser!!)) {
- if (customUserManager.isManagedProfile) {
- messageString = if (isArchive) {
- context.getString(
- R.string.archive_application_text_current_user_work_profile, userName
- )
- } else {
- context.getString(
- R.string.uninstall_application_text_current_user_work_profile, userName
- )
- }
- } else if (customUserManager.isCloneProfile){
- isClonedApp = true
- messageString = context.getString(
- R.string.uninstall_application_text_current_user_clone_profile
- )
- } else if (Flags.allowPrivateProfile()
- && MultiuserFlags.enablePrivateSpaceFeatures()
- && customUserManager.isPrivateProfile
- ) {
- // TODO(b/324244123): Get these Strings from a User Property API.
- messageString = if (isArchive) {
- context.getString(
- R.string.archive_application_text_current_user_private_profile, userName
- )
- } else {
- context.getString(
- R.string.uninstall_application_text_current_user_private_profile
- )
- }
- }
- }
- messageBuilder.append(messageString)
- } else if (isCloneProfile(uninstalledUser!!)) {
- isClonedApp = true
- messageBuilder.append(
- context.getString(
- R.string.uninstall_application_text_current_user_clone_profile
- )
- )
} else if (myUserHandle == UserHandle.SYSTEM &&
hasClonedInstance(targetAppInfo!!.packageName)
) {
- messageBuilder.append(
- context.getString(
- R.string.uninstall_application_text_with_clone_instance,
- targetAppLabel
- )
- )
- } else if (isArchive) {
- messageBuilder.append(context.getString(R.string.archive_application_text))
+ dialogTitle = context.getString(R.string.title_uninstall)
+ messageBuilder.append(context.getString(R.string.message_uninstall_with_clone_instance))
} else {
- messageBuilder.append(context.getString(R.string.uninstall_application_text))
+ val isCrossUserUninstalledRequest = myUserHandle != uninstalledUser
+ val isSameProfileGroup =
+ userManager!!.isSameProfileGroup(myUserHandle, uninstalledUser!!)
+ val isTargetUserAProfile = isCrossUserUninstalledRequest && isSameProfileGroup
+
+ val userManagerForTargetUser = context.createContextAsUser(uninstalledUser!!, 0)
+ .getSystemService(UserManager::class.java)
+
+ val isPrivateSpaceFeatureEnabled = Flags.allowPrivateProfile()
+ && MultiuserFlags.enablePrivateSpaceFeatures()
+
+ var messageString = ""
+ if ((isPrivateSpaceFeatureEnabled)
+ && (userManager.isPrivateProfile
+ || (isTargetUserAProfile && userManagerForTargetUser.isPrivateProfile))) {
+ if (isArchive) {
+ messageString = context.getString(R.string.message_archive_private_space)
+ dialogTitle = context.getString(R.string.title_archive)
+ } else {
+ messageString = context.getString(R.string.message_uninstall_private_space)
+ dialogTitle = context.getString(R.string.title_uninstall)
+ }
+ } else if (userManager.isManagedProfile
+ || (isTargetUserAProfile && userManagerForTargetUser.isManagedProfile)) {
+ if (isArchive) {
+ messageString = context.getString(R.string.message_archive_work_profile)
+ dialogTitle = context.getString(R.string.title_archive)
+ } else {
+ messageString = context.getString(R.string.message_uninstall_work_profile)
+ dialogTitle = context.getString(R.string.title_uninstall)
+ }
+ } else if (userManager.isCloneProfile
+ || (isTargetUserAProfile && userManagerForTargetUser.isCloneProfile)) {
+ isClonedApp = true
+ messageString = context.getString(R.string.message_delete_clone_app,
+ targetAppLabel)
+ dialogTitle = context.getString(R.string.title_uninstall_clone)
+ } else if (isCrossUserUninstalledRequest && !isTargetUserAProfile) {
+ // App is being uninstalled from a different, but non-profile user
+ val userName = userManagerForTargetUser!!.userName
+ if (isArchive) {
+ messageString = context.getString(R.string.message_archive_other_user,
+ userName)
+ dialogTitle = context.getString(R.string.title_archive_other_user)
+ } else {
+ messageString =
+ context.getString(R.string.message_uninstall_other_user, userName)
+ dialogTitle = context.getString(R.string.title_uninstall_other_user)
+ }
+ } else if (isArchive) {
+ dialogTitle = context.getString(R.string.title_archive)
+ positiveButtonText = context.getString(R.string.button_archive)
+ messageString = context.getString(R.string.message_archive)
+ }
+
+ if (messageString.isNotEmpty()) {
+ messageBuilder.append(messageString)
+ }
}
- val message = messageBuilder.toString()
+ val message = if (messageBuilder.isNotEmpty()) {
+ messageBuilder.toString()
+ } else {
+ null
+ }
- val title = if (isClonedApp) {
- context.getString(R.string.cloned_app_label, targetAppLabel)
- } else if (isArchive) {
- context.getString(R.string.archiving_app_label, targetAppLabel)
+ val pkgInfo = try {
+ packageManager.getPackageInfo(
+ targetPackageName!!, PackageInfoFlags.of(PackageManager.MATCH_ARCHIVED_PACKAGES)
+ )
+ } catch (e: PackageManager.NameNotFoundException) {
+ Log.e(LOG_TAG, "Cannot get packageInfo for $targetPackageName", e)
+ null
+ }
+
+ // Create a context from the user from where we need to uninstall the app to help get
+ // correctly badged icon (e.g badging for work profile, private space)
+ val userContext = context.createContextAsUser(uninstalledUser!!, 0)
+ val appSnippet: PackageUtil.AppSnippet? = pkgInfo?.let { getAppSnippet(userContext, it) }
+ appSnippet?.label = if (isClonedApp) {
+ context.getString(R.string.string_cloned_app_label, targetAppLabel)
} else {
targetAppLabel.toString()
}
- var suggestToKeepAppData = false
- try {
- val pkgInfo = packageManager.getPackageInfo(
- targetPackageName!!, PackageInfoFlags.of(PackageManager.MATCH_ARCHIVED_PACKAGES)
- )
- suggestToKeepAppData =
- pkgInfo.applicationInfo != null
- && pkgInfo.applicationInfo!!.hasFragileUserData()
- && !isArchive
- } catch (e: PackageManager.NameNotFoundException) {
- Log.e(LOG_TAG, "Cannot check hasFragileUserData for $targetPackageName", e)
- }
+ var suggestToKeepAppData = pkgInfo?.applicationInfo != null
+ && (pkgInfo.applicationInfo?.hasFragileUserData() == true)
+ && !isArchive
var appDataSize: Long = 0
if (suggestToKeepAppData) {
@@ -347,7 +363,7 @@
)
}
- return UninstallUserActionRequired(title, message, appDataSize, isArchive)
+ return UninstallUserActionRequired(dialogTitle, message, positiveButtonText, appDataSize, appSnippet)
}
/**
@@ -357,7 +373,7 @@
* [android.os.UserManager.isHeadlessSystemUserMode], the system user is not "full",
* so it's not be considered in the calculation.
*/
- private fun isSingleUser(): Boolean {
+ private fun isSingleUserOnDevice(): Boolean {
val userCount = userManager!!.userCount
return userCount == 1 || (UserManager.isHeadlessSystemUserMode() && userCount == 2)
}
@@ -449,9 +465,6 @@
return
}
- // TODO: Check with UX whether to show UninstallUninstalling dialog / notification?
- uninstallResult.value = UninstallUninstalling(targetAppLabel, isClonedApp)
-
val uninstallData = Bundle()
uninstallData.putInt(EXTRA_UNINSTALL_ID, uninstallId)
uninstallData.putString(EXTRA_PACKAGE_NAME, targetPackageName)
@@ -472,21 +485,13 @@
broadcastIntent,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE
)
- if (!startUninstall(
- targetPackageName!!,
- uninstalledUser!!,
- pendingIntent,
- uninstallFromAllUsers,
- keepData
- )
- ) {
- handleUninstallResult(
- PackageInstaller.STATUS_FAILURE,
- PackageManager.DELETE_FAILED_INTERNAL_ERROR,
- null,
- 0
- )
- }
+ startUninstall(
+ targetPackageName!!,
+ uninstalledUser!!,
+ pendingIntent,
+ uninstallFromAllUsers,
+ keepData
+ )
}
private fun handleUninstallResult(
@@ -761,9 +766,6 @@
/**
* Starts an uninstall for the given package.
- *
- * @return `true` if there was no exception while uninstalling. This does not represent
- * the result of the uninstall. Result will be made available in [handleUninstallResult]
*/
private fun startUninstall(
packageName: String,
@@ -771,22 +773,26 @@
pendingIntent: PendingIntent,
uninstallFromAllUsers: Boolean,
keepData: Boolean
- ): Boolean {
+ ) {
var flags = if (uninstallFromAllUsers) PackageManager.DELETE_ALL_USERS else 0
flags = flags or if (keepData) PackageManager.DELETE_KEEP_DATA else 0
flags = flags or deleteFlags
- return try {
+ try {
context.createContextAsUser(targetUser, 0)
.packageManager.packageInstaller.uninstall(
VersionedPackage(packageName, PackageManager.VERSION_CODE_HIGHEST),
flags,
pendingIntent.intentSender
)
- true
} catch (e: IllegalArgumentException) {
Log.e(LOG_TAG, "Failed to uninstall", e)
- false
+ handleUninstallResult(
+ PackageInstaller.STATUS_FAILURE,
+ PackageManager.DELETE_FAILED_INTERNAL_ERROR,
+ null,
+ 0
+ )
}
}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/UninstallStages.kt b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/UninstallStages.kt
index 316e8b7..b85ecb6b 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/UninstallStages.kt
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/UninstallStages.kt
@@ -18,6 +18,7 @@
import android.app.Activity
import android.app.Notification
import android.content.Intent
+import android.graphics.drawable.Drawable
import com.android.packageinstaller.R
sealed class UninstallStage(val stageCode: Int) {
@@ -27,23 +28,27 @@
const val STAGE_ABORTED = 0
const val STAGE_READY = 1
const val STAGE_USER_ACTION_REQUIRED = 2
- const val STAGE_UNINSTALLING = 3
- const val STAGE_SUCCESS = 4
- const val STAGE_FAILED = 5
+ const val STAGE_SUCCESS = 3
+ const val STAGE_FAILED = 4
}
}
class UninstallReady : UninstallStage(STAGE_READY)
data class UninstallUserActionRequired(
- val title: String? = null,
+ val title: String,
val message: String? = null,
+ val buttonText: String,
val appDataSize: Long = 0,
- val isArchive: Boolean = false
-) : UninstallStage(STAGE_USER_ACTION_REQUIRED)
+ val appSnippet: PackageUtil.AppSnippet? = null,
+) : UninstallStage(STAGE_USER_ACTION_REQUIRED) {
-data class UninstallUninstalling(val appLabel: CharSequence, val isCloneUser: Boolean) :
- UninstallStage(STAGE_UNINSTALLING)
+ val appIcon: Drawable?
+ get() = appSnippet?.icon
+
+ val appLabel: String?
+ get() = appSnippet?.let { appSnippet.label as String? }
+}
data class UninstallSuccess(
val resultIntent: Intent? = null,
@@ -88,13 +93,13 @@
init {
when (abortReason) {
ABORT_REASON_APP_UNAVAILABLE -> {
- dialogTitleResource = R.string.app_not_found_dlg_title
- dialogTextResource = R.string.app_not_found_dlg_text
+ dialogTitleResource = R.string.title_uninstall_app_not_found
+ dialogTextResource = R.string.message_uninstall_app_not_found
}
ABORT_REASON_USER_NOT_ALLOWED -> {
- dialogTitleResource = 0
- dialogTextResource = R.string.user_is_not_allowed_dlg_text
+ dialogTitleResource = R.string.title_uninstall_user_not_allowed
+ dialogTextResource = R.string.message_uninstall_user_not_allowed
}
ABORT_REASON_UNINSTALL_DONE -> {
@@ -103,8 +108,8 @@
}
else -> {
- dialogTitleResource = 0
- dialogTextResource = R.string.generic_error_dlg_text
+ dialogTitleResource = R.string.title_uninstall_failed
+ dialogTextResource = R.string.message_uninstall_failed
}
}
}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/InstallActionListener.kt b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/InstallActionListener.kt
index 1d4d178..42562395 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/InstallActionListener.kt
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/InstallActionListener.kt
@@ -40,4 +40,9 @@
* Launch the intent to open the newly installed / updated app.
*/
fun openInstalledApp(intent: Intent?)
+
+ /**
+ * Launch the intent to open Storage Manager in Settings app.
+ */
+ fun sendManageAppsIntent()
}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/InstallLaunch.kt b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/InstallLaunch.kt
index 4a8be8d..b3a9b21 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/InstallLaunch.kt
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/InstallLaunch.kt
@@ -28,12 +28,14 @@
import android.provider.Settings
import android.util.Log
import android.view.Window
+
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.FragmentActivity
import androidx.fragment.app.FragmentManager
import androidx.lifecycle.ViewModelProvider
+
import com.android.packageinstaller.R
import com.android.packageinstaller.v2.model.InstallAborted
import com.android.packageinstaller.v2.model.InstallFailed
@@ -82,6 +84,13 @@
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
requestWindowFeature(Window.FEATURE_NO_TITLE)
+ // The base theme inherits a deviceDefault theme. Applying a material style on the base
+ // theme below will enable using Material components, like the Material Progress Bar in the
+ // fragments
+ theme.applyStyle(
+ com.google.android.material.R.style.Theme_Material3_DynamicColors_DayNight,
+ /* force= */ false)
+
fragmentManager = supportFragmentManager
appOpsManager = getSystemService(AppOpsManager::class.java)
installRepository = InstallRepository(applicationContext)
@@ -243,11 +252,11 @@
}
return when (restriction) {
UserManager.DISALLOW_INSTALL_APPS ->
- SimpleErrorFragment.newInstance(R.string.install_apps_user_restriction_dlg_text)
+ SimpleErrorFragment.newInstance(R.string.message_no_install_apps_restriction)
UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES,
UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY ->
- SimpleErrorFragment.newInstance(R.string.unknown_apps_user_restriction_dlg_text)
+ SimpleErrorFragment.newInstance(R.string.message_no_install_unknown_apps_restriction)
else -> null
}
@@ -337,6 +346,12 @@
}
}
+ override fun sendManageAppsIntent() {
+ val intent = Intent("android.intent.action.MANAGE_PACKAGE_STORAGE")
+ startActivity(intent)
+ setResult(RESULT_FIRST_USER, null, true)
+ }
+
private fun registerAppOpChangeListener(listener: UnknownSourcesListener, packageName: String) {
appOpsManager!!.startWatchingMode(
AppOpsManager.OPSTR_REQUEST_INSTALL_PACKAGES,
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/UninstallLaunch.kt b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/UninstallLaunch.kt
index 08bc766..a944b75 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/UninstallLaunch.kt
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/UninstallLaunch.kt
@@ -23,21 +23,21 @@
import android.util.Log
import android.view.WindowManager
import android.widget.Toast
+
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.FragmentActivity
import androidx.fragment.app.FragmentManager
import androidx.lifecycle.ViewModelProvider
+
import com.android.packageinstaller.v2.model.PackageUtil.localLogv
import com.android.packageinstaller.v2.model.UninstallAborted
import com.android.packageinstaller.v2.model.UninstallFailed
import com.android.packageinstaller.v2.model.UninstallRepository
import com.android.packageinstaller.v2.model.UninstallStage
import com.android.packageinstaller.v2.model.UninstallSuccess
-import com.android.packageinstaller.v2.model.UninstallUninstalling
import com.android.packageinstaller.v2.model.UninstallUserActionRequired
import com.android.packageinstaller.v2.ui.fragments.UninstallConfirmationFragment
import com.android.packageinstaller.v2.ui.fragments.UninstallErrorFragment
-import com.android.packageinstaller.v2.ui.fragments.UninstallUninstallingFragment
import com.android.packageinstaller.v2.viewmodel.UninstallViewModel
import com.android.packageinstaller.v2.viewmodel.UninstallViewModelFactory
@@ -63,6 +63,14 @@
// Never restore any state, esp. never create any fragments. The data in the fragment might
// be stale, if e.g. the app was uninstalled while the activity was destroyed.
super.onCreate(null)
+
+ // The base theme inherits a deviceDefault theme. Applying a material style on the base
+ // theme below will enable using Material components, like the Material Progress Bar in the
+ // fragments
+ theme.applyStyle(
+ com.google.android.material.R.style.Theme_Material3_DynamicColors_DayNight,
+ /* force= */ false)
+
fragmentManager = supportFragmentManager
notificationManager = getSystemService(NotificationManager::class.java)
@@ -114,16 +122,6 @@
showDialogInner(confirmationDialog)
}
- UninstallStage.STAGE_UNINSTALLING -> {
- // TODO: This shows a fragment whether or not user requests a result or not.
- // Originally, if the user does not request a result, we used to show a notification.
- // And a fragment if the user requests a result back. Should we consolidate and
- // show a fragment always?
- val uninstalling = uninstallStage as UninstallUninstalling
- val uninstallingDialog = UninstallUninstallingFragment.newInstance(uninstalling)
- showDialogInner(uninstallingDialog)
- }
-
UninstallStage.STAGE_FAILED -> {
val failed = uninstallStage as UninstallFailed
if (!failed.returnResult) {
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/AnonymousSourceFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/AnonymousSourceFragment.java
index cc40b0c..b430f0d 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/AnonymousSourceFragment.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/AnonymousSourceFragment.java
@@ -22,8 +22,12 @@
import android.content.DialogInterface;
import android.os.Bundle;
import android.util.Log;
+import android.view.View;
+import android.widget.TextView;
+
import androidx.annotation.NonNull;
import androidx.fragment.app.DialogFragment;
+
import com.android.packageinstaller.R;
import com.android.packageinstaller.v2.model.InstallStage;
import com.android.packageinstaller.v2.model.InstallUserActionRequired;
@@ -50,14 +54,22 @@
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
Log.i(LOG_TAG, "Creating " + LOG_TAG);
- mDialog = new AlertDialog.Builder(requireContext())
- .setMessage(R.string.anonymous_source_warning)
- .setPositiveButton(R.string.anonymous_source_continue,
- ((dialog, which) -> mInstallActionListener.onPositiveResponse(
- InstallUserActionRequired.USER_ACTION_REASON_ANONYMOUS_SOURCE)))
- .setNegativeButton(R.string.cancel,
- ((dialog, which) -> mInstallActionListener.onNegativeResponse(
- InstallStage.STAGE_USER_ACTION_REQUIRED))).create();
+
+ View dialogView = getLayoutInflater().inflate(R.layout.install_fragment_layout, null);
+ TextView customMessage = dialogView.requireViewById(R.id.custom_message);
+ customMessage.setText(R.string.message_anonymous_source_warning);
+ customMessage.setVisibility(View.VISIBLE);
+
+ mDialog = new AlertDialog.Builder(
+ requireContext(), R.style.Theme_PackageInstaller_AlertDialog_Variant)
+ .setTitle(R.string.title_anonymous_source_warning)
+ .setView(dialogView)
+ .setPositiveButton(R.string.button_continue,
+ ((dialog, which) -> mInstallActionListener.onPositiveResponse(
+ InstallUserActionRequired.USER_ACTION_REASON_ANONYMOUS_SOURCE)))
+ .setNegativeButton(R.string.button_cancel,
+ ((dialog, which) -> mInstallActionListener.onNegativeResponse(
+ InstallStage.STAGE_USER_ACTION_REQUIRED))).create();
return mDialog;
}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/ExternalSourcesBlockedFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/ExternalSourcesBlockedFragment.java
index 4c69b9d..30ef0af 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/ExternalSourcesBlockedFragment.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/ExternalSourcesBlockedFragment.java
@@ -18,15 +18,19 @@
import static com.android.packageinstaller.v2.model.PackageUtil.ARGS_ACTION_REASON;
import static com.android.packageinstaller.v2.model.PackageUtil.ARGS_APP_SNIPPET;
-import static com.android.packageinstaller.v2.model.PackageUtil.ARGS_IS_UPDATING;
-import static com.android.packageinstaller.v2.model.PackageUtil.ARGS_SOURCE_APP;
+import static com.android.packageinstaller.v2.model.PackageUtil.ARGS_SOURCE_PKG;
+import static com.android.packageinstaller.v2.model.PackageUtil.INSTALL_TYPE_NEW;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
+import android.text.Html;
+import android.text.Spanned;
import android.util.Log;
+import android.view.View;
+import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -59,7 +63,7 @@
* Creates a new instance of this fragment with necessary data set as fragment arguments
*
* @param dialogData {@link InstallUserActionRequired} object containing data to display
- * in the dialog
+ * in the dialog
* @return an instance of the fragment
*/
public static ExternalSourcesBlockedFragment newInstance(
@@ -67,8 +71,7 @@
Bundle args = new Bundle();
args.putInt(ARGS_ACTION_REASON, dialogData.getActionReason());
args.putParcelable(ARGS_APP_SNIPPET, dialogData.getAppSnippet());
- args.putBoolean(ARGS_IS_UPDATING, dialogData.isAppUpdating());
- args.putString(ARGS_SOURCE_APP, dialogData.getSourceApp());
+ args.putString(ARGS_SOURCE_PKG, dialogData.getUnknownSourcePackageName());
ExternalSourcesBlockedFragment fragment = new ExternalSourcesBlockedFragment();
fragment.setArguments(args);
@@ -86,18 +89,28 @@
public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
setDialogData(requireArguments());
+ View dialogView = getLayoutInflater().inflate(R.layout.install_fragment_layout, null);
+
+ TextView customMessage = dialogView.requireViewById(R.id.custom_message);
+ String sourceBlockedString =
+ getString(R.string.message_external_source_blocked, mDialogData.getAppLabel());
+ Spanned styledSourceBlockedString =
+ Html.fromHtml(sourceBlockedString, Html.FROM_HTML_MODE_LEGACY);
+ customMessage.setText(styledSourceBlockedString);
+ customMessage.setVisibility(View.VISIBLE);
+
Log.i(LOG_TAG, "Creating " + LOG_TAG + "\n" + mDialogData);
- mDialog = new AlertDialog.Builder(requireContext())
- .setTitle(mDialogData.getAppLabel())
- .setIcon(mDialogData.getAppIcon())
- .setMessage(R.string.untrusted_external_source_warning)
- .setPositiveButton(R.string.external_sources_settings,
- (dialog, which) -> mInstallActionListener.sendUnknownAppsIntent(
- mDialogData.getSourceApp()))
- .setNegativeButton(R.string.cancel,
- (dialog, which) -> mInstallActionListener.onNegativeResponse(
- mDialogData.getStageCode()))
- .create();
+ mDialog = new AlertDialog.Builder(
+ requireContext(), R.style.Theme_PackageInstaller_AlertDialog_Variant)
+ .setTitle(R.string.title_unknown_source_blocked)
+ .setView(dialogView)
+ .setPositiveButton(R.string.external_sources_settings,
+ (dialog, which) -> mInstallActionListener.sendUnknownAppsIntent(
+ mDialogData.getUnknownSourcePackageName()))
+ .setNegativeButton(R.string.cancel,
+ (dialog, which) -> mInstallActionListener.onNegativeResponse(
+ mDialogData.getStageCode()))
+ .create();
return mDialog;
}
@@ -130,10 +143,9 @@
private void setDialogData(Bundle args) {
int actionReason = args.getInt(ARGS_ACTION_REASON);
AppSnippet appSnippet = args.getParcelable(ARGS_APP_SNIPPET, AppSnippet.class);
- boolean isUpdating = args.getBoolean(ARGS_IS_UPDATING);
- String sourceApp = args.getString(ARGS_SOURCE_APP);
+ String sourcePkg = args.getString(ARGS_SOURCE_PKG);
- mDialogData = new InstallUserActionRequired(actionReason, appSnippet, isUpdating,
- sourceApp);
+ mDialogData = new InstallUserActionRequired(actionReason, appSnippet, INSTALL_TYPE_NEW,
+ null, null, sourcePkg);
}
}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/InstallConfirmationFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/InstallConfirmationFragment.java
index 03768fb..4ebf2a2 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/InstallConfirmationFragment.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/InstallConfirmationFragment.java
@@ -18,8 +18,12 @@
import static com.android.packageinstaller.v2.model.PackageUtil.ARGS_ACTION_REASON;
import static com.android.packageinstaller.v2.model.PackageUtil.ARGS_APP_SNIPPET;
-import static com.android.packageinstaller.v2.model.PackageUtil.ARGS_IS_UPDATING;
-import static com.android.packageinstaller.v2.model.PackageUtil.ARGS_SOURCE_APP;
+import static com.android.packageinstaller.v2.model.PackageUtil.ARGS_EXISTING_OWNER;
+import static com.android.packageinstaller.v2.model.PackageUtil.ARGS_INSTALL_TYPE;
+import static com.android.packageinstaller.v2.model.PackageUtil.ARGS_NEW_OWNER;
+import static com.android.packageinstaller.v2.model.PackageUtil.INSTALL_TYPE_NEW;
+import static com.android.packageinstaller.v2.model.PackageUtil.INSTALL_TYPE_REINSTALL;
+import static com.android.packageinstaller.v2.model.PackageUtil.INSTALL_TYPE_UPDATE;
import android.app.AlertDialog;
import android.app.Dialog;
@@ -30,6 +34,7 @@
import android.text.method.ScrollingMovementMethod;
import android.util.Log;
import android.view.View;
+import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
@@ -61,7 +66,7 @@
* Creates a new instance of this fragment with necessary data set as fragment arguments
*
* @param dialogData {@link InstallUserActionRequired} object containing data to display
- * in the dialog
+ * in the dialog
* @return an instance of the fragment
*/
public static InstallConfirmationFragment newInstance(
@@ -69,8 +74,9 @@
Bundle args = new Bundle();
args.putInt(ARGS_ACTION_REASON, dialogData.getActionReason());
args.putParcelable(ARGS_APP_SNIPPET, dialogData.getAppSnippet());
- args.putBoolean(ARGS_IS_UPDATING, dialogData.isAppUpdating());
- args.putString(ARGS_SOURCE_APP, dialogData.getSourceApp());
+ args.putInt(ARGS_INSTALL_TYPE, dialogData.getInstallType());
+ args.putCharSequence(ARGS_EXISTING_OWNER, dialogData.getExistingUpdateOwnerLabel());
+ args.putCharSequence(ARGS_NEW_OWNER, dialogData.getRequestedUpdateOwnerLabel());
InstallConfirmationFragment fragment = new InstallConfirmationFragment();
fragment.setArguments(args);
@@ -89,45 +95,60 @@
setDialogData(requireArguments());
Log.i(LOG_TAG, "Creating " + LOG_TAG + "\n" + mDialogData);
- View dialogView = getLayoutInflater().inflate(R.layout.install_content_view, null);
+
+ View dialogView = getLayoutInflater().inflate(R.layout.install_fragment_layout, null);
+ dialogView.requireViewById(R.id.app_snippet).setVisibility(View.VISIBLE);
+ ((ImageView) dialogView.requireViewById(R.id.app_icon))
+ .setImageDrawable(mDialogData.getAppIcon());
+ ((TextView) dialogView.requireViewById(R.id.app_label)).setText(mDialogData.getAppLabel());
int positiveBtnTextRes;
- if (mDialogData.isAppUpdating()) {
- if (mDialogData.getSourceApp() != null) {
- positiveBtnTextRes = R.string.update_anyway;
- } else {
- positiveBtnTextRes = R.string.update;
+ String title;
+ switch (mDialogData.getInstallType()) {
+ case INSTALL_TYPE_NEW -> {
+ title = getString(R.string.title_install);
+ positiveBtnTextRes = R.string.button_install;
}
- } else {
- positiveBtnTextRes = R.string.install;
+ case INSTALL_TYPE_REINSTALL -> {
+ title = getString(R.string.title_reinstall);
+ positiveBtnTextRes = R.string.button_reinstall;
+ }
+ case INSTALL_TYPE_UPDATE -> {
+ if (mDialogData.getExistingUpdateOwnerLabel() != null
+ && mDialogData.getRequestedUpdateOwnerLabel() != null) {
+ title = getString(R.string.title_update_ownership_change,
+ mDialogData.getRequestedUpdateOwnerLabel());
+ positiveBtnTextRes = R.string.button_update_anyway;
+
+ TextView customMessage = dialogView.requireViewById(R.id.custom_message);
+ customMessage.setVisibility(View.VISIBLE);
+ String updateOwnerString = getString(R.string.message_update_owner_change,
+ mDialogData.getExistingUpdateOwnerLabel());
+ customMessage.setText(
+ Html.fromHtml(updateOwnerString, Html.FROM_HTML_MODE_LEGACY));
+ customMessage.setMovementMethod(new ScrollingMovementMethod());
+ } else {
+ title = getString(R.string.title_update);
+ positiveBtnTextRes = R.string.button_update;
+ }
+ }
+ default -> {
+ title = getString(R.string.title_install);
+ positiveBtnTextRes = R.string.button_install;
+ }
}
mDialog = new AlertDialog.Builder(requireContext())
- .setIcon(mDialogData.getAppIcon())
- .setTitle(mDialogData.getAppLabel())
+ .setTitle(title)
.setView(dialogView)
.setPositiveButton(positiveBtnTextRes,
(dialogInt, which) -> mInstallActionListener.onPositiveResponse(
InstallUserActionRequired.USER_ACTION_REASON_INSTALL_CONFIRMATION))
- .setNegativeButton(R.string.cancel,
+ .setNegativeButton(R.string.button_cancel,
(dialogInt, which) -> mInstallActionListener.onNegativeResponse(
mDialogData.getStageCode()))
.create();
- TextView viewToEnable;
- if (mDialogData.isAppUpdating()) {
- viewToEnable = dialogView.requireViewById(R.id.install_confirm_question_update);
- String sourcePackageName = mDialogData.getSourceApp();
- if (sourcePackageName != null) {
- // Show the update-ownership change message
- viewToEnable.setText(Html.fromHtml(sourcePackageName, Html.FROM_HTML_MODE_LEGACY));
- }
- } else {
- viewToEnable = dialogView.requireViewById(R.id.install_confirm_question);
- }
- viewToEnable.setVisibility(View.VISIBLE);
- viewToEnable.setMovementMethod(new ScrollingMovementMethod());
-
return mDialog;
}
@@ -160,10 +181,11 @@
private void setDialogData(Bundle args) {
int actionReason = args.getInt(ARGS_ACTION_REASON);
AppSnippet appSnippet = args.getParcelable(ARGS_APP_SNIPPET, AppSnippet.class);
- boolean isUpdating = args.getBoolean(ARGS_IS_UPDATING);
- String sourceApp = args.getString(ARGS_SOURCE_APP);
+ int installType = args.getInt(ARGS_INSTALL_TYPE);
+ CharSequence existingOwner = args.getCharSequence(ARGS_EXISTING_OWNER);
+ CharSequence newOwner = args.getCharSequence(ARGS_NEW_OWNER);
- mDialogData = new InstallUserActionRequired(actionReason, appSnippet, isUpdating,
- sourceApp);
+ mDialogData = new InstallUserActionRequired(actionReason, appSnippet, installType,
+ existingOwner, newOwner, null);
}
}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/InstallFailedFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/InstallFailedFragment.java
index 6f65441..766047b 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/InstallFailedFragment.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/InstallFailedFragment.java
@@ -30,8 +30,11 @@
import android.content.Intent;
import android.content.pm.PackageInstaller;
import android.os.Bundle;
+import android.text.Html;
import android.util.Log;
import android.view.View;
+import android.widget.ImageView;
+import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -61,7 +64,7 @@
* Creates a new instance of this fragment with necessary data set as fragment arguments
*
* @param dialogData {@link InstallFailed} object containing data to display in the
- * dialog
+ * dialog
* @return an instance of the fragment
*/
public static InstallFailedFragment newInstance(@NonNull InstallFailed dialogData) {
@@ -90,48 +93,66 @@
setDialogData(requireArguments());
Log.i(LOG_TAG, "Creating " + LOG_TAG + "\n" + mDialogData);
- View dialogView = getLayoutInflater().inflate(R.layout.install_content_view, null);
- AlertDialog dialog = new AlertDialog.Builder(requireContext())
- .setTitle(mDialogData.getAppLabel())
- .setIcon(mDialogData.getAppIcon())
- .setView(dialogView)
- .setPositiveButton(R.string.done,
- (dialogInt, which) -> mInstallActionListener.onNegativeResponse(
- mDialogData.getStageCode()))
- .create();
- setExplanationFromErrorCode(mDialogData.getStatusCode(), dialogView);
- return dialog;
+ View dialogView = getLayoutInflater().inflate(R.layout.install_fragment_layout, null);
+ dialogView.requireViewById(R.id.custom_message).setVisibility(View.VISIBLE);
+ dialogView.requireViewById(R.id.app_snippet).setVisibility(View.VISIBLE);
+ ((ImageView) dialogView.requireViewById(R.id.app_icon))
+ .setImageDrawable(mDialogData.getAppIcon());
+ ((TextView) dialogView.requireViewById(R.id.app_label)).setText(mDialogData.getAppLabel());
+
+
+ AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(requireContext())
+ .setView(dialogView)
+ .setNegativeButton(R.string.button_close,
+ (dialogInt, which) -> mInstallActionListener.onNegativeResponse(
+ mDialogData.getStageCode()));
+ setDialogStringsFromErrorCode(mDialogData.getLegacyCode(), dialogBuilder, dialogView);
+
+ return dialogBuilder.create();
}
/**
- * Unhide the appropriate label for the statusCode.
+ * Set corresponding dialog title and message for the failure statusCode.
*
* @param statusCode The status code from the package installer.
*/
- private void setExplanationFromErrorCode(int statusCode, View dialogView) {
+ private void setDialogStringsFromErrorCode(int statusCode, AlertDialog.Builder builder,
+ View dialogView) {
Log.i(LOG_TAG, "Installation status code: " + statusCode);
- View viewToEnable;
+ TextView customMessage = dialogView.requireViewById(R.id.custom_message);
switch (statusCode) {
- case PackageInstaller.STATUS_FAILURE_BLOCKED:
- viewToEnable = dialogView.requireViewById(R.id.install_failed_blocked);
- break;
- case PackageInstaller.STATUS_FAILURE_CONFLICT:
- viewToEnable = dialogView.requireViewById(R.id.install_failed_conflict);
- break;
- case PackageInstaller.STATUS_FAILURE_INCOMPATIBLE:
- viewToEnable = dialogView.requireViewById(R.id.install_failed_incompatible);
- break;
- case PackageInstaller.STATUS_FAILURE_INVALID:
- viewToEnable = dialogView.requireViewById(R.id.install_failed_invalid_apk);
- break;
- default:
- viewToEnable = dialogView.requireViewById(R.id.install_failed);
- break;
+ case PackageInstaller.STATUS_FAILURE_BLOCKED -> {
+ customMessage.setText(R.string.message_install_failed_blocked);
+ builder.setTitle(R.string.title_install_failed_blocked);
+ }
+ case PackageInstaller.STATUS_FAILURE_CONFLICT -> {
+ customMessage.setText(R.string.message_install_failed_conflict);
+ builder.setTitle(R.string.title_cant_install_app);
+ }
+ case PackageInstaller.STATUS_FAILURE_INCOMPATIBLE -> {
+ customMessage.setText(R.string.message_install_failed_incompatible);
+ builder.setTitle(R.string.title_install_failed_incompatible);
+ }
+ case PackageInstaller.STATUS_FAILURE_INVALID -> {
+ customMessage.setText(R.string.message_install_failed_invalid);
+ builder.setTitle(R.string.title_cant_install_app);
+ }
+ case PackageInstaller.STATUS_FAILURE_STORAGE -> {
+ customMessage.setText(
+ Html.fromHtml(
+ getString(R.string.message_install_failed_less_storage, 0),
+ Html.FROM_HTML_MODE_LEGACY));
+ builder.setTitle(R.string.title_install_failed_less_storage)
+ .setPositiveButton(R.string.button_manage_apps, (dialog, which) ->
+ mInstallActionListener.sendManageAppsIntent());
+ }
+ default -> {
+ customMessage.setVisibility(View.GONE);
+ builder.setTitle(R.string.title_install_failed_not_installed);
+ }
}
-
- viewToEnable.setVisibility(View.VISIBLE);
}
@Override
@@ -149,6 +170,6 @@
Intent resultIntent = args.getParcelable(ARGS_RESULT_INTENT, Intent.class);
mDialogData = new InstallFailed(appSnippet, legacyCode, statusCode, message,
- shouldReturnResult, resultIntent);
+ shouldReturnResult, resultIntent);
}
}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/InstallInstallingFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/InstallInstallingFragment.java
index 17093cf..f8ba4a3 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/InstallInstallingFragment.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/InstallInstallingFragment.java
@@ -17,6 +17,10 @@
package com.android.packageinstaller.v2.ui.fragments;
import static com.android.packageinstaller.v2.model.PackageUtil.ARGS_APP_SNIPPET;
+import static com.android.packageinstaller.v2.model.PackageUtil.ARGS_INSTALL_TYPE;
+import static com.android.packageinstaller.v2.model.PackageUtil.INSTALL_TYPE_NEW;
+import static com.android.packageinstaller.v2.model.PackageUtil.INSTALL_TYPE_REINSTALL;
+import static com.android.packageinstaller.v2.model.PackageUtil.INSTALL_TYPE_UPDATE;
import android.app.AlertDialog;
import android.app.Dialog;
@@ -24,6 +28,8 @@
import android.os.Bundle;
import android.util.Log;
import android.view.View;
+import android.widget.ImageView;
+import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -50,12 +56,13 @@
* Creates a new instance of this fragment with necessary data set as fragment arguments
*
* @param dialogData {@link InstallInstalling} object containing data to display in the
- * dialog
+ * dialog
* @return an instance of the fragment
*/
public static InstallInstallingFragment newInstance(@NonNull InstallInstalling dialogData) {
Bundle args = new Bundle();
args.putParcelable(ARGS_APP_SNIPPET, dialogData.getAppSnippet());
+ args.putInt(ARGS_INSTALL_TYPE, dialogData.getInstallType());
InstallInstallingFragment fragment = new InstallInstallingFragment();
fragment.setArguments(args);
@@ -68,15 +75,26 @@
setDialogData(requireArguments());
Log.i(LOG_TAG, "Creating " + LOG_TAG + "\n" + mDialogData);
- View dialogView = getLayoutInflater().inflate(R.layout.install_content_view, null);
+
+ View dialogView = getLayoutInflater().inflate(R.layout.install_fragment_layout, null);
+ dialogView.requireViewById(R.id.progress_bar).setVisibility(View.VISIBLE);
+ dialogView.requireViewById(R.id.app_snippet).setVisibility(View.VISIBLE);
+ ((ImageView) dialogView.requireViewById(R.id.app_icon))
+ .setImageDrawable(mDialogData.getAppIcon());
+ ((TextView) dialogView.requireViewById(R.id.app_label)).setText(mDialogData.getAppLabel());
+
+ int titleRes = 0;
+ switch (mDialogData.getInstallType()) {
+ case INSTALL_TYPE_NEW -> titleRes = R.string.title_installing;
+ case INSTALL_TYPE_UPDATE -> titleRes = R.string.title_updating;
+ case INSTALL_TYPE_REINSTALL -> titleRes = R.string.title_reinstalling;
+ }
+
mDialog = new AlertDialog.Builder(requireContext())
- .setTitle(mDialogData.getAppLabel())
- .setIcon(mDialogData.getAppIcon())
+ .setTitle(titleRes)
.setView(dialogView)
- .setNegativeButton(R.string.cancel, null)
.create();
- dialogView.requireViewById(R.id.installing).setVisibility(View.VISIBLE);
this.setCancelable(false);
return mDialog;
@@ -90,6 +108,7 @@
private void setDialogData(Bundle args) {
AppSnippet appSnippet = args.getParcelable(ARGS_APP_SNIPPET, AppSnippet.class);
- mDialogData = new InstallInstalling(appSnippet);
+ int installType = args.getInt(ARGS_INSTALL_TYPE);
+ mDialogData = new InstallInstalling(appSnippet, installType);
}
}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/InstallStagingFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/InstallStagingFragment.java
index 3cab96b..fd9da7d 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/InstallStagingFragment.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/InstallStagingFragment.java
@@ -23,9 +23,11 @@
import android.util.Log;
import android.view.View;
import android.widget.ProgressBar;
+
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.DialogFragment;
+
import com.android.packageinstaller.R;
public class InstallStagingFragment extends DialogFragment {
@@ -38,14 +40,14 @@
@Override
public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
Log.i(LOG_TAG, "Creating " + LOG_TAG);
- View dialogView = getLayoutInflater().inflate(R.layout.install_content_view, null);
- dialogView.requireViewById(R.id.staging).setVisibility(View.VISIBLE);
+
+ View dialogView = getLayoutInflater().inflate(R.layout.install_fragment_layout, null);
+ dialogView.requireViewById(R.id.progress_bar).setVisibility(View.VISIBLE);
mDialog = new AlertDialog.Builder(requireContext())
- .setTitle(getString(R.string.app_name_unknown))
- .setIcon(R.drawable.ic_file_download)
+ .setTitle(R.string.title_install_staging)
.setView(dialogView)
- .setNegativeButton(R.string.cancel, null)
+ .setNegativeButton(R.string.button_cancel, null)
.setCancelable(false)
.create();
@@ -57,7 +59,7 @@
public void onStart() {
super.onStart();
mDialog.getButton(DialogInterface.BUTTON_NEGATIVE).setEnabled(false);
- mProgressBar = mDialog.requireViewById(R.id.progress_indeterminate);
+ mProgressBar = mDialog.requireViewById(R.id.progress_bar);
mProgressBar.setProgress(0);
mProgressBar.setMax(100);
mProgressBar.setIndeterminate(false);
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/InstallSuccessFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/InstallSuccessFragment.java
index 5696afa..a13fb45 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/InstallSuccessFragment.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/InstallSuccessFragment.java
@@ -17,8 +17,12 @@
package com.android.packageinstaller.v2.ui.fragments;
import static com.android.packageinstaller.v2.model.PackageUtil.ARGS_APP_SNIPPET;
+import static com.android.packageinstaller.v2.model.PackageUtil.ARGS_INSTALL_TYPE;
import static com.android.packageinstaller.v2.model.PackageUtil.ARGS_RESULT_INTENT;
import static com.android.packageinstaller.v2.model.PackageUtil.ARGS_SHOULD_RETURN_RESULT;
+import static com.android.packageinstaller.v2.model.PackageUtil.INSTALL_TYPE_NEW;
+import static com.android.packageinstaller.v2.model.PackageUtil.INSTALL_TYPE_REINSTALL;
+import static com.android.packageinstaller.v2.model.PackageUtil.INSTALL_TYPE_UPDATE;
import android.app.AlertDialog;
import android.app.Dialog;
@@ -31,6 +35,8 @@
import android.util.Log;
import android.view.View;
import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -63,7 +69,7 @@
* Create a new instance of this fragment with necessary data set as fragment arguments
*
* @param dialogData {@link InstallSuccess} object containing data to display in the
- * dialog
+ * dialog
* @return an instance of the fragment
*/
public static InstallSuccessFragment newInstance(@NonNull InstallSuccess dialogData) {
@@ -71,6 +77,7 @@
args.putParcelable(ARGS_APP_SNIPPET, dialogData.getAppSnippet());
args.putBoolean(ARGS_SHOULD_RETURN_RESULT, dialogData.getShouldReturnResult());
args.putParcelable(ARGS_RESULT_INTENT, dialogData.getResultIntent());
+ args.putInt(ARGS_INSTALL_TYPE, dialogData.getInstallType());
InstallSuccessFragment fragment = new InstallSuccessFragment();
fragment.setArguments(args);
@@ -90,19 +97,29 @@
setDialogData(requireArguments());
Log.i(LOG_TAG, "Creating " + LOG_TAG + "\n" + mDialogData);
- View dialogView = getLayoutInflater().inflate(R.layout.install_content_view, null);
+
+ View dialogView = getLayoutInflater().inflate(R.layout.install_fragment_layout, null);
+ dialogView.requireViewById(R.id.app_snippet).setVisibility(View.VISIBLE);
+ ((ImageView) dialogView.requireViewById(R.id.app_icon))
+ .setImageDrawable(mDialogData.getAppIcon());
+ ((TextView) dialogView.requireViewById(R.id.app_label)).setText(mDialogData.getAppLabel());
+
+ int titleRes = 0;
+ switch (mDialogData.getInstallType()) {
+ case INSTALL_TYPE_NEW -> titleRes = R.string.title_installed;
+ case INSTALL_TYPE_UPDATE -> titleRes = R.string.title_updated;
+ case INSTALL_TYPE_REINSTALL -> titleRes = R.string.title_reinstalled;
+ }
+
mDialog = new AlertDialog.Builder(requireContext())
- .setTitle(mDialogData.getAppLabel())
- .setIcon(mDialogData.getAppIcon())
+ .setTitle(titleRes)
.setView(dialogView)
- .setNegativeButton(R.string.done,
+ .setNegativeButton(R.string.button_done,
(dialog, which) -> mInstallActionListener.onNegativeResponse(
mDialogData.getStageCode()))
- .setPositiveButton(R.string.launch, (dialog, which) -> {})
+ .setPositiveButton(R.string.button_open, (dialog, which) -> {})
.create();
- dialogView.requireViewById(R.id.install_success).setVisibility(View.VISIBLE);
-
return mDialog;
}
@@ -139,7 +156,9 @@
AppSnippet appSnippet = args.getParcelable(ARGS_APP_SNIPPET, AppSnippet.class);
boolean shouldReturnResult = args.getBoolean(ARGS_SHOULD_RETURN_RESULT);
Intent resultIntent = args.getParcelable(ARGS_RESULT_INTENT, Intent.class);
+ int installType = args.getInt(ARGS_INSTALL_TYPE);
- mDialogData = new InstallSuccess(appSnippet, shouldReturnResult, resultIntent);
+
+ mDialogData = new InstallSuccess(appSnippet, shouldReturnResult, resultIntent, installType);
}
}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/ParseErrorFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/ParseErrorFragment.java
index 6834f44..bf8e5a80 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/ParseErrorFragment.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/ParseErrorFragment.java
@@ -51,7 +51,7 @@
* Create a new instance of this fragment with necessary data set as fragment arguments
*
* @param dialogData {@link InstallAborted} object containing data to display in the
- * dialog
+ * dialog
* @return an instance of the fragment
*/
public static ParseErrorFragment newInstance(@NonNull InstallAborted dialogData) {
@@ -80,8 +80,9 @@
Log.i(LOG_TAG, "Creating " + LOG_TAG + "\n" + mDialogData);
return new AlertDialog.Builder(requireContext())
- .setMessage(R.string.Parse_error_dlg_text)
- .setPositiveButton(R.string.ok,
+ .setTitle(R.string.title_cant_install_app)
+ .setMessage(R.string.message_parse_failed)
+ .setNegativeButton(R.string.button_close,
(dialog, which) ->
mInstallActionListener.onNegativeResponse(
mDialogData.getActivityResultCode(), mDialogData.getResultIntent()))
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/SimpleErrorFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/SimpleErrorFragment.java
index 8b1ccd8..8219228 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/SimpleErrorFragment.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/SimpleErrorFragment.java
@@ -24,6 +24,9 @@
import android.content.DialogInterface;
import android.os.Bundle;
import android.util.Log;
+import android.view.Gravity;
+import android.view.View;
+import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.fragment.app.DialogFragment;
@@ -69,9 +72,20 @@
Log.i(LOG_TAG, "Creating " + LOG_TAG + "\n" +
"Dialog message: " + requireContext().getString(mMessageResId));
+
+ View dialogView = getLayoutInflater().inflate(R.layout.install_fragment_layout, null);
+ TextView customMessage = dialogView.requireViewById(R.id.custom_message);
+ customMessage.setText(mMessageResId);
+ customMessage.setVisibility(View.VISIBLE);
+ customMessage.setGravity(Gravity.CENTER);
+
+ View titleView = getLayoutInflater()
+ .inflate(R.layout.install_blocked_custom_title_layout, null);
+
return new AlertDialog.Builder(requireContext())
- .setMessage(mMessageResId)
- .setPositiveButton(R.string.ok,
+ .setCustomTitle(titleView)
+ .setView(dialogView)
+ .setNegativeButton(R.string.button_close,
(dialog, which) ->
mInstallActionListener.onNegativeResponse(InstallStage.STAGE_ABORTED))
.create();
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/UninstallConfirmationFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/UninstallConfirmationFragment.java
index 860f6a0..11a911f 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/UninstallConfirmationFragment.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/UninstallConfirmationFragment.java
@@ -19,7 +19,8 @@
import static android.text.format.Formatter.formatFileSize;
import static com.android.packageinstaller.v2.model.PackageUtil.ARGS_APP_DATA_SIZE;
-import static com.android.packageinstaller.v2.model.PackageUtil.ARGS_IS_ARCHIVE;
+import static com.android.packageinstaller.v2.model.PackageUtil.ARGS_APP_SNIPPET;
+import static com.android.packageinstaller.v2.model.PackageUtil.ARGS_BUTTON_TEXT;
import static com.android.packageinstaller.v2.model.PackageUtil.ARGS_MESSAGE;
import static com.android.packageinstaller.v2.model.PackageUtil.ARGS_TITLE;
@@ -31,6 +32,7 @@
import android.util.Log;
import android.view.View;
import android.widget.CheckBox;
+import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
@@ -38,6 +40,7 @@
import androidx.fragment.app.DialogFragment;
import com.android.packageinstaller.R;
+import com.android.packageinstaller.v2.model.PackageUtil.AppSnippet;
import com.android.packageinstaller.v2.model.UninstallUserActionRequired;
import com.android.packageinstaller.v2.ui.UninstallActionListener;
@@ -59,16 +62,17 @@
* Create a new instance of this fragment with necessary data set as fragment arguments
*
* @param dialogData {@link UninstallUserActionRequired} object containing data to
- * display in the dialog
+ * display in the dialog
* @return an instance of the fragment
*/
public static UninstallConfirmationFragment newInstance(
@NonNull UninstallUserActionRequired dialogData) {
Bundle args = new Bundle();
- args.putLong(ARGS_APP_DATA_SIZE, dialogData.getAppDataSize());
- args.putBoolean(ARGS_IS_ARCHIVE, dialogData.isArchive());
args.putString(ARGS_TITLE, dialogData.getTitle());
args.putString(ARGS_MESSAGE, dialogData.getMessage());
+ args.putString(ARGS_BUTTON_TEXT, dialogData.getButtonText());
+ args.putLong(ARGS_APP_DATA_SIZE, dialogData.getAppDataSize());
+ args.putParcelable(ARGS_APP_SNIPPET, dialogData.getAppSnippet());
UninstallConfirmationFragment fragment = new UninstallConfirmationFragment();
fragment.setArguments(args);
@@ -87,28 +91,36 @@
setDialogData(requireArguments());
Log.i(LOG_TAG, "Creating " + LOG_TAG + "\n" + mDialogData);
- AlertDialog.Builder builder = new AlertDialog.Builder(requireContext())
- .setTitle(mDialogData.getTitle())
- .setPositiveButton(mDialogData.isArchive() ? R.string.archive : R.string.ok,
- (dialogInt, which) -> mUninstallActionListener.onPositiveResponse(
- mKeepData != null && mKeepData.isChecked()))
- .setNegativeButton(R.string.cancel,
- (dialogInt, which) -> mUninstallActionListener.onNegativeResponse());
+
+ View dialogView = getLayoutInflater().inflate(R.layout.install_fragment_layout, null);
+ dialogView.requireViewById(R.id.app_snippet).setVisibility(View.VISIBLE);
+ ((ImageView) dialogView.requireViewById(R.id.app_icon))
+ .setImageDrawable(mDialogData.getAppIcon());
+ ((TextView) dialogView.requireViewById(R.id.app_label)).setText(mDialogData.getAppLabel());
+
+ if (mDialogData.getMessage() != null) {
+ TextView customMessage = dialogView.requireViewById(R.id.custom_message);
+ customMessage.setText(mDialogData.getMessage());
+ customMessage.setVisibility(View.VISIBLE);
+ }
long appDataSize = mDialogData.getAppDataSize();
- if (appDataSize == 0) {
- builder.setMessage(mDialogData.getMessage());
- } else {
- View dialogView = getLayoutInflater().inflate(R.layout.uninstall_content_view, null);
-
- ((TextView) dialogView.requireViewById(R.id.message)).setText(mDialogData.getMessage());
- mKeepData = dialogView.requireViewById(R.id.keepData);
+ if (appDataSize != 0) {
+ mKeepData = dialogView.requireViewById(R.id.keep_data);
mKeepData.setVisibility(View.VISIBLE);
- mKeepData.setText(getString(R.string.uninstall_keep_data,
+ mKeepData.setText(getString(R.string.message_uninstall_keep_data,
formatFileSize(getContext(), appDataSize)));
-
- builder.setView(dialogView);
}
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(requireContext())
+ .setTitle(mDialogData.getTitle())
+ .setView(dialogView)
+ .setPositiveButton(mDialogData.getButtonText(),
+ (dialogInt, which) -> mUninstallActionListener.onPositiveResponse(
+ mKeepData != null && mKeepData.isChecked()))
+ .setNegativeButton(R.string.button_cancel,
+ (dialogInt, which) -> mUninstallActionListener.onNegativeResponse());
+
return builder.create();
}
@@ -120,10 +132,12 @@
private void setDialogData(Bundle args) {
long appDataSize = args.getLong(ARGS_APP_DATA_SIZE);
- boolean isArchive = args.getBoolean(ARGS_IS_ARCHIVE);
- String title = args.getString(ARGS_TITLE);
+ String buttonText = args.getString(ARGS_BUTTON_TEXT);
String message = args.getString(ARGS_MESSAGE);
+ AppSnippet appSnippet = args.getParcelable(ARGS_APP_SNIPPET, AppSnippet.class);
+ String title = args.getString(ARGS_TITLE);
- mDialogData = new UninstallUserActionRequired(title, message, appDataSize, isArchive);
+ mDialogData = new UninstallUserActionRequired(title, message, buttonText, appDataSize,
+ appSnippet);
}
}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/UninstallErrorFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/UninstallErrorFragment.java
index 9ed6412..87324747 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/UninstallErrorFragment.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/UninstallErrorFragment.java
@@ -50,7 +50,7 @@
* Create a new instance of this fragment with necessary data set as fragment arguments
*
* @param dialogData {@link UninstallAborted} object containing data to display in the
- * dialog
+ * dialog
* @return an instance of the fragment
*/
public static UninstallErrorFragment newInstance(UninstallAborted dialogData) {
@@ -75,13 +75,11 @@
Log.i(LOG_TAG, "Creating " + LOG_TAG + "\n" + mDialogData);
AlertDialog.Builder builder = new AlertDialog.Builder(requireContext())
- .setMessage(mDialogData.getDialogTextResource())
- .setNegativeButton(R.string.ok,
+ .setMessage(mDialogData.getDialogTextResource())
+ .setTitle(mDialogData.getDialogTitleResource())
+ .setPositiveButton(R.string.button_close,
(dialogInt, which) -> mUninstallActionListener.onNegativeResponse());
- if (mDialogData.getDialogTitleResource() != 0) {
- builder.setTitle(mDialogData.getDialogTitleResource());
- }
return builder.create();
}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/UninstallUninstallingFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/UninstallUninstallingFragment.java
deleted file mode 100644
index ae56c4d..0000000
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/UninstallUninstallingFragment.java
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.packageinstaller.v2.ui.fragments;
-
-import static com.android.packageinstaller.v2.model.PackageUtil.ARGS_APP_LABEL;
-import static com.android.packageinstaller.v2.model.PackageUtil.ARGS_IS_CLONE_USER;
-
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.os.Bundle;
-import android.util.Log;
-
-import androidx.annotation.NonNull;
-import androidx.fragment.app.DialogFragment;
-
-import com.android.packageinstaller.R;
-import com.android.packageinstaller.v2.model.UninstallUninstalling;
-
-/**
- * Dialog to show that the app is uninstalling.
- */
-public class UninstallUninstallingFragment extends DialogFragment {
-
- private static final String LOG_TAG = UninstallUninstallingFragment.class.getSimpleName();
- UninstallUninstalling mDialogData;
-
- UninstallUninstallingFragment() {
- // Required for DialogFragment
- }
-
- /**
- * Create a new instance of this fragment with necessary data set as fragment arguments
- *
- * @param dialogData {@link UninstallUninstalling} object containing data to display in
- * the dialog
- * @return an instance of the fragment
- */
- public static UninstallUninstallingFragment newInstance(UninstallUninstalling dialogData) {
- Bundle args = new Bundle();
- args.putCharSequence(ARGS_APP_LABEL, dialogData.getAppLabel());
- args.putBoolean(ARGS_IS_CLONE_USER, dialogData.isCloneUser());
-
- UninstallUninstallingFragment fragment = new UninstallUninstallingFragment();
- fragment.setArguments(args);
- return fragment;
- }
-
- @NonNull
- @Override
- public Dialog onCreateDialog(Bundle savedInstanceState) {
- setDialogData(requireArguments());
-
- Log.i(LOG_TAG, "Creating " + LOG_TAG + "\n" + mDialogData);
- AlertDialog.Builder builder = new AlertDialog.Builder(requireContext())
- .setCancelable(false);
- if (mDialogData.isCloneUser()) {
- builder.setTitle(requireContext().getString(R.string.uninstalling_cloned_app,
- mDialogData.getAppLabel()));
- } else {
- builder.setTitle(requireContext().getString(R.string.uninstalling_app,
- mDialogData.getAppLabel()));
- }
- Dialog dialog = builder.create();
- dialog.setCanceledOnTouchOutside(false);
-
- return dialog;
- }
-
- private void setDialogData(Bundle args) {
- CharSequence label = args.getCharSequence(ARGS_APP_LABEL);
- boolean isCloneUser = args.getBoolean(ARGS_IS_CLONE_USER);
-
- mDialogData = new UninstallUninstalling(label, isCloneUser);
- }
-}
diff --git a/packages/PrintSpooler/flags/flags.aconfig b/packages/PrintSpooler/flags/flags.aconfig
index 5d45b3f..2b1411e 100644
--- a/packages/PrintSpooler/flags/flags.aconfig
+++ b/packages/PrintSpooler/flags/flags.aconfig
@@ -17,4 +17,15 @@
metadata {
purpose: PURPOSE_BUGFIX
}
-}
\ No newline at end of file
+}
+
+flag {
+ name: "printer_button_contrast"
+ namespace: "printing"
+ description: "Use themed colors instead of hard-coded values for print icon"
+ is_fixed_read_only: true
+ bug: "341591022"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/packages/PrintSpooler/res/drawable/ic_pdf_printer.xml b/packages/PrintSpooler/res/drawable/ic_pdf_printer.xml
index b8a0689..d202cf2 100644
--- a/packages/PrintSpooler/res/drawable/ic_pdf_printer.xml
+++ b/packages/PrintSpooler/res/drawable/ic_pdf_printer.xml
@@ -18,8 +18,8 @@
android:height="36dp"
android:viewportWidth="48.0"
android:viewportHeight="48.0"
- android:tint="@*android:color/accent_device_default_light">
+ android:tint="@color/ic_pdf_printer_color">
<path
android:pathData="M40,4L16,4c-2.21,0 -4,1.79 -4,4v24c0,2.21 1.79,4 4,4h24c2.21,0 4,-1.79 4,-4L44,8c0,-2.21 -1.79,-4 -4,-4zM23,19c0,1.66 -1.34,3 -3,3h-2v4h-3L15,14h5c1.66,0 3,1.34 3,3v2zM33,23c0,1.66 -1.34,3 -3,3h-5L25,14h5c1.66,0 3,1.34 3,3v6zM41,17h-3v2h3v3h-3v4h-3L35,14h6v3zM18,19h2v-2h-2v2zM8,12L4,12v28c0,2.21 1.79,4 4,4h28v-4L8,40L8,12zM28,23h2v-6h-2v6z"
android:fillColor="@android:color/black"/>
-</vector>
\ No newline at end of file
+</vector>
diff --git a/packages/PrintSpooler/res/drawable/print_button_background.xml b/packages/PrintSpooler/res/drawable/print_button_background.xml
index ad16547..838df8f 100644
--- a/packages/PrintSpooler/res/drawable/print_button_background.xml
+++ b/packages/PrintSpooler/res/drawable/print_button_background.xml
@@ -18,7 +18,8 @@
android:shape="oval">
<solid
- android:color="?android:attr/colorAccent">
+ android:color="@color/print_button_background"
+ >
</solid>
<size
diff --git a/packages/PrintSpooler/res/values-gu/strings.xml b/packages/PrintSpooler/res/values-gu/strings.xml
index 7419c2a..79edeac 100644
--- a/packages/PrintSpooler/res/values-gu/strings.xml
+++ b/packages/PrintSpooler/res/values-gu/strings.xml
@@ -39,7 +39,7 @@
<string name="all_printers" msgid="5018829726861876202">"બધા પ્રિન્ટર્સ…"</string>
<string name="print_dialog" msgid="32628687461331979">"પ્રિન્ટ સંવાદ"</string>
<string name="current_page_template" msgid="5145005201131935302">"<xliff:g id="CURRENT_PAGE">%1$d</xliff:g>/<xliff:g id="PAGE_COUNT">%2$d</xliff:g>"</string>
- <string name="page_description_template" msgid="6831239682256197161">"<xliff:g id="PAGE_COUNT">%2$d</xliff:g> માંથી <xliff:g id="CURRENT_PAGE">%1$d</xliff:g> પૃષ્ઠ"</string>
+ <string name="page_description_template" msgid="6831239682256197161">"<xliff:g id="PAGE_COUNT">%2$d</xliff:g>માંથી <xliff:g id="CURRENT_PAGE">%1$d</xliff:g> પેજ"</string>
<string name="summary_template" msgid="8899734908625669193">"સારાંશ, કૉપિઝ <xliff:g id="COPIES">%1$s</xliff:g>, કાગળનું કદ <xliff:g id="PAPER_SIZE">%2$s</xliff:g>"</string>
<string name="expand_handle" msgid="7282974448109280522">"વિસ્તૃત કરો હેન્ડલ"</string>
<string name="collapse_handle" msgid="6886637989442507451">"સંકુચિત કરો હેન્ડલ"</string>
diff --git a/packages/PrintSpooler/res/values-lv/strings.xml b/packages/PrintSpooler/res/values-lv/strings.xml
index bf9fb3b..bbc90d4 100644
--- a/packages/PrintSpooler/res/values-lv/strings.xml
+++ b/packages/PrintSpooler/res/values-lv/strings.xml
@@ -66,7 +66,7 @@
<string name="notification_channel_failure" msgid="9042250774797916414">"Neizdevās veikt drukas darbus"</string>
<string name="could_not_create_file" msgid="3425025039427448443">"Nevarēja izveidot failu."</string>
<string name="print_services_disabled_toast" msgid="9089060734685174685">"Daži drukas pakalpojumi ir atspējoti."</string>
- <string name="print_searching_for_printers" msgid="6550424555079932867">"Printeru meklēšana"</string>
+ <string name="print_searching_for_printers" msgid="6550424555079932867">"Notiek printeru meklēšana"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"Nav iespējots neviens drukas pakalpojums"</string>
<string name="print_no_printers" msgid="4869403323900054866">"Netika atrasts neviens printeris."</string>
<string name="cannot_add_printer" msgid="7840348733668023106">"Nevar pievienot printerus"</string>
diff --git a/packages/PrintSpooler/res/values/colors.xml b/packages/PrintSpooler/res/values/colors.xml
index cb9e886..490d513 100644
--- a/packages/PrintSpooler/res/values/colors.xml
+++ b/packages/PrintSpooler/res/values/colors.xml
@@ -14,11 +14,17 @@
limitations under the License.
-->
-<resources>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
<color name="print_preview_scrim_color">#99000000</color>
<color name="unselected_page_background_color">#C0C0C0</color>
<color name="material_grey_500">#ffa3a3a3</color>
+ <color name="ic_pdf_printer_color" android:featureFlag="!com.android.printspooler.flags.printer_button_contrast">@*android:color/accent_device_default_light</color>
+ <color name="ic_pdf_printer_color" android:featureFlag="com.android.printspooler.flags.printer_button_contrast">@androidprv:color/materialColorPrimary</color>
+
+ <color name="print_button_background" android:featureFlag="!com.android.printspooler.flags.printer_button_contrast">?android:attr/colorAccent</color>
+ <color name="print_button_background" android:featureFlag="com.android.printspooler.flags.printer_button_contrast">@androidprv:color/materialColorPrimary</color>
</resources>
diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp
index 99446cd..4ba86e0 100644
--- a/packages/SettingsLib/Android.bp
+++ b/packages/SettingsLib/Android.bp
@@ -10,7 +10,6 @@
android_library {
name: "SettingsLib",
defaults: [
- "SettingsLibAvatarPickerDefaults",
"SettingsLintDefaults",
],
@@ -114,32 +113,3 @@
name: "settingslib_media_flags_lib",
aconfig_declarations: "settingslib_media_flags",
}
-
-soong_config_module_type {
- name: "avatar_picker_java_defaults",
- module_type: "java_defaults",
- config_namespace: "SettingsLib",
- bool_variables: [
- "legacy_avatar_picker_app_enabled",
- ],
- properties: [
- "manifest",
- "static_libs",
- ],
-}
-
-soong_config_bool_variable {
- name: "legacy_avatar_picker_app_enabled",
-}
-
-avatar_picker_java_defaults {
- name: "SettingsLibAvatarPickerDefaults",
- soong_config_variables: {
- // If flag is enabled, add the library
- legacy_avatar_picker_app_enabled: {
- static_libs: [
- "SettingsLibAvatarPicker",
- ],
- },
- },
-}
diff --git a/packages/SettingsLib/AvatarPicker/Android.bp b/packages/SettingsLib/AvatarPicker/Android.bp
deleted file mode 100644
index 1d42cd4..0000000
--- a/packages/SettingsLib/AvatarPicker/Android.bp
+++ /dev/null
@@ -1,31 +0,0 @@
-package {
- // See: http://go/android-license-faq
- // A large-scale-change added 'default_applicable_licenses' to import
- // all of the 'license_kinds' from "frameworks_base_license"
- // to get the below license kinds:
- // SPDX-license-identifier-Apache-2.0
- default_applicable_licenses: ["frameworks_base_license"],
-}
-
-android_library {
- name: "SettingsLibAvatarPicker",
- manifest: "AndroidManifest.xml",
- use_resource_processor: true,
- platform_apis: true,
-
- defaults: [
- "SettingsLintDefaults",
- ],
-
- static_libs: [
- "SettingsLibSettingsTheme",
- "setupdesign",
- "guava",
- ],
-
- resource_dirs: ["res"],
- srcs: [
- "src/**/*.java",
- "src/**/*.kt",
- ],
-}
diff --git a/packages/SettingsLib/AvatarPicker/AndroidManifest.xml b/packages/SettingsLib/AvatarPicker/AndroidManifest.xml
deleted file mode 100644
index 73dd35b..0000000
--- a/packages/SettingsLib/AvatarPicker/AndroidManifest.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.settingslib.avatarpicker">
-
- <uses-sdk android:minSdkVersion="23" />
- <application>
- <activity
- android:name=".AvatarPickerActivity"
- android:theme="@style/SudThemeGlifV2.DayNight"
- android:exported="true">
- <intent-filter>
- <action android:name="com.android.avatarpicker.FULL_SCREEN_ACTIVITY" />
- <category android:name="android.intent.category.DEFAULT" />
- </intent-filter>
- </activity>
- </application>
-</manifest>
diff --git a/packages/SettingsLib/AvatarPicker/res/drawable/avatar_choose_photo_circled.xml b/packages/SettingsLib/AvatarPicker/res/drawable/avatar_choose_photo_circled.xml
deleted file mode 100644
index 27bb87f..0000000
--- a/packages/SettingsLib/AvatarPicker/res/drawable/avatar_choose_photo_circled.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<!--
- Copyright (C) 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- -->
-<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
- <item>
- <shape android:shape="oval">
- <stroke
- android:width="2dp"
- android:color="?android:attr/colorPrimary" />
- </shape>
- </item>
- <item
- android:bottom="@dimen/avatar_picker_icon_inset"
- android:drawable="@drawable/ic_avatar_choose_photo"
- android:left="@dimen/avatar_picker_icon_inset"
- android:right="@dimen/avatar_picker_icon_inset"
- android:top="@dimen/avatar_picker_icon_inset" />
-</layer-list>
diff --git a/packages/SettingsLib/AvatarPicker/res/drawable/avatar_selector.xml b/packages/SettingsLib/AvatarPicker/res/drawable/avatar_selector.xml
deleted file mode 100644
index 1fb521a..0000000
--- a/packages/SettingsLib/AvatarPicker/res/drawable/avatar_selector.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?><!--
- Copyright (C) 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- -->
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_selected="true">
- <shape android:shape="oval">
- <stroke android:width="@dimen/avatar_picker_padding" android:color="?android:attr/colorPrimary" />
- </shape>
- </item>
-</selector>
\ No newline at end of file
diff --git a/packages/SettingsLib/AvatarPicker/res/drawable/avatar_take_photo_circled.xml b/packages/SettingsLib/AvatarPicker/res/drawable/avatar_take_photo_circled.xml
deleted file mode 100644
index d678e9b..0000000
--- a/packages/SettingsLib/AvatarPicker/res/drawable/avatar_take_photo_circled.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<!--
- Copyright (C) 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- -->
-<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
- <item>
- <shape android:shape="oval">
- <stroke
- android:width="2dp"
- android:color="?android:attr/colorPrimary" />
- </shape>
- </item>
- <item
- android:bottom="@dimen/avatar_picker_icon_inset"
- android:drawable="@drawable/ic_avatar_take_photo"
- android:left="@dimen/avatar_picker_icon_inset"
- android:right="@dimen/avatar_picker_icon_inset"
- android:top="@dimen/avatar_picker_icon_inset" />
-</layer-list>
diff --git a/packages/SettingsLib/AvatarPicker/res/drawable/ic_account_circle.xml b/packages/SettingsLib/AvatarPicker/res/drawable/ic_account_circle.xml
deleted file mode 100644
index 6421f91..0000000
--- a/packages/SettingsLib/AvatarPicker/res/drawable/ic_account_circle.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<!--
- Copyright (C) 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- -->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportHeight="24"
- android:viewportWidth="24">
- <path
- android:fillColor="@android:color/white"
- android:pathData="M5.85,17.1Q7.125,16.125 8.7,15.562Q10.275,15 12,15Q13.725,15 15.3,15.562Q16.875,16.125 18.15,17.1Q19.025,16.075 19.513,14.775Q20,13.475 20,12Q20,8.675 17.663,6.337Q15.325,4 12,4Q8.675,4 6.338,6.337Q4,8.675 4,12Q4,13.475 4.488,14.775Q4.975,16.075 5.85,17.1ZM12,13Q10.525,13 9.512,11.988Q8.5,10.975 8.5,9.5Q8.5,8.025 9.512,7.012Q10.525,6 12,6Q13.475,6 14.488,7.012Q15.5,8.025 15.5,9.5Q15.5,10.975 14.488,11.988Q13.475,13 12,13ZM12,22Q9.925,22 8.1,21.212Q6.275,20.425 4.925,19.075Q3.575,17.725 2.788,15.9Q2,14.075 2,12Q2,9.925 2.788,8.1Q3.575,6.275 4.925,4.925Q6.275,3.575 8.1,2.787Q9.925,2 12,2Q14.075,2 15.9,2.787Q17.725,3.575 19.075,4.925Q20.425,6.275 21.212,8.1Q22,9.925 22,12Q22,14.075 21.212,15.9Q20.425,17.725 19.075,19.075Q17.725,20.425 15.9,21.212Q14.075,22 12,22ZM12,20Q13.325,20 14.5,19.613Q15.675,19.225 16.65,18.5Q15.675,17.775 14.5,17.387Q13.325,17 12,17Q10.675,17 9.5,17.387Q8.325,17.775 7.35,18.5Q8.325,19.225 9.5,19.613Q10.675,20 12,20ZM12,11Q12.65,11 13.075,10.575Q13.5,10.15 13.5,9.5Q13.5,8.85 13.075,8.425Q12.65,8 12,8Q11.35,8 10.925,8.425Q10.5,8.85 10.5,9.5Q10.5,10.15 10.925,10.575Q11.35,11 12,11ZM12,9.5Q12,9.5 12,9.5Q12,9.5 12,9.5Q12,9.5 12,9.5Q12,9.5 12,9.5Q12,9.5 12,9.5Q12,9.5 12,9.5Q12,9.5 12,9.5Q12,9.5 12,9.5ZM12,18.5Q12,18.5 12,18.5Q12,18.5 12,18.5Q12,18.5 12,18.5Q12,18.5 12,18.5Q12,18.5 12,18.5Q12,18.5 12,18.5Q12,18.5 12,18.5Q12,18.5 12,18.5Z" />
-</vector>
diff --git a/packages/SettingsLib/AvatarPicker/res/drawable/ic_account_circle_filled.xml b/packages/SettingsLib/AvatarPicker/res/drawable/ic_account_circle_filled.xml
deleted file mode 100644
index 645fdf7..0000000
--- a/packages/SettingsLib/AvatarPicker/res/drawable/ic_account_circle_filled.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<!--
- Copyright (C) 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- -->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportHeight="24"
- android:viewportWidth="24">
- <path
- android:fillColor="@android:color/white"
- android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM18.36,16.83c-1.43,-1.74 -4.9,-2.33 -6.36,-2.33s-4.93,0.59 -6.36,2.33A7.95,7.95 0,0 1,4 12c0,-4.41 3.59,-8 8,-8s8,3.59 8,8c0,1.82 -0.62,3.49 -1.64,4.83z" />
- <path
- android:fillColor="@android:color/white"
- android:pathData="M12,6c-1.94,0 -3.5,1.56 -3.5,3.5S10.06,13 12,13s3.5,-1.56 3.5,-3.5S13.94,6 12,6z" />
-</vector>
diff --git a/packages/SettingsLib/AvatarPicker/res/drawable/ic_account_circle_outline.xml b/packages/SettingsLib/AvatarPicker/res/drawable/ic_account_circle_outline.xml
deleted file mode 100644
index a5c1038..0000000
--- a/packages/SettingsLib/AvatarPicker/res/drawable/ic_account_circle_outline.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<!--
- Copyright (C) 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- -->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportHeight="24"
- android:viewportWidth="24">
- <path
- android:fillColor="?android:attr/colorPrimary"
- android:pathData="M5.85,17.1q1.275,-0.975 2.85,-1.538Q10.275,15 12,15q1.725,0 3.3,0.563 1.575,0.562 2.85,1.537 0.875,-1.025 1.363,-2.325Q20,13.475 20,12q0,-3.325 -2.337,-5.662Q15.325,4 12,4T6.338,6.338Q4,8.675 4,12q0,1.475 0.487,2.775 0.488,1.3 1.363,2.325zM12,13q-1.475,0 -2.488,-1.012Q8.5,10.975 8.5,9.5t1.012,-2.487Q10.525,6 12,6t2.488,1.013Q15.5,8.024 15.5,9.5t-1.012,2.488Q13.475,13 12,13zM12,22q-2.075,0 -3.9,-0.788 -1.825,-0.787 -3.175,-2.137 -1.35,-1.35 -2.137,-3.175Q2,14.075 2,12t0.788,-3.9q0.787,-1.825 2.137,-3.175 1.35,-1.35 3.175,-2.137Q9.925,2 12,2t3.9,0.788q1.825,0.787 3.175,2.137 1.35,1.35 2.137,3.175Q22,9.925 22,12t-0.788,3.9q-0.787,1.825 -2.137,3.175 -1.35,1.35 -3.175,2.137Q14.075,22 12,22zM12,20q1.325,0 2.5,-0.387 1.175,-0.388 2.15,-1.113 -0.975,-0.725 -2.15,-1.113Q13.325,17 12,17t-2.5,0.387q-1.175,0.388 -2.15,1.113 0.975,0.725 2.15,1.113Q10.675,20 12,20zM12,11q0.65,0 1.075,-0.425 0.425,-0.425 0.425,-1.075 0,-0.65 -0.425,-1.075Q12.65,8 12,8q-0.65,0 -1.075,0.425Q10.5,8.85 10.5,9.5q0,0.65 0.425,1.075Q11.35,11 12,11zM12,9.5zM12,18.5z" />
-</vector>
-
diff --git a/packages/SettingsLib/AvatarPicker/res/layout/avatar_item.xml b/packages/SettingsLib/AvatarPicker/res/layout/avatar_item.xml
deleted file mode 100644
index cc4e8a7..0000000
--- a/packages/SettingsLib/AvatarPicker/res/layout/avatar_item.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?><!--
- Copyright (C) 2022 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- -->
-<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/avatar_image"
- android:layout_width="@dimen/avatar_size_in_picker"
- android:layout_height="@dimen/avatar_size_in_picker"
- android:layout_gravity="center"
- android:layout_margin="@dimen/avatar_picker_margin"
- android:background="@drawable/avatar_selector"
- android:padding="@dimen/avatar_picker_padding" />
diff --git a/packages/SettingsLib/AvatarPicker/res/layout/avatar_picker.xml b/packages/SettingsLib/AvatarPicker/res/layout/avatar_picker.xml
deleted file mode 100644
index e9d375e..0000000
--- a/packages/SettingsLib/AvatarPicker/res/layout/avatar_picker.xml
+++ /dev/null
@@ -1,37 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?><!--
- Copyright (C) 2022 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- -->
-<com.google.android.setupdesign.GlifLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- android:id="@+id/glif_layout"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:icon="@drawable/ic_account_circle_outline"
- app:sucHeaderText="@string/avatar_picker_title"
- app:sucUsePartnerResource="true">
-
- <LinearLayout
- style="@style/SudContentFrame"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:gravity="center_horizontal">
-
- <androidx.recyclerview.widget.RecyclerView
- android:id="@+id/avatar_grid"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content" />
- </LinearLayout>
-
-</com.google.android.setupdesign.GlifLayout>
\ No newline at end of file
diff --git a/packages/SettingsLib/AvatarPicker/res/values-af/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-af/strings.xml
deleted file mode 100644
index 7209cc2..0000000
--- a/packages/SettingsLib/AvatarPicker/res/values-af/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="user_image_choose_photo" msgid="5630717762469961028">"Kies \'n prent"</string>
- <string name="user_image_take_photo" msgid="3147097821937166738">"Neem \'n foto"</string>
- <string name="avatar_picker_title" msgid="7478146965334560463">"Kies ’n profielprent"</string>
- <string name="default_user_icon_description" msgid="6018582161341388812">"Verstekgebruikerikoon"</string>
- <string name="done" msgid="3587741621903511576">"Klaar"</string>
-</resources>
diff --git a/packages/SettingsLib/AvatarPicker/res/values-am/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-am/strings.xml
deleted file mode 100644
index 713e0d95..0000000
--- a/packages/SettingsLib/AvatarPicker/res/values-am/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="user_image_choose_photo" msgid="5630717762469961028">"ምስል ይምረጡ"</string>
- <string name="user_image_take_photo" msgid="3147097821937166738">"ፎቶ ያንሱ"</string>
- <string name="avatar_picker_title" msgid="7478146965334560463">"የመገለጫ ሥዕል ይምረጡ"</string>
- <string name="default_user_icon_description" msgid="6018582161341388812">"ነባሪ የተጠቃሚ አዶ"</string>
- <string name="done" msgid="3587741621903511576">"ተከናውኗል"</string>
-</resources>
diff --git a/packages/SettingsLib/AvatarPicker/res/values-ar/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-ar/strings.xml
deleted file mode 100644
index 1b5dbc0..0000000
--- a/packages/SettingsLib/AvatarPicker/res/values-ar/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="user_image_choose_photo" msgid="5630717762469961028">"اختيار صورة"</string>
- <string name="user_image_take_photo" msgid="3147097821937166738">"التقاط صورة"</string>
- <string name="avatar_picker_title" msgid="7478146965334560463">"اختيار صورة للملف الشخصي"</string>
- <string name="default_user_icon_description" msgid="6018582161341388812">"رمز المستخدم التلقائي"</string>
- <string name="done" msgid="3587741621903511576">"تم"</string>
-</resources>
diff --git a/packages/SettingsLib/AvatarPicker/res/values-as/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-as/strings.xml
deleted file mode 100644
index 6281328..0000000
--- a/packages/SettingsLib/AvatarPicker/res/values-as/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="user_image_choose_photo" msgid="5630717762469961028">"এখন প্ৰতিচ্ছবি বাছনি কৰক"</string>
- <string name="user_image_take_photo" msgid="3147097821937166738">"এখন ফট’ তোলক"</string>
- <string name="avatar_picker_title" msgid="7478146965334560463">"এখন প্ৰ’ফাইল চিত্ৰ বাছনি কৰক"</string>
- <string name="default_user_icon_description" msgid="6018582161341388812">"ডিফ’ল্ট ব্যৱহাৰকাৰীৰ চিহ্ন"</string>
- <string name="done" msgid="3587741621903511576">"কৰা হ’ল"</string>
-</resources>
diff --git a/packages/SettingsLib/AvatarPicker/res/values-az/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-az/strings.xml
deleted file mode 100644
index d47ffce..0000000
--- a/packages/SettingsLib/AvatarPicker/res/values-az/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="user_image_choose_photo" msgid="5630717762469961028">"Şəkil seçin"</string>
- <string name="user_image_take_photo" msgid="3147097821937166738">"Foto çəkin"</string>
- <string name="avatar_picker_title" msgid="7478146965334560463">"Profil şəkli seçin"</string>
- <string name="default_user_icon_description" msgid="6018582161341388812">"Defolt istifadəçi ikonası"</string>
- <string name="done" msgid="3587741621903511576">"Hazırdır"</string>
-</resources>
diff --git a/packages/SettingsLib/AvatarPicker/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-b+sr+Latn/strings.xml
deleted file mode 100644
index 886bd5d..0000000
--- a/packages/SettingsLib/AvatarPicker/res/values-b+sr+Latn/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="user_image_choose_photo" msgid="5630717762469961028">"Odaberite sliku"</string>
- <string name="user_image_take_photo" msgid="3147097821937166738">"Slikajte"</string>
- <string name="avatar_picker_title" msgid="7478146965334560463">"Odaberite sliku profila"</string>
- <string name="default_user_icon_description" msgid="6018582161341388812">"Podrazumevana ikona korisnika"</string>
- <string name="done" msgid="3587741621903511576">"Gotovo"</string>
-</resources>
diff --git a/packages/SettingsLib/AvatarPicker/res/values-be/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-be/strings.xml
deleted file mode 100644
index 38a1340..0000000
--- a/packages/SettingsLib/AvatarPicker/res/values-be/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="user_image_choose_photo" msgid="5630717762469961028">"Выбраць відарыс"</string>
- <string name="user_image_take_photo" msgid="3147097821937166738">"Зрабіць фота"</string>
- <string name="avatar_picker_title" msgid="7478146965334560463">"Выберыце відарыс профілю"</string>
- <string name="default_user_icon_description" msgid="6018582161341388812">"Стандартны карыстальніцкі значок"</string>
- <string name="done" msgid="3587741621903511576">"Гатова"</string>
-</resources>
diff --git a/packages/SettingsLib/AvatarPicker/res/values-bg/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-bg/strings.xml
deleted file mode 100644
index 90e78b5..0000000
--- a/packages/SettingsLib/AvatarPicker/res/values-bg/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="user_image_choose_photo" msgid="5630717762469961028">"Избиране на изображение"</string>
- <string name="user_image_take_photo" msgid="3147097821937166738">"Правене на снимка"</string>
- <string name="avatar_picker_title" msgid="7478146965334560463">"Изберете снимка на потребителския профил"</string>
- <string name="default_user_icon_description" msgid="6018582161341388812">"Икона за основния потребител"</string>
- <string name="done" msgid="3587741621903511576">"Готово"</string>
-</resources>
diff --git a/packages/SettingsLib/AvatarPicker/res/values-bn/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-bn/strings.xml
deleted file mode 100644
index c56c9da..0000000
--- a/packages/SettingsLib/AvatarPicker/res/values-bn/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="user_image_choose_photo" msgid="5630717762469961028">"একটি ছবি বেছে নিন"</string>
- <string name="user_image_take_photo" msgid="3147097821937166738">"ফটো তুলুন"</string>
- <string name="avatar_picker_title" msgid="7478146965334560463">"কোনও একটি প্রোফাইল ছবি বেছে নিন"</string>
- <string name="default_user_icon_description" msgid="6018582161341388812">"ডিফল্ট ব্যবহারকারীর আইকন"</string>
- <string name="done" msgid="3587741621903511576">"হয়ে গেছে"</string>
-</resources>
diff --git a/packages/SettingsLib/AvatarPicker/res/values-bs/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-bs/strings.xml
deleted file mode 100644
index 1dd707d..0000000
--- a/packages/SettingsLib/AvatarPicker/res/values-bs/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="user_image_choose_photo" msgid="5630717762469961028">"Odaberite sliku"</string>
- <string name="user_image_take_photo" msgid="3147097821937166738">"Snimite fotografiju"</string>
- <string name="avatar_picker_title" msgid="7478146965334560463">"Odaberite sliku profila"</string>
- <string name="default_user_icon_description" msgid="6018582161341388812">"Zadana ikona korisnika"</string>
- <string name="done" msgid="3587741621903511576">"Gotovo"</string>
-</resources>
diff --git a/packages/SettingsLib/AvatarPicker/res/values-ca/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-ca/strings.xml
deleted file mode 100644
index d8d3171..0000000
--- a/packages/SettingsLib/AvatarPicker/res/values-ca/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="user_image_choose_photo" msgid="5630717762469961028">"Tria una imatge"</string>
- <string name="user_image_take_photo" msgid="3147097821937166738">"Fes una foto"</string>
- <string name="avatar_picker_title" msgid="7478146965334560463">"Tria una foto de perfil"</string>
- <string name="default_user_icon_description" msgid="6018582161341388812">"Icona d\'usuari predeterminat"</string>
- <string name="done" msgid="3587741621903511576">"Fet"</string>
-</resources>
diff --git a/packages/SettingsLib/AvatarPicker/res/values-cs/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-cs/strings.xml
deleted file mode 100644
index 6ee5b3e..0000000
--- a/packages/SettingsLib/AvatarPicker/res/values-cs/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="user_image_choose_photo" msgid="5630717762469961028">"Zvolit obrázek"</string>
- <string name="user_image_take_photo" msgid="3147097821937166738">"Vyfotit"</string>
- <string name="avatar_picker_title" msgid="7478146965334560463">"Vyberte profilový obrázek"</string>
- <string name="default_user_icon_description" msgid="6018582161341388812">"Výchozí uživatelská ikona"</string>
- <string name="done" msgid="3587741621903511576">"Hotovo"</string>
-</resources>
diff --git a/packages/SettingsLib/AvatarPicker/res/values-da/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-da/strings.xml
deleted file mode 100644
index 0583399..0000000
--- a/packages/SettingsLib/AvatarPicker/res/values-da/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="user_image_choose_photo" msgid="5630717762469961028">"Vælg et billede"</string>
- <string name="user_image_take_photo" msgid="3147097821937166738">"Tag et billede"</string>
- <string name="avatar_picker_title" msgid="7478146965334560463">"Vælg et profilbillede"</string>
- <string name="default_user_icon_description" msgid="6018582161341388812">"Ikon for standardbruger"</string>
- <string name="done" msgid="3587741621903511576">"Udfør"</string>
-</resources>
diff --git a/packages/SettingsLib/AvatarPicker/res/values-de/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-de/strings.xml
deleted file mode 100644
index 345b4fa..0000000
--- a/packages/SettingsLib/AvatarPicker/res/values-de/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="user_image_choose_photo" msgid="5630717762469961028">"Bild auswählen"</string>
- <string name="user_image_take_photo" msgid="3147097821937166738">"Foto aufnehmen"</string>
- <string name="avatar_picker_title" msgid="7478146965334560463">"Profilbild auswählen"</string>
- <string name="default_user_icon_description" msgid="6018582161341388812">"Standardmäßiges Nutzersymbol"</string>
- <string name="done" msgid="3587741621903511576">"Fertig"</string>
-</resources>
diff --git a/packages/SettingsLib/AvatarPicker/res/values-el/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-el/strings.xml
deleted file mode 100644
index 7cecd45..0000000
--- a/packages/SettingsLib/AvatarPicker/res/values-el/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="user_image_choose_photo" msgid="5630717762469961028">"Επιλέξτε μια εικόνα"</string>
- <string name="user_image_take_photo" msgid="3147097821937166738">"Λήψη φωτογραφίας"</string>
- <string name="avatar_picker_title" msgid="7478146965334560463">"Επιλογή φωτογραφίας προφίλ"</string>
- <string name="default_user_icon_description" msgid="6018582161341388812">"Προεπιλεγμένο εικονίδιο χρήστη"</string>
- <string name="done" msgid="3587741621903511576">"Τέλος"</string>
-</resources>
diff --git a/packages/SettingsLib/AvatarPicker/res/values-en-rAU/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-en-rAU/strings.xml
deleted file mode 100644
index f9905d0..0000000
--- a/packages/SettingsLib/AvatarPicker/res/values-en-rAU/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="user_image_choose_photo" msgid="5630717762469961028">"Choose an image"</string>
- <string name="user_image_take_photo" msgid="3147097821937166738">"Take a photo"</string>
- <string name="avatar_picker_title" msgid="7478146965334560463">"Choose a profile picture"</string>
- <string name="default_user_icon_description" msgid="6018582161341388812">"Default user icon"</string>
- <string name="done" msgid="3587741621903511576">"Done"</string>
-</resources>
diff --git a/packages/SettingsLib/AvatarPicker/res/values-en-rCA/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-en-rCA/strings.xml
deleted file mode 100644
index f9905d0..0000000
--- a/packages/SettingsLib/AvatarPicker/res/values-en-rCA/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="user_image_choose_photo" msgid="5630717762469961028">"Choose an image"</string>
- <string name="user_image_take_photo" msgid="3147097821937166738">"Take a photo"</string>
- <string name="avatar_picker_title" msgid="7478146965334560463">"Choose a profile picture"</string>
- <string name="default_user_icon_description" msgid="6018582161341388812">"Default user icon"</string>
- <string name="done" msgid="3587741621903511576">"Done"</string>
-</resources>
diff --git a/packages/SettingsLib/AvatarPicker/res/values-en-rGB/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-en-rGB/strings.xml
deleted file mode 100644
index f9905d0..0000000
--- a/packages/SettingsLib/AvatarPicker/res/values-en-rGB/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="user_image_choose_photo" msgid="5630717762469961028">"Choose an image"</string>
- <string name="user_image_take_photo" msgid="3147097821937166738">"Take a photo"</string>
- <string name="avatar_picker_title" msgid="7478146965334560463">"Choose a profile picture"</string>
- <string name="default_user_icon_description" msgid="6018582161341388812">"Default user icon"</string>
- <string name="done" msgid="3587741621903511576">"Done"</string>
-</resources>
diff --git a/packages/SettingsLib/AvatarPicker/res/values-en-rIN/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-en-rIN/strings.xml
deleted file mode 100644
index f9905d0..0000000
--- a/packages/SettingsLib/AvatarPicker/res/values-en-rIN/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="user_image_choose_photo" msgid="5630717762469961028">"Choose an image"</string>
- <string name="user_image_take_photo" msgid="3147097821937166738">"Take a photo"</string>
- <string name="avatar_picker_title" msgid="7478146965334560463">"Choose a profile picture"</string>
- <string name="default_user_icon_description" msgid="6018582161341388812">"Default user icon"</string>
- <string name="done" msgid="3587741621903511576">"Done"</string>
-</resources>
diff --git a/packages/SettingsLib/AvatarPicker/res/values-es-rUS/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-es-rUS/strings.xml
deleted file mode 100644
index 8e9d407..0000000
--- a/packages/SettingsLib/AvatarPicker/res/values-es-rUS/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="user_image_choose_photo" msgid="5630717762469961028">"Elegir una imagen"</string>
- <string name="user_image_take_photo" msgid="3147097821937166738">"Tomar una foto"</string>
- <string name="avatar_picker_title" msgid="7478146965334560463">"Elige una foto de perfil"</string>
- <string name="default_user_icon_description" msgid="6018582161341388812">"Ícono de usuario predeterminado"</string>
- <string name="done" msgid="3587741621903511576">"Listo"</string>
-</resources>
diff --git a/packages/SettingsLib/AvatarPicker/res/values-es/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-es/strings.xml
deleted file mode 100644
index a7da134..0000000
--- a/packages/SettingsLib/AvatarPicker/res/values-es/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="user_image_choose_photo" msgid="5630717762469961028">"Seleccionar una imagen"</string>
- <string name="user_image_take_photo" msgid="3147097821937166738">"Hacer una foto"</string>
- <string name="avatar_picker_title" msgid="7478146965334560463">"Elige una imagen de perfil"</string>
- <string name="default_user_icon_description" msgid="6018582161341388812">"Icono de usuario predeterminado"</string>
- <string name="done" msgid="3587741621903511576">"Hecho"</string>
-</resources>
diff --git a/packages/SettingsLib/AvatarPicker/res/values-et/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-et/strings.xml
deleted file mode 100644
index ecf140d..0000000
--- a/packages/SettingsLib/AvatarPicker/res/values-et/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="user_image_choose_photo" msgid="5630717762469961028">"Vali pilt"</string>
- <string name="user_image_take_photo" msgid="3147097821937166738">"Pildista"</string>
- <string name="avatar_picker_title" msgid="7478146965334560463">"Profiilipildi valimine"</string>
- <string name="default_user_icon_description" msgid="6018582161341388812">"Vaikekasutajaikoon"</string>
- <string name="done" msgid="3587741621903511576">"Valmis"</string>
-</resources>
diff --git a/packages/SettingsLib/AvatarPicker/res/values-eu/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-eu/strings.xml
deleted file mode 100644
index c55d559..0000000
--- a/packages/SettingsLib/AvatarPicker/res/values-eu/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="user_image_choose_photo" msgid="5630717762469961028">"Aukeratu irudi bat"</string>
- <string name="user_image_take_photo" msgid="3147097821937166738">"Atera argazki bat"</string>
- <string name="avatar_picker_title" msgid="7478146965334560463">"Aukeratu profileko argazki bat"</string>
- <string name="default_user_icon_description" msgid="6018582161341388812">"Erabiltzaile lehenetsiaren ikonoa"</string>
- <string name="done" msgid="3587741621903511576">"Eginda"</string>
-</resources>
diff --git a/packages/SettingsLib/AvatarPicker/res/values-fa/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-fa/strings.xml
deleted file mode 100644
index 7db55cf..0000000
--- a/packages/SettingsLib/AvatarPicker/res/values-fa/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="user_image_choose_photo" msgid="5630717762469961028">"انتخاب تصویر"</string>
- <string name="user_image_take_photo" msgid="3147097821937166738">"عکس گرفتن"</string>
- <string name="avatar_picker_title" msgid="7478146965334560463">"انتخاب عکس نمایه"</string>
- <string name="default_user_icon_description" msgid="6018582161341388812">"نماد کاربر پیشفرض"</string>
- <string name="done" msgid="3587741621903511576">"تمام"</string>
-</resources>
diff --git a/packages/SettingsLib/AvatarPicker/res/values-fi/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-fi/strings.xml
deleted file mode 100644
index 7a0cca1..0000000
--- a/packages/SettingsLib/AvatarPicker/res/values-fi/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="user_image_choose_photo" msgid="5630717762469961028">"Valitse kuva"</string>
- <string name="user_image_take_photo" msgid="3147097821937166738">"Ota kuva"</string>
- <string name="avatar_picker_title" msgid="7478146965334560463">"Valitse profiilikuva"</string>
- <string name="default_user_icon_description" msgid="6018582161341388812">"Oletuskäyttäjäkuvake"</string>
- <string name="done" msgid="3587741621903511576">"Valmis"</string>
-</resources>
diff --git a/packages/SettingsLib/AvatarPicker/res/values-fr-rCA/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-fr-rCA/strings.xml
deleted file mode 100644
index e439471..0000000
--- a/packages/SettingsLib/AvatarPicker/res/values-fr-rCA/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="user_image_choose_photo" msgid="5630717762469961028">"Sélectionner une image"</string>
- <string name="user_image_take_photo" msgid="3147097821937166738">"Prendre une photo"</string>
- <string name="avatar_picker_title" msgid="7478146965334560463">"Choisir une photo de profil"</string>
- <string name="default_user_icon_description" msgid="6018582161341388812">"Icône d\'utilisateur par défaut"</string>
- <string name="done" msgid="3587741621903511576">"Terminé"</string>
-</resources>
diff --git a/packages/SettingsLib/AvatarPicker/res/values-fr/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-fr/strings.xml
deleted file mode 100644
index 2ffeb43..0000000
--- a/packages/SettingsLib/AvatarPicker/res/values-fr/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="user_image_choose_photo" msgid="5630717762469961028">"Choisir une image"</string>
- <string name="user_image_take_photo" msgid="3147097821937166738">"Prendre une photo"</string>
- <string name="avatar_picker_title" msgid="7478146965334560463">"Choisir une photo de profil"</string>
- <string name="default_user_icon_description" msgid="6018582161341388812">"Icône de l\'utilisateur par défaut"</string>
- <string name="done" msgid="3587741621903511576">"OK"</string>
-</resources>
diff --git a/packages/SettingsLib/AvatarPicker/res/values-gl/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-gl/strings.xml
deleted file mode 100644
index 5e46474..0000000
--- a/packages/SettingsLib/AvatarPicker/res/values-gl/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="user_image_choose_photo" msgid="5630717762469961028">"Escoller unha imaxe"</string>
- <string name="user_image_take_photo" msgid="3147097821937166738">"Tirar unha foto"</string>
- <string name="avatar_picker_title" msgid="7478146965334560463">"Escoller unha imaxe do perfil"</string>
- <string name="default_user_icon_description" msgid="6018582161341388812">"Icona do usuario predeterminado"</string>
- <string name="done" msgid="3587741621903511576">"Feito"</string>
-</resources>
diff --git a/packages/SettingsLib/AvatarPicker/res/values-gu/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-gu/strings.xml
deleted file mode 100644
index 96e6753..0000000
--- a/packages/SettingsLib/AvatarPicker/res/values-gu/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="user_image_choose_photo" msgid="5630717762469961028">"છબી પસંદ કરો"</string>
- <string name="user_image_take_photo" msgid="3147097821937166738">"ફોટો લો"</string>
- <string name="avatar_picker_title" msgid="7478146965334560463">"પ્રોફાઇલ ફોટો પસંદ કરો"</string>
- <string name="default_user_icon_description" msgid="6018582161341388812">"ડિફૉલ્ટ વપરાશકર્તાનું આઇકન"</string>
- <string name="done" msgid="3587741621903511576">"થઈ ગયું"</string>
-</resources>
diff --git a/packages/SettingsLib/AvatarPicker/res/values-hi/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-hi/strings.xml
deleted file mode 100644
index 0878ff2..0000000
--- a/packages/SettingsLib/AvatarPicker/res/values-hi/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="user_image_choose_photo" msgid="5630717762469961028">"इमेज चुनें"</string>
- <string name="user_image_take_photo" msgid="3147097821937166738">"फ़ोटो खींचें"</string>
- <string name="avatar_picker_title" msgid="7478146965334560463">"प्रोफ़ाइल फ़ोटो चुनें"</string>
- <string name="default_user_icon_description" msgid="6018582161341388812">"उपयोगकर्ता के लिए डिफ़ॉल्ट आइकॉन"</string>
- <string name="done" msgid="3587741621903511576">"हो गया"</string>
-</resources>
diff --git a/packages/SettingsLib/AvatarPicker/res/values-hr/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-hr/strings.xml
deleted file mode 100644
index b599ddf..0000000
--- a/packages/SettingsLib/AvatarPicker/res/values-hr/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="user_image_choose_photo" msgid="5630717762469961028">"Odaberite sliku"</string>
- <string name="user_image_take_photo" msgid="3147097821937166738">"Snimi fotografiju"</string>
- <string name="avatar_picker_title" msgid="7478146965334560463">"Odaberite profilnu sliku"</string>
- <string name="default_user_icon_description" msgid="6018582161341388812">"Ikona zadanog korisnika"</string>
- <string name="done" msgid="3587741621903511576">"Gotovo"</string>
-</resources>
diff --git a/packages/SettingsLib/AvatarPicker/res/values-hu/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-hu/strings.xml
deleted file mode 100644
index 55f4a60..0000000
--- a/packages/SettingsLib/AvatarPicker/res/values-hu/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="user_image_choose_photo" msgid="5630717762469961028">"Kép kiválasztása"</string>
- <string name="user_image_take_photo" msgid="3147097821937166738">"Fotó készítése"</string>
- <string name="avatar_picker_title" msgid="7478146965334560463">"Profilkép választása"</string>
- <string name="default_user_icon_description" msgid="6018582161341388812">"Alapértelmezett felhasználó ikonja"</string>
- <string name="done" msgid="3587741621903511576">"Kész"</string>
-</resources>
diff --git a/packages/SettingsLib/AvatarPicker/res/values-hy/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-hy/strings.xml
deleted file mode 100644
index d897cd264..0000000
--- a/packages/SettingsLib/AvatarPicker/res/values-hy/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="user_image_choose_photo" msgid="5630717762469961028">"Ընտրել պատկեր"</string>
- <string name="user_image_take_photo" msgid="3147097821937166738">"Լուսանկարել"</string>
- <string name="avatar_picker_title" msgid="7478146965334560463">"Ընտրեք պրոֆիլի նկար"</string>
- <string name="default_user_icon_description" msgid="6018582161341388812">"Օգտատիրոջ կանխադրված պատկերակ"</string>
- <string name="done" msgid="3587741621903511576">"Պատրաստ է"</string>
-</resources>
diff --git a/packages/SettingsLib/AvatarPicker/res/values-in/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-in/strings.xml
deleted file mode 100644
index 9b5cbf3..0000000
--- a/packages/SettingsLib/AvatarPicker/res/values-in/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="user_image_choose_photo" msgid="5630717762469961028">"Pilih gambar"</string>
- <string name="user_image_take_photo" msgid="3147097821937166738">"Ambil foto"</string>
- <string name="avatar_picker_title" msgid="7478146965334560463">"Pilih foto profil"</string>
- <string name="default_user_icon_description" msgid="6018582161341388812">"Ikon pengguna default"</string>
- <string name="done" msgid="3587741621903511576">"Selesai"</string>
-</resources>
diff --git a/packages/SettingsLib/AvatarPicker/res/values-is/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-is/strings.xml
deleted file mode 100644
index 4007c38..0000000
--- a/packages/SettingsLib/AvatarPicker/res/values-is/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="user_image_choose_photo" msgid="5630717762469961028">"Velja mynd"</string>
- <string name="user_image_take_photo" msgid="3147097821937166738">"Taka mynd"</string>
- <string name="avatar_picker_title" msgid="7478146965334560463">"Veldu prófílmynd"</string>
- <string name="default_user_icon_description" msgid="6018582161341388812">"Tákn sjálfgefins notanda"</string>
- <string name="done" msgid="3587741621903511576">"Lokið"</string>
-</resources>
diff --git a/packages/SettingsLib/AvatarPicker/res/values-it/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-it/strings.xml
deleted file mode 100644
index caeb93b..0000000
--- a/packages/SettingsLib/AvatarPicker/res/values-it/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="user_image_choose_photo" msgid="5630717762469961028">"Scegli un\'immagine"</string>
- <string name="user_image_take_photo" msgid="3147097821937166738">"Scatta una foto"</string>
- <string name="avatar_picker_title" msgid="7478146965334560463">"Scegli un\'immagine del profilo"</string>
- <string name="default_user_icon_description" msgid="6018582161341388812">"Icona dell\'utente predefinito"</string>
- <string name="done" msgid="3587741621903511576">"Fine"</string>
-</resources>
diff --git a/packages/SettingsLib/AvatarPicker/res/values-iw/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-iw/strings.xml
deleted file mode 100644
index 03ca68e..0000000
--- a/packages/SettingsLib/AvatarPicker/res/values-iw/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="user_image_choose_photo" msgid="5630717762469961028">"לבחירת תמונה"</string>
- <string name="user_image_take_photo" msgid="3147097821937166738">"צילום תמונה"</string>
- <string name="avatar_picker_title" msgid="7478146965334560463">"בחירה של תמונת הפרופיל"</string>
- <string name="default_user_icon_description" msgid="6018582161341388812">"סמל המשתמש שמוגדר כברירת מחדל"</string>
- <string name="done" msgid="3587741621903511576">"סיום"</string>
-</resources>
diff --git a/packages/SettingsLib/AvatarPicker/res/values-ja/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-ja/strings.xml
deleted file mode 100644
index 15a4cdc..0000000
--- a/packages/SettingsLib/AvatarPicker/res/values-ja/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="user_image_choose_photo" msgid="5630717762469961028">"画像を選択"</string>
- <string name="user_image_take_photo" msgid="3147097821937166738">"写真を撮る"</string>
- <string name="avatar_picker_title" msgid="7478146965334560463">"プロフィール写真の選択"</string>
- <string name="default_user_icon_description" msgid="6018582161341388812">"デフォルト ユーザー アイコン"</string>
- <string name="done" msgid="3587741621903511576">"完了"</string>
-</resources>
diff --git a/packages/SettingsLib/AvatarPicker/res/values-ka/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-ka/strings.xml
deleted file mode 100644
index ae61afc..0000000
--- a/packages/SettingsLib/AvatarPicker/res/values-ka/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="user_image_choose_photo" msgid="5630717762469961028">"სურათის არჩევა"</string>
- <string name="user_image_take_photo" msgid="3147097821937166738">"ფოტოს გადაღება"</string>
- <string name="avatar_picker_title" msgid="7478146965334560463">"პროფილის სურათის არჩევა"</string>
- <string name="default_user_icon_description" msgid="6018582161341388812">"მომხმარებლის ნაგულისხმევი ხატულა"</string>
- <string name="done" msgid="3587741621903511576">"მზადაა"</string>
-</resources>
diff --git a/packages/SettingsLib/AvatarPicker/res/values-kk/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-kk/strings.xml
deleted file mode 100644
index f1e8950..0000000
--- a/packages/SettingsLib/AvatarPicker/res/values-kk/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="user_image_choose_photo" msgid="5630717762469961028">"Кескін таңдау"</string>
- <string name="user_image_take_photo" msgid="3147097821937166738">"Суретке түсіру"</string>
- <string name="avatar_picker_title" msgid="7478146965334560463">"Профиль суретін таңдау"</string>
- <string name="default_user_icon_description" msgid="6018582161341388812">"Әдепкі пайдаланушы белгішесі"</string>
- <string name="done" msgid="3587741621903511576">"Дайын"</string>
-</resources>
diff --git a/packages/SettingsLib/AvatarPicker/res/values-km/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-km/strings.xml
deleted file mode 100644
index a86491e..0000000
--- a/packages/SettingsLib/AvatarPicker/res/values-km/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="user_image_choose_photo" msgid="5630717762469961028">"ជ្រើសរើសរូបភាព"</string>
- <string name="user_image_take_photo" msgid="3147097821937166738">"ថតរូប"</string>
- <string name="avatar_picker_title" msgid="7478146965334560463">"ជ្រើសរើសរូបភាពកម្រងព័ត៌មាន"</string>
- <string name="default_user_icon_description" msgid="6018582161341388812">"រូបអ្នកប្រើប្រាស់លំនាំដើម"</string>
- <string name="done" msgid="3587741621903511576">"រួចរាល់"</string>
-</resources>
diff --git a/packages/SettingsLib/AvatarPicker/res/values-kn/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-kn/strings.xml
deleted file mode 100644
index 67f8899..0000000
--- a/packages/SettingsLib/AvatarPicker/res/values-kn/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="user_image_choose_photo" msgid="5630717762469961028">"ಚಿತ್ರವನ್ನು ಆಯ್ಕೆಮಾಡಿ"</string>
- <string name="user_image_take_photo" msgid="3147097821937166738">"ಫೋಟೋವನ್ನು ಸೆರೆಹಿಡಿಯಿರಿ"</string>
- <string name="avatar_picker_title" msgid="7478146965334560463">"ಪ್ರೊಫೈಲ್ ಚಿತ್ರವನ್ನು ಆಯ್ಕೆ ಮಾಡಿ"</string>
- <string name="default_user_icon_description" msgid="6018582161341388812">"ಡೀಫಾಲ್ಟ್ ಬಳಕೆದಾರರ ಐಕಾನ್"</string>
- <string name="done" msgid="3587741621903511576">"ಮುಗಿದಿದೆ"</string>
-</resources>
diff --git a/packages/SettingsLib/AvatarPicker/res/values-ko/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-ko/strings.xml
deleted file mode 100644
index 682ebfd..0000000
--- a/packages/SettingsLib/AvatarPicker/res/values-ko/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="user_image_choose_photo" msgid="5630717762469961028">"이미지 선택"</string>
- <string name="user_image_take_photo" msgid="3147097821937166738">"사진 찍기"</string>
- <string name="avatar_picker_title" msgid="7478146965334560463">"프로필 사진 선택"</string>
- <string name="default_user_icon_description" msgid="6018582161341388812">"기본 사용자 아이콘"</string>
- <string name="done" msgid="3587741621903511576">"완료"</string>
-</resources>
diff --git a/packages/SettingsLib/AvatarPicker/res/values-ky/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-ky/strings.xml
deleted file mode 100644
index 3d8e7bf..0000000
--- a/packages/SettingsLib/AvatarPicker/res/values-ky/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="user_image_choose_photo" msgid="5630717762469961028">"Сүрөт тандоо"</string>
- <string name="user_image_take_photo" msgid="3147097821937166738">"Сүрөткө тартуу"</string>
- <string name="avatar_picker_title" msgid="7478146965334560463">"Профилдин сүрөтүн тандоо"</string>
- <string name="default_user_icon_description" msgid="6018582161341388812">"Демейки колдонуучунун сүрөтчөсү"</string>
- <string name="done" msgid="3587741621903511576">"Бүттү"</string>
-</resources>
diff --git a/packages/SettingsLib/AvatarPicker/res/values-lo/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-lo/strings.xml
deleted file mode 100644
index 64bd871..0000000
--- a/packages/SettingsLib/AvatarPicker/res/values-lo/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="user_image_choose_photo" msgid="5630717762469961028">"ເລືອກຮູບ"</string>
- <string name="user_image_take_photo" msgid="3147097821937166738">"ຖ່າຍຮູບ"</string>
- <string name="avatar_picker_title" msgid="7478146965334560463">"ເລືອກຮູບໂປຣໄຟລ໌"</string>
- <string name="default_user_icon_description" msgid="6018582161341388812">"ໄອຄອນຜູ້ໃຊ້ເລີ່ມຕົ້ນ"</string>
- <string name="done" msgid="3587741621903511576">"ແລ້ວໆ"</string>
-</resources>
diff --git a/packages/SettingsLib/AvatarPicker/res/values-lt/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-lt/strings.xml
deleted file mode 100644
index 4bc183b..0000000
--- a/packages/SettingsLib/AvatarPicker/res/values-lt/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="user_image_choose_photo" msgid="5630717762469961028">"Pasirinkti vaizdą"</string>
- <string name="user_image_take_photo" msgid="3147097821937166738">"Fotografuoti"</string>
- <string name="avatar_picker_title" msgid="7478146965334560463">"Profilio nuotraukos pasirinkimas"</string>
- <string name="default_user_icon_description" msgid="6018582161341388812">"Numatytojo naudotojo piktograma"</string>
- <string name="done" msgid="3587741621903511576">"Atlikta"</string>
-</resources>
diff --git a/packages/SettingsLib/AvatarPicker/res/values-lv/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-lv/strings.xml
deleted file mode 100644
index 72e6ec4..0000000
--- a/packages/SettingsLib/AvatarPicker/res/values-lv/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="user_image_choose_photo" msgid="5630717762469961028">"Izvēlēties attēlu"</string>
- <string name="user_image_take_photo" msgid="3147097821937166738">"Uzņemt fotoattēlu"</string>
- <string name="avatar_picker_title" msgid="7478146965334560463">"Profila attēla izvēle"</string>
- <string name="default_user_icon_description" msgid="6018582161341388812">"Noklusējuma lietotāja ikona"</string>
- <string name="done" msgid="3587741621903511576">"Gatavs"</string>
-</resources>
diff --git a/packages/SettingsLib/AvatarPicker/res/values-mk/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-mk/strings.xml
deleted file mode 100644
index 4b6939e..0000000
--- a/packages/SettingsLib/AvatarPicker/res/values-mk/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="user_image_choose_photo" msgid="5630717762469961028">"Изберете слика"</string>
- <string name="user_image_take_photo" msgid="3147097821937166738">"Фотографирај"</string>
- <string name="avatar_picker_title" msgid="7478146965334560463">"Изберете профилна слика"</string>
- <string name="default_user_icon_description" msgid="6018582161341388812">"Икона за стандарден корисник"</string>
- <string name="done" msgid="3587741621903511576">"Готово"</string>
-</resources>
diff --git a/packages/SettingsLib/AvatarPicker/res/values-ml/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-ml/strings.xml
deleted file mode 100644
index aec39b1..0000000
--- a/packages/SettingsLib/AvatarPicker/res/values-ml/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="user_image_choose_photo" msgid="5630717762469961028">"ഒരു ചിത്രം തിരഞ്ഞെടുക്കുക"</string>
- <string name="user_image_take_photo" msgid="3147097821937166738">"ഒരു ഫോട്ടോ എടുക്കുക"</string>
- <string name="avatar_picker_title" msgid="7478146965334560463">"പ്രൊഫൈൽ ചിത്രം തിരഞ്ഞെടുക്കുക"</string>
- <string name="default_user_icon_description" msgid="6018582161341388812">"ഡിഫോൾട്ട് ഉപയോക്തൃ ഐക്കൺ"</string>
- <string name="done" msgid="3587741621903511576">"പൂർത്തിയായി"</string>
-</resources>
diff --git a/packages/SettingsLib/AvatarPicker/res/values-mn/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-mn/strings.xml
deleted file mode 100644
index 4d13e87..0000000
--- a/packages/SettingsLib/AvatarPicker/res/values-mn/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="user_image_choose_photo" msgid="5630717762469961028">"Зураг сонгох"</string>
- <string name="user_image_take_photo" msgid="3147097821937166738">"Зураг авах"</string>
- <string name="avatar_picker_title" msgid="7478146965334560463">"Профайл зураг сонгох"</string>
- <string name="default_user_icon_description" msgid="6018582161341388812">"Өгөгдмөл хэрэглэгчийн дүрс тэмдэг"</string>
- <string name="done" msgid="3587741621903511576">"Болсон"</string>
-</resources>
diff --git a/packages/SettingsLib/AvatarPicker/res/values-mr/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-mr/strings.xml
deleted file mode 100644
index acc2c46..0000000
--- a/packages/SettingsLib/AvatarPicker/res/values-mr/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="user_image_choose_photo" msgid="5630717762469961028">"इमेज निवडा"</string>
- <string name="user_image_take_photo" msgid="3147097821937166738">"फोटो काढा"</string>
- <string name="avatar_picker_title" msgid="7478146965334560463">"प्रोफाइल फोटो निवडा"</string>
- <string name="default_user_icon_description" msgid="6018582161341388812">"डीफॉल्ट वापरकर्ता आयकन"</string>
- <string name="done" msgid="3587741621903511576">"पूर्ण झाले"</string>
-</resources>
diff --git a/packages/SettingsLib/AvatarPicker/res/values-ms/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-ms/strings.xml
deleted file mode 100644
index 45f1b36..0000000
--- a/packages/SettingsLib/AvatarPicker/res/values-ms/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="user_image_choose_photo" msgid="5630717762469961028">"Pilih imej"</string>
- <string name="user_image_take_photo" msgid="3147097821937166738">"Ambil foto"</string>
- <string name="avatar_picker_title" msgid="7478146965334560463">"Pilih gambar profil"</string>
- <string name="default_user_icon_description" msgid="6018582161341388812">"Ikon pengguna lalai"</string>
- <string name="done" msgid="3587741621903511576">"Selesai"</string>
-</resources>
diff --git a/packages/SettingsLib/AvatarPicker/res/values-my/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-my/strings.xml
deleted file mode 100644
index 6973084..0000000
--- a/packages/SettingsLib/AvatarPicker/res/values-my/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="user_image_choose_photo" msgid="5630717762469961028">"ပုံရွေးရန်"</string>
- <string name="user_image_take_photo" msgid="3147097821937166738">"ဓာတ်ပုံရိုက်ရန်"</string>
- <string name="avatar_picker_title" msgid="7478146965334560463">"ပရိုဖိုင်ပုံ ရွေးပါ"</string>
- <string name="default_user_icon_description" msgid="6018582161341388812">"မူရင်းအသုံးပြုသူ သင်္ကေတ"</string>
- <string name="done" msgid="3587741621903511576">"ပြီးပြီ"</string>
-</resources>
diff --git a/packages/SettingsLib/AvatarPicker/res/values-nb/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-nb/strings.xml
deleted file mode 100644
index 8a55006..0000000
--- a/packages/SettingsLib/AvatarPicker/res/values-nb/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="user_image_choose_photo" msgid="5630717762469961028">"Velg et bilde"</string>
- <string name="user_image_take_photo" msgid="3147097821937166738">"Ta et bilde"</string>
- <string name="avatar_picker_title" msgid="7478146965334560463">"Velg et profilbilde"</string>
- <string name="default_user_icon_description" msgid="6018582161341388812">"Standard brukerikon"</string>
- <string name="done" msgid="3587741621903511576">"Ferdig"</string>
-</resources>
diff --git a/packages/SettingsLib/AvatarPicker/res/values-ne/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-ne/strings.xml
deleted file mode 100644
index 8373058..0000000
--- a/packages/SettingsLib/AvatarPicker/res/values-ne/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="user_image_choose_photo" msgid="5630717762469961028">"कुनै फोटो छनौट गर्नुहोस्"</string>
- <string name="user_image_take_photo" msgid="3147097821937166738">"फोटो खिच्नुहोस्"</string>
- <string name="avatar_picker_title" msgid="7478146965334560463">"प्रोफाइल फोटो छान्नुहोस्"</string>
- <string name="default_user_icon_description" msgid="6018582161341388812">"प्रयोगकर्ताको डिफल्ट आइकन"</string>
- <string name="done" msgid="3587741621903511576">"सम्पन्न भयो"</string>
-</resources>
diff --git a/packages/SettingsLib/AvatarPicker/res/values-nl/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-nl/strings.xml
deleted file mode 100644
index cb3bd51..0000000
--- a/packages/SettingsLib/AvatarPicker/res/values-nl/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="user_image_choose_photo" msgid="5630717762469961028">"Een afbeelding kiezen"</string>
- <string name="user_image_take_photo" msgid="3147097821937166738">"Een foto maken"</string>
- <string name="avatar_picker_title" msgid="7478146965334560463">"Kies een profielfoto"</string>
- <string name="default_user_icon_description" msgid="6018582161341388812">"Standaard gebruikersicoon"</string>
- <string name="done" msgid="3587741621903511576">"Klaar"</string>
-</resources>
diff --git a/packages/SettingsLib/AvatarPicker/res/values-or/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-or/strings.xml
deleted file mode 100644
index dd54ac3..0000000
--- a/packages/SettingsLib/AvatarPicker/res/values-or/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="user_image_choose_photo" msgid="5630717762469961028">"ଗୋଟିଏ ଛବି ବାଛନ୍ତୁ"</string>
- <string name="user_image_take_photo" msgid="3147097821937166738">"ଗୋଟିଏ ଫଟୋ ଉଠାନ୍ତୁ"</string>
- <string name="avatar_picker_title" msgid="7478146965334560463">"ଏକ ପ୍ରୋଫାଇଲ ଛବି ବାଛନ୍ତୁ"</string>
- <string name="default_user_icon_description" msgid="6018582161341388812">"ଡିଫଲ୍ଟ ୟୁଜର ଆଇକନ"</string>
- <string name="done" msgid="3587741621903511576">"ହୋଇଗଲା"</string>
-</resources>
diff --git a/packages/SettingsLib/AvatarPicker/res/values-pa/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-pa/strings.xml
deleted file mode 100644
index a336226..0000000
--- a/packages/SettingsLib/AvatarPicker/res/values-pa/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="user_image_choose_photo" msgid="5630717762469961028">"ਚਿੱਤਰ ਚੁਣੋ"</string>
- <string name="user_image_take_photo" msgid="3147097821937166738">"ਫ਼ੋਟੋ ਖਿੱਚੋ"</string>
- <string name="avatar_picker_title" msgid="7478146965334560463">"ਕੋਈ ਪ੍ਰੋਫਾਈਲ ਤਸਵੀਰ ਚੁਣੋ"</string>
- <string name="default_user_icon_description" msgid="6018582161341388812">"ਪੂਰਵ-ਨਿਰਧਾਰਿਤ ਵਰਤੋਂਕਾਰ ਪ੍ਰਤੀਕ"</string>
- <string name="done" msgid="3587741621903511576">"ਹੋ ਗਿਆ"</string>
-</resources>
diff --git a/packages/SettingsLib/AvatarPicker/res/values-pl/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-pl/strings.xml
deleted file mode 100644
index 35787c9..0000000
--- a/packages/SettingsLib/AvatarPicker/res/values-pl/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="user_image_choose_photo" msgid="5630717762469961028">"Wybierz obraz"</string>
- <string name="user_image_take_photo" msgid="3147097821937166738">"Zrób zdjęcie"</string>
- <string name="avatar_picker_title" msgid="7478146965334560463">"Wybierz zdjęcie profilowe"</string>
- <string name="default_user_icon_description" msgid="6018582161341388812">"Ikona domyślnego użytkownika"</string>
- <string name="done" msgid="3587741621903511576">"Gotowe"</string>
-</resources>
diff --git a/packages/SettingsLib/AvatarPicker/res/values-pt-rBR/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-pt-rBR/strings.xml
deleted file mode 100644
index ba1ce2a..0000000
--- a/packages/SettingsLib/AvatarPicker/res/values-pt-rBR/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="user_image_choose_photo" msgid="5630717762469961028">"Escolher uma imagem"</string>
- <string name="user_image_take_photo" msgid="3147097821937166738">"Tirar uma foto"</string>
- <string name="avatar_picker_title" msgid="7478146965334560463">"Escolha uma foto de perfil"</string>
- <string name="default_user_icon_description" msgid="6018582161341388812">"Ícone de usuário padrão"</string>
- <string name="done" msgid="3587741621903511576">"Concluir"</string>
-</resources>
diff --git a/packages/SettingsLib/AvatarPicker/res/values-pt-rPT/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-pt-rPT/strings.xml
deleted file mode 100644
index 18f36c3..0000000
--- a/packages/SettingsLib/AvatarPicker/res/values-pt-rPT/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="user_image_choose_photo" msgid="5630717762469961028">"Escolher uma imagem"</string>
- <string name="user_image_take_photo" msgid="3147097821937166738">"Tirar uma foto"</string>
- <string name="avatar_picker_title" msgid="7478146965334560463">"Escolha uma imagem do perfil"</string>
- <string name="default_user_icon_description" msgid="6018582161341388812">"Ícone do utilizador predefinido"</string>
- <string name="done" msgid="3587741621903511576">"Concluído"</string>
-</resources>
diff --git a/packages/SettingsLib/AvatarPicker/res/values-pt/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-pt/strings.xml
deleted file mode 100644
index ba1ce2a..0000000
--- a/packages/SettingsLib/AvatarPicker/res/values-pt/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="user_image_choose_photo" msgid="5630717762469961028">"Escolher uma imagem"</string>
- <string name="user_image_take_photo" msgid="3147097821937166738">"Tirar uma foto"</string>
- <string name="avatar_picker_title" msgid="7478146965334560463">"Escolha uma foto de perfil"</string>
- <string name="default_user_icon_description" msgid="6018582161341388812">"Ícone de usuário padrão"</string>
- <string name="done" msgid="3587741621903511576">"Concluir"</string>
-</resources>
diff --git a/packages/SettingsLib/AvatarPicker/res/values-ro/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-ro/strings.xml
deleted file mode 100644
index 51bcd57..0000000
--- a/packages/SettingsLib/AvatarPicker/res/values-ro/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="user_image_choose_photo" msgid="5630717762469961028">"Alege o imagine"</string>
- <string name="user_image_take_photo" msgid="3147097821937166738">"Fă o fotografie"</string>
- <string name="avatar_picker_title" msgid="7478146965334560463">"Alege o fotografie de profil"</string>
- <string name="default_user_icon_description" msgid="6018582161341388812">"Pictograma prestabilită a utilizatorului"</string>
- <string name="done" msgid="3587741621903511576">"Gata"</string>
-</resources>
diff --git a/packages/SettingsLib/AvatarPicker/res/values-ru/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-ru/strings.xml
deleted file mode 100644
index 59fb913..0000000
--- a/packages/SettingsLib/AvatarPicker/res/values-ru/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="user_image_choose_photo" msgid="5630717762469961028">"Выбрать фото"</string>
- <string name="user_image_take_photo" msgid="3147097821937166738">"Сделать снимок"</string>
- <string name="avatar_picker_title" msgid="7478146965334560463">"Выберите фото профиля"</string>
- <string name="default_user_icon_description" msgid="6018582161341388812">"Значок пользователя по умолчанию"</string>
- <string name="done" msgid="3587741621903511576">"Готово"</string>
-</resources>
diff --git a/packages/SettingsLib/AvatarPicker/res/values-si/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-si/strings.xml
deleted file mode 100644
index db7e0b8..0000000
--- a/packages/SettingsLib/AvatarPicker/res/values-si/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="user_image_choose_photo" msgid="5630717762469961028">"රූපයක් තෝරන්න"</string>
- <string name="user_image_take_photo" msgid="3147097821937166738">"ඡායාරූපයක් ගන්න"</string>
- <string name="avatar_picker_title" msgid="7478146965334560463">"පැතිකඩ පින්තූරයක් තේරීම"</string>
- <string name="default_user_icon_description" msgid="6018582161341388812">"පෙරනිමි පරිශීලක නිරූපකය"</string>
- <string name="done" msgid="3587741621903511576">"නිමයි"</string>
-</resources>
diff --git a/packages/SettingsLib/AvatarPicker/res/values-sk/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-sk/strings.xml
deleted file mode 100644
index 36e94f3..0000000
--- a/packages/SettingsLib/AvatarPicker/res/values-sk/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="user_image_choose_photo" msgid="5630717762469961028">"Vybrať obrázok"</string>
- <string name="user_image_take_photo" msgid="3147097821937166738">"Odfotiť"</string>
- <string name="avatar_picker_title" msgid="7478146965334560463">"Vyberte profilovú fotku"</string>
- <string name="default_user_icon_description" msgid="6018582161341388812">"Predvolená ikona používateľa"</string>
- <string name="done" msgid="3587741621903511576">"Hotovo"</string>
-</resources>
diff --git a/packages/SettingsLib/AvatarPicker/res/values-sl/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-sl/strings.xml
deleted file mode 100644
index 7d0bf10..0000000
--- a/packages/SettingsLib/AvatarPicker/res/values-sl/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="user_image_choose_photo" msgid="5630717762469961028">"Izbira slike"</string>
- <string name="user_image_take_photo" msgid="3147097821937166738">"Fotografiranje"</string>
- <string name="avatar_picker_title" msgid="7478146965334560463">"Izbira profilne slike"</string>
- <string name="default_user_icon_description" msgid="6018582161341388812">"Privzeta ikona uporabnika"</string>
- <string name="done" msgid="3587741621903511576">"Končano"</string>
-</resources>
diff --git a/packages/SettingsLib/AvatarPicker/res/values-sq/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-sq/strings.xml
deleted file mode 100644
index 0093380..0000000
--- a/packages/SettingsLib/AvatarPicker/res/values-sq/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="user_image_choose_photo" msgid="5630717762469961028">"Zgjidh një imazh"</string>
- <string name="user_image_take_photo" msgid="3147097821937166738">"Bëj një fotografi"</string>
- <string name="avatar_picker_title" msgid="7478146965334560463">"Zgjidh një fotografi profili"</string>
- <string name="default_user_icon_description" msgid="6018582161341388812">"Ikona e parazgjedhur e përdoruesit"</string>
- <string name="done" msgid="3587741621903511576">"U krye"</string>
-</resources>
diff --git a/packages/SettingsLib/AvatarPicker/res/values-sr/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-sr/strings.xml
deleted file mode 100644
index 971d8c5..0000000
--- a/packages/SettingsLib/AvatarPicker/res/values-sr/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="user_image_choose_photo" msgid="5630717762469961028">"Одаберите слику"</string>
- <string name="user_image_take_photo" msgid="3147097821937166738">"Сликајте"</string>
- <string name="avatar_picker_title" msgid="7478146965334560463">"Одаберите слику профила"</string>
- <string name="default_user_icon_description" msgid="6018582161341388812">"Подразумевана икона корисника"</string>
- <string name="done" msgid="3587741621903511576">"Готово"</string>
-</resources>
diff --git a/packages/SettingsLib/AvatarPicker/res/values-sv/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-sv/strings.xml
deleted file mode 100644
index 9196e2a..0000000
--- a/packages/SettingsLib/AvatarPicker/res/values-sv/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="user_image_choose_photo" msgid="5630717762469961028">"Välj en bild"</string>
- <string name="user_image_take_photo" msgid="3147097821937166738">"Ta ett foto"</string>
- <string name="avatar_picker_title" msgid="7478146965334560463">"Välj en profilbild"</string>
- <string name="default_user_icon_description" msgid="6018582161341388812">"Ikon för standardanvändare"</string>
- <string name="done" msgid="3587741621903511576">"Klar"</string>
-</resources>
diff --git a/packages/SettingsLib/AvatarPicker/res/values-sw/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-sw/strings.xml
deleted file mode 100644
index a0bc554..0000000
--- a/packages/SettingsLib/AvatarPicker/res/values-sw/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="user_image_choose_photo" msgid="5630717762469961028">"Chagua picha"</string>
- <string name="user_image_take_photo" msgid="3147097821937166738">"Piga picha"</string>
- <string name="avatar_picker_title" msgid="7478146965334560463">"Chagua picha ya wasifu"</string>
- <string name="default_user_icon_description" msgid="6018582161341388812">"Aikoni chaguomsingi ya mtumiaji"</string>
- <string name="done" msgid="3587741621903511576">"Nimemaliza"</string>
-</resources>
diff --git a/packages/SettingsLib/AvatarPicker/res/values-ta/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-ta/strings.xml
deleted file mode 100644
index 13c7b5b..0000000
--- a/packages/SettingsLib/AvatarPicker/res/values-ta/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="user_image_choose_photo" msgid="5630717762469961028">"படத்தைத் தேர்வுசெய்க"</string>
- <string name="user_image_take_photo" msgid="3147097821937166738">"படமெடு"</string>
- <string name="avatar_picker_title" msgid="7478146965334560463">"சுயவிவரப் படத்தைத் தேர்வுசெய்யுங்கள்"</string>
- <string name="default_user_icon_description" msgid="6018582161341388812">"இயல்புநிலைப் பயனர் ஐகான்"</string>
- <string name="done" msgid="3587741621903511576">"முடிந்தது"</string>
-</resources>
diff --git a/packages/SettingsLib/AvatarPicker/res/values-te/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-te/strings.xml
deleted file mode 100644
index b731848..0000000
--- a/packages/SettingsLib/AvatarPicker/res/values-te/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="user_image_choose_photo" msgid="5630717762469961028">"చిత్రాన్ని ఎంచుకోండి"</string>
- <string name="user_image_take_photo" msgid="3147097821937166738">"ఫోటోను తీయి"</string>
- <string name="avatar_picker_title" msgid="7478146965334560463">"ప్రొఫైల్ ఫోటోను ఎంచుకోండి"</string>
- <string name="default_user_icon_description" msgid="6018582161341388812">"ఆటోమేటిక్ సెట్టింగ్ యూజర్ చిహ్నం"</string>
- <string name="done" msgid="3587741621903511576">"పూర్తయింది"</string>
-</resources>
diff --git a/packages/SettingsLib/AvatarPicker/res/values-th/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-th/strings.xml
deleted file mode 100644
index b8b4324..0000000
--- a/packages/SettingsLib/AvatarPicker/res/values-th/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="user_image_choose_photo" msgid="5630717762469961028">"เลือกรูปภาพ"</string>
- <string name="user_image_take_photo" msgid="3147097821937166738">"ถ่ายรูป"</string>
- <string name="avatar_picker_title" msgid="7478146965334560463">"เลือกรูปโปรไฟล์"</string>
- <string name="default_user_icon_description" msgid="6018582161341388812">"ไอคอนผู้ใช้เริ่มต้น"</string>
- <string name="done" msgid="3587741621903511576">"เสร็จสิ้น"</string>
-</resources>
diff --git a/packages/SettingsLib/AvatarPicker/res/values-tl/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-tl/strings.xml
deleted file mode 100644
index 77b28cd..0000000
--- a/packages/SettingsLib/AvatarPicker/res/values-tl/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="user_image_choose_photo" msgid="5630717762469961028">"Pumili ng larawan"</string>
- <string name="user_image_take_photo" msgid="3147097821937166738">"Kumuha ng larawan"</string>
- <string name="avatar_picker_title" msgid="7478146965334560463">"Pumili ng larawan sa profile"</string>
- <string name="default_user_icon_description" msgid="6018582161341388812">"Icon ng default na user"</string>
- <string name="done" msgid="3587741621903511576">"Tapos na"</string>
-</resources>
diff --git a/packages/SettingsLib/AvatarPicker/res/values-tr/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-tr/strings.xml
deleted file mode 100644
index 3e10257..0000000
--- a/packages/SettingsLib/AvatarPicker/res/values-tr/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="user_image_choose_photo" msgid="5630717762469961028">"Resim seç"</string>
- <string name="user_image_take_photo" msgid="3147097821937166738">"Fotoğraf çek"</string>
- <string name="avatar_picker_title" msgid="7478146965334560463">"Profil fotoğrafı seçin"</string>
- <string name="default_user_icon_description" msgid="6018582161341388812">"Varsayılan kullanıcı simgesi"</string>
- <string name="done" msgid="3587741621903511576">"Bitti"</string>
-</resources>
diff --git a/packages/SettingsLib/AvatarPicker/res/values-uk/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-uk/strings.xml
deleted file mode 100644
index ee4a2fc..0000000
--- a/packages/SettingsLib/AvatarPicker/res/values-uk/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="user_image_choose_photo" msgid="5630717762469961028">"Вибрати зображення"</string>
- <string name="user_image_take_photo" msgid="3147097821937166738">"Зробити фото"</string>
- <string name="avatar_picker_title" msgid="7478146965334560463">"Виберіть зображення профілю"</string>
- <string name="default_user_icon_description" msgid="6018582161341388812">"Значок користувача за умовчанням"</string>
- <string name="done" msgid="3587741621903511576">"Готово"</string>
-</resources>
diff --git a/packages/SettingsLib/AvatarPicker/res/values-ur/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-ur/strings.xml
deleted file mode 100644
index 7972ee1..0000000
--- a/packages/SettingsLib/AvatarPicker/res/values-ur/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="user_image_choose_photo" msgid="5630717762469961028">"ایک تصویر منتخب کریں"</string>
- <string name="user_image_take_photo" msgid="3147097821937166738">"ایک تصویر لیں"</string>
- <string name="avatar_picker_title" msgid="7478146965334560463">"پروفائل کی تصویر منتخب کریں"</string>
- <string name="default_user_icon_description" msgid="6018582161341388812">"ڈیفالٹ صارف کا آئیکن"</string>
- <string name="done" msgid="3587741621903511576">"ہو گیا"</string>
-</resources>
diff --git a/packages/SettingsLib/AvatarPicker/res/values-uz/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-uz/strings.xml
deleted file mode 100644
index 208be10..0000000
--- a/packages/SettingsLib/AvatarPicker/res/values-uz/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="user_image_choose_photo" msgid="5630717762469961028">"Rasm tanlash"</string>
- <string name="user_image_take_photo" msgid="3147097821937166738">"Suratga olish"</string>
- <string name="avatar_picker_title" msgid="7478146965334560463">"Profil rasmini tanlash"</string>
- <string name="default_user_icon_description" msgid="6018582161341388812">"Foydalanuvchining standart belgisi"</string>
- <string name="done" msgid="3587741621903511576">"Tayyor"</string>
-</resources>
diff --git a/packages/SettingsLib/AvatarPicker/res/values-vi/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-vi/strings.xml
deleted file mode 100644
index c6b4ef5..0000000
--- a/packages/SettingsLib/AvatarPicker/res/values-vi/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="user_image_choose_photo" msgid="5630717762469961028">"Chọn hình ảnh"</string>
- <string name="user_image_take_photo" msgid="3147097821937166738">"Chụp ảnh"</string>
- <string name="avatar_picker_title" msgid="7478146965334560463">"Chọn một ảnh hồ sơ"</string>
- <string name="default_user_icon_description" msgid="6018582161341388812">"Biểu tượng người dùng mặc định"</string>
- <string name="done" msgid="3587741621903511576">"Xong"</string>
-</resources>
diff --git a/packages/SettingsLib/AvatarPicker/res/values-zh-rCN/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-zh-rCN/strings.xml
deleted file mode 100644
index 684449f..0000000
--- a/packages/SettingsLib/AvatarPicker/res/values-zh-rCN/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="user_image_choose_photo" msgid="5630717762469961028">"选择图片"</string>
- <string name="user_image_take_photo" msgid="3147097821937166738">"拍照"</string>
- <string name="avatar_picker_title" msgid="7478146965334560463">"选择个人资料照片"</string>
- <string name="default_user_icon_description" msgid="6018582161341388812">"默认用户图标"</string>
- <string name="done" msgid="3587741621903511576">"完成"</string>
-</resources>
diff --git a/packages/SettingsLib/AvatarPicker/res/values-zh-rHK/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-zh-rHK/strings.xml
deleted file mode 100644
index e0d4052..0000000
--- a/packages/SettingsLib/AvatarPicker/res/values-zh-rHK/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="user_image_choose_photo" msgid="5630717762469961028">"選擇圖片"</string>
- <string name="user_image_take_photo" msgid="3147097821937166738">"拍攝相片"</string>
- <string name="avatar_picker_title" msgid="7478146965334560463">"選擇個人檔案相片"</string>
- <string name="default_user_icon_description" msgid="6018582161341388812">"預設使用者圖示"</string>
- <string name="done" msgid="3587741621903511576">"完成"</string>
-</resources>
diff --git a/packages/SettingsLib/AvatarPicker/res/values-zh-rTW/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-zh-rTW/strings.xml
deleted file mode 100644
index d82ad7e..0000000
--- a/packages/SettingsLib/AvatarPicker/res/values-zh-rTW/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="user_image_choose_photo" msgid="5630717762469961028">"選擇圖片"</string>
- <string name="user_image_take_photo" msgid="3147097821937166738">"拍照"</string>
- <string name="avatar_picker_title" msgid="7478146965334560463">"選擇個人資料相片"</string>
- <string name="default_user_icon_description" msgid="6018582161341388812">"預設使用者圖示"</string>
- <string name="done" msgid="3587741621903511576">"完成"</string>
-</resources>
diff --git a/packages/SettingsLib/AvatarPicker/res/values-zu/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-zu/strings.xml
deleted file mode 100644
index 8678332..0000000
--- a/packages/SettingsLib/AvatarPicker/res/values-zu/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="user_image_choose_photo" msgid="5630717762469961028">"Khetha isithombe"</string>
- <string name="user_image_take_photo" msgid="3147097821937166738">"Thatha isithombe"</string>
- <string name="avatar_picker_title" msgid="7478146965334560463">"Khetha isithombe sephrofayela"</string>
- <string name="default_user_icon_description" msgid="6018582161341388812">"Isithonjana somsebenzisi sokuzenzakalelayo"</string>
- <string name="done" msgid="3587741621903511576">"Kwenziwe"</string>
-</resources>
diff --git a/packages/SettingsLib/AvatarPicker/res/values/arrays.xml b/packages/SettingsLib/AvatarPicker/res/values/arrays.xml
deleted file mode 100644
index 042bc41..0000000
--- a/packages/SettingsLib/AvatarPicker/res/values/arrays.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?><!--
- Copyright 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
--->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-
- <!-- Images offered as options in the avatar picker. If populated, the avatar_image_descriptions
- array must also be populated with a content description for each image. -->
- <array name="avatar_images" />
-
- <!-- Content descriptions for each of the images in the avatar_images array. When overlaid
- these values should be translated, but this empty array must not be translated or it may
- replace the real descriptions with an empty array. -->
- <string-array name="avatar_image_descriptions" translatable="false" />
-</resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/AvatarPicker/res/values/dimens.xml b/packages/SettingsLib/AvatarPicker/res/values/dimens.xml
deleted file mode 100644
index df54dc2..0000000
--- a/packages/SettingsLib/AvatarPicker/res/values/dimens.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?><!--
- Copyright (C) 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License
- -->
-<resources>
- <dimen name="avatar_size_in_picker">96dp</dimen>
- <dimen name="avatar_picker_padding">6dp</dimen>
- <dimen name="avatar_picker_margin">2dp</dimen>
- <dimen name="avatar_picker_icon_inset">25dp</dimen>
- <integer name="avatar_picker_columns">3</integer>
-</resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/AvatarPicker/res/values/strings.xml b/packages/SettingsLib/AvatarPicker/res/values/strings.xml
deleted file mode 100644
index 1ead128..0000000
--- a/packages/SettingsLib/AvatarPicker/res/values/strings.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?><!--
- Copyright (C) 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License
- -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-
- <!-- An option in a photo selection dialog to choose a pre-existing image [CHAR LIMIT=50] -->
- <string name="user_image_choose_photo">Choose an image</string>
-
- <!-- An option in a photo selection dialog to take a new photo [CHAR LIMIT=50] -->
- <string name="user_image_take_photo">Take a photo</string>
-
- <!-- Title for a screen allowing the user to choose a profile picture. [CHAR LIMIT=NONE] -->
- <string name="avatar_picker_title">Choose a profile picture</string>
-
- <!-- Content description for a default user icon. [CHAR LIMIT=NONE] -->
- <string name="default_user_icon_description">Default user icon</string>
-
- <!-- Button label for generic Done action, to be pressed when an action has been completed [CHAR LIMIT=20] -->
- <string name="done">Done</string>
-</resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/AvatarPicker/src/AvatarPhotoController.java b/packages/SettingsLib/AvatarPicker/src/AvatarPhotoController.java
deleted file mode 100644
index c20392a..0000000
--- a/packages/SettingsLib/AvatarPicker/src/AvatarPhotoController.java
+++ /dev/null
@@ -1,400 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settingslib.avatarpicker;
-
-import android.app.Activity;
-import android.content.ClipData;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.Canvas;
-import android.graphics.Matrix;
-import android.graphics.Paint;
-import android.graphics.RectF;
-import android.media.ExifInterface;
-import android.net.Uri;
-import android.os.StrictMode;
-import android.provider.MediaStore;
-import android.util.EventLog;
-import android.util.Log;
-
-import androidx.annotation.Nullable;
-import androidx.core.content.FileProvider;
-
-import com.google.common.util.concurrent.FutureCallback;
-import com.google.common.util.concurrent.Futures;
-import com.google.common.util.concurrent.ListenableFuture;
-
-import libcore.io.Streams;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.List;
-
-class AvatarPhotoController {
-
- interface AvatarUi {
- boolean isFinishing();
-
- void returnUriResult(Uri uri);
-
- void startActivityForResult(Intent intent, int resultCode);
-
- boolean startSystemActivityForResult(Intent intent, int resultCode);
-
- int getPhotoSize();
- }
-
- interface ContextInjector {
- File getCacheDir();
-
- Uri createTempImageUri(File parentDir, String fileName, boolean purge);
-
- ContentResolver getContentResolver();
-
- Context getContext();
- }
-
- private static final String TAG = "AvatarPhotoController";
-
- static final int REQUEST_CODE_CHOOSE_PHOTO = 1001;
- static final int REQUEST_CODE_TAKE_PHOTO = 1002;
- static final int REQUEST_CODE_CROP_PHOTO = 1003;
-
- /**
- * Delay to allow the photo picker exit animation to complete before the crop activity opens.
- */
- private static final long DELAY_BEFORE_CROP_MILLIS = 150;
-
- private static final String IMAGES_DIR = "multi_user";
- private static final String PRE_CROP_PICTURE_FILE_NAME = "PreCropEditUserPhoto.jpg";
- private static final String CROP_PICTURE_FILE_NAME = "CropEditUserPhoto.jpg";
- private static final String TAKE_PICTURE_FILE_NAME = "TakeEditUserPhoto.jpg";
-
- private final int mPhotoSize;
-
- private final AvatarUi mAvatarUi;
- private final ContextInjector mContextInjector;
-
- private final File mImagesDir;
- private final Uri mPreCropPictureUri;
- private final Uri mCropPictureUri;
- private final Uri mTakePictureUri;
-
- AvatarPhotoController(AvatarUi avatarUi, ContextInjector contextInjector, boolean waiting) {
- mAvatarUi = avatarUi;
- mContextInjector = contextInjector;
-
- mImagesDir = new File(mContextInjector.getCacheDir(), IMAGES_DIR);
- mImagesDir.mkdir();
- mPreCropPictureUri = mContextInjector
- .createTempImageUri(mImagesDir, PRE_CROP_PICTURE_FILE_NAME, !waiting);
- mCropPictureUri =
- mContextInjector.createTempImageUri(mImagesDir, CROP_PICTURE_FILE_NAME, !waiting);
- mTakePictureUri =
- mContextInjector.createTempImageUri(mImagesDir, TAKE_PICTURE_FILE_NAME, !waiting);
- mPhotoSize = mAvatarUi.getPhotoSize();
- }
-
- /**
- * Handles activity result from containing activity/fragment after a take/choose/crop photo
- * action result is received.
- */
- public boolean onActivityResult(int requestCode, int resultCode, Intent data) {
- if (resultCode != Activity.RESULT_OK) {
- return false;
- }
- final Uri pictureUri = data != null && data.getData() != null
- ? data.getData() : mTakePictureUri;
-
- // Check if the result is a content uri
- if (!ContentResolver.SCHEME_CONTENT.equals(pictureUri.getScheme())) {
- Log.e(TAG, "Invalid pictureUri scheme: " + pictureUri.getScheme());
- EventLog.writeEvent(0x534e4554, "172939189", -1, pictureUri.getPath());
- return false;
- }
-
- switch (requestCode) {
- case REQUEST_CODE_CROP_PHOTO:
- mAvatarUi.returnUriResult(pictureUri);
- return true;
- case REQUEST_CODE_TAKE_PHOTO:
- if (mTakePictureUri.equals(pictureUri)) {
- cropPhoto(pictureUri);
- } else {
- copyAndCropPhoto(pictureUri, false);
- }
- return true;
- case REQUEST_CODE_CHOOSE_PHOTO:
- copyAndCropPhoto(pictureUri, true);
- return true;
- }
- return false;
- }
-
- void takePhoto() {
- Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE_SECURE);
- appendOutputExtra(intent, mTakePictureUri);
- mAvatarUi.startActivityForResult(intent, REQUEST_CODE_TAKE_PHOTO);
- }
-
- void choosePhoto() {
- Intent intent = new Intent(MediaStore.ACTION_PICK_IMAGES, null);
- intent.setType("image/*");
- mAvatarUi.startActivityForResult(intent, REQUEST_CODE_CHOOSE_PHOTO);
- }
-
- private void copyAndCropPhoto(final Uri pictureUri, boolean delayBeforeCrop) {
- ListenableFuture<Uri> future = ThreadUtils.getBackgroundExecutor().submit(() -> {
- final ContentResolver cr = mContextInjector.getContentResolver();
- try {
- InputStream in = cr.openInputStream(pictureUri);
- OutputStream out = cr.openOutputStream(mPreCropPictureUri);
- Streams.copy(in, out);
- return mPreCropPictureUri;
- } catch (IOException e) {
- Log.w(TAG, "Failed to copy photo", e);
- return null;
- }
- });
- Futures.addCallback(future, new FutureCallback<>() {
- @Override
- public void onSuccess(@Nullable Uri result) {
- if (result == null) {
- return;
- }
- Runnable cropRunnable = () -> {
- if (!mAvatarUi.isFinishing()) {
- cropPhoto(mPreCropPictureUri);
- }
- };
- if (delayBeforeCrop) {
- mContextInjector.getContext().getMainThreadHandler()
- .postDelayed(cropRunnable, DELAY_BEFORE_CROP_MILLIS);
- } else {
- cropRunnable.run();
- }
- }
-
- @Override
- public void onFailure(Throwable t) {
- Log.e(TAG, "Error performing copy-and-crop", t);
- }
- }, mContextInjector.getContext().getMainExecutor());
- }
-
- private void cropPhoto(final Uri pictureUri) {
- // TODO: Use a public intent, when there is one.
- Intent intent = new Intent("com.android.camera.action.CROP");
- intent.setDataAndType(pictureUri, "image/*");
- appendOutputExtra(intent, mCropPictureUri);
- appendCropExtras(intent);
- try {
- StrictMode.disableDeathOnFileUriExposure();
- if (mAvatarUi.startSystemActivityForResult(intent, REQUEST_CODE_CROP_PHOTO)) {
- return;
- }
- } finally {
- StrictMode.enableDeathOnFileUriExposure();
- }
- onPhotoNotCropped(pictureUri);
- }
-
- private void appendOutputExtra(Intent intent, Uri pictureUri) {
- intent.putExtra(MediaStore.EXTRA_OUTPUT, pictureUri);
- intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION
- | Intent.FLAG_GRANT_READ_URI_PERMISSION);
- intent.setClipData(ClipData.newRawUri(MediaStore.EXTRA_OUTPUT, pictureUri));
- }
-
- private void appendCropExtras(Intent intent) {
- intent.putExtra("crop", "true");
- intent.putExtra("scale", true);
- intent.putExtra("scaleUpIfNeeded", true);
- intent.putExtra("aspectX", 1);
- intent.putExtra("aspectY", 1);
- intent.putExtra("outputX", mPhotoSize);
- intent.putExtra("outputY", mPhotoSize);
- }
-
- private void onPhotoNotCropped(final Uri data) {
- ListenableFuture<Bitmap> future = ThreadUtils.getBackgroundExecutor().submit(() -> {
- // Scale and crop to a square aspect ratio
- Bitmap croppedImage = Bitmap.createBitmap(mPhotoSize, mPhotoSize,
- Bitmap.Config.ARGB_8888);
- Canvas canvas = new Canvas(croppedImage);
- Bitmap fullImage;
- try (InputStream imageStream = mContextInjector.getContentResolver()
- .openInputStream(data)) {
- fullImage = BitmapFactory.decodeStream(imageStream);
- }
- if (fullImage == null) {
- Log.e(TAG, "Image data could not be decoded");
- return null;
- }
- int rotation = getRotation(data);
- final int squareSize = Math.min(fullImage.getWidth(),
- fullImage.getHeight());
- final int left = (fullImage.getWidth() - squareSize) / 2;
- final int top = (fullImage.getHeight() - squareSize) / 2;
-
- Matrix matrix = new Matrix();
- RectF rectSource = new RectF(left, top,
- left + squareSize, top + squareSize);
- RectF rectDest = new RectF(0, 0, mPhotoSize, mPhotoSize);
- matrix.setRectToRect(rectSource, rectDest, Matrix.ScaleToFit.CENTER);
- matrix.postRotate(rotation, mPhotoSize / 2f, mPhotoSize / 2f);
- canvas.drawBitmap(fullImage, matrix, new Paint());
- saveBitmapToFile(croppedImage, new File(mImagesDir, CROP_PICTURE_FILE_NAME));
- return croppedImage;
- });
- Futures.addCallback(future, new FutureCallback<>() {
- @Override
- public void onSuccess(@Nullable Bitmap result) {
- if (result != null) {
- mAvatarUi.returnUriResult(mCropPictureUri);
- }
- }
-
- @Override
- public void onFailure(Throwable t) {
- Log.e(TAG, "Error performing internal crop", t);
- }
- }, mContextInjector.getContext().getMainExecutor());
- }
-
- /**
- * Reads the image's exif data and determines the rotation degree needed to display the image
- * in portrait mode.
- */
- private int getRotation(Uri selectedImage) {
- int rotation = -1;
- try {
- InputStream imageStream =
- mContextInjector.getContentResolver().openInputStream(selectedImage);
- ExifInterface exif = new ExifInterface(imageStream);
- rotation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, -1);
- } catch (IOException exception) {
- Log.e(TAG, "Error while getting rotation", exception);
- }
-
- switch (rotation) {
- case ExifInterface.ORIENTATION_ROTATE_90:
- return 90;
- case ExifInterface.ORIENTATION_ROTATE_180:
- return 180;
- case ExifInterface.ORIENTATION_ROTATE_270:
- return 270;
- default:
- return 0;
- }
- }
-
- private void saveBitmapToFile(Bitmap bitmap, File file) {
- try {
- OutputStream os = new FileOutputStream(file);
- bitmap.compress(Bitmap.CompressFormat.PNG, 100, os);
- os.flush();
- os.close();
- } catch (IOException e) {
- Log.e(TAG, "Cannot create temp file", e);
- }
- }
-
- static class AvatarUiImpl implements AvatarUi {
- private final AvatarPickerActivity mActivity;
-
- AvatarUiImpl(AvatarPickerActivity activity) {
- mActivity = activity;
- }
-
- @Override
- public boolean isFinishing() {
- return mActivity.isFinishing() || mActivity.isDestroyed();
- }
-
- @Override
- public void returnUriResult(Uri uri) {
- mActivity.returnUriResult(uri);
- }
-
- @Override
- public void startActivityForResult(Intent intent, int resultCode) {
- mActivity.startActivityForResult(intent, resultCode);
- }
-
- @Override
- public boolean startSystemActivityForResult(Intent intent, int code) {
- List<ResolveInfo> resolveInfos = mActivity.getPackageManager()
- .queryIntentActivities(intent, PackageManager.MATCH_SYSTEM_ONLY);
- if (resolveInfos.isEmpty()) {
- Log.w(TAG, "No system package activity could be found for code " + code);
- return false;
- }
- intent.setPackage(resolveInfos.get(0).activityInfo.packageName);
- mActivity.startActivityForResult(intent, code);
- return true;
- }
-
- @Override
- public int getPhotoSize() {
- return mActivity.getResources()
- .getDimensionPixelSize(com.android.internal.R.dimen.user_icon_size);
- }
- }
-
- static class ContextInjectorImpl implements ContextInjector {
- private final Context mContext;
- private final String mFileAuthority;
-
- ContextInjectorImpl(Context context, String fileAuthority) {
- mContext = context;
- mFileAuthority = fileAuthority;
- }
-
- @Override
- public File getCacheDir() {
- return mContext.getCacheDir();
- }
-
- @Override
- public Uri createTempImageUri(File parentDir, String fileName, boolean purge) {
- final File fullPath = new File(parentDir, fileName);
- if (purge) {
- fullPath.delete();
- }
- return FileProvider.getUriForFile(mContext, mFileAuthority, fullPath);
- }
-
- @Override
- public ContentResolver getContentResolver() {
- return mContext.getContentResolver();
- }
-
- @Override
- public Context getContext() {
- return mContext;
- }
- }
-}
diff --git a/packages/SettingsLib/AvatarPicker/src/AvatarPickerActivity.java b/packages/SettingsLib/AvatarPicker/src/AvatarPickerActivity.java
deleted file mode 100644
index de101b1..0000000
--- a/packages/SettingsLib/AvatarPicker/src/AvatarPickerActivity.java
+++ /dev/null
@@ -1,391 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settingslib.avatarpicker;
-
-import android.app.Activity;
-import android.content.ContentResolver;
-import android.content.Intent;
-import android.content.res.TypedArray;
-import android.graphics.Bitmap;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.net.Uri;
-import android.os.Bundle;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ImageView;
-
-import androidx.annotation.NonNull;
-import androidx.core.graphics.drawable.RoundedBitmapDrawable;
-import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory;
-import androidx.recyclerview.widget.GridLayoutManager;
-import androidx.recyclerview.widget.RecyclerView;
-
-import com.android.internal.util.UserIcons;
-
-import com.google.android.setupcompat.template.FooterBarMixin;
-import com.google.android.setupcompat.template.FooterButton;
-import com.google.android.setupdesign.GlifLayout;
-import com.google.android.setupdesign.util.ThemeHelper;
-import com.google.android.setupdesign.util.ThemeResolver;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-/**
- * Activity to allow the user to choose a user profile picture.
- *
- * <p>Options are provided to take a photo or choose a photo using the photo picker. In addition,
- * preselected avatar images may be provided in the resource array {@code avatar_images}. If
- * provided, every element of that array must be a bitmap drawable.
- *
- * <p>If preselected images are not provided, the default avatar will be shown instead, in a range
- * of colors.
- *
- * <p>This activity should be started with startActivityForResult. If a photo or a preselected image
- * is selected, a Uri will be returned in the data field of the result intent. If a colored default
- * avatar is selected, the chosen color will be returned as {@code EXTRA_DEFAULT_ICON_TINT_COLOR}
- * and the data field will be empty.
- */
-public class AvatarPickerActivity extends Activity {
-
- static final String EXTRA_FILE_AUTHORITY = "file_authority";
- static final String EXTRA_DEFAULT_ICON_TINT_COLOR = "default_icon_tint_color";
-
- private static final String KEY_AWAITING_RESULT = "awaiting_result";
- private static final String KEY_SELECTED_POSITION = "selected_position";
-
- private boolean mWaitingForActivityResult;
-
- private FooterButton mDoneButton;
- private AvatarAdapter mAdapter;
-
- private AvatarPhotoController mAvatarPhotoController;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- boolean dayNightEnabled = ThemeHelper.isSetupWizardDayNightEnabled(this);
- ThemeResolver themeResolver =
- new ThemeResolver.Builder(ThemeResolver.getDefault())
- .setDefaultTheme(ThemeHelper.getSuwDefaultTheme(this))
- .setUseDayNight(true)
- .build();
- int themeResId = themeResolver.resolve("", /* suppressDayNight= */ !dayNightEnabled);
- setTheme(themeResId);
- ThemeHelper.trySetDynamicColor(this);
- setContentView(R.layout.avatar_picker);
- setUpButtons();
-
- RecyclerView recyclerView = findViewById(R.id.avatar_grid);
- mAdapter = new AvatarAdapter();
- recyclerView.setAdapter(mAdapter);
- recyclerView.setLayoutManager(new GridLayoutManager(this,
- getResources().getInteger(R.integer.avatar_picker_columns)));
-
- restoreState(savedInstanceState);
-
- mAvatarPhotoController = new AvatarPhotoController(
- new AvatarPhotoController.AvatarUiImpl(this),
- new AvatarPhotoController.ContextInjectorImpl(this, getFileAuthority()),
- mWaitingForActivityResult);
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- mAdapter.onAdapterResume();
- }
-
- private void setUpButtons() {
- GlifLayout glifLayout = findViewById(R.id.glif_layout);
- FooterBarMixin mixin = glifLayout.getMixin(FooterBarMixin.class);
-
- FooterButton secondaryButton =
- new FooterButton.Builder(this)
- .setText(getString(android.R.string.cancel))
- .setListener(view -> cancel())
- .build();
-
- mDoneButton =
- new FooterButton.Builder(this)
- .setText(getString(R.string.done))
- .setListener(view -> mAdapter.returnSelectionResult())
- .build();
- mDoneButton.setEnabled(false);
-
- mixin.setSecondaryButton(secondaryButton);
- mixin.setPrimaryButton(mDoneButton);
- }
-
- private String getFileAuthority() {
- String authority = getIntent().getStringExtra(EXTRA_FILE_AUTHORITY);
- if (authority == null) {
- Log.e(this.getClass().getName(), "File authority must be provided");
- finish();
- }
- return authority;
- }
-
- @Override
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- mWaitingForActivityResult = false;
- mAvatarPhotoController.onActivityResult(requestCode, resultCode, data);
- }
-
- @Override
- protected void onSaveInstanceState(@NonNull Bundle outState) {
- outState.putBoolean(KEY_AWAITING_RESULT, mWaitingForActivityResult);
- outState.putInt(KEY_SELECTED_POSITION, mAdapter.mSelectedPosition);
- super.onSaveInstanceState(outState);
- }
-
- private void restoreState(Bundle savedInstanceState) {
- if (savedInstanceState != null) {
- mWaitingForActivityResult = savedInstanceState.getBoolean(KEY_AWAITING_RESULT, false);
- mAdapter.mSelectedPosition =
- savedInstanceState.getInt(KEY_SELECTED_POSITION, AvatarAdapter.NONE);
- mDoneButton.setEnabled(mAdapter.mSelectedPosition != AvatarAdapter.NONE);
- }
- }
-
- @Override
- public void startActivityForResult(Intent intent, int requestCode) {
- mWaitingForActivityResult = true;
- super.startActivityForResult(intent, requestCode);
- }
-
- void returnUriResult(Uri uri) {
- Intent resultData = new Intent();
- resultData.setData(uri);
- setResult(RESULT_OK, resultData);
- finish();
- }
-
- void returnColorResult(int color) {
- Intent resultData = new Intent();
- resultData.putExtra(EXTRA_DEFAULT_ICON_TINT_COLOR, color);
- setResult(RESULT_OK, resultData);
- finish();
- }
-
- private void cancel() {
- setResult(RESULT_CANCELED);
- finish();
- }
-
- private class AvatarAdapter extends RecyclerView.Adapter<AvatarViewHolder> {
-
- private static final int NONE = -1;
-
- private final int mTakePhotoPosition;
- private final int mChoosePhotoPosition;
- private final int mPreselectedImageStartPosition;
-
- private final List<Drawable> mImageDrawables;
- private final List<String> mImageDescriptions;
- private final TypedArray mPreselectedImages;
- private final int[] mUserIconColors;
- private int mSelectedPosition = NONE;
-
- private int mLastSelectedPosition = NONE;
-
- AvatarAdapter() {
- final boolean canTakePhoto =
- PhotoCapabilityUtils.canTakePhoto(AvatarPickerActivity.this);
- final boolean canChoosePhoto =
- PhotoCapabilityUtils.canChoosePhoto(AvatarPickerActivity.this);
- mTakePhotoPosition = (canTakePhoto ? 0 : NONE);
- mChoosePhotoPosition = (canChoosePhoto ? (canTakePhoto ? 1 : 0) : NONE);
- mPreselectedImageStartPosition = (canTakePhoto ? 1 : 0) + (canChoosePhoto ? 1 : 0);
-
- mPreselectedImages = getResources().obtainTypedArray(R.array.avatar_images);
- mUserIconColors = UserIcons.getUserIconColors(getResources());
- mImageDrawables = buildDrawableList();
- mImageDescriptions = buildDescriptionsList();
- }
-
- @NonNull
- @Override
- public AvatarViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int position) {
- LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
- View itemView = layoutInflater.inflate(R.layout.avatar_item, parent, false);
- return new AvatarViewHolder(itemView);
- }
-
- @Override
- public void onBindViewHolder(@NonNull AvatarViewHolder viewHolder, int position) {
- if (position == mTakePhotoPosition) {
- viewHolder.setDrawable(getDrawable(R.drawable.avatar_take_photo_circled));
- viewHolder.setContentDescription(getString(R.string.user_image_take_photo));
-
- } else if (position == mChoosePhotoPosition) {
- viewHolder.setDrawable(getDrawable(R.drawable.avatar_choose_photo_circled));
- viewHolder.setContentDescription(getString(R.string.user_image_choose_photo));
-
- } else if (position >= mPreselectedImageStartPosition) {
- int index = indexFromPosition(position);
- viewHolder.setSelected(position == mSelectedPosition);
- viewHolder.setDrawable(mImageDrawables.get(index));
- if (mImageDescriptions != null && index < mImageDescriptions.size()) {
- viewHolder.setContentDescription(mImageDescriptions.get(index));
- } else {
- viewHolder.setContentDescription(getString(
- R.string.default_user_icon_description));
- }
- }
- viewHolder.setClickListener(view -> onViewHolderSelected(position));
- }
-
- private void onViewHolderSelected(int position) {
- if ((mTakePhotoPosition == position) && (mLastSelectedPosition != position)) {
- mAvatarPhotoController.takePhoto();
- } else if ((mChoosePhotoPosition == position) && (mLastSelectedPosition != position)) {
- mAvatarPhotoController.choosePhoto();
- } else {
- if (mSelectedPosition == position) {
- deselect(position);
- } else {
- select(position);
- }
- }
- mLastSelectedPosition = position;
- }
-
- public void onAdapterResume() {
- mLastSelectedPosition = NONE;
- }
-
- @Override
- public int getItemCount() {
- return mPreselectedImageStartPosition + mImageDrawables.size();
- }
-
- private List<Drawable> buildDrawableList() {
- List<Drawable> result = new ArrayList<>();
-
- for (int i = 0; i < mPreselectedImages.length(); i++) {
- Drawable drawable = mPreselectedImages.getDrawable(i);
- if (drawable instanceof BitmapDrawable) {
- result.add(circularDrawableFrom((BitmapDrawable) drawable));
- } else {
- throw new IllegalStateException("Avatar drawables must be bitmaps");
- }
- }
- if (!result.isEmpty()) {
- return result;
- }
-
- // No preselected images. Use tinted default icon.
- for (int i = 0; i < mUserIconColors.length; i++) {
- result.add(UserIcons.getDefaultUserIconInColor(getResources(), mUserIconColors[i]));
- }
- return result;
- }
-
- private List<String> buildDescriptionsList() {
- if (mPreselectedImages.length() > 0) {
- return Arrays.asList(
- getResources().getStringArray(R.array.avatar_image_descriptions));
- }
-
- return null;
- }
-
- private Drawable circularDrawableFrom(BitmapDrawable drawable) {
- Bitmap bitmap = drawable.getBitmap();
-
- RoundedBitmapDrawable roundedBitmapDrawable =
- RoundedBitmapDrawableFactory.create(getResources(), bitmap);
- roundedBitmapDrawable.setCircular(true);
-
- return roundedBitmapDrawable;
- }
-
- private int indexFromPosition(int position) {
- return position - mPreselectedImageStartPosition;
- }
-
- private void select(int position) {
- final int oldSelection = mSelectedPosition;
- mSelectedPosition = position;
- notifyItemChanged(position);
- if (oldSelection != NONE) {
- notifyItemChanged(oldSelection);
- } else {
- mDoneButton.setEnabled(true);
- }
- }
-
- private void deselect(int position) {
- mSelectedPosition = NONE;
- notifyItemChanged(position);
- mDoneButton.setEnabled(false);
- }
-
- private void returnSelectionResult() {
- int index = indexFromPosition(mSelectedPosition);
- if (mPreselectedImages.length() > 0) {
- int resourceId = mPreselectedImages.getResourceId(index, -1);
- if (resourceId == -1) {
- throw new IllegalStateException("Preselected avatar images must be resources.");
- }
- returnUriResult(uriForResourceId(resourceId));
- } else {
- returnColorResult(
- mUserIconColors[index]);
- }
- }
-
- private Uri uriForResourceId(int resourceId) {
- return new Uri.Builder()
- .scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
- .authority(getResources().getResourcePackageName(resourceId))
- .appendPath(getResources().getResourceTypeName(resourceId))
- .appendPath(getResources().getResourceEntryName(resourceId))
- .build();
- }
- }
-
- private static class AvatarViewHolder extends RecyclerView.ViewHolder {
- private final ImageView mImageView;
-
- AvatarViewHolder(View view) {
- super(view);
- mImageView = view.findViewById(R.id.avatar_image);
- }
-
- public void setDrawable(Drawable drawable) {
- mImageView.setImageDrawable(drawable);
- }
-
- public void setContentDescription(String desc) {
- mImageView.setContentDescription(desc);
- }
-
- public void setClickListener(View.OnClickListener listener) {
- mImageView.setOnClickListener(listener);
- }
-
- public void setSelected(boolean selected) {
- mImageView.setSelected(selected);
- }
- }
-}
diff --git a/packages/SettingsLib/AvatarPicker/src/PhotoCapabilityUtils.java b/packages/SettingsLib/AvatarPicker/src/PhotoCapabilityUtils.java
deleted file mode 100644
index 43cb0f5..0000000
--- a/packages/SettingsLib/AvatarPicker/src/PhotoCapabilityUtils.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settingslib.avatarpicker;
-
-import android.app.KeyguardManager;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.provider.MediaStore;
-
-/**
- * Utility class that contains helper methods to determine if the current user has permission and
- * the device is in a proper state to start an activity for a given action.
- */
-public class PhotoCapabilityUtils {
-
- /**
- * Check if the current user can perform any activity for
- * android.media.action.IMAGE_CAPTURE action.
- */
- public static boolean canTakePhoto(Context context) {
- return context.getPackageManager().queryIntentActivities(
- new Intent(MediaStore.ACTION_IMAGE_CAPTURE),
- PackageManager.MATCH_DEFAULT_ONLY).size() > 0;
- }
-
- /**
- * Check if the current user can perform any activity for
- * ACTION_PICK_IMAGES action for images.
- * Returns false if the device is currently locked and
- * requires a PIN, pattern or password to unlock.
- */
- public static boolean canChoosePhoto(Context context) {
- Intent intent = new Intent(MediaStore.ACTION_PICK_IMAGES);
- intent.setType("image/*");
- boolean canPerformActivityForGetImage =
- context.getPackageManager().queryIntentActivities(intent, 0).size() > 0;
- // on locked device we can't access the images
- return canPerformActivityForGetImage && !isDeviceLocked(context);
- }
-
- /**
- * Check if the current user can perform any activity for
- * com.android.camera.action.CROP action for images.
- * Returns false if the device is currently locked and
- * requires a PIN, pattern or password to unlock.
- */
- public static boolean canCropPhoto(Context context) {
- Intent intent = new Intent("com.android.camera.action.CROP");
- intent.setType("image/*");
- boolean canPerformActivityForCropping =
- context.getPackageManager().queryIntentActivities(intent, 0).size() > 0;
- // on locked device we can't start a cropping activity
- return canPerformActivityForCropping && !isDeviceLocked(context);
- }
-
- private static boolean isDeviceLocked(Context context) {
- KeyguardManager keyguardManager = context.getSystemService(KeyguardManager.class);
- return keyguardManager == null || keyguardManager.isDeviceLocked();
- }
-
-}
diff --git a/packages/SettingsLib/AvatarPicker/src/ThreadUtils.java b/packages/SettingsLib/AvatarPicker/src/ThreadUtils.java
deleted file mode 100644
index dc19e66..0000000
--- a/packages/SettingsLib/AvatarPicker/src/ThreadUtils.java
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settingslib.avatarpicker;
-
-import android.os.Handler;
-import android.os.Looper;
-
-import androidx.annotation.NonNull;
-
-import com.google.common.util.concurrent.ListenableFuture;
-import com.google.common.util.concurrent.ListeningExecutorService;
-import com.google.common.util.concurrent.MoreExecutors;
-
-import java.util.concurrent.Callable;
-import java.util.concurrent.Executors;
-
-// copied from SettinsLib/utils
-public class ThreadUtils {
-
- private static volatile Thread sMainThread;
- private static volatile Handler sMainThreadHandler;
- private static volatile ListeningExecutorService sListeningService;
-
- /**
- * Returns true if the current thread is the UI thread.
- */
- public static boolean isMainThread() {
- if (sMainThread == null) {
- sMainThread = Looper.getMainLooper().getThread();
- }
- return Thread.currentThread() == sMainThread;
- }
-
- /**
- * Returns a shared UI thread handler.
- */
- @NonNull
- public static Handler getUiThreadHandler() {
- if (sMainThreadHandler == null) {
- sMainThreadHandler = new Handler(Looper.getMainLooper());
- }
-
- return sMainThreadHandler;
- }
-
- /**
- * Checks that the current thread is the UI thread. Otherwise throws an exception.
- */
- public static void ensureMainThread() {
- if (!isMainThread()) {
- throw new RuntimeException("Must be called on the UI thread");
- }
- }
-
- /**
- * Posts runnable in background using shared background thread pool.
- *
- * @return A future of the task that can be monitored for updates or cancelled.
- */
- @SuppressWarnings("rawtypes")
- @NonNull
- public static ListenableFuture postOnBackgroundThread(@NonNull Runnable runnable) {
- return getBackgroundExecutor().submit(runnable);
- }
-
- /**
- * Posts callable in background using shared background thread pool.
- *
- * @return A future of the task that can be monitored for updates or cancelled.
- */
- @NonNull
- public static <T> ListenableFuture<T> postOnBackgroundThread(@NonNull Callable<T> callable) {
- return getBackgroundExecutor().submit(callable);
- }
-
- /**
- * Posts the runnable on the main thread.
- *
- * @deprecated moving work to the main thread should be done via the main executor provided to
- * {@link com.google.common.util.concurrent.FutureCallback} via
- * {@link android.content.Context#getMainExecutor()} or by calling an SDK method such as
- * {@link android.app.Activity#runOnUiThread(Runnable)} or
- * {@link android.content.Context#getMainThreadHandler()} where appropriate.
- */
- @Deprecated
- public static void postOnMainThread(@NonNull Runnable runnable) {
- getUiThreadHandler().post(runnable);
- }
-
- /**
- * Provides a shared {@link ListeningExecutorService} created using a fixed thread pool executor
- */
- @NonNull
- public static synchronized ListeningExecutorService getBackgroundExecutor() {
- if (sListeningService == null) {
- sListeningService = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(
- Runtime.getRuntime().availableProcessors()));
- }
- return sListeningService;
- }
-}
diff --git a/packages/SettingsLib/ButtonPreference/res/layout-v36/settingslib_expressive_button_filled.xml b/packages/SettingsLib/ButtonPreference/res/layout/settingslib_expressive_button_filled.xml
similarity index 100%
rename from packages/SettingsLib/ButtonPreference/res/layout-v36/settingslib_expressive_button_filled.xml
rename to packages/SettingsLib/ButtonPreference/res/layout/settingslib_expressive_button_filled.xml
diff --git a/packages/SettingsLib/ButtonPreference/res/layout-v36/settingslib_expressive_button_filled_extra.xml b/packages/SettingsLib/ButtonPreference/res/layout/settingslib_expressive_button_filled_extra.xml
similarity index 100%
rename from packages/SettingsLib/ButtonPreference/res/layout-v36/settingslib_expressive_button_filled_extra.xml
rename to packages/SettingsLib/ButtonPreference/res/layout/settingslib_expressive_button_filled_extra.xml
diff --git a/packages/SettingsLib/ButtonPreference/res/layout-v36/settingslib_expressive_button_filled_large.xml b/packages/SettingsLib/ButtonPreference/res/layout/settingslib_expressive_button_filled_large.xml
similarity index 100%
rename from packages/SettingsLib/ButtonPreference/res/layout-v36/settingslib_expressive_button_filled_large.xml
rename to packages/SettingsLib/ButtonPreference/res/layout/settingslib_expressive_button_filled_large.xml
diff --git a/packages/SettingsLib/ButtonPreference/res/layout-v36/settingslib_expressive_button_outline.xml b/packages/SettingsLib/ButtonPreference/res/layout/settingslib_expressive_button_outline.xml
similarity index 100%
rename from packages/SettingsLib/ButtonPreference/res/layout-v36/settingslib_expressive_button_outline.xml
rename to packages/SettingsLib/ButtonPreference/res/layout/settingslib_expressive_button_outline.xml
diff --git a/packages/SettingsLib/ButtonPreference/res/layout-v36/settingslib_expressive_button_outline_extra.xml b/packages/SettingsLib/ButtonPreference/res/layout/settingslib_expressive_button_outline_extra.xml
similarity index 100%
rename from packages/SettingsLib/ButtonPreference/res/layout-v36/settingslib_expressive_button_outline_extra.xml
rename to packages/SettingsLib/ButtonPreference/res/layout/settingslib_expressive_button_outline_extra.xml
diff --git a/packages/SettingsLib/ButtonPreference/res/layout-v36/settingslib_expressive_button_outline_large.xml b/packages/SettingsLib/ButtonPreference/res/layout/settingslib_expressive_button_outline_large.xml
similarity index 100%
rename from packages/SettingsLib/ButtonPreference/res/layout-v36/settingslib_expressive_button_outline_large.xml
rename to packages/SettingsLib/ButtonPreference/res/layout/settingslib_expressive_button_outline_large.xml
diff --git a/packages/SettingsLib/ButtonPreference/res/layout-v36/settingslib_expressive_button_tonal.xml b/packages/SettingsLib/ButtonPreference/res/layout/settingslib_expressive_button_tonal.xml
similarity index 100%
rename from packages/SettingsLib/ButtonPreference/res/layout-v36/settingslib_expressive_button_tonal.xml
rename to packages/SettingsLib/ButtonPreference/res/layout/settingslib_expressive_button_tonal.xml
diff --git a/packages/SettingsLib/ButtonPreference/res/layout-v36/settingslib_expressive_button_tonal_extra.xml b/packages/SettingsLib/ButtonPreference/res/layout/settingslib_expressive_button_tonal_extra.xml
similarity index 100%
rename from packages/SettingsLib/ButtonPreference/res/layout-v36/settingslib_expressive_button_tonal_extra.xml
rename to packages/SettingsLib/ButtonPreference/res/layout/settingslib_expressive_button_tonal_extra.xml
diff --git a/packages/SettingsLib/ButtonPreference/res/layout-v36/settingslib_expressive_button_tonal_large.xml b/packages/SettingsLib/ButtonPreference/res/layout/settingslib_expressive_button_tonal_large.xml
similarity index 100%
rename from packages/SettingsLib/ButtonPreference/res/layout-v36/settingslib_expressive_button_tonal_large.xml
rename to packages/SettingsLib/ButtonPreference/res/layout/settingslib_expressive_button_tonal_large.xml
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarAppCompatActivity.java b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarAppCompatActivity.java
index c3efeec..34e3477 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarAppCompatActivity.java
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarAppCompatActivity.java
@@ -32,6 +32,7 @@
import com.android.settingslib.collapsingtoolbar.widget.ScrollableToolbarItemLayout;
import com.android.settingslib.widget.SettingsThemeHelper;
+import com.android.settingslib.widget.SetupWizardHelper;
import com.google.android.material.appbar.AppBarLayout;
import com.google.android.material.appbar.CollapsingToolbarLayout;
@@ -90,6 +91,10 @@
View view = getToolbarDelegate().onCreateView(getLayoutInflater(), null, this);
super.setContentView(view);
+
+ if (SetupWizardHelper.isAnySetupWizard(getIntent())) {
+ findViewById(R.id.content_parent).setFitsSystemWindows(false);
+ }
}
@Override
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java
index fcd537f..31c6f0b 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java
@@ -33,6 +33,7 @@
import com.android.settingslib.collapsingtoolbar.widget.ScrollableToolbarItemLayout;
import com.android.settingslib.widget.SettingsThemeHelper;
+import com.android.settingslib.widget.SetupWizardHelper;
import com.google.android.material.appbar.AppBarLayout;
import com.google.android.material.appbar.CollapsingToolbarLayout;
@@ -82,6 +83,10 @@
View view = getToolbarDelegate().onCreateView(getLayoutInflater(), null, this);
super.setContentView(view);
+
+ if (SetupWizardHelper.isAnySetupWizard(getIntent())) {
+ findViewById(R.id.content_parent).setFitsSystemWindows(false);
+ }
}
@Override
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/EdgeToEdgeUtils.java b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/EdgeToEdgeUtils.java
index 42ffa67..c9dc064 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/EdgeToEdgeUtils.java
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/EdgeToEdgeUtils.java
@@ -26,6 +26,8 @@
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
+import com.android.settingslib.widget.SetupWizardHelper;
+
/**
* Util class for edge to edge.
*/
@@ -42,6 +44,10 @@
return;
}
+ if (SetupWizardHelper.isAnySetupWizard(activity.getIntent())) {
+ return;
+ }
+
EdgeToEdge.enable(activity);
ViewCompat.setOnApplyWindowInsetsListener(activity.findViewById(android.R.id.content),
diff --git a/packages/SettingsLib/DeviceStateRotationLock/src/com/android/settingslib/devicestate/DeviceStateAutoRotateSettingManagerImpl.java b/packages/SettingsLib/DeviceStateRotationLock/src/com/android/settingslib/devicestate/DeviceStateAutoRotateSettingManagerImpl.java
index e49f73f..07f8fd6 100644
--- a/packages/SettingsLib/DeviceStateRotationLock/src/com/android/settingslib/devicestate/DeviceStateAutoRotateSettingManagerImpl.java
+++ b/packages/SettingsLib/DeviceStateRotationLock/src/com/android/settingslib/devicestate/DeviceStateAutoRotateSettingManagerImpl.java
@@ -125,27 +125,25 @@
DEVICE_STATE_ROTATION_LOCK,
UserHandle.USER_CURRENT);
if (serializedSetting == null || serializedSetting.isEmpty()) return null;
- try {
- final String[] deserializedSettings = serializedSetting.split(SEPARATOR_REGEX);
- if (deserializedSettings.length % 2 != 0) {
- throw new IllegalStateException("Odd number of elements in the list");
- }
- final SparseIntArray deviceStateAutoRotateSetting = new SparseIntArray();
- for (int i = 0; i < deserializedSettings.length; i += 2) {
- final int key = Integer.parseInt(deserializedSettings[i]);
- final int value = Integer.parseInt(deserializedSettings[i + 1]);
- if (value < 0 || value > 2) {
- throw new IllegalStateException(
- "Invalid value in pair: key=" + deserializedSettings[i] + ", value="
- + deserializedSettings[i + 1]);
- }
- deviceStateAutoRotateSetting.put(key, value);
- }
- return deviceStateAutoRotateSetting;
- } catch (Exception e) {
- Log.w(TAG, "Invalid format in serializedSetting=" + serializedSetting, e);
+ final String[] deserializedSettings = serializedSetting.split(SEPARATOR_REGEX);
+ if (deserializedSettings.length % 2 != 0) {
+ Log.e(TAG, "Invalid format in serializedSetting=" + serializedSetting
+ + "\nOdd number of elements in the list");
return null;
}
+ final SparseIntArray deviceStateAutoRotateSetting = new SparseIntArray();
+ for (int i = 0; i < deserializedSettings.length; i += 2) {
+ final int key = Integer.parseInt(deserializedSettings[i]);
+ final int value = Integer.parseInt(deserializedSettings[i + 1]);
+ if (value < 0 || value > 2) {
+ Log.e(TAG, "Invalid format in serializedSetting=" + serializedSetting
+ + "\nInvalid value in pair: key=" + deserializedSettings[i] + ", value="
+ + deserializedSettings[i + 1]);
+ return null;
+ }
+ deviceStateAutoRotateSetting.put(key, value);
+ }
+ return deviceStateAutoRotateSetting;
}
@Override
@@ -213,7 +211,6 @@
R.array.config_perDeviceStateRotationLockDefaults);
for (String entry : perDeviceStateAutoRotateDefaults) {
final PostureEntry parsedEntry = parsePostureEntry(entry);
- if (parsedEntry == null) return;
final int posture = parsedEntry.posture;
final int autoRotateValue = parsedEntry.autoRotateValue;
@@ -221,28 +218,28 @@
final Integer deviceState = mPostureDeviceStateConverter.postureToDeviceState(posture);
if (deviceState == null) {
- Log.wtf(TAG, "No matching device state for posture: " + posture);
- } else {
- mSettableDeviceState.add(new SettableDeviceState(deviceState,
- autoRotateValue != DEVICE_STATE_ROTATION_LOCK_IGNORED)
- );
+ throw new IllegalStateException("No matching device state for posture: " + posture);
}
+ mSettableDeviceState.add(new SettableDeviceState(deviceState,
+ autoRotateValue != DEVICE_STATE_ROTATION_LOCK_IGNORED));
if (autoRotateValue == DEVICE_STATE_ROTATION_LOCK_IGNORED
&& fallbackPosture != null) {
mFallbackPostureMap.put(posture, fallbackPosture);
} else if (autoRotateValue == DEVICE_STATE_ROTATION_LOCK_IGNORED) {
- Log.w(TAG, "Auto rotate setting is IGNORED, but no fallback-posture defined");
+ throw new IllegalStateException(
+ "Auto rotate setting is IGNORED for posture=" + posture
+ + ", but no fallback-posture defined");
}
mDefaultDeviceStateAutoRotateSetting.put(posture, autoRotateValue);
}
}
+ @NonNull
private PostureEntry parsePostureEntry(String entry) {
final String[] values = entry.split(SEPARATOR_REGEX);
if (values.length < 2 || values.length > 3) { // It should contain 2 or 3 values.
- Log.wtf(TAG, "Invalid number of values in entry: " + entry);
- return null;
+ throw new IllegalStateException("Invalid number of values in entry: " + entry);
}
try {
final int posture = Integer.parseInt(values[0]);
@@ -253,12 +250,11 @@
return new PostureEntry(posture, autoRotateValue, fallbackPosture);
} catch (NumberFormatException e) {
- Log.wtf(TAG, "Invalid number format in '" + entry + "'", e);
- return null;
+ throw new IllegalStateException(
+ "Invalid number format in '" + entry + "'" + e.getMessage());
}
}
-
private Integer extractSettingForDevicePosture(
int devicePosture,
SparseIntArray deviceStateAutoRotateSetting
diff --git a/packages/SettingsLib/FooterPreference/res/layout-v31/preference_footer.xml b/packages/SettingsLib/FooterPreference/res/layout-v31/preference_footer.xml
index 470a83d..4120732 100644
--- a/packages/SettingsLib/FooterPreference/res/layout-v31/preference_footer.xml
+++ b/packages/SettingsLib/FooterPreference/res/layout-v31/preference_footer.xml
@@ -24,7 +24,9 @@
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
android:background="?android:attr/selectableItemBackground"
android:orientation="vertical"
- android:clipToPadding="false">
+ android:clipToPadding="false"
+ android:layoutDirection="locale"
+ android:textDirection="locale">
<LinearLayout
android:id="@+id/icon_frame"
diff --git a/packages/SettingsLib/FooterPreference/res/layout-v33/preference_footer.xml b/packages/SettingsLib/FooterPreference/res/layout-v33/preference_footer.xml
index 4b5fd44..5afa0c2 100644
--- a/packages/SettingsLib/FooterPreference/res/layout-v33/preference_footer.xml
+++ b/packages/SettingsLib/FooterPreference/res/layout-v33/preference_footer.xml
@@ -24,7 +24,9 @@
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
android:background="?android:attr/selectableItemBackground"
android:orientation="vertical"
- android:clipToPadding="false">
+ android:clipToPadding="false"
+ android:layoutDirection="locale"
+ android:textDirection="locale">
<LinearLayout
android:id="@+id/icon_frame"
diff --git a/packages/SettingsLib/FooterPreference/res/layout/preference_footer.xml b/packages/SettingsLib/FooterPreference/res/layout/preference_footer.xml
index 1adcead..72be285 100644
--- a/packages/SettingsLib/FooterPreference/res/layout/preference_footer.xml
+++ b/packages/SettingsLib/FooterPreference/res/layout/preference_footer.xml
@@ -23,7 +23,9 @@
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
android:background="?android:attr/selectableItemBackground"
- android:clipToPadding="false">
+ android:clipToPadding="false"
+ android:layoutDirection="locale"
+ android:textDirection="locale">
<LinearLayout
android:id="@+id/icon_frame"
diff --git a/packages/SettingsLib/FooterPreference/res/values-my/strings.xml b/packages/SettingsLib/FooterPreference/res/values-my/strings.xml
index 751a87a..deb658d 100644
--- a/packages/SettingsLib/FooterPreference/res/values-my/strings.xml
+++ b/packages/SettingsLib/FooterPreference/res/values-my/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="settingslib_learn_more_text" msgid="7385478101223578464">"ပိုမိုလေ့လာရန်"</string>
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"ပိုလေ့လာရန်"</string>
</resources>
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 37f4754..65e0169 100644
--- a/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java
+++ b/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java
@@ -43,7 +43,8 @@
* A custom preference acting as "footer" of a page. It has a field for icon and text. It is added
* to screen as the last preference.
*/
-public class FooterPreference extends Preference implements GroupSectionDividerMixin {
+public class FooterPreference extends Preference
+ implements GroupSectionDividerMixin, OnScreenWidgetMixin {
private static final String TAG = "FooterPreference";
public static final String KEY_FOOTER = "footer_preference";
diff --git a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/GetPreferenceGraphApiHandler.kt b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/GetPreferenceGraphApiHandler.kt
index 2b535e3..da5bf69 100644
--- a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/GetPreferenceGraphApiHandler.kt
+++ b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/GetPreferenceGraphApiHandler.kt
@@ -38,11 +38,9 @@
private val preferenceScreenProviders: Set<Class<out PreferenceScreenProvider>> = emptySet(),
) : ApiHandler<GetPreferenceGraphRequest, PreferenceGraphProto> {
- override val requestCodec: MessageCodec<GetPreferenceGraphRequest>
- get() = GetPreferenceGraphRequestCodec
+ override val requestCodec = GetPreferenceGraphRequestCodec()
- override val responseCodec: MessageCodec<PreferenceGraphProto>
- get() = PreferenceGraphProtoCodec
+ override val responseCodec = PreferenceGraphProtoCodec()
override fun hasPermission(
application: Application,
@@ -99,7 +97,7 @@
val flags: Int = PreferenceGetterFlags.ALL,
)
-object GetPreferenceGraphRequestCodec : MessageCodec<GetPreferenceGraphRequest> {
+class GetPreferenceGraphRequestCodec : MessageCodec<GetPreferenceGraphRequest> {
override fun encode(data: GetPreferenceGraphRequest): Bundle =
Bundle(4).apply {
putParcelableArray(KEY_SCREENS, data.screens.toTypedArray())
@@ -126,18 +124,22 @@
)
}
- private const val KEY_SCREENS = "s"
- private const val KEY_VISITED_SCREENS = "v"
- private const val KEY_LOCALE = "l"
- private const val KEY_FLAGS = "f"
+ companion object {
+ private const val KEY_SCREENS = "s"
+ private const val KEY_VISITED_SCREENS = "v"
+ private const val KEY_LOCALE = "l"
+ private const val KEY_FLAGS = "f"
+ }
}
-object PreferenceGraphProtoCodec : MessageCodec<PreferenceGraphProto> {
+class PreferenceGraphProtoCodec : MessageCodec<PreferenceGraphProto> {
override fun encode(data: PreferenceGraphProto): Bundle =
Bundle(1).apply { putByteArray(KEY_GRAPH, data.toByteArray()) }
override fun decode(data: Bundle): PreferenceGraphProto =
PreferenceGraphProto.parseFrom(data.getByteArray(KEY_GRAPH)!!)
- private const val KEY_GRAPH = "g"
+ companion object {
+ private const val KEY_GRAPH = "g"
+ }
}
diff --git a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceSetterApi.kt b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceSetterApi.kt
index 72f6934..28a2ef9 100644
--- a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceSetterApi.kt
+++ b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceSetterApi.kt
@@ -88,11 +88,9 @@
class PreferenceSetterApiDescriptor(override val id: Int) :
ApiDescriptor<PreferenceSetterRequest, Int> {
- override val requestCodec: MessageCodec<PreferenceSetterRequest>
- get() = PreferenceSetterRequestCodec
+ override val requestCodec = PreferenceSetterRequestCodec()
- override val responseCodec: MessageCodec<Int>
- get() = IntMessageCodec
+ override val responseCodec = IntMessageCodec()
}
/** Preference setter API implementation. */
@@ -207,11 +205,9 @@
return result
}
- override val requestCodec: MessageCodec<PreferenceSetterRequest>
- get() = PreferenceSetterRequestCodec
+ override val requestCodec = PreferenceSetterRequestCodec()
- override val responseCodec: MessageCodec<Int>
- get() = IntMessageCodec
+ override val responseCodec = IntMessageCodec()
}
/** Evaluates the write permit of a persistent preference. */
@@ -225,7 +221,7 @@
?: getWritePermit(context, value, callingPid, callingUid)
/** Message codec for [PreferenceSetterRequest]. */
-object PreferenceSetterRequestCodec : MessageCodec<PreferenceSetterRequest> {
+class PreferenceSetterRequestCodec : MessageCodec<PreferenceSetterRequest> {
override fun encode(data: PreferenceSetterRequest) =
Bundle(3).apply {
putString(SCREEN_KEY, data.screenKey)
@@ -242,7 +238,9 @@
PreferenceValueProto.parseFrom(data.getByteArray(null)!!),
)
- private const val SCREEN_KEY = "s"
- private const val KEY = "k"
- private const val ARGS = "a"
+ companion object {
+ private const val SCREEN_KEY = "s"
+ private const val KEY = "k"
+ private const val ARGS = "a"
+ }
}
diff --git a/packages/SettingsLib/IntroPreference/res/layout/settingslib_expressive_preference_intro.xml b/packages/SettingsLib/IntroPreference/res/layout/settingslib_expressive_preference_intro.xml
index cacd274..744cda5 100644
--- a/packages/SettingsLib/IntroPreference/res/layout/settingslib_expressive_preference_intro.xml
+++ b/packages/SettingsLib/IntroPreference/res/layout/settingslib_expressive_preference_intro.xml
@@ -29,6 +29,7 @@
<ImageView
android:id="@android:id/icon"
+ android:importantForAccessibility="no"
style="@style/SettingsLibEntityHeaderIcon"/>
<TextView
@@ -41,7 +42,7 @@
android:layout_height="wrap_content"
android:gravity="center"
android:minLines="1"
- app:isCollapsable="true"/>
+ app:isCollapsable="false"/>
</LinearLayout>
diff --git a/packages/SettingsLib/IntroPreference/src/com/android/settingslib/widget/IntroPreference.kt b/packages/SettingsLib/IntroPreference/src/com/android/settingslib/widget/IntroPreference.kt
index 806580b..9b0c529 100644
--- a/packages/SettingsLib/IntroPreference/src/com/android/settingslib/widget/IntroPreference.kt
+++ b/packages/SettingsLib/IntroPreference/src/com/android/settingslib/widget/IntroPreference.kt
@@ -31,9 +31,11 @@
attrs: AttributeSet? = null,
defStyleAttr: Int = 0,
defStyleRes: Int = 0,
-) : Preference(context, attrs, defStyleAttr, defStyleRes), GroupSectionDividerMixin {
+) : Preference(context, attrs, defStyleAttr, defStyleRes),
+ GroupSectionDividerMixin,
+ OnScreenWidgetMixin {
- private var isCollapsable: Boolean = true
+ private var isCollapsable: Boolean = DEFAULT_COLLAPSABLE
private var minLines: Int = DEFAULT_MIN_LINES
private var hyperlinkListener: View.OnClickListener? = null
private var learnMoreListener: View.OnClickListener? = null
@@ -53,13 +55,11 @@
setCollapsable(isCollapsable)
setMinLines(minLines)
visibility = if (summary.isNullOrEmpty()) View.GONE else View.VISIBLE
- setText(summary.toString())
- if (hyperlinkListener != null) {
- setHyperlinkListener(hyperlinkListener)
- }
- if (learnMoreListener != null) {
+ summary?.let { setText(it.toString()) }
+ hyperlinkListener?.let { setHyperlinkListener(it) }
+ learnMoreListener?.let {
setLearnMoreText(learnMoreText)
- setLearnMoreAction(learnMoreListener)
+ setLearnMoreAction(it)
}
}
}
@@ -124,5 +124,6 @@
companion object {
private const val DEFAULT_MAX_LINES = 10
private const val DEFAULT_MIN_LINES = 1
+ private const val DEFAULT_COLLAPSABLE = false
}
}
diff --git a/packages/SettingsLib/Ipc/src/com/android/settingslib/ipc/MessageCodecs.kt b/packages/SettingsLib/Ipc/src/com/android/settingslib/ipc/MessageCodecs.kt
index 4b7572b..6c9475e 100644
--- a/packages/SettingsLib/Ipc/src/com/android/settingslib/ipc/MessageCodecs.kt
+++ b/packages/SettingsLib/Ipc/src/com/android/settingslib/ipc/MessageCodecs.kt
@@ -19,7 +19,7 @@
import android.os.Bundle
/** [MessageCodec] for [Int]. */
-object IntMessageCodec : MessageCodec<Int> {
+class IntMessageCodec : MessageCodec<Int> {
override fun encode(data: Int): Bundle = Bundle(1).apply { putInt(null, data) }
override fun decode(data: Bundle): Int = data.getInt(null)
diff --git a/packages/SettingsLib/Ipc/testutils/com/android/settingslib/ipc/MessengerServiceRule.kt b/packages/SettingsLib/Ipc/testutils/com/android/settingslib/ipc/MessengerServiceRule.kt
index 8b2deaf..ffe3241 100644
--- a/packages/SettingsLib/Ipc/testutils/com/android/settingslib/ipc/MessengerServiceRule.kt
+++ b/packages/SettingsLib/Ipc/testutils/com/android/settingslib/ipc/MessengerServiceRule.kt
@@ -42,6 +42,16 @@
private var serviceController: ServiceController<out Service>? = null
+ init {
+ if (isRobolectric) {
+ // `starting` is invoked by `startingQuietly`, which swallows exception. Hence we must
+ // check thread in constructor instead.
+ check(Thread.currentThread() != Looper.getMainLooper().thread) {
+ "To avoid deadlock, run test with @LooperMode(LooperMode.Mode.INSTRUMENTATION_TEST)"
+ }
+ }
+ }
+
override fun starting(description: Description) {
if (isRobolectric) {
runBlocking { setupRobolectricService() }
@@ -58,11 +68,6 @@
}
private suspend fun setupRobolectricService() {
- if (Thread.currentThread() == Looper.getMainLooper().thread) {
- throw IllegalStateException(
- "To avoid deadlock, run test with @LooperMode(LooperMode.Mode.INSTRUMENTATION_TEST)"
- )
- }
withContext(Dispatchers.Main) {
serviceController = Robolectric.buildService(serviceClass)
val service = serviceController!!.create().get()
diff --git a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchPreference.java b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchPreference.java
index 5170581..9853c66 100644
--- a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchPreference.java
+++ b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchPreference.java
@@ -81,8 +81,10 @@
// To support onPreferenceChange callback, it needs to call callChangeListener() when
// MainSwitchBar is clicked.
mainSwitchBar.setOnClickListener(view -> {
- boolean isChecked = isChecked();
- if (!callChangeListener(isChecked)) {
+ boolean isChecked = !isChecked();
+ if (callChangeListener(isChecked)) {
+ setChecked(isChecked);
+ } else {
// Change checked state back if listener doesn't like it.
// Note that CompoundButton maintains internal state to avoid infinite recursion.
mainSwitchBar.setChecked(!isChecked);
@@ -104,7 +106,6 @@
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
- super.setChecked(isChecked);
for (OnCheckedChangeListener listener : mSwitchChangeListeners) {
listener.onCheckedChanged(buttonView, isChecked);
}
diff --git a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceStateProviders.kt b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceStateProviders.kt
index ca51381..21e4751 100644
--- a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceStateProviders.kt
+++ b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceStateProviders.kt
@@ -23,7 +23,6 @@
import androidx.activity.result.ActivityResultCallback
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContract
-import androidx.lifecycle.LifecycleCoroutineScope
import com.android.settingslib.datastore.KeyValueStore
import kotlinx.coroutines.CoroutineScope
@@ -149,7 +148,7 @@
*
* @see [androidx.lifecycle.lifecycleScope]
*/
- abstract val lifecycleScope: LifecycleCoroutineScope
+ abstract val lifecycleScope: CoroutineScope
/** Returns the preference widget object associated with given key. */
abstract fun <T> findPreference(key: String): T?
diff --git a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceTypes.kt b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceTypes.kt
index edd45d3..4f0a431 100644
--- a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceTypes.kt
+++ b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceTypes.kt
@@ -49,6 +49,12 @@
index in getMinValue(context)..getMaxValue(context)
}
+/** A persistent preference that has a long value. */
+interface LongValuePreference : PersistentPreference<Long> {
+ override val valueType: Class<Long>
+ get() = Long::class.javaObjectType
+}
+
/** A preference that provides a two-state toggleable option. */
open class SwitchPreference
@JvmOverloads
diff --git a/packages/SettingsLib/Preference/Android.bp b/packages/SettingsLib/Preference/Android.bp
index fb06be9..2bb5d18 100644
--- a/packages/SettingsLib/Preference/Android.bp
+++ b/packages/SettingsLib/Preference/Android.bp
@@ -33,6 +33,7 @@
"SettingsLibDataStore",
"SettingsLibMetadata",
"SettingsLibMainSwitchPreference",
+ "SettingsLibSliderPreference",
"androidx.annotation_annotation",
"androidx.preference_preference",
"guava",
diff --git a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBinding.kt b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBinding.kt
index 8896af4..2f640fd 100644
--- a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBinding.kt
+++ b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBinding.kt
@@ -35,6 +35,7 @@
import com.android.settingslib.metadata.getPreferenceScreenTitle
import com.android.settingslib.metadata.getPreferenceSummary
import com.android.settingslib.metadata.getPreferenceTitle
+import com.android.settingslib.widget.SliderPreference
/** Binding of preference widget and preference metadata. */
interface PreferenceBinding {
@@ -124,6 +125,10 @@
preference.min = getMinValue(context)
preference.max = getMaxValue(context)
preference.seekBarIncrement = getIncrementStep(context)
+ } else if (preference is SliderPreference && this is IntRangeValuePreference) {
+ preference.min = getMinValue(context)
+ preference.max = getMaxValue(context)
+ preference.sliderIncrement = getIncrementStep(context)
}
}
}
diff --git a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBindingFactory.kt b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBindingFactory.kt
index 33b614e..9e8e0d3 100644
--- a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBindingFactory.kt
+++ b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBindingFactory.kt
@@ -54,6 +54,8 @@
/** Default [PreferenceBindingFactory]. */
open class DefaultPreferenceBindingFactory : PreferenceBindingFactory {
+ /** Default [PreferenceBinding]. */
+ private val defaultBinding: PreferenceBinding = object : PreferenceBinding {}
override fun getPreferenceBinding(metadata: PreferenceMetadata) =
metadata as? PreferenceBinding
@@ -61,7 +63,7 @@
is SwitchPreference -> SwitchPreferenceBinding.INSTANCE
is PreferenceCategory -> PreferenceCategoryBinding.INSTANCE
is MainSwitchPreference -> MainSwitchPreferenceBinding.INSTANCE
- else -> DefaultPreferenceBinding
+ else -> defaultBinding
}
}
diff --git a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBindings.kt b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBindings.kt
index 71c46fa..dfd5829 100644
--- a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBindings.kt
+++ b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBindings.kt
@@ -71,6 +71,3 @@
@JvmStatic val INSTANCE = object : MainSwitchPreferenceBinding {}
}
}
-
-/** Default [PreferenceBinding] for [Preference]. */
-object DefaultPreferenceBinding : PreferenceBinding
diff --git a/packages/SettingsLib/ProfileSelector/res/values-mk/strings.xml b/packages/SettingsLib/ProfileSelector/res/values-mk/strings.xml
index e8c2bf5..ea0c11b3 100644
--- a/packages/SettingsLib/ProfileSelector/res/values-mk/strings.xml
+++ b/packages/SettingsLib/ProfileSelector/res/values-mk/strings.xml
@@ -17,7 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="settingslib_category_personal" msgid="1142302328104700620">"Лични"</string>
+ <string name="settingslib_category_personal" msgid="1142302328104700620">"Лично"</string>
<string name="settingslib_category_work" msgid="4867750733682444676">"Работа"</string>
<string name="settingslib_category_private" msgid="5039276873477591386">"Приватно"</string>
</resources>
diff --git a/packages/SettingsLib/SearchWidget/res/values-bn/strings.xml b/packages/SettingsLib/SearchWidget/res/values-bn/strings.xml
index 473231a..9f9e60f 100644
--- a/packages/SettingsLib/SearchWidget/res/values-bn/strings.xml
+++ b/packages/SettingsLib/SearchWidget/res/values-bn/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/SearchWidget/res/values-de/strings.xml b/packages/SettingsLib/SearchWidget/res/values-de/strings.xml
index d46c3d8..02d4723 100644
--- a/packages/SettingsLib/SearchWidget/res/values-de/strings.xml
+++ b/packages/SettingsLib/SearchWidget/res/values-de/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">"Einstellungen durchsuchen"</string>
+ <string name="search_menu" msgid="1914043873178389845">"In Einstellungen suchen"</string>
</resources>
diff --git a/packages/SettingsLib/SearchWidget/res/values-fi/strings.xml b/packages/SettingsLib/SearchWidget/res/values-fi/strings.xml
index c38c35b..b9e9eb6 100644
--- a/packages/SettingsLib/SearchWidget/res/values-fi/strings.xml
+++ b/packages/SettingsLib/SearchWidget/res/values-fi/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">"Hakuasetukset"</string>
+ <string name="search_menu" msgid="1914043873178389845">"Hae asetuksista"</string>
</resources>
diff --git a/packages/SettingsLib/SearchWidget/res/values-mr/strings.xml b/packages/SettingsLib/SearchWidget/res/values-mr/strings.xml
index 248e90c..27f9c0da 100644
--- a/packages/SettingsLib/SearchWidget/res/values-mr/strings.xml
+++ b/packages/SettingsLib/SearchWidget/res/values-mr/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/SegmentedButtonPreference/src/com/android/settingslib/widget/SegmentedButtonPreference.kt b/packages/SettingsLib/SegmentedButtonPreference/src/com/android/settingslib/widget/SegmentedButtonPreference.kt
index 27eee77..307ba4c 100644
--- a/packages/SettingsLib/SegmentedButtonPreference/src/com/android/settingslib/widget/SegmentedButtonPreference.kt
+++ b/packages/SettingsLib/SegmentedButtonPreference/src/com/android/settingslib/widget/SegmentedButtonPreference.kt
@@ -18,10 +18,10 @@
import android.content.Context
import android.util.AttributeSet
-import android.view.View.GONE
-import android.view.View.VISIBLE
+import android.view.View
import android.widget.TextView
import androidx.annotation.DrawableRes
+import androidx.core.view.isGone
import androidx.preference.Preference
import androidx.preference.PreferenceViewHolder
import com.android.settingslib.widget.preference.segmentedbutton.R
@@ -43,6 +43,9 @@
private val buttonVisibilityData = mutableListOf<Pair<Int, Boolean>>() // (index, visibility)
private val buttonEnableData = mutableListOf<Pair<Int, Boolean>>() // (index, enable)
+ // Default checked button
+ private var checkedIndex: Int = -1
+
init {
layoutResource = R.layout.settingslib_expressive_preference_segmentedbutton
}
@@ -62,6 +65,7 @@
applyButtonSetupData()
applyButtonVisibilityData()
applyButtonEnableData()
+ applyCheckIndex(checkedIndex)
buttonGroup?.apply {
clearOnButtonCheckedListeners()
buttonCheckedListener?.let { listener ->
@@ -100,6 +104,21 @@
}
}
+ fun setCheckedIndex(index: Int) {
+ if (buttonGroup == null) {
+ // Store data for later application
+ checkedIndex = index
+ } else {
+ // Apply data
+ applyCheckIndex(index)
+ }
+ }
+
+ fun getCheckedIndex(): Int {
+ val checkedButtonId = buttonGroup?.checkedButtonId ?: return -1
+ return buttonGroup?.indexOfChild(buttonGroup?.findViewById(checkedButtonId)) ?: -1
+ }
+
fun setOnButtonClickListener(listener: MaterialButtonToggleGroup.OnButtonCheckedListener) {
buttonCheckedListener = listener
notifyChanged()
@@ -140,16 +159,22 @@
private fun applyButtonVisibilityData(index: Int, visible: Boolean) {
if (index in 0 until buttonLabels.size) {
- (buttonGroup?.getChildAt(index) as? MaterialButton)?.visibility =
- if (visible) VISIBLE else GONE
-
- buttonLabels[index].visibility = if (visible) VISIBLE else GONE
+ buttonGroup?.getChildAt(index)?.isGone = !visible
+ buttonLabels[index].isGone = !visible
}
}
private fun applyButtonEnableData(index: Int, enabled: Boolean) {
- if (index in 0 until buttonLabels.size) {
- (buttonGroup?.getChildAt(index) as? MaterialButton)?.isEnabled = enabled
+ buttonGroup?.getChildAt(index)?.isEnabled = enabled
+ }
+
+ private fun applyCheckIndex(index: Int) {
+ buttonGroup?.getChildAt(index)?.let { button ->
+ if (button.id == View.NO_ID || button.isGone) {
+ return
+ }
+
+ buttonGroup?.check(button.id)
}
}
}
\ No newline at end of file
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/Android.bp b/packages/SettingsLib/SelectorWithWidgetPreference/Android.bp
index 78e27fe..73878f4 100644
--- a/packages/SettingsLib/SelectorWithWidgetPreference/Android.bp
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/Android.bp
@@ -20,7 +20,6 @@
static_libs: [
"androidx.preference_preference",
"SettingsLibSettingsTheme",
- "settingslib_selectorwithwidgetpreference_flags_lib",
],
sdk_version: "system_current",
@@ -32,26 +31,3 @@
"com.android.healthfitness",
],
}
-
-aconfig_declarations {
- name: "settingslib_selectorwithwidgetpreference_flags",
- package: "com.android.settingslib.widget.selectorwithwidgetpreference.flags",
- container: "system",
- srcs: [
- "aconfig/selectorwithwidgetpreference.aconfig",
- ],
-}
-
-java_aconfig_library {
- name: "settingslib_selectorwithwidgetpreference_flags_lib",
- aconfig_declarations: "settingslib_selectorwithwidgetpreference_flags",
-
- min_sdk_version: "30",
- sdk_version: "system_current",
- apex_available: [
- "//apex_available:platform",
- "com.android.permission",
- "com.android.mediaprovider",
- "com.android.healthfitness",
- ],
-}
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/aconfig/selectorwithwidgetpreference.aconfig b/packages/SettingsLib/SelectorWithWidgetPreference/aconfig/selectorwithwidgetpreference.aconfig
deleted file mode 100644
index 70cda47..0000000
--- a/packages/SettingsLib/SelectorWithWidgetPreference/aconfig/selectorwithwidgetpreference.aconfig
+++ /dev/null
@@ -1,13 +0,0 @@
-package: "com.android.settingslib.widget.selectorwithwidgetpreference.flags"
-container: "system"
-
-flag {
- name: "allow_set_title_max_lines"
- namespace: "accessibility"
- description: "Allow changes to the title max lines so it's not always fixed to 2"
- bug: "356726764"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
diff --git a/packages/SettingsLib/Service/Android.bp b/packages/SettingsLib/Service/Android.bp
index 65d0e4c..4795baf 100644
--- a/packages/SettingsLib/Service/Android.bp
+++ b/packages/SettingsLib/Service/Android.bp
@@ -17,4 +17,7 @@
"SettingsLibGraph",
],
kotlincflags: ["-Xjvm-default=all"],
+ flags_packages: [
+ "aconfig_settingslib_flags",
+ ],
}
diff --git a/packages/SettingsLib/Service/AndroidManifest.xml b/packages/SettingsLib/Service/AndroidManifest.xml
index 56d7818..a0c08c4 100644
--- a/packages/SettingsLib/Service/AndroidManifest.xml
+++ b/packages/SettingsLib/Service/AndroidManifest.xml
@@ -3,4 +3,17 @@
package="com.android.settingslib.service">
<uses-sdk android:minSdkVersion="21" />
+
+ <application>
+ <!-- Service to expose Preference Metadata and Get/Set functionality -->
+ <service
+ android:name="com.android.settingslib.service.SettingsPreferenceService"
+ android:exported="true"
+ android:featureFlag="com.android.settingslib.flags.settings_catalyst"
+ android:permission="android.permission.READ_SYSTEM_PREFERENCES">
+ <intent-filter>
+ <action android:name="android.service.settings.preferences.action.PREFERENCE_SERVICE" />
+ </intent-filter>
+ </service>
+ </application>
</manifest>
diff --git a/packages/SettingsLib/Service/src/com/android/settingslib/service/PreferenceServiceRequestTransformer.kt b/packages/SettingsLib/Service/src/com/android/settingslib/service/PreferenceServiceRequestTransformer.kt
new file mode 100644
index 0000000..d0a1979
--- /dev/null
+++ b/packages/SettingsLib/Service/src/com/android/settingslib/service/PreferenceServiceRequestTransformer.kt
@@ -0,0 +1,237 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.service
+
+import android.content.Context
+import android.os.Bundle
+import android.service.settings.preferences.GetValueRequest
+import android.service.settings.preferences.GetValueResult
+import android.service.settings.preferences.MetadataResult
+import android.service.settings.preferences.SetValueRequest
+import android.service.settings.preferences.SetValueResult
+import android.service.settings.preferences.SettingsPreferenceMetadata
+import android.service.settings.preferences.SettingsPreferenceValue
+import com.android.settingslib.graph.PreferenceGetterErrorCode
+import com.android.settingslib.graph.PreferenceGetterFlags
+import com.android.settingslib.graph.PreferenceGetterRequest
+import com.android.settingslib.graph.PreferenceGetterResponse
+import com.android.settingslib.graph.PreferenceSetterRequest
+import com.android.settingslib.graph.PreferenceSetterResult
+import com.android.settingslib.graph.getAllPermissions
+import com.android.settingslib.graph.getText
+import com.android.settingslib.graph.preferenceValueProto
+import com.android.settingslib.graph.proto.PreferenceGraphProto
+import com.android.settingslib.graph.proto.PreferenceOrGroupProto
+import com.android.settingslib.graph.proto.PreferenceProto
+import com.android.settingslib.graph.proto.PreferenceValueProto
+import com.android.settingslib.graph.toBundle
+import com.android.settingslib.graph.toIntent
+import com.android.settingslib.metadata.PreferenceCoordinate
+import com.android.settingslib.metadata.ReadWritePermit
+import com.android.settingslib.metadata.SensitivityLevel
+
+/** Transform Catalyst Graph result to Framework GET METADATA result */
+fun transformCatalystGetMetadataResponse(
+ context: Context,
+ graph: PreferenceGraphProto
+): MetadataResult {
+ val preferences = mutableSetOf<PreferenceWithScreen>()
+ // recursive function to visit all nodes in preference group
+ fun traverseGroupOrPref(
+ screenKey: String,
+ groupOrPref: PreferenceOrGroupProto,
+ ) {
+ when (groupOrPref.kindCase) {
+ PreferenceOrGroupProto.KindCase.PREFERENCE ->
+ preferences.add(
+ PreferenceWithScreen(screenKey, groupOrPref.preference)
+ )
+ PreferenceOrGroupProto.KindCase.GROUP -> {
+ for (child in groupOrPref.group.preferencesList) {
+ traverseGroupOrPref(screenKey, child)
+ }
+ }
+ else -> {}
+ }
+ }
+ // traverse all screens and all preferences on screen
+ for ((screenKey, screen) in graph.screensMap) {
+ for (groupOrPref in screen.root.preferencesList) {
+ traverseGroupOrPref(screenKey, groupOrPref)
+ }
+ for (parameterizedScreen in screen.parameterizedScreensList) {
+ val args = parameterizedScreen.args.toBundle()
+ // TODO: support parameterized screen with non empty arguments
+ if (!args.isEmpty) continue
+ for (groupOrPref in parameterizedScreen.screen.root.preferencesList) {
+ traverseGroupOrPref(screenKey, groupOrPref)
+ }
+ }
+ }
+
+ return if (preferences.isNotEmpty()) {
+ MetadataResult.Builder(MetadataResult.RESULT_OK)
+ .setMetadataList(
+ preferences.map {
+ it.preference.toMetadata(context, it.screenKey)
+ }
+ )
+ .build()
+ } else {
+ MetadataResult.Builder(MetadataResult.RESULT_UNSUPPORTED).build()
+ }
+}
+
+/** Translate Framework GET VALUE request to Catalyst GET VALUE request */
+fun transformFrameworkGetValueRequest(
+ request: GetValueRequest,
+ flags: Int = PreferenceGetterFlags.ALL
+): PreferenceGetterRequest {
+ val coord = PreferenceCoordinate(request.screenKey, request.preferenceKey)
+ return PreferenceGetterRequest(
+ arrayOf(coord),
+ flags
+ )
+}
+
+/** Translate Catalyst GET VALUE result to Framework GET VALUE result */
+fun transformCatalystGetValueResponse(
+ context: Context, request: GetValueRequest, response: PreferenceGetterResponse
+): GetValueResult? {
+ val coord = PreferenceCoordinate(request.screenKey, request.preferenceKey)
+ val errorResponse = response.errors[coord]
+ val valueResponse = response.preferences[coord]
+ when {
+ errorResponse != null -> {
+ val errorCode = when (errorResponse) {
+ PreferenceGetterErrorCode.NOT_FOUND -> GetValueResult.RESULT_UNSUPPORTED
+ PreferenceGetterErrorCode.DISALLOW -> GetValueResult.RESULT_DISALLOW
+ else -> GetValueResult.RESULT_INTERNAL_ERROR
+ }
+ return GetValueResult.Builder(errorCode).build()
+ }
+ valueResponse != null -> {
+ val metadata = valueResponse.toMetadata(context, coord.screenKey)
+ val value = valueResponse.value.toSettingsPreferenceValue()
+ return when (value) {
+ null -> GetValueResult.Builder(GetValueResult.RESULT_UNSUPPORTED)
+ else -> GetValueResult.Builder(GetValueResult.RESULT_OK).setValue(value)
+ }.setMetadata(metadata).build()
+ }
+ else -> return null
+ }
+}
+
+private fun PreferenceValueProto.toSettingsPreferenceValue(): SettingsPreferenceValue? =
+ when (valueCase.number) {
+ PreferenceValueProto.BOOLEAN_VALUE_FIELD_NUMBER -> {
+ SettingsPreferenceValue.Builder(
+ SettingsPreferenceValue.TYPE_BOOLEAN
+ ).setBooleanValue(booleanValue)
+ }
+ PreferenceValueProto.INT_VALUE_FIELD_NUMBER -> {
+ SettingsPreferenceValue.Builder(
+ SettingsPreferenceValue.TYPE_INT
+ ).setIntValue(intValue)
+ }
+ else -> null
+ }?.build()
+
+/** Translate Framework SET VALUE request to Catalyst SET VALUE request */
+fun transformFrameworkSetValueRequest(request: SetValueRequest): PreferenceSetterRequest? {
+ val valueProto = when (request.preferenceValue.type) {
+ SettingsPreferenceValue.TYPE_BOOLEAN -> preferenceValueProto {
+ booleanValue = request.preferenceValue.booleanValue
+ }
+ SettingsPreferenceValue.TYPE_INT -> preferenceValueProto {
+ intValue = request.preferenceValue.intValue
+ }
+ else -> return null
+ }
+ // TODO: support parameterized screen
+ return PreferenceSetterRequest(request.screenKey, null, request.preferenceKey, valueProto)
+}
+
+/** Translate Catalyst SET VALUE result to Framework SET VALUE result */
+fun transformCatalystSetValueResponse(@PreferenceSetterResult response: Int): SetValueResult {
+ val resultCode = when (response) {
+ PreferenceSetterResult.OK -> SetValueResult.RESULT_OK
+ PreferenceSetterResult.UNAVAILABLE -> SetValueResult.RESULT_UNAVAILABLE
+ PreferenceSetterResult.DISABLED -> SetValueResult.RESULT_DISABLED
+ PreferenceSetterResult.UNSUPPORTED -> SetValueResult.RESULT_UNSUPPORTED
+ PreferenceSetterResult.DISALLOW -> SetValueResult.RESULT_DISALLOW
+ PreferenceSetterResult.REQUIRE_APP_PERMISSION ->
+ SetValueResult.RESULT_REQUIRE_APP_PERMISSION
+ PreferenceSetterResult.REQUIRE_USER_AGREEMENT -> SetValueResult.RESULT_REQUIRE_USER_CONSENT
+ PreferenceSetterResult.RESTRICTED -> SetValueResult.RESULT_RESTRICTED
+ PreferenceSetterResult.INVALID_REQUEST -> SetValueResult.RESULT_INVALID_REQUEST
+ else -> SetValueResult.RESULT_INTERNAL_ERROR
+ }
+ return SetValueResult.Builder(resultCode).build()
+}
+
+private data class PreferenceWithScreen(
+ val screenKey: String,
+ val preference: PreferenceProto,
+)
+
+private const val KEY_INT_RANGE = "key_int_range"
+private const val KEY_MIN = "key_min"
+private const val KEY_MAX = "key_max"
+private const val KEY_STEP = "key_step"
+private const val KEY_TAGS = "tags"
+
+private fun PreferenceProto.toMetadata(
+ context: Context,
+ screenKey: String
+): SettingsPreferenceMetadata {
+ val sensitivity = when (sensitivityLevel) {
+ SensitivityLevel.NO_SENSITIVITY -> SettingsPreferenceMetadata.NO_SENSITIVITY
+ SensitivityLevel.LOW_SENSITIVITY -> SettingsPreferenceMetadata.EXPECT_POST_CONFIRMATION
+ SensitivityLevel.MEDIUM_SENSITIVITY -> SettingsPreferenceMetadata.DEEPLINK_ONLY
+ SensitivityLevel.HIGH_SENSITIVITY -> SettingsPreferenceMetadata.DEEPLINK_ONLY
+ else -> SettingsPreferenceMetadata.NO_DIRECT_ACCESS
+ }
+ val extras = Bundle()
+ if (valueDescriptor.hasRangeValue()
+ && valueDescriptor.rangeValue.hasMin()
+ && valueDescriptor.rangeValue.hasMax()) {
+ val intRange = Bundle()
+ intRange.putInt(KEY_MIN, valueDescriptor.rangeValue.min)
+ intRange.putInt(KEY_MAX, valueDescriptor.rangeValue.max)
+ if (valueDescriptor.rangeValue.hasStep()) {
+ intRange.putInt(KEY_STEP, valueDescriptor.rangeValue.step)
+ }
+ extras.putBundle(KEY_INT_RANGE, intRange)
+ }
+ if (tagsCount > 0) extras.putStringArray(KEY_TAGS, tagsList.toTypedArray())
+ val writePermit = ReadWritePermit.getWritePermit(readWritePermit)
+ return SettingsPreferenceMetadata.Builder(screenKey, key)
+ .setTitle(title.getText(context))
+ .setSummary(summary.getText(context))
+ .setEnabled(enabled)
+ .setAvailable(available)
+ .setRestricted(restricted)
+ .setWritable(persistent && writePermit == ReadWritePermit.ALLOW)
+ .setLaunchIntent(launchIntent.toIntent())
+ .setWriteSensitivity(sensitivity)
+ // Returns all the permissions that are used, some of which are exclusive (e.g. p1 or p2)
+ .setReadPermissions(readPermissions.getAllPermissions())
+ .setWritePermissions(writePermissions.getAllPermissions())
+ .setExtras(extras)
+ .build()
+}
diff --git a/packages/SettingsLib/Service/src/com/android/settingslib/service/SettingsPreferenceService.kt b/packages/SettingsLib/Service/src/com/android/settingslib/service/SettingsPreferenceService.kt
new file mode 100644
index 0000000..11d6154
--- /dev/null
+++ b/packages/SettingsLib/Service/src/com/android/settingslib/service/SettingsPreferenceService.kt
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.service
+
+import android.Manifest
+import android.app.AppOpsManager
+import android.os.Binder
+import android.os.Build
+import android.os.OutcomeReceiver
+import android.service.settings.preferences.GetValueRequest
+import android.service.settings.preferences.GetValueResult
+import android.service.settings.preferences.MetadataRequest
+import android.service.settings.preferences.MetadataResult
+import android.service.settings.preferences.SetValueRequest
+import android.service.settings.preferences.SetValueResult
+import android.util.Log
+import androidx.annotation.RequiresApi
+import com.android.settingslib.graph.GetPreferenceGraphApiHandler
+import com.android.settingslib.graph.GetPreferenceGraphRequest
+import com.android.settingslib.graph.PreferenceGetterApiHandler
+import com.android.settingslib.graph.PreferenceGetterFlags
+import com.android.settingslib.graph.PreferenceSetterApiHandler
+import com.android.settingslib.ipc.ApiPermissionChecker
+import com.android.settingslib.ipc.AppOpApiPermissionChecker
+import com.android.settingslib.metadata.PreferenceRemoteOpMetricsLogger
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.SupervisorJob
+import kotlinx.coroutines.launch
+
+@RequiresApi(Build.VERSION_CODES.BAKLAVA)
+class SettingsPreferenceService(
+ private val scope: CoroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.IO),
+ metricsLogger: PreferenceRemoteOpMetricsLogger?,
+) : android.service.settings.preferences.SettingsPreferenceService() {
+
+ constructor() : this(CoroutineScope(SupervisorJob() + Dispatchers.IO), null)
+
+ private val getApiHandler: PreferenceGetterApiHandler
+ private val setApiHandler: PreferenceSetterApiHandler
+ private val graphApi: GetPreferenceGraphApiHandler
+
+ init {
+ // SettingsPreferenceService specifies READ_SYSTEM_PREFERENCES permission in
+ // AndroidManifest.xml
+ getApiHandler =
+ PreferenceGetterApiHandler(1, ApiPermissionChecker.alwaysAllow(), metricsLogger)
+ setApiHandler =
+ PreferenceSetterApiHandler(
+ 2,
+ AppOpApiPermissionChecker(
+ AppOpsManager.OP_WRITE_SYSTEM_PREFERENCES,
+ Manifest.permission.WRITE_SYSTEM_PREFERENCES,
+ ),
+ metricsLogger,
+ )
+ graphApi =
+ GetPreferenceGraphApiHandler(3, ApiPermissionChecker.alwaysAllow(), metricsLogger)
+ }
+
+ override fun onGetAllPreferenceMetadata(
+ request: MetadataRequest,
+ callback: OutcomeReceiver<MetadataResult, Exception>,
+ ) {
+ // MUST get pid/uid in binder thread
+ val callingPid = Binder.getCallingPid()
+ val callingUid = Binder.getCallingUid()
+ Log.i(TAG, "GetAllPreferenceMetadata pid=$callingPid uid=$callingUid")
+ scope.launch {
+ val graphProto =
+ graphApi.invoke(
+ application,
+ callingPid,
+ callingUid,
+ GetPreferenceGraphRequest(flags = PreferenceGetterFlags.METADATA),
+ )
+ val result =
+ transformCatalystGetMetadataResponse(this@SettingsPreferenceService, graphProto)
+ callback.onResult(result)
+ }
+ }
+
+ override fun onGetPreferenceValue(
+ request: GetValueRequest,
+ callback: OutcomeReceiver<GetValueResult, Exception>,
+ ) {
+ // MUST get pid/uid in binder thread
+ val callingPid = Binder.getCallingPid()
+ val callingUid = Binder.getCallingUid()
+ Log.i(TAG, "GetPreferenceValue pid=$callingPid uid=$callingUid")
+ scope.launch {
+ val apiRequest = transformFrameworkGetValueRequest(request)
+ val response = getApiHandler.invoke(application, callingPid, callingUid, apiRequest)
+ val result =
+ transformCatalystGetValueResponse(this@SettingsPreferenceService, request, response)
+ if (result == null) {
+ callback.onError(IllegalStateException("No response"))
+ } else {
+ callback.onResult(result)
+ }
+ }
+ }
+
+ override fun onSetPreferenceValue(
+ request: SetValueRequest,
+ callback: OutcomeReceiver<SetValueResult, Exception>,
+ ) {
+ // MUST get pid/uid in binder thread
+ val callingPid = Binder.getCallingPid()
+ val callingUid = Binder.getCallingUid()
+ Log.i(TAG, "SetPreferenceValue pid=$callingPid uid=$callingUid")
+ scope.launch {
+ val apiRequest = transformFrameworkSetValueRequest(request)
+ if (apiRequest == null) {
+ callback.onResult(
+ SetValueResult.Builder(SetValueResult.RESULT_INVALID_REQUEST).build()
+ )
+ } else {
+ val response = setApiHandler.invoke(application, callingPid, callingUid, apiRequest)
+
+ callback.onResult(transformCatalystSetValueResponse(response))
+ }
+ }
+ }
+
+ companion object {
+ private const val TAG = "PrefServiceForGraph"
+ }
+}
diff --git a/packages/SettingsLib/SettingsTheme/res/layout/settingslib_expressive_collapsable_textview.xml b/packages/SettingsLib/SettingsTheme/res/layout/settingslib_expressive_collapsable_textview.xml
index cc55cac..c71e507 100644
--- a/packages/SettingsLib/SettingsTheme/res/layout/settingslib_expressive_collapsable_textview.xml
+++ b/packages/SettingsLib/SettingsTheme/res/layout/settingslib_expressive_collapsable_textview.xml
@@ -55,12 +55,30 @@
android:visibility="gone"
style="@style/SettingslibTextAppearance.LinkableTextStyle.Expressive"/>
- <com.google.android.material.button.MaterialButton
+ <LinearLayout
android:id="@+id/collapse_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingTop="@dimen/settingslib_expressive_space_extrasmall4"
+ android:paddingBottom="@dimen/settingslib_expressive_space_extrasmall4"
app:layout_constraintTop_toBottomOf="@id/settingslib_expressive_learn_more"
app:layout_constraintStart_toStartOf="parent"
- android:textColor="@color/settingslib_materialColorOnSurface"
- android:text="@string/settingslib_expressive_text_expand"
- app:icon="@drawable/settingslib_expressive_icon_expand"
- style="@style/SettingslibTextButtonStyle.Expressive"/>
-</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
+ android:gravity="center"
+ android:filterTouchesWhenObscured="false">
+
+ <ImageView
+ android:id="@android:id/icon1"
+ android:layout_width="@dimen/settingslib_expressive_space_small4"
+ android:layout_height="@dimen/settingslib_expressive_space_small4"
+ android:importantForAccessibility="no"
+ android:scaleType="centerInside"/>
+
+ <TextView
+ android:id="@android:id/text1"
+ android:layout_marginStart="@dimen/settingslib_expressive_space_extrasmall4"
+ style="@style/TextAppearance.SettingsLib.BodyLarge.Emphasized"
+ android:layout_width="wrap_content"
+ android:layout_height="@dimen/settingslib_expressive_space_small4"/>
+ </LinearLayout>
+
+</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-night-v36/colors.xml b/packages/SettingsLib/SettingsTheme/res/values-night-v35/colors.xml
similarity index 100%
rename from packages/SettingsLib/SettingsTheme/res/values-night-v36/colors.xml
rename to packages/SettingsLib/SettingsTheme/res/values-night-v35/colors.xml
diff --git a/packages/SettingsLib/SettingsTheme/res/values-night-v36/styles.xml b/packages/SettingsLib/SettingsTheme/res/values-night-v35/styles.xml
similarity index 100%
rename from packages/SettingsLib/SettingsTheme/res/values-night-v36/styles.xml
rename to packages/SettingsLib/SettingsTheme/res/values-night-v35/styles.xml
diff --git a/packages/SettingsLib/SettingsTheme/res/values-v31/themes.xml b/packages/SettingsLib/SettingsTheme/res/values-v31/themes.xml
index 2d6b6cf..3ccbbc0 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-v31/themes.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-v31/themes.xml
@@ -28,7 +28,6 @@
<item name="switchStyle">@style/SwitchCompat.SettingsLib</item>
<item name="android:progressBarStyleHorizontal">@style/HorizontalProgressBar.SettingsLib</item>
<item name="android:listDivider">@drawable/settingslib_list_divider</item>
- <item name="android:colorBackground">@color/settingslib_materialColorSurfaceContainerLowest</item>
</style>
<style name="Theme.SettingsBase" parent="Theme.SettingsBase_v31" />
diff --git a/packages/SettingsLib/SettingsTheme/res/values-v36/colors.xml b/packages/SettingsLib/SettingsTheme/res/values-v35/colors.xml
similarity index 100%
rename from packages/SettingsLib/SettingsTheme/res/values-v36/colors.xml
rename to packages/SettingsLib/SettingsTheme/res/values-v35/colors.xml
diff --git a/packages/SettingsLib/SettingsTheme/res/values-v36/dimens.xml b/packages/SettingsLib/SettingsTheme/res/values-v35/dimens.xml
similarity index 100%
rename from packages/SettingsLib/SettingsTheme/res/values-v36/dimens.xml
rename to packages/SettingsLib/SettingsTheme/res/values-v35/dimens.xml
diff --git a/packages/SettingsLib/SettingsTheme/res/values-v36/styles.xml b/packages/SettingsLib/SettingsTheme/res/values-v35/styles.xml
similarity index 100%
rename from packages/SettingsLib/SettingsTheme/res/values-v36/styles.xml
rename to packages/SettingsLib/SettingsTheme/res/values-v35/styles.xml
diff --git a/packages/SettingsLib/SettingsTheme/res/values-v36/themes.xml b/packages/SettingsLib/SettingsTheme/res/values-v35/themes.xml
similarity index 92%
rename from packages/SettingsLib/SettingsTheme/res/values-v36/themes.xml
rename to packages/SettingsLib/SettingsTheme/res/values-v35/themes.xml
index 54bd069..30efd2f 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-v36/themes.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-v35/themes.xml
@@ -16,9 +16,9 @@
-->
<resources>
- <style name="Theme.SettingsBase_v36" parent="Theme.SettingsBase_v33" >
+ <style name="Theme.SettingsBase_v35" parent="Theme.SettingsBase_v33" >
<item name="android:colorAccent">@color/settingslib_materialColorPrimary</item>
- <item name="android:colorBackground">@color/settingslib_materialColorSurfaceContainer</item>
+ <item name="android:colorBackground">@color/settingslib_materialColorSurfaceContainerLowest</item>
<item name="android:textColorPrimary">@color/settingslib_materialColorOnSurface</item>
<item name="android:textColorSecondary">@color/settingslib_text_color_secondary</item>
<item name="android:textColorTertiary">@color/settingslib_materialColorOutline</item>
@@ -28,5 +28,5 @@
<item name="android:clipChildren">false</item>
</style>
- <style name="Theme.SettingsBase" parent="Theme.SettingsBase_v36" />
+ <style name="Theme.SettingsBase" parent="Theme.SettingsBase_v35" />
</resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/values-v36/styles_expressive.xml b/packages/SettingsLib/SettingsTheme/res/values-v36/styles_expressive.xml
index 99153f7..2d94350 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-v36/styles_expressive.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-v36/styles_expressive.xml
@@ -127,7 +127,8 @@
</style>
<style name="Widget.SettingsLib.ButtonBar" parent="@style/Widget.AppCompat.ButtonBar.AlertDialog">
- <item name="android:paddingBottom">@dimen/settingslib_expressive_space_small4</item>
+ <item name="android:paddingBottom">@dimen/settingslib_expressive_space_small1</item>
+ <item name="android:minHeight">@dimen/settingslib_expressive_space_large2</item>
</style>
<style name="Widget.SettingsLib.DialogButton" parent="@style/Widget.AppCompat.Button">
diff --git a/packages/SettingsLib/SettingsTheme/res/values-v36/themes_expressive.xml b/packages/SettingsLib/SettingsTheme/res/values-v36/themes_expressive.xml
index 36edf62..917ebbb 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-v36/themes_expressive.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-v36/themes_expressive.xml
@@ -17,6 +17,9 @@
<resources>
<style name="Theme.SettingsBase.Expressive">
+ <!-- Set up background color -->
+ <item name="android:colorBackground">@color/settingslib_materialColorSurfaceContainer</item>
+
<!-- Set up Preference title text style -->
<item name="android:textAppearanceListItem">@style/TextAppearance.SettingsLib.PreferenceTitle</item>
<item name="android:textAppearanceListItemSecondary">@style/TextAppearance.SettingsLib.PreferenceSummary</item>
@@ -54,6 +57,8 @@
<!-- For AndroidX AlertDialog -->
<item name="alertDialogTheme">@style/Theme.AlertDialog.SettingsLib.Expressive</item>
+ <!-- For AlertDialog -->
+ <item name="android:alertDialogTheme">@style/Theme.AlertDialog.SettingsLib.Expressive</item>
</style>
<style name="Theme.AlertDialog.SettingsLib.Expressive">
diff --git a/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/CollapsableTextView.kt b/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/CollapsableTextView.kt
index 007dc51..082eea7 100644
--- a/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/CollapsableTextView.kt
+++ b/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/CollapsableTextView.kt
@@ -29,6 +29,8 @@
import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
+import android.widget.ImageView
+import android.widget.LinearLayout
import android.widget.TextView
import androidx.constraintlayout.widget.ConstraintLayout
import com.android.settingslib.widget.theme.R
@@ -45,7 +47,9 @@
private var minLines: Int = DEFAULT_MIN_LINES
private val titleTextView: TextView
- private val collapseButton: MaterialButton
+ private val collapseButton: LinearLayout
+ private val collapseButtonIcon: ImageView?
+ private val collapseButtonText: TextView?
private val collapseButtonResources: CollapseButtonResources
private var hyperlinkListener: View.OnClickListener? = null
private var learnMoreListener: View.OnClickListener? = null
@@ -59,6 +63,8 @@
.inflate(R.layout.settingslib_expressive_collapsable_textview, this)
titleTextView = findViewById(android.R.id.title)
collapseButton = findViewById(R.id.collapse_button)
+ collapseButtonIcon = collapseButton.findViewById(android.R.id.icon1)
+ collapseButtonText = collapseButton.findViewById(android.R.id.text1)
learnMoreTextView = findViewById(R.id.settingslib_expressive_learn_more)
collapseButtonResources = CollapseButtonResources(
@@ -223,20 +229,16 @@
private fun updateView() {
when {
isCollapsed -> {
- collapseButton.apply {
- text = collapseButtonResources.expandText
- icon = collapseButtonResources.expandIcon
- }
+ collapseButtonIcon?.setImageDrawable(collapseButtonResources.expandIcon)
+ collapseButtonText?.text = collapseButtonResources.expandText
titleTextView.maxLines = minLines
titleTextView.ellipsize = null
titleTextView.scrollBarSize = 0
}
else -> {
- collapseButton.apply {
- text = collapseButtonResources.collapseText
- icon = collapseButtonResources.collapseIcon
- }
+ collapseButtonIcon?.setImageDrawable(collapseButtonResources.collapseIcon)
+ collapseButtonText?.text = collapseButtonResources.collapseText
titleTextView.maxLines = DEFAULT_MAX_LINES
titleTextView.ellipsize = TextUtils.TruncateAt.END
}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/model/DualShadeEducationalTooltipModel.kt b/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/OnScreenWidgetMixin.kt
similarity index 70%
copy from packages/SystemUI/src/com/android/systemui/scene/domain/model/DualShadeEducationalTooltipModel.kt
copy to packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/OnScreenWidgetMixin.kt
index f63e132..8c34e9c 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/model/DualShadeEducationalTooltipModel.kt
+++ b/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/OnScreenWidgetMixin.kt
@@ -14,11 +14,12 @@
* limitations under the License.
*/
-package com.android.systemui.scene.domain.model
+package com.android.settingslib.widget
-/** Enumerates the type of tooltip to show. */
-enum class DualShadeEducationalTooltipModel {
- None,
- ForNotificationsShade,
- ForQuickSettingsShade,
-}
+/**
+ * A base interface to indicate that a widget is placed onto the screen.
+ *
+ * Widget implements this interface will be treated as on-screen widget and applied proper padding.
+ */
+interface OnScreenWidgetMixin
+
diff --git a/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsPreferenceGroupAdapter.kt b/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsPreferenceGroupAdapter.kt
index d1c88de..aca8419 100644
--- a/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsPreferenceGroupAdapter.kt
+++ b/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsPreferenceGroupAdapter.kt
@@ -199,6 +199,12 @@
return when {
// This item handles edge to edge itself
item is NormalPaddingMixin && item is GroupSectionDividerMixin -> 0 to 0
+ // Item is placed directly on screen needs to have extra padding
+ item is OnScreenWidgetMixin -> {
+ val extraPadding = item.context.resources.getDimensionPixelSize(
+ R.dimen.settingslib_expressive_space_extrasmall4)
+ mNormalPaddingStart + extraPadding to mNormalPaddingEnd + extraPadding
+ }
// According to mappingPreferenceGroup(), backgroundRes == 0 means this item is
// GroupSectionDividerMixin or PreferenceCategory, which is design to have normal
@@ -223,6 +229,10 @@
isSelected: Boolean,
isHighlighted: Boolean,
): Int {
+ if (position !in mRoundCornerMappingList.indices) {
+ return 0
+ }
+
val cornerType = mRoundCornerMappingList[position]
if ((cornerType and ROUND_CORNER_CENTER) == 0) {
diff --git a/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsThemeHelper.kt b/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsThemeHelper.kt
index d9e385d..ae1e182 100644
--- a/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsThemeHelper.kt
+++ b/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsThemeHelper.kt
@@ -20,6 +20,7 @@
import android.content.Context
import android.content.ContextWrapper
import android.os.Build
+import androidx.annotation.ChecksSdkIntAtLeast
import com.android.settingslib.widget.theme.flags.Flags
object SettingsThemeHelper {
@@ -32,6 +33,7 @@
return result.contains("tablet")
}
+ @ChecksSdkIntAtLeast(Build.VERSION_CODES.BAKLAVA)
@JvmStatic
fun isExpressiveTheme(context: Context): Boolean {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.BAKLAVA) {
@@ -39,18 +41,14 @@
}
// Enable if overridden by system property
if (getPropBoolean(context, IS_EXPRESSIVE_DESIGN_ENABLED, false)) {
- return true;
- }
- // Disable if feature flag is disabled.
- if (!Flags.isExpressiveDesignEnabled()) {
- return false;
+ return true
}
// Allow the activity to override.
val activity = getActivityFromContext(context)
if (activity is ExpressiveDesignEnabledProvider) {
return activity.isExpressiveDesignEnabled()
}
- return true
+ return Flags.isExpressiveDesignEnabled()
}
private fun getActivityFromContext(context: Context): Activity? {
diff --git a/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsTitlelessPreferenceCategory.kt b/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsTitlelessPreferenceCategory.kt
index 157b183..3d9aff1 100644
--- a/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsTitlelessPreferenceCategory.kt
+++ b/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsTitlelessPreferenceCategory.kt
@@ -19,6 +19,7 @@
import android.content.Context
import android.util.AttributeSet
import androidx.preference.PreferenceCategory
+import androidx.preference.PreferenceViewHolder
import com.android.settingslib.widget.theme.R
class SettingsTitlelessPreferenceCategory @JvmOverloads constructor(
@@ -36,4 +37,10 @@
R.layout.settingslib_preference_category_no_title
}
}
+
+ override fun onBindViewHolder(holder: PreferenceViewHolder) {
+ super.onBindViewHolder(holder)
+ holder.setDividerAllowedAbove(false)
+ holder.setDividerAllowedBelow(false)
+ }
}
diff --git a/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SetupWizardHelper.kt b/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SetupWizardHelper.kt
new file mode 100644
index 0000000..f62cfa4
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SetupWizardHelper.kt
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.widget
+
+import android.content.Intent
+import android.os.Build
+
+object SetupWizardHelper {
+
+ private const val EXTRA_IS_SETUP_FLOW = "isSetupFlow"
+ private const val EXTRA_IS_FIRST_RUN = "firstRun"
+ private const val EXTRA_IS_PRE_DEFERRED_SETUP = "preDeferredSetup"
+ private const val EXTRA_IS_DEFERRED_SETUP = "deferredSetup"
+
+ /**
+ * Checks if the current context is within any setup wizard flow.
+ *
+ * On Android Q and above, it checks for the presence of the [EXTRA_IS_SETUP_FLOW] intent extra.
+ * On older versions, it checks for the presence of specific extras indicating initial,
+ * pre-deferred, or deferred setup ([EXTRA_IS_FIRST_RUN], [EXTRA_IS_PRE_DEFERRED_SETUP],
+ * [EXTRA_IS_DEFERRED_SETUP]).
+ *
+ * @param intent The intent to check.
+ * @return True if within any setup wizard flow, false otherwise.
+ */
+ @JvmStatic
+ fun isAnySetupWizard(intent: Intent?): Boolean {
+ if (intent == null) {
+ return false
+ }
+
+ return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
+ intent.getBooleanExtra(EXTRA_IS_SETUP_FLOW, false)
+ } else {
+ isLegacySetupWizard(intent)
+ }
+ }
+
+ private fun isLegacySetupWizard(intent: Intent): Boolean {
+ return intent.run {
+ getBooleanExtra(EXTRA_IS_FIRST_RUN, false) ||
+ getBooleanExtra(EXTRA_IS_PRE_DEFERRED_SETUP, false) ||
+ getBooleanExtra(EXTRA_IS_DEFERRED_SETUP, false)
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/SettingsLib/Spa/build.gradle.kts b/packages/SettingsLib/Spa/build.gradle.kts
index 25406d7..06dda03 100644
--- a/packages/SettingsLib/Spa/build.gradle.kts
+++ b/packages/SettingsLib/Spa/build.gradle.kts
@@ -28,7 +28,7 @@
allprojects {
extra["androidTop"] = androidTop
- extra["jetpackComposeVersion"] = "1.8.0-rc01"
+ extra["jetpackComposeVersion"] = "1.8.0-rc02"
}
subprojects {
diff --git a/packages/SettingsLib/Spa/gradle/libs.versions.toml b/packages/SettingsLib/Spa/gradle/libs.versions.toml
index d041eb0..0c0c330 100644
--- a/packages/SettingsLib/Spa/gradle/libs.versions.toml
+++ b/packages/SettingsLib/Spa/gradle/libs.versions.toml
@@ -15,14 +15,16 @@
#
[versions]
-agp = "8.9.0"
+agp = "8.9.1"
dexmaker-mockito = "2.28.3"
jvm = "21"
kotlin = "2.1.10"
+mockito-kotlin = "4.1.0"
truth = "1.4.4"
[libraries]
dexmaker-mockito = { module = "com.linkedin.dexmaker:dexmaker-mockito", version.ref = "dexmaker-mockito" }
+mockito-kotlin = { module = "org.mockito.kotlin:mockito-kotlin", version.ref = "mockito-kotlin" }
truth = { module = "com.google.truth:truth", version.ref = "truth" }
[plugins]
diff --git a/packages/SettingsLib/Spa/spa/build.gradle.kts b/packages/SettingsLib/Spa/spa/build.gradle.kts
index 57f5520..696f2a0 100644
--- a/packages/SettingsLib/Spa/spa/build.gradle.kts
+++ b/packages/SettingsLib/Spa/spa/build.gradle.kts
@@ -52,14 +52,14 @@
dependencies {
api(project(":SettingsLibColor"))
api("androidx.appcompat:appcompat:1.7.0")
- api("androidx.compose.material3:material3:1.4.0-alpha10")
+ api("androidx.compose.material3:material3:1.4.0-alpha12")
api("androidx.compose.material:material-icons-extended:1.7.8")
api("androidx.compose.runtime:runtime-livedata:$jetpackComposeVersion")
api("androidx.compose.ui:ui-tooling-preview:$jetpackComposeVersion")
api("androidx.graphics:graphics-shapes-android:1.0.1")
api("androidx.lifecycle:lifecycle-livedata-ktx")
api("androidx.lifecycle:lifecycle-runtime-compose")
- api("androidx.navigation:navigation-compose:2.9.0-alpha08")
+ api("androidx.navigation:navigation-compose:2.9.0-beta01")
api("com.github.PhilJay:MPAndroidChart:v3.1.0-alpha")
api("com.google.android.material:material:1.13.0-alpha08")
debugApi("androidx.compose.ui:ui-tooling:$jetpackComposeVersion")
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SpaEnvironment.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SpaEnvironment.kt
index 53f97d2..3667e51 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SpaEnvironment.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SpaEnvironment.kt
@@ -19,6 +19,7 @@
import android.app.Activity
import android.content.Context
import android.util.Log
+import androidx.annotation.VisibleForTesting
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.LocalContext
import com.android.settingslib.spa.framework.util.SystemProperties
@@ -28,10 +29,13 @@
object SpaEnvironmentFactory {
private var spaEnvironment: SpaEnvironment? = null
- fun reset() {
- spaEnvironment = null
- }
-
+ /**
+ * Resets the SpaEnvironment to the given instance, which is usually required step to set up
+ * SPA.
+ *
+ * This is usually be called in an Application class, but could be called in app initializors or
+ * setup listeners.
+ */
fun reset(env: SpaEnvironment) {
spaEnvironment = env
Log.d(TAG, "reset")
@@ -65,11 +69,26 @@
*
* Useful when there is fallback logic.
*/
- val optionalInstance: SpaEnvironment?
+ internal val optionalInstance: SpaEnvironment?
get() = spaEnvironment
+
+ @VisibleForTesting
+ internal fun clear() {
+ spaEnvironment = null
+ }
}
+/**
+ * The environment of SPA.
+ *
+ * This class is used to hold the global configurations of SPA.
+ *
+ * To set up SpaEnvironment,
+ * 1. create a concrete class that extends [SpaEnvironment].
+ * 2. call [SpaEnvironmentFactory.reset] with your implementation to set the global environment.
+ */
abstract class SpaEnvironment(context: Context) {
+ /** The repository of all page providers, SPA pages are setup here. */
abstract val pageProviderRepository: Lazy<SettingsPageProviderRepository>
val entryRepository = lazy { SettingsEntryRepository(pageProviderRepository.value) }
@@ -93,7 +112,6 @@
SystemProperties.getBoolean("is_expressive_design_enabled", false)
}
- // TODO: add other environment setup here.
companion object {
/**
* Whether debug mode is on or off.
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsShape.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsShape.kt
index 61607bc..7e1e3a0 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsShape.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsShape.kt
@@ -16,6 +16,7 @@
package com.android.settingslib.spa.framework.theme
+import androidx.compose.foundation.shape.CornerSize
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.ui.unit.dp
@@ -31,4 +32,7 @@
val CornerExtraLarge = RoundedCornerShape(28.dp)
val CornerExtraLarge1 = RoundedCornerShape(40.dp)
+
+ val BottomCornerMedium2 =
+ RoundedCornerShape(CornerSize(0), CornerSize(0), CornerSize(20.dp), CornerSize(20.dp))
}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Category.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Category.kt
index 395328f..5a4b364 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Category.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Category.kt
@@ -106,7 +106,9 @@
}
.then(
if (isSpaExpressiveEnabled)
- Modifier.fillMaxWidth().clip(SettingsShape.CornerMedium2)
+ Modifier
+ .fillMaxWidth()
+ .clip(SettingsShape.CornerMedium2)
else Modifier
),
verticalArrangement =
@@ -142,8 +144,10 @@
footer: @Composable () -> Unit = {},
header: @Composable () -> Unit,
) {
+ header()
Column(
- Modifier.padding(
+ Modifier
+ .padding(
PaddingValues(
start = SettingsDimension.paddingLarge,
end = SettingsDimension.paddingLarge,
@@ -158,11 +162,15 @@
verticalArrangement = Arrangement.spacedBy(SettingsDimension.paddingTiny),
state = state,
) {
- item { CompositionLocalProvider(LocalIsInCategory provides true) { header() } }
-
items(count = list.size, key = key) {
title?.invoke(it)?.let { title -> CategoryTitle(title) }
- CompositionLocalProvider(LocalIsInCategory provides true) { entry(it)() }
+ if (list.isNotEmpty() && it < list.size - 1) {
+ CompositionLocalProvider(LocalIsInCategory provides true) { entry(it)() }
+ } else {
+ Column(modifier = Modifier.clip(SettingsShape.BottomCornerMedium2)) {
+ CompositionLocalProvider(LocalIsInCategory provides true) { entry(it)() }
+ }
+ }
}
item { CompositionLocalProvider(LocalIsInCategory provides true) { footer() } }
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/common/SettingsEntryTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/common/SettingsEntryTest.kt
index d0a040c..2c4534a 100644
--- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/common/SettingsEntryTest.kt
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/common/SettingsEntryTest.kt
@@ -164,7 +164,7 @@
assertThat(entry2.isAllowSearch).isFalse()
// Clear SppHome in spa environment
- SpaEnvironmentFactory.reset()
+ SpaEnvironmentFactory.clear()
val entry3 = entryBuilder.build()
assertThat(entry3.id).isEqualTo(genEntryId("myEntry", owner))
assertThat(entry3.label).isEqualTo("myEntryDisplay")
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/common/SpaEnvironmentTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/common/SpaEnvironmentTest.kt
index 3b0e36b..434a4e9 100644
--- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/common/SpaEnvironmentTest.kt
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/common/SpaEnvironmentTest.kt
@@ -37,7 +37,7 @@
@Test
fun testSpaEnvironmentFactory() {
- SpaEnvironmentFactory.reset()
+ SpaEnvironmentFactory.clear()
Truth.assertThat(SpaEnvironmentFactory.isReady()).isFalse()
Assert.assertThrows(UnsupportedOperationException::class.java) {
SpaEnvironmentFactory.instance
@@ -50,7 +50,7 @@
@Test
fun testSpaEnvironmentFactoryForPreview() {
- SpaEnvironmentFactory.reset()
+ SpaEnvironmentFactory.clear()
composeTestRule.setContent {
Truth.assertThat(SpaEnvironmentFactory.isReady()).isFalse()
SpaEnvironmentFactory.resetForPreview()
diff --git a/packages/SettingsLib/Spa/testutils/build.gradle.kts b/packages/SettingsLib/Spa/testutils/build.gradle.kts
index 03cd243..7a6afce 100644
--- a/packages/SettingsLib/Spa/testutils/build.gradle.kts
+++ b/packages/SettingsLib/Spa/testutils/build.gradle.kts
@@ -39,12 +39,8 @@
api("androidx.arch.core:core-testing:2.2.0")
api("androidx.compose.ui:ui-test-junit4:$jetpackComposeVersion")
api("androidx.lifecycle:lifecycle-runtime-testing")
- api("org.mockito.kotlin:mockito-kotlin:2.2.11")
- api("org.mockito:mockito-core") {
- version {
- strictly("2.28.2")
- }
- }
+ api(libs.mockito.kotlin)
+ api("org.mockito:mockito-core:4.3.0")
api(libs.truth)
debugApi("androidx.compose.ui:ui-test-manifest:$jetpackComposeVersion")
}
diff --git a/packages/SettingsLib/TEST_MAPPING b/packages/SettingsLib/TEST_MAPPING
index f6ada4c1a..53a2b3b 100644
--- a/packages/SettingsLib/TEST_MAPPING
+++ b/packages/SettingsLib/TEST_MAPPING
@@ -8,6 +8,20 @@
},
{
"name": "SettingsSpaUnitTests"
+ },
+ {
+ "name": "HealthFitnessScreenDiffTests",
+ "options": [
+ {
+ "exclude-annotation": "org.junit.Ignore"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation": "android.platform.test.annotations.Postsubmit"
+ }
+ ]
}
]
}
diff --git a/packages/SettingsLib/TopIntroPreference/res/layout/settingslib_expressive_top_intro.xml b/packages/SettingsLib/TopIntroPreference/res/layout/settingslib_expressive_top_intro.xml
index 1c6b1eb..ecbee9b 100644
--- a/packages/SettingsLib/TopIntroPreference/res/layout/settingslib_expressive_top_intro.xml
+++ b/packages/SettingsLib/TopIntroPreference/res/layout/settingslib_expressive_top_intro.xml
@@ -14,7 +14,8 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
@@ -24,5 +25,7 @@
<com.android.settingslib.widget.CollapsableTextView
android:id="@+id/collapsable_text_view"
android:layout_width="match_parent"
- android:layout_height="wrap_content" />
-</LinearLayout>
\ No newline at end of file
+ android:layout_height="wrap_content"
+ android:minLines="2"
+ app:isCollapsable="false"/>
+</RelativeLayout>
diff --git a/packages/SettingsLib/TopIntroPreference/src/com/android/settingslib/widget/TopIntroPreference.kt b/packages/SettingsLib/TopIntroPreference/src/com/android/settingslib/widget/TopIntroPreference.kt
index 08ba836..beed420 100644
--- a/packages/SettingsLib/TopIntroPreference/src/com/android/settingslib/widget/TopIntroPreference.kt
+++ b/packages/SettingsLib/TopIntroPreference/src/com/android/settingslib/widget/TopIntroPreference.kt
@@ -31,7 +31,9 @@
attrs: AttributeSet? = null,
defStyleAttr: Int = 0,
defStyleRes: Int = 0,
-) : Preference(context, attrs, defStyleAttr, defStyleRes), GroupSectionDividerMixin {
+) : Preference(context, attrs, defStyleAttr, defStyleRes),
+ GroupSectionDividerMixin,
+ OnScreenWidgetMixin {
private var isCollapsable: Boolean = false
private var minLines: Int = 2
@@ -48,7 +50,7 @@
private fun initAttributes(context: Context, attrs: AttributeSet?, defStyleAttr: Int) {
context.obtainStyledAttributes(attrs, COLLAPSABLE_TEXT_VIEW_ATTRS, defStyleAttr, 0).apply {
- isCollapsable = getBoolean(IS_COLLAPSABLE, false)
+ isCollapsable = getBoolean(IS_COLLAPSABLE, DEFAULT_COLLAPSABLE)
minLines =
getInt(MIN_LINES, if (isCollapsable) DEFAULT_MIN_LINES else DEFAULT_MAX_LINES)
.coerceIn(1, DEFAULT_MAX_LINES)
@@ -65,13 +67,11 @@
setCollapsable(isCollapsable)
setMinLines(minLines)
visibility = if (title.isNullOrEmpty()) View.GONE else View.VISIBLE
- setText(title.toString())
- if (hyperlinkListener != null) {
- setHyperlinkListener(hyperlinkListener)
- }
- if (learnMoreListener != null) {
+ title?.let { setText(it.toString()) }
+ hyperlinkListener?.let { setHyperlinkListener(it) }
+ learnMoreListener?.let {
setLearnMoreText(learnMoreText)
- setLearnMoreAction(learnMoreListener)
+ setLearnMoreAction(it)
}
}
}
@@ -83,6 +83,7 @@
*/
fun setCollapsable(collapsable: Boolean) {
isCollapsable = collapsable
+ minLines = if (isCollapsable) DEFAULT_MIN_LINES else DEFAULT_MAX_LINES
notifyChanged()
}
@@ -135,6 +136,7 @@
companion object {
private const val DEFAULT_MAX_LINES = 10
private const val DEFAULT_MIN_LINES = 2
+ private const val DEFAULT_COLLAPSABLE = false
private val COLLAPSABLE_TEXT_VIEW_ATTRS =
com.android.settingslib.widget.theme.R.styleable.CollapsableTextView
diff --git a/packages/SettingsLib/ValuePreference/Android.bp b/packages/SettingsLib/ValuePreference/Android.bp
index 1cdcd22..f23ad52 100644
--- a/packages/SettingsLib/ValuePreference/Android.bp
+++ b/packages/SettingsLib/ValuePreference/Android.bp
@@ -27,5 +27,6 @@
min_sdk_version: "23",
apex_available: [
"//apex_available:platform",
+ "com.android.healthfitness",
],
}
diff --git a/packages/SettingsLib/aconfig/settingslib.aconfig b/packages/SettingsLib/aconfig/settingslib.aconfig
index 486b70f..2aeedc8 100644
--- a/packages/SettingsLib/aconfig/settingslib.aconfig
+++ b/packages/SettingsLib/aconfig/settingslib.aconfig
@@ -262,3 +262,13 @@
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "fix_battery_level_in_connection_summary"
+ namespace: "cross_device_experiences"
+ description: "Fix battery level in connection summary"
+ bug: "396543491"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/packages/SettingsLib/res/drawable/ic_float_portrait_2.xml b/packages/SettingsLib/res/drawable/ic_float_portrait_2.xml
new file mode 100644
index 0000000..e5b3c60
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_float_portrait_2.xml
@@ -0,0 +1,26 @@
+<!--
+ Copyright (C) 2025 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="960"
+ android:viewportHeight="960"
+ android:tint="?android:attr/colorControlNormal"
+ android:autoMirrored="true">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M320,440L560,440L560,240L320,240L320,440ZM800,800Q800,833 776.5,856.5Q753,880 720,880L240,880Q207,880 183.5,856.5Q160,833 160,800L160,160Q160,127 183.5,103.5Q207,80 240,80L720,80Q753,80 776.5,103.5Q800,127 800,160L800,800ZM720,800L720,160Q720,160 720,160Q720,160 720,160L240,160Q240,160 240,160Q240,160 240,160L240,800Q240,800 240,800Q240,800 240,800L720,800Q720,800 720,800Q720,800 720,800ZM720,160Q720,160 720,160Q720,160 720,160L240,160Q240,160 240,160Q240,160 240,160L240,160Q240,160 240,160Q240,160 240,160L720,160Q720,160 720,160Q720,160 720,160Z"/>
+</vector>
diff --git a/packages/SettingsLib/res/layout/dialog_with_icon.xml b/packages/SettingsLib/res/layout/dialog_with_icon.xml
index 1c54291..4881de3 100644
--- a/packages/SettingsLib/res/layout/dialog_with_icon.xml
+++ b/packages/SettingsLib/res/layout/dialog_with_icon.xml
@@ -45,7 +45,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hyphenationFrequency="fullFast"
- android:gravity="center"
+ android:gravity="start"
style="@style/TextAppearanceSmall"/>
<LinearLayout
diff --git a/packages/SettingsLib/res/layout/edit_user_info_dialog_content.xml b/packages/SettingsLib/res/layout/edit_user_info_dialog_content.xml
index 3ed1724..18712ac 100644
--- a/packages/SettingsLib/res/layout/edit_user_info_dialog_content.xml
+++ b/packages/SettingsLib/res/layout/edit_user_info_dialog_content.xml
@@ -40,7 +40,7 @@
</FrameLayout>
<TextView
android:id="@+id/edit_user_info_message"
- android:gravity="center"
+ android:gravity="start"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="6dp"
diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml
index 54074ec..8e0928c 100644
--- a/packages/SettingsLib/res/values-af/strings.xml
+++ b/packages/SettingsLib/res/values-af/strings.xml
@@ -551,11 +551,15 @@
</string-array>
<string name="charge_length_format" msgid="6941645744588690932">"<xliff:g id="ID_1">%1$s</xliff:g> gelede"</string>
<string name="remaining_length_format" msgid="4310625772926171089">"<xliff:g id="ID_1">%1$s</xliff:g> oor"</string>
+ <string name="screen_zoom_summary_smallest" msgid="1042159093382395789">"KIeinste"</string>
+ <string name="screen_zoom_summary_smaller" msgid="7452722393802683776">"Kleiner"</string>
<string name="screen_zoom_summary_small" msgid="6050633151263074260">"Klein"</string>
<string name="screen_zoom_summary_default" msgid="1888865694033865408">"Verstek"</string>
<string name="screen_zoom_summary_large" msgid="4706951482598978984">"Groot"</string>
- <string name="screen_zoom_summary_very_large" msgid="7317423942896999029">"Groter"</string>
- <string name="screen_zoom_summary_extremely_large" msgid="1438045624562358554">"Grootste"</string>
+ <string name="screen_zoom_summary_larger" msgid="7346406528348409139">"Groter"</string>
+ <string name="screen_zoom_summary_very_large" msgid="6853699118427019538">"Baie groot"</string>
+ <string name="screen_zoom_summary_extra_large" msgid="4898389280769475551">"Ekstra groot"</string>
+ <string name="screen_zoom_summary_largest" msgid="5120290329622591371">"Grootste"</string>
<string name="screen_zoom_summary_custom" msgid="3468154096832912210">"Gepasmaak (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
<string name="content_description_menu_button" msgid="6254844309171779931">"Kieslys"</string>
<string name="retail_demo_reset_message" msgid="5392824901108195463">"Voer wagwoord in om fabriekterugstelling in demonstrasiemodus uit te voer"</string>
diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml
index 1fab31b..7fbecd8 100644
--- a/packages/SettingsLib/res/values-am/strings.xml
+++ b/packages/SettingsLib/res/values-am/strings.xml
@@ -551,11 +551,15 @@
</string-array>
<string name="charge_length_format" msgid="6941645744588690932">"ከ<xliff:g id="ID_1">%1$s</xliff:g> በፊት"</string>
<string name="remaining_length_format" msgid="4310625772926171089">"<xliff:g id="ID_1">%1$s</xliff:g> ቀርቷል"</string>
+ <string name="screen_zoom_summary_smallest" msgid="1042159093382395789">"አነስተኛው"</string>
+ <string name="screen_zoom_summary_smaller" msgid="7452722393802683776">"አነስ ያለ"</string>
<string name="screen_zoom_summary_small" msgid="6050633151263074260">"ትንሽ"</string>
<string name="screen_zoom_summary_default" msgid="1888865694033865408">"ነባሪ"</string>
<string name="screen_zoom_summary_large" msgid="4706951482598978984">"ትልቅ"</string>
- <string name="screen_zoom_summary_very_large" msgid="7317423942896999029">"ተለቅ ያለ"</string>
- <string name="screen_zoom_summary_extremely_large" msgid="1438045624562358554">"በጣም ተለቅ ያለ"</string>
+ <string name="screen_zoom_summary_larger" msgid="7346406528348409139">"ተለቅ ያለ"</string>
+ <string name="screen_zoom_summary_very_large" msgid="6853699118427019538">"በጣም ትልቅ"</string>
+ <string name="screen_zoom_summary_extra_large" msgid="4898389280769475551">"ተጨማሪ ትልቅ"</string>
+ <string name="screen_zoom_summary_largest" msgid="5120290329622591371">"ትልቁ"</string>
<string name="screen_zoom_summary_custom" msgid="3468154096832912210">"ብጁ (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
<string name="content_description_menu_button" msgid="6254844309171779931">"ምናሌ"</string>
<string name="retail_demo_reset_message" msgid="5392824901108195463">"የፋብሪካ ዳግም ማስጀመር በማሳያ ሁነታ ውስጥ ለማከናወን የይለፍ ቃል ያስገቡ"</string>
diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml
index 6cb8154..ac0e1fc 100644
--- a/packages/SettingsLib/res/values-ar/strings.xml
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -248,7 +248,7 @@
<string name="choose_profile" msgid="343803890897657450">"تحديد الملف"</string>
<string name="category_personal" msgid="6236798763159385225">"الملف الشخصي"</string>
<string name="category_work" msgid="4014193632325996115">"ملف العمل"</string>
- <string name="category_private" msgid="4244892185452788977">"المساخة الخاصة"</string>
+ <string name="category_private" msgid="4244892185452788977">"الملف الخاص"</string>
<string name="category_clone" msgid="1554511758987195974">"استنساخ"</string>
<string name="development_settings_title" msgid="140296922921597393">"خيارات المطورين"</string>
<string name="development_settings_enable" msgid="4285094651288242183">"تفعيل خيارات المطورين"</string>
@@ -551,11 +551,15 @@
</string-array>
<string name="charge_length_format" msgid="6941645744588690932">"قبل <xliff:g id="ID_1">%1$s</xliff:g>"</string>
<string name="remaining_length_format" msgid="4310625772926171089">"يتبقى <xliff:g id="ID_1">%1$s</xliff:g>"</string>
+ <string name="screen_zoom_summary_smallest" msgid="1042159093382395789">"الأصغر"</string>
+ <string name="screen_zoom_summary_smaller" msgid="7452722393802683776">"أصغر"</string>
<string name="screen_zoom_summary_small" msgid="6050633151263074260">"صغير"</string>
<string name="screen_zoom_summary_default" msgid="1888865694033865408">"الإعداد التلقائي"</string>
<string name="screen_zoom_summary_large" msgid="4706951482598978984">"كبير"</string>
- <string name="screen_zoom_summary_very_large" msgid="7317423942896999029">"أكبر"</string>
- <string name="screen_zoom_summary_extremely_large" msgid="1438045624562358554">"أكبر مستوى"</string>
+ <string name="screen_zoom_summary_larger" msgid="7346406528348409139">"أكبر"</string>
+ <string name="screen_zoom_summary_very_large" msgid="6853699118427019538">"كبير جدًا"</string>
+ <string name="screen_zoom_summary_extra_large" msgid="4898389280769475551">"كبير جدًا"</string>
+ <string name="screen_zoom_summary_largest" msgid="5120290329622591371">"الأكبر"</string>
<string name="screen_zoom_summary_custom" msgid="3468154096832912210">"مخصص (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
<string name="content_description_menu_button" msgid="6254844309171779931">"القائمة"</string>
<string name="retail_demo_reset_message" msgid="5392824901108195463">"إدخال كلمة المرور لإعادة الضبط على الإعدادات الأصلية في الوضع التجريبي"</string>
@@ -571,7 +575,7 @@
<string name="ims_reg_status_not_registered" msgid="2989287366045704694">"غير مُسجَّل"</string>
<string name="status_unavailable" msgid="5279036186589861608">"غير متاح"</string>
<string name="wifi_status_mac_randomized" msgid="466382542497832189">"يتم اختيار عنوان MAC بشكل انتقائي."</string>
- <string name="wifi_tether_connected_summary" msgid="5100712926640492336">"{count,plural, =1{هناك جهاز واحد متصل.}zero{هناك # جهاز متصل.}two{هناك جهازان متصلان.}few{هناك # أجهزة متصلة.}many{هناك # جهازًا متصلاً.}other{هناك # جهاز متصل.}}"</string>
+ <string name="wifi_tether_connected_summary" msgid="5100712926640492336">"{count,plural, =1{هناك جهاز واحد متصل}zero{ليس هناك أي أجهزة متّصلة}two{هناك جهازان متصلان}few{هناك # أجهزة متصلة}many{هناك # جهازًا متصلاً}other{هناك # جهاز متصل}}"</string>
<string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"وقت أكثر."</string>
<string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"وقت أقل."</string>
<string name="cancel" msgid="5665114069455378395">"إلغاء"</string>
diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml
index b39593e..0eac060 100644
--- a/packages/SettingsLib/res/values-as/strings.xml
+++ b/packages/SettingsLib/res/values-as/strings.xml
@@ -551,11 +551,15 @@
</string-array>
<string name="charge_length_format" msgid="6941645744588690932">"<xliff:g id="ID_1">%1$s</xliff:g> আগত"</string>
<string name="remaining_length_format" msgid="4310625772926171089">"<xliff:g id="ID_1">%1$s</xliff:g> বাকী আছে"</string>
+ <string name="screen_zoom_summary_smallest" msgid="1042159093382395789">"আটাইতকৈ সৰু"</string>
+ <string name="screen_zoom_summary_smaller" msgid="7452722393802683776">"অধিক সৰু"</string>
<string name="screen_zoom_summary_small" msgid="6050633151263074260">"সৰু"</string>
<string name="screen_zoom_summary_default" msgid="1888865694033865408">"ডিফ’ল্ট"</string>
<string name="screen_zoom_summary_large" msgid="4706951482598978984">"ডাঙৰ"</string>
- <string name="screen_zoom_summary_very_large" msgid="7317423942896999029">"বৃহত্তৰ"</string>
- <string name="screen_zoom_summary_extremely_large" msgid="1438045624562358554">"সকলোতকৈ ডাঙৰ"</string>
+ <string name="screen_zoom_summary_larger" msgid="7346406528348409139">"অধিক ডাঙৰ"</string>
+ <string name="screen_zoom_summary_very_large" msgid="6853699118427019538">"বেছি ডাঙৰ"</string>
+ <string name="screen_zoom_summary_extra_large" msgid="4898389280769475551">"বেছি ডাঙৰ আকাৰৰ"</string>
+ <string name="screen_zoom_summary_largest" msgid="5120290329622591371">"সকলোতকৈ ডাঙৰ"</string>
<string name="screen_zoom_summary_custom" msgid="3468154096832912210">"নিজৰ উপযোগিতা অনুযায়ী (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
<string name="content_description_menu_button" msgid="6254844309171779931">"মেনু"</string>
<string name="retail_demo_reset_message" msgid="5392824901108195463">"ডেম’ ম\'ডত ফেক্টৰী ৰিছেট কৰিবলৈ পাছৱৰ্ড দিয়ক"</string>
diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml
index de74a46..b0b5415 100644
--- a/packages/SettingsLib/res/values-az/strings.xml
+++ b/packages/SettingsLib/res/values-az/strings.xml
@@ -551,11 +551,15 @@
</string-array>
<string name="charge_length_format" msgid="6941645744588690932">"<xliff:g id="ID_1">%1$s</xliff:g> əvvəl"</string>
<string name="remaining_length_format" msgid="4310625772926171089">"<xliff:g id="ID_1">%1$s</xliff:g> qalıb"</string>
+ <string name="screen_zoom_summary_smallest" msgid="1042159093382395789">"Ən kiçik"</string>
+ <string name="screen_zoom_summary_smaller" msgid="7452722393802683776">"Daha kiçik"</string>
<string name="screen_zoom_summary_small" msgid="6050633151263074260">"Kiçik"</string>
<string name="screen_zoom_summary_default" msgid="1888865694033865408">"Defolt"</string>
<string name="screen_zoom_summary_large" msgid="4706951482598978984">"Böyük"</string>
- <string name="screen_zoom_summary_very_large" msgid="7317423942896999029">"Daha böyük"</string>
- <string name="screen_zoom_summary_extremely_large" msgid="1438045624562358554">"Ən böyük"</string>
+ <string name="screen_zoom_summary_larger" msgid="7346406528348409139">"Daha böyük"</string>
+ <string name="screen_zoom_summary_very_large" msgid="6853699118427019538">"Çox böyük"</string>
+ <string name="screen_zoom_summary_extra_large" msgid="4898389280769475551">"Həddən artıq böyük"</string>
+ <string name="screen_zoom_summary_largest" msgid="5120290329622591371">"Ən böyük"</string>
<string name="screen_zoom_summary_custom" msgid="3468154096832912210">"Fərdi (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
<string name="content_description_menu_button" msgid="6254844309171779931">"Menyu"</string>
<string name="retail_demo_reset_message" msgid="5392824901108195463">"Demo rejimində sıfırlamaq üçün parol daxil edin"</string>
diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
index 3db1617..162b935 100644
--- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
+++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
@@ -551,11 +551,15 @@
</string-array>
<string name="charge_length_format" msgid="6941645744588690932">"Pre <xliff:g id="ID_1">%1$s</xliff:g>"</string>
<string name="remaining_length_format" msgid="4310625772926171089">"Još <xliff:g id="ID_1">%1$s</xliff:g>"</string>
+ <string name="screen_zoom_summary_smallest" msgid="1042159093382395789">"Najmanje"</string>
+ <string name="screen_zoom_summary_smaller" msgid="7452722393802683776">"Manje"</string>
<string name="screen_zoom_summary_small" msgid="6050633151263074260">"Mali"</string>
<string name="screen_zoom_summary_default" msgid="1888865694033865408">"Podrazumevano"</string>
<string name="screen_zoom_summary_large" msgid="4706951482598978984">"Veliki"</string>
- <string name="screen_zoom_summary_very_large" msgid="7317423942896999029">"Veći"</string>
- <string name="screen_zoom_summary_extremely_large" msgid="1438045624562358554">"Najveći"</string>
+ <string name="screen_zoom_summary_larger" msgid="7346406528348409139">"Veće"</string>
+ <string name="screen_zoom_summary_very_large" msgid="6853699118427019538">"Veoma veliko"</string>
+ <string name="screen_zoom_summary_extra_large" msgid="4898389280769475551">"Veoma veliko"</string>
+ <string name="screen_zoom_summary_largest" msgid="5120290329622591371">"Najveće"</string>
<string name="screen_zoom_summary_custom" msgid="3468154096832912210">"Prilagođeni (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
<string name="content_description_menu_button" msgid="6254844309171779931">"Meni"</string>
<string name="retail_demo_reset_message" msgid="5392824901108195463">"Unesite lozinku da biste obavili resetovanje na fabrička podešavanja u režimu demonstracije"</string>
diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml
index 74dd666..39e93596 100644
--- a/packages/SettingsLib/res/values-be/strings.xml
+++ b/packages/SettingsLib/res/values-be/strings.xml
@@ -551,11 +551,15 @@
</string-array>
<string name="charge_length_format" msgid="6941645744588690932">"<xliff:g id="ID_1">%1$s</xliff:g> таму"</string>
<string name="remaining_length_format" msgid="4310625772926171089">"Засталося <xliff:g id="ID_1">%1$s</xliff:g>"</string>
+ <string name="screen_zoom_summary_smallest" msgid="1042159093382395789">"Самы маленькі"</string>
+ <string name="screen_zoom_summary_smaller" msgid="7452722393802683776">"Меншы"</string>
<string name="screen_zoom_summary_small" msgid="6050633151263074260">"Маленькі"</string>
<string name="screen_zoom_summary_default" msgid="1888865694033865408">"Стандартны"</string>
<string name="screen_zoom_summary_large" msgid="4706951482598978984">"Вялікі"</string>
- <string name="screen_zoom_summary_very_large" msgid="7317423942896999029">"Большы"</string>
- <string name="screen_zoom_summary_extremely_large" msgid="1438045624562358554">"Самы вялікі"</string>
+ <string name="screen_zoom_summary_larger" msgid="7346406528348409139">"Большы"</string>
+ <string name="screen_zoom_summary_very_large" msgid="6853699118427019538">"Вельмі вялікі"</string>
+ <string name="screen_zoom_summary_extra_large" msgid="4898389280769475551">"Вялізны"</string>
+ <string name="screen_zoom_summary_largest" msgid="5120290329622591371">"Самы вялікі"</string>
<string name="screen_zoom_summary_custom" msgid="3468154096832912210">"Карыстальніцкі (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
<string name="content_description_menu_button" msgid="6254844309171779931">"Меню"</string>
<string name="retail_demo_reset_message" msgid="5392824901108195463">"Каб выканаць скід да заводскіх налад у дэманстрацыйным рэжыме, увядзіце пароль"</string>
diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml
index b0ff126..cee36b5 100644
--- a/packages/SettingsLib/res/values-bg/strings.xml
+++ b/packages/SettingsLib/res/values-bg/strings.xml
@@ -551,11 +551,15 @@
</string-array>
<string name="charge_length_format" msgid="6941645744588690932">"Преди <xliff:g id="ID_1">%1$s</xliff:g>"</string>
<string name="remaining_length_format" msgid="4310625772926171089">"Оставащо време: <xliff:g id="ID_1">%1$s</xliff:g>"</string>
+ <string name="screen_zoom_summary_smallest" msgid="1042159093382395789">"Най-малък"</string>
+ <string name="screen_zoom_summary_smaller" msgid="7452722393802683776">"По-малък"</string>
<string name="screen_zoom_summary_small" msgid="6050633151263074260">"Малко"</string>
<string name="screen_zoom_summary_default" msgid="1888865694033865408">"По подразбиране"</string>
<string name="screen_zoom_summary_large" msgid="4706951482598978984">"Голямо"</string>
- <string name="screen_zoom_summary_very_large" msgid="7317423942896999029">"По-голямо"</string>
- <string name="screen_zoom_summary_extremely_large" msgid="1438045624562358554">"Най-голямо"</string>
+ <string name="screen_zoom_summary_larger" msgid="7346406528348409139">"По-голям"</string>
+ <string name="screen_zoom_summary_very_large" msgid="6853699118427019538">"Много голям"</string>
+ <string name="screen_zoom_summary_extra_large" msgid="4898389280769475551">"Много голям"</string>
+ <string name="screen_zoom_summary_largest" msgid="5120290329622591371">"Най-голям"</string>
<string name="screen_zoom_summary_custom" msgid="3468154096832912210">"Персонализирано (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
<string name="content_description_menu_button" msgid="6254844309171779931">"Меню"</string>
<string name="retail_demo_reset_message" msgid="5392824901108195463">"Въведете парола за възст. на фабр. настройки в демонстр. режим"</string>
diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml
index 9afef62..35453ff 100644
--- a/packages/SettingsLib/res/values-bn/strings.xml
+++ b/packages/SettingsLib/res/values-bn/strings.xml
@@ -327,7 +327,7 @@
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3233402355917446304">"ব্লুটুথ অডিও LDAC কোডেক: প্লেব্যাক গুণমান"</string>
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="7274396574659784285">"ব্লুটুথ অডিও LDAC কোডেক ট্রিগার করুন\nএটি বেছে নেওয়া আছে: প্লেব্যাকের কোয়ালিটি"</string>
<string name="bluetooth_select_a2dp_codec_streaming_label" msgid="2040810756832027227">"স্ট্রিমিং: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
- <string name="select_private_dns_configuration_title" msgid="7887550926056143018">"প্রাইভেট ডিএনএস"</string>
+ <string name="select_private_dns_configuration_title" msgid="7887550926056143018">"ব্যক্তিগত ডিএনএস"</string>
<string name="select_private_dns_configuration_dialog_title" msgid="3731422918335951912">"ব্যক্তিগত ডিএনএস মোড বেছে নিন"</string>
<string name="private_dns_mode_off" msgid="7065962499349997041">"বন্ধ আছে"</string>
<string name="private_dns_mode_opportunistic" msgid="1947864819060442354">"অটোমেটিক"</string>
@@ -492,7 +492,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"প্রোটানোম্যালি (লাল-সবুজ)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"ট্রিট্যানোম্যালি (নীল-হলুদ)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"রঙ সংশোধন"</string>
- <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"\'রঙ সংশোধন করা\' ফিচারের সাহায্যে এইসব কাজে করা যেতে পারে:<br/> <ol> <li>&nbsp;আরও সঠিকভাবে রঙ দেখা</li> <li>&nbsp;ফোকাস করার জন্য রঙ সরানো</li> </ol>"</string>
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"\'রঙ সংশোধন\' ফিচারের সাহায্যে এইসব কাজে করা যেতে পারে:<br/> <ol> <li>&nbsp;আরও সঠিকভাবে রঙ দেখা</li> <li>&nbsp;ফোকাস করার জন্য রঙ সরানো</li> </ol>"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> এর দ্বারা ওভাররাইড করা হয়েছে"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - ব্যাটারিকে সুরক্ষিত রাখতে চার্জিং হোল্ড করা হয়েছে"</string>
@@ -551,11 +551,15 @@
</string-array>
<string name="charge_length_format" msgid="6941645744588690932">"<xliff:g id="ID_1">%1$s</xliff:g> আগে"</string>
<string name="remaining_length_format" msgid="4310625772926171089">"<xliff:g id="ID_1">%1$s</xliff:g> বাকি আছে"</string>
+ <string name="screen_zoom_summary_smallest" msgid="1042159093382395789">"সবচেয়ে ছোট"</string>
+ <string name="screen_zoom_summary_smaller" msgid="7452722393802683776">"আরও ছোট"</string>
<string name="screen_zoom_summary_small" msgid="6050633151263074260">"ক্ষুদ্র"</string>
<string name="screen_zoom_summary_default" msgid="1888865694033865408">"ডিফল্ট"</string>
<string name="screen_zoom_summary_large" msgid="4706951482598978984">"বড়"</string>
- <string name="screen_zoom_summary_very_large" msgid="7317423942896999029">"খুব বড়"</string>
- <string name="screen_zoom_summary_extremely_large" msgid="1438045624562358554">"বৃহত্তম"</string>
+ <string name="screen_zoom_summary_larger" msgid="7346406528348409139">"আরও বড়"</string>
+ <string name="screen_zoom_summary_very_large" msgid="6853699118427019538">"খুব বড়"</string>
+ <string name="screen_zoom_summary_extra_large" msgid="4898389280769475551">"অত্যন্ত বড়"</string>
+ <string name="screen_zoom_summary_largest" msgid="5120290329622591371">"সবচেয়ে বড়"</string>
<string name="screen_zoom_summary_custom" msgid="3468154096832912210">"কাস্টম (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
<string name="content_description_menu_button" msgid="6254844309171779931">"মেনু"</string>
<string name="retail_demo_reset_message" msgid="5392824901108195463">"ডেমো মোডে ফ্যাক্টরি রিসেট করতে পাসওয়ার্ড দিন"</string>
diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml
index 22de5005..b10543e 100644
--- a/packages/SettingsLib/res/values-bs/strings.xml
+++ b/packages/SettingsLib/res/values-bs/strings.xml
@@ -551,11 +551,15 @@
</string-array>
<string name="charge_length_format" msgid="6941645744588690932">"prije <xliff:g id="ID_1">%1$s</xliff:g>"</string>
<string name="remaining_length_format" msgid="4310625772926171089">"Još otprilike <xliff:g id="ID_1">%1$s</xliff:g>"</string>
+ <string name="screen_zoom_summary_smallest" msgid="1042159093382395789">"Najmanje"</string>
+ <string name="screen_zoom_summary_smaller" msgid="7452722393802683776">"Manje"</string>
<string name="screen_zoom_summary_small" msgid="6050633151263074260">"Malo"</string>
<string name="screen_zoom_summary_default" msgid="1888865694033865408">"Zadano"</string>
<string name="screen_zoom_summary_large" msgid="4706951482598978984">"Veliko"</string>
- <string name="screen_zoom_summary_very_large" msgid="7317423942896999029">"Vrlo veliko"</string>
- <string name="screen_zoom_summary_extremely_large" msgid="1438045624562358554">"Najveći"</string>
+ <string name="screen_zoom_summary_larger" msgid="7346406528348409139">"Veće"</string>
+ <string name="screen_zoom_summary_very_large" msgid="6853699118427019538">"Veoma veliko"</string>
+ <string name="screen_zoom_summary_extra_large" msgid="4898389280769475551">"Veoma veliko"</string>
+ <string name="screen_zoom_summary_largest" msgid="5120290329622591371">"Najveće"</string>
<string name="screen_zoom_summary_custom" msgid="3468154096832912210">"Prilagodi (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
<string name="content_description_menu_button" msgid="6254844309171779931">"Meni"</string>
<string name="retail_demo_reset_message" msgid="5392824901108195463">"Unesite lozinku da izvršite vraćanje na fabričke postavke u načinu rada za demonstraciju"</string>
diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml
index da50f091..387b187 100644
--- a/packages/SettingsLib/res/values-ca/strings.xml
+++ b/packages/SettingsLib/res/values-ca/strings.xml
@@ -551,11 +551,15 @@
</string-array>
<string name="charge_length_format" msgid="6941645744588690932">"Fa <xliff:g id="ID_1">%1$s</xliff:g>"</string>
<string name="remaining_length_format" msgid="4310625772926171089">"Temps restant: <xliff:g id="ID_1">%1$s</xliff:g>"</string>
+ <string name="screen_zoom_summary_smallest" msgid="1042159093382395789">"Més petit possible"</string>
+ <string name="screen_zoom_summary_smaller" msgid="7452722393802683776">"Més petit"</string>
<string name="screen_zoom_summary_small" msgid="6050633151263074260">"Petit"</string>
<string name="screen_zoom_summary_default" msgid="1888865694033865408">"Predeterminat"</string>
<string name="screen_zoom_summary_large" msgid="4706951482598978984">"Gran"</string>
- <string name="screen_zoom_summary_very_large" msgid="7317423942896999029">"Més gran"</string>
- <string name="screen_zoom_summary_extremely_large" msgid="1438045624562358554">"El més gran"</string>
+ <string name="screen_zoom_summary_larger" msgid="7346406528348409139">"Més gran"</string>
+ <string name="screen_zoom_summary_very_large" msgid="6853699118427019538">"Molt gran"</string>
+ <string name="screen_zoom_summary_extra_large" msgid="4898389280769475551">"Extragran"</string>
+ <string name="screen_zoom_summary_largest" msgid="5120290329622591371">"Més gran possible"</string>
<string name="screen_zoom_summary_custom" msgid="3468154096832912210">"Personalitzat (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
<string name="content_description_menu_button" msgid="6254844309171779931">"Menú"</string>
<string name="retail_demo_reset_message" msgid="5392824901108195463">"Introdueix la contrasenya per restablir les dades de fàbrica en mode de demostració"</string>
diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml
index 9d61a5a..cd9aef3 100644
--- a/packages/SettingsLib/res/values-cs/strings.xml
+++ b/packages/SettingsLib/res/values-cs/strings.xml
@@ -127,7 +127,7 @@
<string name="bluetooth_guest_no_battery_level_lea_support" msgid="2977038548753103470">"Zařízení hosta (podporuje sdílení zvuku)"</string>
<string name="bluetooth_active_media_only_no_battery_level" msgid="71106861912593126">"Aktivní (pouze média)"</string>
<string name="bluetooth_guest_no_battery_level" msgid="9122974160381136920">"Zařízení hosta"</string>
- <string name="bluetooth_guest_media_only_no_battery_level" msgid="7666347601796705721">"Zařízení hosta (jen media)"</string>
+ <string name="bluetooth_guest_media_only_no_battery_level" msgid="7666347601796705721">"Zařízení hosta (jen média)"</string>
<string name="bluetooth_saved_device_lea_support" msgid="7231323139968285768">"Podporuje sdílení zvuku"</string>
<string name="bluetooth_guest_saved_device_lea_support" msgid="5621291599518569876">"Zařízení hosta. Podporuje sdílení zvuku"</string>
<string name="bluetooth_hearing_aid_media_only_left_active" msgid="1632152540901488645">"Aktivní (pouze média), pouze levé"</string>
@@ -209,7 +209,7 @@
<string name="tts_settings" msgid="8130616705989351312">"Nastavení převodu textu na řeč"</string>
<string name="tts_settings_title" msgid="7602210956640483039">"Převod textu na řeč"</string>
<string name="tts_default_rate_title" msgid="3964187817364304022">"Rychlost řeči"</string>
- <string name="tts_default_rate_summary" msgid="3781937042151716987">"Rychlost mluveného textu"</string>
+ <string name="tts_default_rate_summary" msgid="3781937042151716987">"Jak rychle je text předčítán"</string>
<string name="tts_default_pitch_title" msgid="6988592215554485479">"Výška"</string>
<string name="tts_default_pitch_summary" msgid="9132719475281551884">"Určuje tón syntetizované řeči"</string>
<string name="tts_default_lang_title" msgid="4698933575028098940">"Jazyk"</string>
@@ -492,7 +492,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomálie (červená a zelená)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomálie (modrá a žlutá)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Korekce barev"</string>
- <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"Korekce barev se může hodit, když chcete:<br/> <ol> <li>&nbsp;Vidět barvy přesněji.</li> <li>&nbsp;Odstranit barvy kvůli zlepšení soustředění.</li> </ol>"</string>
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"Korekce barev se může hodit, když chcete:<br/> <ol> <li>&nbsp;Vidět barvy přesněji.</li> <li>&nbsp;Odstranit barvy kvůli lepšímu soustředění.</li> </ol>"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Přepsáno nastavením <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> – Nabíjení je pozastaveno za účelem ochrany baterie"</string>
@@ -551,11 +551,15 @@
</string-array>
<string name="charge_length_format" msgid="6941645744588690932">"Před <xliff:g id="ID_1">%1$s</xliff:g>"</string>
<string name="remaining_length_format" msgid="4310625772926171089">"zbývá: <xliff:g id="ID_1">%1$s</xliff:g>"</string>
+ <string name="screen_zoom_summary_smallest" msgid="1042159093382395789">"Nejmenší"</string>
+ <string name="screen_zoom_summary_smaller" msgid="7452722393802683776">"Menší"</string>
<string name="screen_zoom_summary_small" msgid="6050633151263074260">"Malé"</string>
<string name="screen_zoom_summary_default" msgid="1888865694033865408">"Výchozí"</string>
<string name="screen_zoom_summary_large" msgid="4706951482598978984">"Velké"</string>
- <string name="screen_zoom_summary_very_large" msgid="7317423942896999029">"Větší"</string>
- <string name="screen_zoom_summary_extremely_large" msgid="1438045624562358554">"Největší"</string>
+ <string name="screen_zoom_summary_larger" msgid="7346406528348409139">"Větší"</string>
+ <string name="screen_zoom_summary_very_large" msgid="6853699118427019538">"Velmi velké"</string>
+ <string name="screen_zoom_summary_extra_large" msgid="4898389280769475551">"Extra velké"</string>
+ <string name="screen_zoom_summary_largest" msgid="5120290329622591371">"Největší"</string>
<string name="screen_zoom_summary_custom" msgid="3468154096832912210">"Vlastní (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
<string name="content_description_menu_button" msgid="6254844309171779931">"Nabídka"</string>
<string name="retail_demo_reset_message" msgid="5392824901108195463">"Chcete-li v ukázkovém režimu obnovit zařízení do továrního nastavení, zadejte heslo"</string>
diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml
index 91b6420..fd25532 100644
--- a/packages/SettingsLib/res/values-da/strings.xml
+++ b/packages/SettingsLib/res/values-da/strings.xml
@@ -551,11 +551,15 @@
</string-array>
<string name="charge_length_format" msgid="6941645744588690932">"For <xliff:g id="ID_1">%1$s</xliff:g> siden"</string>
<string name="remaining_length_format" msgid="4310625772926171089">"<xliff:g id="ID_1">%1$s</xliff:g> tilbage"</string>
+ <string name="screen_zoom_summary_smallest" msgid="1042159093382395789">"Mindste"</string>
+ <string name="screen_zoom_summary_smaller" msgid="7452722393802683776">"Mindre"</string>
<string name="screen_zoom_summary_small" msgid="6050633151263074260">"Lille"</string>
<string name="screen_zoom_summary_default" msgid="1888865694033865408">"Standard"</string>
<string name="screen_zoom_summary_large" msgid="4706951482598978984">"Stor"</string>
- <string name="screen_zoom_summary_very_large" msgid="7317423942896999029">"Større"</string>
- <string name="screen_zoom_summary_extremely_large" msgid="1438045624562358554">"Størst"</string>
+ <string name="screen_zoom_summary_larger" msgid="7346406528348409139">"Større"</string>
+ <string name="screen_zoom_summary_very_large" msgid="6853699118427019538">"Meget stor"</string>
+ <string name="screen_zoom_summary_extra_large" msgid="4898389280769475551">"Ekstra stor"</string>
+ <string name="screen_zoom_summary_largest" msgid="5120290329622591371">"Størst"</string>
<string name="screen_zoom_summary_custom" msgid="3468154096832912210">"Tilpasset (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
<string name="content_description_menu_button" msgid="6254844309171779931">"Menu"</string>
<string name="retail_demo_reset_message" msgid="5392824901108195463">"Angiv adgangskode for at gendanne fabriksdata i demotilstand"</string>
diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml
index 0c7ea02..ffcab0d 100644
--- a/packages/SettingsLib/res/values-de/strings.xml
+++ b/packages/SettingsLib/res/values-de/strings.xml
@@ -492,7 +492,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomalie (Rot-Grün-Sehschwäche)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomalie (Blau-Gelb-Sehschwäche)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Farbkorrektur"</string>
- <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"Die Farbkorrektur kann nützlich sein, wenn du:<br/> <ol> <li>&nbsp;Farben noch genauer sehen möchtest</li> <li>&nbsp;Bestimmte Farben entfernen möchtest, um dich besser zu konzentrieren</li> </ol>"</string>
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"Die Farbkorrektur kann nützlich sein, wenn du:<br/> <ol> <li>&nbsp;Farben deutlicher sehen möchtest</li> <li>&nbsp;Farben entfernen möchtest, um dich besser zu konzentrieren</li> </ol>"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Außer Kraft gesetzt von \"<xliff:g id="TITLE">%1$s</xliff:g>\""</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> – Ladevorgang zum Schutz des Akkus angehalten"</string>
@@ -551,11 +551,15 @@
</string-array>
<string name="charge_length_format" msgid="6941645744588690932">"Vor <xliff:g id="ID_1">%1$s</xliff:g>"</string>
<string name="remaining_length_format" msgid="4310625772926171089">"Restlaufzeit: <xliff:g id="ID_1">%1$s</xliff:g>"</string>
+ <string name="screen_zoom_summary_smallest" msgid="1042159093382395789">"Minimale Größe"</string>
+ <string name="screen_zoom_summary_smaller" msgid="7452722393802683776">"Kleiner"</string>
<string name="screen_zoom_summary_small" msgid="6050633151263074260">"Klein"</string>
<string name="screen_zoom_summary_default" msgid="1888865694033865408">"Standard"</string>
<string name="screen_zoom_summary_large" msgid="4706951482598978984">"Groß"</string>
- <string name="screen_zoom_summary_very_large" msgid="7317423942896999029">"Größer"</string>
- <string name="screen_zoom_summary_extremely_large" msgid="1438045624562358554">"Am größten"</string>
+ <string name="screen_zoom_summary_larger" msgid="7346406528348409139">"Größer"</string>
+ <string name="screen_zoom_summary_very_large" msgid="6853699118427019538">"Sehr groß"</string>
+ <string name="screen_zoom_summary_extra_large" msgid="4898389280769475551">"Extragroß"</string>
+ <string name="screen_zoom_summary_largest" msgid="5120290329622591371">"Maximale Größe"</string>
<string name="screen_zoom_summary_custom" msgid="3468154096832912210">"Benutzerdefiniert (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
<string name="content_description_menu_button" msgid="6254844309171779931">"Menü"</string>
<string name="retail_demo_reset_message" msgid="5392824901108195463">"Passwort eingeben, um Gerät im Demomodus auf Werkseinstellungen zurückzusetzen"</string>
diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml
index 17423ba..46dca88 100644
--- a/packages/SettingsLib/res/values-el/strings.xml
+++ b/packages/SettingsLib/res/values-el/strings.xml
@@ -551,11 +551,15 @@
</string-array>
<string name="charge_length_format" msgid="6941645744588690932">"Πριν <xliff:g id="ID_1">%1$s</xliff:g>"</string>
<string name="remaining_length_format" msgid="4310625772926171089">"Απομένουν <xliff:g id="ID_1">%1$s</xliff:g>"</string>
+ <string name="screen_zoom_summary_smallest" msgid="1042159093382395789">"Μικρότερο δυνατό"</string>
+ <string name="screen_zoom_summary_smaller" msgid="7452722393802683776">"Μικρότερο"</string>
<string name="screen_zoom_summary_small" msgid="6050633151263074260">"Μικρά"</string>
<string name="screen_zoom_summary_default" msgid="1888865694033865408">"Προεπιλογή"</string>
<string name="screen_zoom_summary_large" msgid="4706951482598978984">"Μεγάλα"</string>
- <string name="screen_zoom_summary_very_large" msgid="7317423942896999029">"Πιο μεγάλα"</string>
- <string name="screen_zoom_summary_extremely_large" msgid="1438045624562358554">"Μεγαλύτερα"</string>
+ <string name="screen_zoom_summary_larger" msgid="7346406528348409139">"Μεγαλύτερο"</string>
+ <string name="screen_zoom_summary_very_large" msgid="6853699118427019538">"Πολύ μεγάλο"</string>
+ <string name="screen_zoom_summary_extra_large" msgid="4898389280769475551">"Πάρα πολύ μεγάλο"</string>
+ <string name="screen_zoom_summary_largest" msgid="5120290329622591371">"Μεγαλύτερο δυνατό"</string>
<string name="screen_zoom_summary_custom" msgid="3468154096832912210">"Προσαρμοσμένη (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
<string name="content_description_menu_button" msgid="6254844309171779931">"Μενού"</string>
<string name="retail_demo_reset_message" msgid="5392824901108195463">"Εισαγάγετε κωδικό πρόσβασης για επαναφορά εργοστασιακών ρυθμίσεων στη λειτουργία επίδειξης"</string>
diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml
index 109558e..485adfd 100644
--- a/packages/SettingsLib/res/values-en-rAU/strings.xml
+++ b/packages/SettingsLib/res/values-en-rAU/strings.xml
@@ -551,11 +551,15 @@
</string-array>
<string name="charge_length_format" msgid="6941645744588690932">"<xliff:g id="ID_1">%1$s</xliff:g> ago"</string>
<string name="remaining_length_format" msgid="4310625772926171089">"<xliff:g id="ID_1">%1$s</xliff:g> left"</string>
+ <string name="screen_zoom_summary_smallest" msgid="1042159093382395789">"Smallest"</string>
+ <string name="screen_zoom_summary_smaller" msgid="7452722393802683776">"Smaller"</string>
<string name="screen_zoom_summary_small" msgid="6050633151263074260">"Small"</string>
<string name="screen_zoom_summary_default" msgid="1888865694033865408">"Default"</string>
<string name="screen_zoom_summary_large" msgid="4706951482598978984">"Large"</string>
- <string name="screen_zoom_summary_very_large" msgid="7317423942896999029">"Larger"</string>
- <string name="screen_zoom_summary_extremely_large" msgid="1438045624562358554">"Largest"</string>
+ <string name="screen_zoom_summary_larger" msgid="7346406528348409139">"Larger"</string>
+ <string name="screen_zoom_summary_very_large" msgid="6853699118427019538">"Very large"</string>
+ <string name="screen_zoom_summary_extra_large" msgid="4898389280769475551">"Extra large"</string>
+ <string name="screen_zoom_summary_largest" msgid="5120290329622591371">"Largest"</string>
<string name="screen_zoom_summary_custom" msgid="3468154096832912210">"Custom (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
<string name="content_description_menu_button" msgid="6254844309171779931">"Menu"</string>
<string name="retail_demo_reset_message" msgid="5392824901108195463">"Enter password to perform factory reset in demo mode"</string>
diff --git a/packages/SettingsLib/res/values-en-rCA/strings.xml b/packages/SettingsLib/res/values-en-rCA/strings.xml
index 002b104..7651598 100644
--- a/packages/SettingsLib/res/values-en-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-en-rCA/strings.xml
@@ -551,11 +551,15 @@
</string-array>
<string name="charge_length_format" msgid="6941645744588690932">"<xliff:g id="ID_1">%1$s</xliff:g> ago"</string>
<string name="remaining_length_format" msgid="4310625772926171089">"<xliff:g id="ID_1">%1$s</xliff:g> left"</string>
+ <string name="screen_zoom_summary_smallest" msgid="1042159093382395789">"Smallest"</string>
+ <string name="screen_zoom_summary_smaller" msgid="7452722393802683776">"Smaller"</string>
<string name="screen_zoom_summary_small" msgid="6050633151263074260">"Small"</string>
<string name="screen_zoom_summary_default" msgid="1888865694033865408">"Default"</string>
<string name="screen_zoom_summary_large" msgid="4706951482598978984">"Large"</string>
- <string name="screen_zoom_summary_very_large" msgid="7317423942896999029">"Larger"</string>
- <string name="screen_zoom_summary_extremely_large" msgid="1438045624562358554">"Largest"</string>
+ <string name="screen_zoom_summary_larger" msgid="7346406528348409139">"Larger"</string>
+ <string name="screen_zoom_summary_very_large" msgid="6853699118427019538">"Very Large"</string>
+ <string name="screen_zoom_summary_extra_large" msgid="4898389280769475551">"Extra Large"</string>
+ <string name="screen_zoom_summary_largest" msgid="5120290329622591371">"Largest"</string>
<string name="screen_zoom_summary_custom" msgid="3468154096832912210">"Custom (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
<string name="content_description_menu_button" msgid="6254844309171779931">"Menu"</string>
<string name="retail_demo_reset_message" msgid="5392824901108195463">"Enter password to perform factory reset in demo mode"</string>
diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml
index 109558e..485adfd 100644
--- a/packages/SettingsLib/res/values-en-rGB/strings.xml
+++ b/packages/SettingsLib/res/values-en-rGB/strings.xml
@@ -551,11 +551,15 @@
</string-array>
<string name="charge_length_format" msgid="6941645744588690932">"<xliff:g id="ID_1">%1$s</xliff:g> ago"</string>
<string name="remaining_length_format" msgid="4310625772926171089">"<xliff:g id="ID_1">%1$s</xliff:g> left"</string>
+ <string name="screen_zoom_summary_smallest" msgid="1042159093382395789">"Smallest"</string>
+ <string name="screen_zoom_summary_smaller" msgid="7452722393802683776">"Smaller"</string>
<string name="screen_zoom_summary_small" msgid="6050633151263074260">"Small"</string>
<string name="screen_zoom_summary_default" msgid="1888865694033865408">"Default"</string>
<string name="screen_zoom_summary_large" msgid="4706951482598978984">"Large"</string>
- <string name="screen_zoom_summary_very_large" msgid="7317423942896999029">"Larger"</string>
- <string name="screen_zoom_summary_extremely_large" msgid="1438045624562358554">"Largest"</string>
+ <string name="screen_zoom_summary_larger" msgid="7346406528348409139">"Larger"</string>
+ <string name="screen_zoom_summary_very_large" msgid="6853699118427019538">"Very large"</string>
+ <string name="screen_zoom_summary_extra_large" msgid="4898389280769475551">"Extra large"</string>
+ <string name="screen_zoom_summary_largest" msgid="5120290329622591371">"Largest"</string>
<string name="screen_zoom_summary_custom" msgid="3468154096832912210">"Custom (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
<string name="content_description_menu_button" msgid="6254844309171779931">"Menu"</string>
<string name="retail_demo_reset_message" msgid="5392824901108195463">"Enter password to perform factory reset in demo mode"</string>
diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml
index 109558e..485adfd 100644
--- a/packages/SettingsLib/res/values-en-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-en-rIN/strings.xml
@@ -551,11 +551,15 @@
</string-array>
<string name="charge_length_format" msgid="6941645744588690932">"<xliff:g id="ID_1">%1$s</xliff:g> ago"</string>
<string name="remaining_length_format" msgid="4310625772926171089">"<xliff:g id="ID_1">%1$s</xliff:g> left"</string>
+ <string name="screen_zoom_summary_smallest" msgid="1042159093382395789">"Smallest"</string>
+ <string name="screen_zoom_summary_smaller" msgid="7452722393802683776">"Smaller"</string>
<string name="screen_zoom_summary_small" msgid="6050633151263074260">"Small"</string>
<string name="screen_zoom_summary_default" msgid="1888865694033865408">"Default"</string>
<string name="screen_zoom_summary_large" msgid="4706951482598978984">"Large"</string>
- <string name="screen_zoom_summary_very_large" msgid="7317423942896999029">"Larger"</string>
- <string name="screen_zoom_summary_extremely_large" msgid="1438045624562358554">"Largest"</string>
+ <string name="screen_zoom_summary_larger" msgid="7346406528348409139">"Larger"</string>
+ <string name="screen_zoom_summary_very_large" msgid="6853699118427019538">"Very large"</string>
+ <string name="screen_zoom_summary_extra_large" msgid="4898389280769475551">"Extra large"</string>
+ <string name="screen_zoom_summary_largest" msgid="5120290329622591371">"Largest"</string>
<string name="screen_zoom_summary_custom" msgid="3468154096832912210">"Custom (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
<string name="content_description_menu_button" msgid="6254844309171779931">"Menu"</string>
<string name="retail_demo_reset_message" msgid="5392824901108195463">"Enter password to perform factory reset in demo mode"</string>
diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml
index 216ae57..223e48c 100644
--- a/packages/SettingsLib/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/res/values-es-rUS/strings.xml
@@ -551,11 +551,15 @@
</string-array>
<string name="charge_length_format" msgid="6941645744588690932">"Hace <xliff:g id="ID_1">%1$s</xliff:g>"</string>
<string name="remaining_length_format" msgid="4310625772926171089">"Tiempo restante: <xliff:g id="ID_1">%1$s</xliff:g>"</string>
+ <string name="screen_zoom_summary_smallest" msgid="1042159093382395789">"Mínimo"</string>
+ <string name="screen_zoom_summary_smaller" msgid="7452722393802683776">"Más pequeño"</string>
<string name="screen_zoom_summary_small" msgid="6050633151263074260">"Pequeño"</string>
<string name="screen_zoom_summary_default" msgid="1888865694033865408">"Predeterminado"</string>
<string name="screen_zoom_summary_large" msgid="4706951482598978984">"Grande"</string>
- <string name="screen_zoom_summary_very_large" msgid="7317423942896999029">"Más grande"</string>
- <string name="screen_zoom_summary_extremely_large" msgid="1438045624562358554">"Máximo"</string>
+ <string name="screen_zoom_summary_larger" msgid="7346406528348409139">"Más grande"</string>
+ <string name="screen_zoom_summary_very_large" msgid="6853699118427019538">"Muy grande"</string>
+ <string name="screen_zoom_summary_extra_large" msgid="4898389280769475551">"Extragrande"</string>
+ <string name="screen_zoom_summary_largest" msgid="5120290329622591371">"Máximo"</string>
<string name="screen_zoom_summary_custom" msgid="3468154096832912210">"Personalizado (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
<string name="content_description_menu_button" msgid="6254844309171779931">"Menú"</string>
<string name="retail_demo_reset_message" msgid="5392824901108195463">"Ingresa contraseña y restablece en demo"</string>
diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml
index 6bbff4b7..87075cb 100644
--- a/packages/SettingsLib/res/values-es/strings.xml
+++ b/packages/SettingsLib/res/values-es/strings.xml
@@ -551,11 +551,15 @@
</string-array>
<string name="charge_length_format" msgid="6941645744588690932">"Hace <xliff:g id="ID_1">%1$s</xliff:g>"</string>
<string name="remaining_length_format" msgid="4310625772926171089">"Tiempo restante: <xliff:g id="ID_1">%1$s</xliff:g>"</string>
+ <string name="screen_zoom_summary_smallest" msgid="1042159093382395789">"El más pequeño"</string>
+ <string name="screen_zoom_summary_smaller" msgid="7452722393802683776">"Más pequeño"</string>
<string name="screen_zoom_summary_small" msgid="6050633151263074260">"Pequeño"</string>
<string name="screen_zoom_summary_default" msgid="1888865694033865408">"Predeterminado"</string>
<string name="screen_zoom_summary_large" msgid="4706951482598978984">"Grande"</string>
- <string name="screen_zoom_summary_very_large" msgid="7317423942896999029">"Más grande"</string>
- <string name="screen_zoom_summary_extremely_large" msgid="1438045624562358554">"Lo más grande posible"</string>
+ <string name="screen_zoom_summary_larger" msgid="7346406528348409139">"Más grande"</string>
+ <string name="screen_zoom_summary_very_large" msgid="6853699118427019538">"Muy grande"</string>
+ <string name="screen_zoom_summary_extra_large" msgid="4898389280769475551">"Extragrande"</string>
+ <string name="screen_zoom_summary_largest" msgid="5120290329622591371">"El más grande"</string>
<string name="screen_zoom_summary_custom" msgid="3468154096832912210">"Personalizado (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
<string name="content_description_menu_button" msgid="6254844309171779931">"Menú"</string>
<string name="retail_demo_reset_message" msgid="5392824901108195463">"Escribe una contraseña para restablecer estado de fábrica en modo Demo"</string>
diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml
index 19df69d..644ced5 100644
--- a/packages/SettingsLib/res/values-et/strings.xml
+++ b/packages/SettingsLib/res/values-et/strings.xml
@@ -551,11 +551,15 @@
</string-array>
<string name="charge_length_format" msgid="6941645744588690932">"<xliff:g id="ID_1">%1$s</xliff:g> tagasi"</string>
<string name="remaining_length_format" msgid="4310625772926171089">"<xliff:g id="ID_1">%1$s</xliff:g> on jäänud"</string>
+ <string name="screen_zoom_summary_smallest" msgid="1042159093382395789">"Väikseim"</string>
+ <string name="screen_zoom_summary_smaller" msgid="7452722393802683776">"Väiksem"</string>
<string name="screen_zoom_summary_small" msgid="6050633151263074260">"Väike"</string>
<string name="screen_zoom_summary_default" msgid="1888865694033865408">"Vaikeseade"</string>
<string name="screen_zoom_summary_large" msgid="4706951482598978984">"Suur"</string>
- <string name="screen_zoom_summary_very_large" msgid="7317423942896999029">"Suurem"</string>
- <string name="screen_zoom_summary_extremely_large" msgid="1438045624562358554">"Suurim"</string>
+ <string name="screen_zoom_summary_larger" msgid="7346406528348409139">"Suurem"</string>
+ <string name="screen_zoom_summary_very_large" msgid="6853699118427019538">"Väga suur"</string>
+ <string name="screen_zoom_summary_extra_large" msgid="4898389280769475551">"Ülisuur"</string>
+ <string name="screen_zoom_summary_largest" msgid="5120290329622591371">"Suurim"</string>
<string name="screen_zoom_summary_custom" msgid="3468154096832912210">"Kohandatud (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
<string name="content_description_menu_button" msgid="6254844309171779931">"Menüü"</string>
<string name="retail_demo_reset_message" msgid="5392824901108195463">"Sisestage parool, et demorežiimis tehaseseadetele lähtestada"</string>
diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml
index 788febb..ff5ef36 100644
--- a/packages/SettingsLib/res/values-eu/strings.xml
+++ b/packages/SettingsLib/res/values-eu/strings.xml
@@ -551,11 +551,15 @@
</string-array>
<string name="charge_length_format" msgid="6941645744588690932">"Duela <xliff:g id="ID_1">%1$s</xliff:g>"</string>
<string name="remaining_length_format" msgid="4310625772926171089">"<xliff:g id="ID_1">%1$s</xliff:g> geratzen dira"</string>
+ <string name="screen_zoom_summary_smallest" msgid="1042159093382395789">"Txikiena"</string>
+ <string name="screen_zoom_summary_smaller" msgid="7452722393802683776">"Txikiagoa"</string>
<string name="screen_zoom_summary_small" msgid="6050633151263074260">"Txikia"</string>
<string name="screen_zoom_summary_default" msgid="1888865694033865408">"Lehenetsia"</string>
<string name="screen_zoom_summary_large" msgid="4706951482598978984">"Handia"</string>
- <string name="screen_zoom_summary_very_large" msgid="7317423942896999029">"Oso handia"</string>
- <string name="screen_zoom_summary_extremely_large" msgid="1438045624562358554">"Handiena"</string>
+ <string name="screen_zoom_summary_larger" msgid="7346406528348409139">"Oso handia"</string>
+ <string name="screen_zoom_summary_very_large" msgid="6853699118427019538">"Oso handia"</string>
+ <string name="screen_zoom_summary_extra_large" msgid="4898389280769475551">"Estra handia"</string>
+ <string name="screen_zoom_summary_largest" msgid="5120290329622591371">"Handiena"</string>
<string name="screen_zoom_summary_custom" msgid="3468154096832912210">"Pertsonalizatua (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
<string name="content_description_menu_button" msgid="6254844309171779931">"Menua"</string>
<string name="retail_demo_reset_message" msgid="5392824901108195463">"Idatzi pasahitza jatorrizko datuak demo moduan berrezartzeko"</string>
diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml
index 75cf19c..8f5ce45 100644
--- a/packages/SettingsLib/res/values-fa/strings.xml
+++ b/packages/SettingsLib/res/values-fa/strings.xml
@@ -551,11 +551,15 @@
</string-array>
<string name="charge_length_format" msgid="6941645744588690932">"<xliff:g id="ID_1">%1$s</xliff:g> پیش"</string>
<string name="remaining_length_format" msgid="4310625772926171089">"<xliff:g id="ID_1">%1$s</xliff:g> باقی مانده است"</string>
+ <string name="screen_zoom_summary_smallest" msgid="1042159093382395789">"کوچکترین"</string>
+ <string name="screen_zoom_summary_smaller" msgid="7452722393802683776">"کوچکتر"</string>
<string name="screen_zoom_summary_small" msgid="6050633151263074260">"کوچک"</string>
<string name="screen_zoom_summary_default" msgid="1888865694033865408">"پیشفرض"</string>
<string name="screen_zoom_summary_large" msgid="4706951482598978984">"بزرگ"</string>
- <string name="screen_zoom_summary_very_large" msgid="7317423942896999029">"بزرگتر"</string>
- <string name="screen_zoom_summary_extremely_large" msgid="1438045624562358554">"بزرگترین"</string>
+ <string name="screen_zoom_summary_larger" msgid="7346406528348409139">"بزرگتر"</string>
+ <string name="screen_zoom_summary_very_large" msgid="6853699118427019538">"خیلی بزرگ"</string>
+ <string name="screen_zoom_summary_extra_large" msgid="4898389280769475551">"بسیار بزرگ"</string>
+ <string name="screen_zoom_summary_largest" msgid="5120290329622591371">"بزرگترین"</string>
<string name="screen_zoom_summary_custom" msgid="3468154096832912210">"سفارشی (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
<string name="content_description_menu_button" msgid="6254844309171779931">"منو"</string>
<string name="retail_demo_reset_message" msgid="5392824901108195463">"برای انجام بازنشانی کارخانهای در حالت نمایشی، گذرواژه را وارد کنید"</string>
diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml
index 7854c67..1e49a51 100644
--- a/packages/SettingsLib/res/values-fi/strings.xml
+++ b/packages/SettingsLib/res/values-fi/strings.xml
@@ -198,7 +198,7 @@
<string name="data_usage_ota" msgid="7984667793701597001">"Järjestelmäpäivitykset"</string>
<string name="tether_settings_title_usb" msgid="3728686573430917722">"Jaettu yhteys USB:n kautta"</string>
<string name="tether_settings_title_wifi" msgid="4803402057533895526">"Kannettava yhteyspiste"</string>
- <string name="tether_settings_title_bluetooth" msgid="916519902721399656">"Jaettu Bluetooth-yhteys"</string>
+ <string name="tether_settings_title_bluetooth" msgid="916519902721399656">"Bluetoothin jakaminen"</string>
<string name="tether_settings_title_usb_bluetooth" msgid="1727111807207577322">"Jaettu yhteys"</string>
<string name="tether_settings_title_all" msgid="8910259483383010470">"Jaettu yhteys ja kannettava yhteyspiste"</string>
<string name="managed_user_title" msgid="449081789742645723">"Kaikki työsovellukset"</string>
@@ -551,11 +551,15 @@
</string-array>
<string name="charge_length_format" msgid="6941645744588690932">"<xliff:g id="ID_1">%1$s</xliff:g> sitten"</string>
<string name="remaining_length_format" msgid="4310625772926171089">"<xliff:g id="ID_1">%1$s</xliff:g> jäljellä"</string>
+ <string name="screen_zoom_summary_smallest" msgid="1042159093382395789">"Pienin"</string>
+ <string name="screen_zoom_summary_smaller" msgid="7452722393802683776">"Pienempi"</string>
<string name="screen_zoom_summary_small" msgid="6050633151263074260">"Pieni"</string>
<string name="screen_zoom_summary_default" msgid="1888865694033865408">"Oletus"</string>
<string name="screen_zoom_summary_large" msgid="4706951482598978984">"Suuri"</string>
- <string name="screen_zoom_summary_very_large" msgid="7317423942896999029">"Suurempi"</string>
- <string name="screen_zoom_summary_extremely_large" msgid="1438045624562358554">"Suurin"</string>
+ <string name="screen_zoom_summary_larger" msgid="7346406528348409139">"Suurempi"</string>
+ <string name="screen_zoom_summary_very_large" msgid="6853699118427019538">"Hyvin suuri"</string>
+ <string name="screen_zoom_summary_extra_large" msgid="4898389280769475551">"Erittäin suuri"</string>
+ <string name="screen_zoom_summary_largest" msgid="5120290329622591371">"Suurin"</string>
<string name="screen_zoom_summary_custom" msgid="3468154096832912210">"Muokattu (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
<string name="content_description_menu_button" msgid="6254844309171779931">"Valikko"</string>
<string name="retail_demo_reset_message" msgid="5392824901108195463">"Palauta tehdasasetukset antamalla salasana"</string>
diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml
index 3f267e0..dc5c6ce 100644
--- a/packages/SettingsLib/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml
@@ -133,7 +133,7 @@
<string name="bluetooth_hearing_aid_media_only_left_active" msgid="1632152540901488645">"Actif (contenu multimédia uniquement), côté gauche seulement"</string>
<string name="bluetooth_hearing_aid_media_only_right_active" msgid="3854140683042617230">"Actif (contenu multimédia uniquement), côté droit seulement"</string>
<string name="bluetooth_hearing_aid_media_only_left_and_right_active" msgid="1299913413062528417">"Actif (contenu multimédia uniquement), côtés gauche et droit"</string>
- <string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"Paramètres audio du support"</string>
+ <string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"Paramètres audio Bluetooth"</string>
<string name="bluetooth_profile_headset" msgid="5395952236133499331">"Appels téléphoniques"</string>
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Transfert de fichier"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Périphérique d\'entrée"</string>
@@ -211,7 +211,7 @@
<string name="tts_default_rate_title" msgid="3964187817364304022">"Débit"</string>
<string name="tts_default_rate_summary" msgid="3781937042151716987">"Vitesse à laquelle le texte est énoncé"</string>
<string name="tts_default_pitch_title" msgid="6988592215554485479">"Ton"</string>
- <string name="tts_default_pitch_summary" msgid="9132719475281551884">"Touche le ton utilisé pour la synthèse vocale"</string>
+ <string name="tts_default_pitch_summary" msgid="9132719475281551884">"Modifie le ton utilisé pour la synthèse vocale"</string>
<string name="tts_default_lang_title" msgid="4698933575028098940">"Langue"</string>
<string name="tts_lang_use_system" msgid="6312945299804012406">"Utiliser la langue du système"</string>
<string name="tts_lang_not_selected" msgid="7927823081096056147">"Langue non sélectionnée"</string>
@@ -492,7 +492,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomalie (rouge/vert)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomalie (bleu/jaune)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Correction des couleurs"</string>
- <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"La correction des couleurs peut être utile lorsque vous souhaitez :<br/> <ol> <li> voir les couleurs avec plus de précision;</li> <li> retirer les couleurs pour vous aider à vous concentrer.</li> </ol>"</string>
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"La correction des couleurs peut être utile lorsque vous souhaitez :<br/> <ol> <li>&nbsp;voir les couleurs avec plus de précision;</li> <li>&nbsp;retirer les couleurs pour vous aider à vous concentrer.</li> </ol>"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Remplacé par <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> : <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> – La recharge a été mise en pause pour protéger la pile"</string>
@@ -551,11 +551,15 @@
</string-array>
<string name="charge_length_format" msgid="6941645744588690932">"Il y a <xliff:g id="ID_1">%1$s</xliff:g>"</string>
<string name="remaining_length_format" msgid="4310625772926171089">"Temps restant : <xliff:g id="ID_1">%1$s</xliff:g>"</string>
+ <string name="screen_zoom_summary_smallest" msgid="1042159093382395789">"Le plus petit"</string>
+ <string name="screen_zoom_summary_smaller" msgid="7452722393802683776">"Plus petit"</string>
<string name="screen_zoom_summary_small" msgid="6050633151263074260">"Petit"</string>
<string name="screen_zoom_summary_default" msgid="1888865694033865408">"Par défaut"</string>
<string name="screen_zoom_summary_large" msgid="4706951482598978984">"Grand"</string>
- <string name="screen_zoom_summary_very_large" msgid="7317423942896999029">"Plus grande"</string>
- <string name="screen_zoom_summary_extremely_large" msgid="1438045624562358554">"La plus grande"</string>
+ <string name="screen_zoom_summary_larger" msgid="7346406528348409139">"Plus grand"</string>
+ <string name="screen_zoom_summary_very_large" msgid="6853699118427019538">"Très grand"</string>
+ <string name="screen_zoom_summary_extra_large" msgid="4898389280769475551">"Très grand"</string>
+ <string name="screen_zoom_summary_largest" msgid="5120290329622591371">"Le plus grand"</string>
<string name="screen_zoom_summary_custom" msgid="3468154096832912210">"Personnalisée (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
<string name="content_description_menu_button" msgid="6254844309171779931">"Menu"</string>
<string name="retail_demo_reset_message" msgid="5392824901108195463">"Entrez m. passe pour réinit. en mode Démo"</string>
@@ -601,14 +605,14 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"Jusqu\'à la désactivation"</string>
<string name="zen_mode_starred_contacts_empty_name" msgid="933552939706125937">"(Sans nom)"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"À l\'instant"</string>
- <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Ce téléphone"</string>
+ <string name="media_transfer_this_device_name" msgid="2357329267148436433">"ce téléphone"</string>
<string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Cette tablette"</string>
<string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"Cet ordinateur (interne)"</string>
<string name="media_transfer_this_device_name_tv" msgid="8508713779441163887">"Ce téléviseur"</string>
<string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Haut-parleur du socle"</string>
<string name="media_transfer_external_device_name" msgid="2588672258721846418">"Appareil externe"</string>
<string name="media_transfer_default_device_name" msgid="4315604017399871828">"Appareil connecté"</string>
- <string name="media_transfer_this_phone" msgid="7194341457812151531">"Ce téléphone"</string>
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"ce téléphone"</string>
<string name="media_transfer_digital_line_name" msgid="312091711951124301">"S/PDIF"</string>
<string name="media_transfer_analog_line_name" msgid="1841163866716302104">"Analogique"</string>
<string name="media_transfer_aux_line_name" msgid="894135835967856689">"AUX"</string>
diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml
index 33c1883..7af2b4b 100644
--- a/packages/SettingsLib/res/values-fr/strings.xml
+++ b/packages/SettingsLib/res/values-fr/strings.xml
@@ -520,7 +520,7 @@
<string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - En charge"</string>
<string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - Complètement chargé d\'ici <xliff:g id="TIME">%3$s</xliff:g>"</string>
<string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - Complètement chargé dans <xliff:g id="TIME">%2$s</xliff:g>"</string>
- <string name="power_remaining_charging_duration_only_v2" msgid="5358176435722950193">"Complètement chargé dans <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_remaining_charging_duration_only_v2" msgid="5358176435722950193">"Recharge complète d\'ici <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_fast_charging_duration_only_v2" msgid="6270950195810579563">"Complètement chargé d\'ici <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"Inconnu"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Batterie en charge"</string>
@@ -551,11 +551,15 @@
</string-array>
<string name="charge_length_format" msgid="6941645744588690932">"Il y a <xliff:g id="ID_1">%1$s</xliff:g>"</string>
<string name="remaining_length_format" msgid="4310625772926171089">"Il reste <xliff:g id="ID_1">%1$s</xliff:g>"</string>
+ <string name="screen_zoom_summary_smallest" msgid="1042159093382395789">"Le plus petit"</string>
+ <string name="screen_zoom_summary_smaller" msgid="7452722393802683776">"Plus petit"</string>
<string name="screen_zoom_summary_small" msgid="6050633151263074260">"Petit"</string>
<string name="screen_zoom_summary_default" msgid="1888865694033865408">"Par défaut"</string>
<string name="screen_zoom_summary_large" msgid="4706951482598978984">"Grand"</string>
- <string name="screen_zoom_summary_very_large" msgid="7317423942896999029">"Très grand"</string>
- <string name="screen_zoom_summary_extremely_large" msgid="1438045624562358554">"Extrêmement grand"</string>
+ <string name="screen_zoom_summary_larger" msgid="7346406528348409139">"Plus grand"</string>
+ <string name="screen_zoom_summary_very_large" msgid="6853699118427019538">"Très grand"</string>
+ <string name="screen_zoom_summary_extra_large" msgid="4898389280769475551">"XL"</string>
+ <string name="screen_zoom_summary_largest" msgid="5120290329622591371">"Le plus grand"</string>
<string name="screen_zoom_summary_custom" msgid="3468154096832912210">"Personnalisé (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
<string name="content_description_menu_button" msgid="6254844309171779931">"Menu"</string>
<string name="retail_demo_reset_message" msgid="5392824901108195463">"Saisir mot de passe pour rétablir conf. d\'usine en mode démo."</string>
diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml
index 8044fd8..ca0d736 100644
--- a/packages/SettingsLib/res/values-gl/strings.xml
+++ b/packages/SettingsLib/res/values-gl/strings.xml
@@ -551,11 +551,15 @@
</string-array>
<string name="charge_length_format" msgid="6941645744588690932">"Hai <xliff:g id="ID_1">%1$s</xliff:g>"</string>
<string name="remaining_length_format" msgid="4310625772926171089">"Queda: <xliff:g id="ID_1">%1$s</xliff:g>"</string>
- <string name="screen_zoom_summary_small" msgid="6050633151263074260">"Pequeno"</string>
+ <string name="screen_zoom_summary_smallest" msgid="1042159093382395789">"Tamaño mínimo"</string>
+ <string name="screen_zoom_summary_smaller" msgid="7452722393802683776">"Tamaño máis pequeno"</string>
+ <string name="screen_zoom_summary_small" msgid="6050633151263074260">"Tamaño pequeno"</string>
<string name="screen_zoom_summary_default" msgid="1888865694033865408">"Predeterminado"</string>
- <string name="screen_zoom_summary_large" msgid="4706951482598978984">"Grande"</string>
- <string name="screen_zoom_summary_very_large" msgid="7317423942896999029">"Máis grande"</string>
- <string name="screen_zoom_summary_extremely_large" msgid="1438045624562358554">"O máis grande"</string>
+ <string name="screen_zoom_summary_large" msgid="4706951482598978984">"Tamaño grande"</string>
+ <string name="screen_zoom_summary_larger" msgid="7346406528348409139">"Tamaño máis grande"</string>
+ <string name="screen_zoom_summary_very_large" msgid="6853699118427019538">"Tamaño moi grande"</string>
+ <string name="screen_zoom_summary_extra_large" msgid="4898389280769475551">"Tamaño extragrande"</string>
+ <string name="screen_zoom_summary_largest" msgid="5120290329622591371">"Tamaño máximo"</string>
<string name="screen_zoom_summary_custom" msgid="3468154096832912210">"Personalizado (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
<string name="content_description_menu_button" msgid="6254844309171779931">"Menú"</string>
<string name="retail_demo_reset_message" msgid="5392824901108195463">"Insire contrasinal para restablec. en demostración"</string>
diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml
index c8491e0..a836fbc 100644
--- a/packages/SettingsLib/res/values-gu/strings.xml
+++ b/packages/SettingsLib/res/values-gu/strings.xml
@@ -551,11 +551,15 @@
</string-array>
<string name="charge_length_format" msgid="6941645744588690932">"<xliff:g id="ID_1">%1$s</xliff:g> પહેલાં"</string>
<string name="remaining_length_format" msgid="4310625772926171089">"<xliff:g id="ID_1">%1$s</xliff:g> બાકી"</string>
+ <string name="screen_zoom_summary_smallest" msgid="1042159093382395789">"સૌથી નાના"</string>
+ <string name="screen_zoom_summary_smaller" msgid="7452722393802683776">"વધુ નાના"</string>
<string name="screen_zoom_summary_small" msgid="6050633151263074260">"નાનું"</string>
<string name="screen_zoom_summary_default" msgid="1888865694033865408">"ડિફૉલ્ટ"</string>
<string name="screen_zoom_summary_large" msgid="4706951482598978984">"મોટું"</string>
- <string name="screen_zoom_summary_very_large" msgid="7317423942896999029">"વધુ મોટું"</string>
- <string name="screen_zoom_summary_extremely_large" msgid="1438045624562358554">"સૌથી મોટું"</string>
+ <string name="screen_zoom_summary_larger" msgid="7346406528348409139">"વધુ મોટા"</string>
+ <string name="screen_zoom_summary_very_large" msgid="6853699118427019538">"બહુ મોટા"</string>
+ <string name="screen_zoom_summary_extra_large" msgid="4898389280769475551">"અતિરિક્ત મોટા"</string>
+ <string name="screen_zoom_summary_largest" msgid="5120290329622591371">"સૌથી મોટા"</string>
<string name="screen_zoom_summary_custom" msgid="3468154096832912210">"કસ્ટમ (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
<string name="content_description_menu_button" msgid="6254844309171779931">"મેનુ"</string>
<string name="retail_demo_reset_message" msgid="5392824901108195463">"ડેમો મોડમાં ફેક્ટરી રીસેટ પાસવર્ડ દાખલ કરો"</string>
diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml
index 52b540b..18aa79c 100644
--- a/packages/SettingsLib/res/values-hi/strings.xml
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -95,7 +95,7 @@
<string name="bluetooth_connected_no_a2dp_battery_level" msgid="6499078454894324287">"जुड़ गया (मीडिया ऑडियो को छोड़कर), बैटरी का लेवल <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_no_a2dp_battery_level" msgid="8477440576953067242">"जुड़ गया (फ़ोन या मीडिया ऑडियो को छोड़कर), बैटरी का लेवल <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
<string name="bluetooth_active_battery_level" msgid="2685517576209066008">"चालू है. <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> बैटरी."</string>
- <string name="bluetooth_active_battery_level_untethered" msgid="4961338936672922617">"चालू है. बायां हेडसेट: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, दायां हेडसेट:<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> बैटरी."</string>
+ <string name="bluetooth_active_battery_level_untethered" msgid="4961338936672922617">"चालू है. बायां हेडसेट: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, दायां हेडसेट: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> बैटरी."</string>
<string name="bluetooth_active_battery_level_untethered_left" msgid="2895644748625343977">"चालू है. बायां हेडसेट: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> बैटरी."</string>
<string name="bluetooth_active_battery_level_untethered_right" msgid="7407517998880370179">"चालू है. दायां हेडसेट: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> बैटरी."</string>
<string name="bluetooth_battery_level" msgid="2893696778200201555">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> बैटरी"</string>
@@ -492,7 +492,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"लाल रंग पहचान न पाना (लाल-हरा)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"नीला रंग पहचान न पाना (नीला-पीला)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"रंग में सुधार करने की सुविधा"</string>
- <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"रंग में सुधार करने की सुविधा का इस्तेमाल, इन मामलों में किया जा सकता है:<br/> <ol> <li>&nbsp;आपको ज़्यादा सटीक तरह से रंग देखने हों</li> <li>&nbsp;ज़्यादा फ़ोकस करने के लिए, आपको कुछ खास रंग हटाने हों</li> </ol>"</string>
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"रंग में सुधार करने की सुविधा से खास तौर पर तब मदद मिलती है, जब:<br/> <ol> <li>&nbsp;आपको ज़्यादा सटीक तरह से रंग देखने हों</li> <li>&nbsp;ज़्यादा फ़ोकस करने के लिए, आपको कुछ खास रंग हटाने हों</li> </ol>"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> के द्वारा ओवरराइड किया गया"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - बैटरी को सुरक्षित रखने के लिए, फ़ोन को चार्ज होने से रोक दिया गया है"</string>
@@ -551,11 +551,15 @@
</string-array>
<string name="charge_length_format" msgid="6941645744588690932">"<xliff:g id="ID_1">%1$s</xliff:g> पहले"</string>
<string name="remaining_length_format" msgid="4310625772926171089">"<xliff:g id="ID_1">%1$s</xliff:g> शेष"</string>
+ <string name="screen_zoom_summary_smallest" msgid="1042159093382395789">"सबसे छोटा"</string>
+ <string name="screen_zoom_summary_smaller" msgid="7452722393802683776">"छोटा"</string>
<string name="screen_zoom_summary_small" msgid="6050633151263074260">"छोटा"</string>
<string name="screen_zoom_summary_default" msgid="1888865694033865408">"डिफ़ॉल्ट"</string>
<string name="screen_zoom_summary_large" msgid="4706951482598978984">"बड़ा"</string>
- <string name="screen_zoom_summary_very_large" msgid="7317423942896999029">"ज़्यादा बड़ा"</string>
- <string name="screen_zoom_summary_extremely_large" msgid="1438045624562358554">"सबसे बड़ा"</string>
+ <string name="screen_zoom_summary_larger" msgid="7346406528348409139">"ज़्यादा बड़ा"</string>
+ <string name="screen_zoom_summary_very_large" msgid="6853699118427019538">"बहुत बड़ा"</string>
+ <string name="screen_zoom_summary_extra_large" msgid="4898389280769475551">"ज़्यादा बड़ा"</string>
+ <string name="screen_zoom_summary_largest" msgid="5120290329622591371">"सबसे बड़ा"</string>
<string name="screen_zoom_summary_custom" msgid="3468154096832912210">"कस्टम (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
<string name="content_description_menu_button" msgid="6254844309171779931">"मेन्यू"</string>
<string name="retail_demo_reset_message" msgid="5392824901108195463">"डेमो मोड में फ़ैक्टरी रीसेट के लिए पासवर्ड डालें"</string>
@@ -574,7 +578,7 @@
<string name="wifi_tether_connected_summary" msgid="5100712926640492336">"{count,plural, =1{1 डिवाइस को कनेक्ट किया गया}one{# डिवाइस को कनेक्ट किया गया}other{# डिवाइसों को कनेक्ट किया गया}}"</string>
<string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"ज़्यादा समय."</string>
<string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"कम समय."</string>
- <string name="cancel" msgid="5665114069455378395">"रद्द करें"</string>
+ <string name="cancel" msgid="5665114069455378395">"अभी नहीं"</string>
<string name="next" msgid="2699398661093607009">"आगे बढ़ें"</string>
<string name="back" msgid="5554327870352703710">"वापस जाएं"</string>
<string name="save" msgid="3745809743277153149">"सेव करें"</string>
diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml
index 380fbe8..764bb86 100644
--- a/packages/SettingsLib/res/values-hr/strings.xml
+++ b/packages/SettingsLib/res/values-hr/strings.xml
@@ -81,7 +81,7 @@
<string name="speed_label_fast" msgid="2677719134596044051">"Brzo"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Vrlo brzo"</string>
<string name="wifi_passpoint_expired" msgid="6540867261754427561">"Isteklo"</string>
- <string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
+ <string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
<string name="bluetooth_disconnected" msgid="7739366554710388701">"Niste povezani"</string>
<string name="bluetooth_disconnecting" msgid="7638892134401574338">"Isključivanje…"</string>
<string name="bluetooth_connecting" msgid="5871702668260192755">"Povezivanje…"</string>
@@ -551,11 +551,15 @@
</string-array>
<string name="charge_length_format" msgid="6941645744588690932">"Prije <xliff:g id="ID_1">%1$s</xliff:g>"</string>
<string name="remaining_length_format" msgid="4310625772926171089">"Još <xliff:g id="ID_1">%1$s</xliff:g>"</string>
+ <string name="screen_zoom_summary_smallest" msgid="1042159093382395789">"Najmanja"</string>
+ <string name="screen_zoom_summary_smaller" msgid="7452722393802683776">"Manja"</string>
<string name="screen_zoom_summary_small" msgid="6050633151263074260">"Malo"</string>
<string name="screen_zoom_summary_default" msgid="1888865694033865408">"Zadano"</string>
<string name="screen_zoom_summary_large" msgid="4706951482598978984">"Veliko"</string>
- <string name="screen_zoom_summary_very_large" msgid="7317423942896999029">"Veće"</string>
- <string name="screen_zoom_summary_extremely_large" msgid="1438045624562358554">"Najveće"</string>
+ <string name="screen_zoom_summary_larger" msgid="7346406528348409139">"Veća"</string>
+ <string name="screen_zoom_summary_very_large" msgid="6853699118427019538">"Vrlo velika"</string>
+ <string name="screen_zoom_summary_extra_large" msgid="4898389280769475551">"Posebno velika"</string>
+ <string name="screen_zoom_summary_largest" msgid="5120290329622591371">"Najveća"</string>
<string name="screen_zoom_summary_custom" msgid="3468154096832912210">"Prilagođeno (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
<string name="content_description_menu_button" msgid="6254844309171779931">"Izbornik"</string>
<string name="retail_demo_reset_message" msgid="5392824901108195463">"Unesite zaporku za resetiranje u demo načinu"</string>
diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml
index 9d08e05..1f69cb0 100644
--- a/packages/SettingsLib/res/values-hu/strings.xml
+++ b/packages/SettingsLib/res/values-hu/strings.xml
@@ -138,7 +138,7 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Fájlátvitel"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Beviteli eszköz"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Internetelérés"</string>
- <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"Hozzáférés a címtárhoz és híváslistához"</string>
+ <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"Hozzáférés a névjegyekhez és a híváslistához"</string>
<string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"Az információk pl. a hívásértesítéshez lesznek felhasználva"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Internetkapcsolat megosztása"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Szöveges üzenetek"</string>
@@ -492,7 +492,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomália (piros– zöld)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomália (kék–sárga)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Színkorrekció"</string>
- <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"A színjavítás funkció például a következő esetekben lehet hasznos:<br/> <ol> <li>&nbsp;Ha jobban szeretné látni a színeket.</li> <li>&nbsp;Ha a lényeges részek kiemelése érdekében el szeretne távolítani bizonyos színeket.</li> </ol>"</string>
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"A színkorrekció funkció például a következő esetekben lehet hasznos:<br/> <ol> <li>&nbsp;Ha jobban szeretné látni a színeket.</li> <li>&nbsp;Ha a lényeges részek kiemelése érdekében el szeretne távolítani bizonyos színeket.</li> </ol>"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Felülírva erre: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> – Az akkumulátor védelme érdekében a töltés szünetel"</string>
@@ -551,11 +551,15 @@
</string-array>
<string name="charge_length_format" msgid="6941645744588690932">"Ennyi ideje: <xliff:g id="ID_1">%1$s</xliff:g>"</string>
<string name="remaining_length_format" msgid="4310625772926171089">"<xliff:g id="ID_1">%1$s</xliff:g> van hátra"</string>
+ <string name="screen_zoom_summary_smallest" msgid="1042159093382395789">"Legkisebb"</string>
+ <string name="screen_zoom_summary_smaller" msgid="7452722393802683776">"Kisebb"</string>
<string name="screen_zoom_summary_small" msgid="6050633151263074260">"Kicsi"</string>
<string name="screen_zoom_summary_default" msgid="1888865694033865408">"Alapértelmezett"</string>
<string name="screen_zoom_summary_large" msgid="4706951482598978984">"Nagy"</string>
- <string name="screen_zoom_summary_very_large" msgid="7317423942896999029">"Nagyobb"</string>
- <string name="screen_zoom_summary_extremely_large" msgid="1438045624562358554">"Legnagyobb"</string>
+ <string name="screen_zoom_summary_larger" msgid="7346406528348409139">"Nagyobb"</string>
+ <string name="screen_zoom_summary_very_large" msgid="6853699118427019538">"Nagyon nagy"</string>
+ <string name="screen_zoom_summary_extra_large" msgid="4898389280769475551">"Extra nagy"</string>
+ <string name="screen_zoom_summary_largest" msgid="5120290329622591371">"Legnagyobb"</string>
<string name="screen_zoom_summary_custom" msgid="3468154096832912210">"Egyéni (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
<string name="content_description_menu_button" msgid="6254844309171779931">"Menü"</string>
<string name="retail_demo_reset_message" msgid="5392824901108195463">"Írja be a jelszót a visszaállításhoz"</string>
diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml
index 425cb07..1f971fb 100644
--- a/packages/SettingsLib/res/values-hy/strings.xml
+++ b/packages/SettingsLib/res/values-hy/strings.xml
@@ -551,11 +551,15 @@
</string-array>
<string name="charge_length_format" msgid="6941645744588690932">"<xliff:g id="ID_1">%1$s</xliff:g> առաջ"</string>
<string name="remaining_length_format" msgid="4310625772926171089">"Մնացել է <xliff:g id="ID_1">%1$s</xliff:g>"</string>
+ <string name="screen_zoom_summary_smallest" msgid="1042159093382395789">"Ամենափոքր"</string>
+ <string name="screen_zoom_summary_smaller" msgid="7452722393802683776">"Ավելի փոքր"</string>
<string name="screen_zoom_summary_small" msgid="6050633151263074260">"Փոքր"</string>
<string name="screen_zoom_summary_default" msgid="1888865694033865408">"Կանխադրված"</string>
<string name="screen_zoom_summary_large" msgid="4706951482598978984">"Մեծ"</string>
- <string name="screen_zoom_summary_very_large" msgid="7317423942896999029">"Ավելի մեծ"</string>
- <string name="screen_zoom_summary_extremely_large" msgid="1438045624562358554">"Ամենամեծ"</string>
+ <string name="screen_zoom_summary_larger" msgid="7346406528348409139">"Ավելի մեծ"</string>
+ <string name="screen_zoom_summary_very_large" msgid="6853699118427019538">"Շատ մեծ"</string>
+ <string name="screen_zoom_summary_extra_large" msgid="4898389280769475551">"Գերմեծ"</string>
+ <string name="screen_zoom_summary_largest" msgid="5120290329622591371">"Ամենամեծ"</string>
<string name="screen_zoom_summary_custom" msgid="3468154096832912210">"Հատուկ (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
<string name="content_description_menu_button" msgid="6254844309171779931">"Ընտրացանկ"</string>
<string name="retail_demo_reset_message" msgid="5392824901108195463">"Մուտքագրեք գաղտնաբառը՝ ցուցադրական ռեժիմում գործարանային կարգավորումների վերականգնում կատարելու համար"</string>
diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml
index 99b3bdc..a46ed92 100644
--- a/packages/SettingsLib/res/values-in/strings.xml
+++ b/packages/SettingsLib/res/values-in/strings.xml
@@ -520,7 +520,7 @@
<string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - Mengisi daya"</string>
<string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - Penuh pukul <xliff:g id="TIME">%3$s</xliff:g>"</string>
<string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - Baterai terisi penuh dalam <xliff:g id="TIME">%2$s</xliff:g>"</string>
- <string name="power_remaining_charging_duration_only_v2" msgid="5358176435722950193">"Baterai terisi penuh dalam <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_remaining_charging_duration_only_v2" msgid="5358176435722950193">"Baterai akan terisi penuh pada <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_fast_charging_duration_only_v2" msgid="6270950195810579563">"Penuh pukul <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"Tidak diketahui"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Mengisi daya"</string>
@@ -551,11 +551,15 @@
</string-array>
<string name="charge_length_format" msgid="6941645744588690932">"<xliff:g id="ID_1">%1$s</xliff:g> lalu"</string>
<string name="remaining_length_format" msgid="4310625772926171089">"Tersisa <xliff:g id="ID_1">%1$s</xliff:g>"</string>
+ <string name="screen_zoom_summary_smallest" msgid="1042159093382395789">"Terkecil"</string>
+ <string name="screen_zoom_summary_smaller" msgid="7452722393802683776">"Lebih Kecil"</string>
<string name="screen_zoom_summary_small" msgid="6050633151263074260">"Kecil"</string>
<string name="screen_zoom_summary_default" msgid="1888865694033865408">"Default"</string>
<string name="screen_zoom_summary_large" msgid="4706951482598978984">"Besar"</string>
- <string name="screen_zoom_summary_very_large" msgid="7317423942896999029">"Lebih besar"</string>
- <string name="screen_zoom_summary_extremely_large" msgid="1438045624562358554">"Terbesar"</string>
+ <string name="screen_zoom_summary_larger" msgid="7346406528348409139">"Lebih Besar"</string>
+ <string name="screen_zoom_summary_very_large" msgid="6853699118427019538">"Sangat Besar"</string>
+ <string name="screen_zoom_summary_extra_large" msgid="4898389280769475551">"Ekstra Besar"</string>
+ <string name="screen_zoom_summary_largest" msgid="5120290329622591371">"Terbesar"</string>
<string name="screen_zoom_summary_custom" msgid="3468154096832912210">"(<xliff:g id="DENSITYDPI">%d</xliff:g>) khusus"</string>
<string name="content_description_menu_button" msgid="6254844309171779931">"Menu"</string>
<string name="retail_demo_reset_message" msgid="5392824901108195463">"Masukkan sandi untuk mengembalikan ke setelan pabrik dalam mode demo"</string>
diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml
index b337f35..d802b18 100644
--- a/packages/SettingsLib/res/values-is/strings.xml
+++ b/packages/SettingsLib/res/values-is/strings.xml
@@ -551,11 +551,15 @@
</string-array>
<string name="charge_length_format" msgid="6941645744588690932">"Fyrir <xliff:g id="ID_1">%1$s</xliff:g>"</string>
<string name="remaining_length_format" msgid="4310625772926171089">"<xliff:g id="ID_1">%1$s</xliff:g> eftir"</string>
+ <string name="screen_zoom_summary_smallest" msgid="1042159093382395789">"Minnst"</string>
+ <string name="screen_zoom_summary_smaller" msgid="7452722393802683776">"Minna"</string>
<string name="screen_zoom_summary_small" msgid="6050633151263074260">"Lítið"</string>
<string name="screen_zoom_summary_default" msgid="1888865694033865408">"Sjálfgefið"</string>
<string name="screen_zoom_summary_large" msgid="4706951482598978984">"Stórt"</string>
- <string name="screen_zoom_summary_very_large" msgid="7317423942896999029">"Stærra"</string>
- <string name="screen_zoom_summary_extremely_large" msgid="1438045624562358554">"Stærst"</string>
+ <string name="screen_zoom_summary_larger" msgid="7346406528348409139">"Stærra"</string>
+ <string name="screen_zoom_summary_very_large" msgid="6853699118427019538">"Mjög stórt"</string>
+ <string name="screen_zoom_summary_extra_large" msgid="4898389280769475551">"Mjög stórt"</string>
+ <string name="screen_zoom_summary_largest" msgid="5120290329622591371">"Stærst"</string>
<string name="screen_zoom_summary_custom" msgid="3468154096832912210">"Sérsniðið (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
<string name="content_description_menu_button" msgid="6254844309171779931">"Valmynd"</string>
<string name="retail_demo_reset_message" msgid="5392824901108195463">"Sláðu inn aðgangsorð til að framkvæma núllstillingu í sýnisútgáfu"</string>
diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml
index 8347e6a..3fba17b 100644
--- a/packages/SettingsLib/res/values-it/strings.xml
+++ b/packages/SettingsLib/res/values-it/strings.xml
@@ -137,7 +137,7 @@
<string name="bluetooth_profile_headset" msgid="5395952236133499331">"Telefonate"</string>
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Trasferimento file"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Dispositivo di input"</string>
- <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Accesso a Internet"</string>
+ <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Accesso a internet"</string>
<string name="bluetooth_profile_pbap" msgid="2103406516858653017">"Consenti l\'accesso ai contatti e alla cronologia delle chiamate"</string>
<string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"Le informazioni saranno utilizzate per gli annunci delle chiamate e altro ancora"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Condivisione connessione Internet"</string>
@@ -551,11 +551,15 @@
</string-array>
<string name="charge_length_format" msgid="6941645744588690932">"<xliff:g id="ID_1">%1$s</xliff:g> fa"</string>
<string name="remaining_length_format" msgid="4310625772926171089">"<xliff:g id="ID_1">%1$s</xliff:g> rimanenti"</string>
+ <string name="screen_zoom_summary_smallest" msgid="1042159093382395789">"Minimo"</string>
+ <string name="screen_zoom_summary_smaller" msgid="7452722393802683776">"Più piccolo"</string>
<string name="screen_zoom_summary_small" msgid="6050633151263074260">"Piccole"</string>
<string name="screen_zoom_summary_default" msgid="1888865694033865408">"Predefinite"</string>
<string name="screen_zoom_summary_large" msgid="4706951482598978984">"Grandi"</string>
- <string name="screen_zoom_summary_very_large" msgid="7317423942896999029">"Più grandi"</string>
- <string name="screen_zoom_summary_extremely_large" msgid="1438045624562358554">"Massimo"</string>
+ <string name="screen_zoom_summary_larger" msgid="7346406528348409139">"Più grande"</string>
+ <string name="screen_zoom_summary_very_large" msgid="6853699118427019538">"Molto grande"</string>
+ <string name="screen_zoom_summary_extra_large" msgid="4898389280769475551">"Extra large"</string>
+ <string name="screen_zoom_summary_largest" msgid="5120290329622591371">"Massimo"</string>
<string name="screen_zoom_summary_custom" msgid="3468154096832912210">"Personalizzato (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
<string name="content_description_menu_button" msgid="6254844309171779931">"Menu"</string>
<string name="retail_demo_reset_message" msgid="5392824901108195463">"Inserisci la password per eseguire il ripristino dei dati di fabbrica in modalità demo"</string>
diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml
index b1ef8c5..6187708 100644
--- a/packages/SettingsLib/res/values-iw/strings.xml
+++ b/packages/SettingsLib/res/values-iw/strings.xml
@@ -56,7 +56,7 @@
<string name="wifi_disabled_password_failure" msgid="6892387079613226738">"בעיית אימות"</string>
<string name="wifi_cant_connect" msgid="5718417542623056783">"לא ניתן להתחבר"</string>
<string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"לא ניתן להתחבר אל <xliff:g id="AP_NAME">%1$s</xliff:g>"</string>
- <string name="wifi_check_password_try_again" msgid="8817789642851605628">"יש לבדוק את הסיסמה ולנסות שוב"</string>
+ <string name="wifi_check_password_try_again" msgid="8817789642851605628">"צריך לבדוק את הסיסמה ולנסות שוב"</string>
<string name="wifi_not_in_range" msgid="1541760821805777772">"מחוץ לטווח"</string>
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"לא יתבצע חיבור באופן אוטומטי"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"אין גישה לאינטרנט"</string>
@@ -551,11 +551,15 @@
</string-array>
<string name="charge_length_format" msgid="6941645744588690932">"לפני <xliff:g id="ID_1">%1$s</xliff:g>"</string>
<string name="remaining_length_format" msgid="4310625772926171089">"נשארו <xliff:g id="ID_1">%1$s</xliff:g>"</string>
+ <string name="screen_zoom_summary_smallest" msgid="1042159093382395789">"הכי קטן"</string>
+ <string name="screen_zoom_summary_smaller" msgid="7452722393802683776">"קטן יותר"</string>
<string name="screen_zoom_summary_small" msgid="6050633151263074260">"קטן"</string>
<string name="screen_zoom_summary_default" msgid="1888865694033865408">"ברירת מחדל"</string>
<string name="screen_zoom_summary_large" msgid="4706951482598978984">"גדול"</string>
- <string name="screen_zoom_summary_very_large" msgid="7317423942896999029">"יותר גדול"</string>
- <string name="screen_zoom_summary_extremely_large" msgid="1438045624562358554">"הכי גדול"</string>
+ <string name="screen_zoom_summary_larger" msgid="7346406528348409139">"יותר גדול"</string>
+ <string name="screen_zoom_summary_very_large" msgid="6853699118427019538">"גדול מאוד"</string>
+ <string name="screen_zoom_summary_extra_large" msgid="4898389280769475551">"גדול במיוחד"</string>
+ <string name="screen_zoom_summary_largest" msgid="5120290329622591371">"הכי גדול"</string>
<string name="screen_zoom_summary_custom" msgid="3468154096832912210">"מותאם אישית (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
<string name="content_description_menu_button" msgid="6254844309171779931">"תפריט"</string>
<string name="retail_demo_reset_message" msgid="5392824901108195463">"יש להזין סיסמה כדי לבצע איפוס להגדרות היצרן במצב הדגמה"</string>
diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml
index 7485026..9dec009 100644
--- a/packages/SettingsLib/res/values-ja/strings.xml
+++ b/packages/SettingsLib/res/values-ja/strings.xml
@@ -551,11 +551,15 @@
</string-array>
<string name="charge_length_format" msgid="6941645744588690932">"<xliff:g id="ID_1">%1$s</xliff:g>前"</string>
<string name="remaining_length_format" msgid="4310625772926171089">"あと <xliff:g id="ID_1">%1$s</xliff:g>"</string>
+ <string name="screen_zoom_summary_smallest" msgid="1042159093382395789">"最小"</string>
+ <string name="screen_zoom_summary_smaller" msgid="7452722393802683776">"小"</string>
<string name="screen_zoom_summary_small" msgid="6050633151263074260">"小"</string>
<string name="screen_zoom_summary_default" msgid="1888865694033865408">"デフォルト"</string>
<string name="screen_zoom_summary_large" msgid="4706951482598978984">"大"</string>
- <string name="screen_zoom_summary_very_large" msgid="7317423942896999029">"特大"</string>
- <string name="screen_zoom_summary_extremely_large" msgid="1438045624562358554">"最大"</string>
+ <string name="screen_zoom_summary_larger" msgid="7346406528348409139">"大"</string>
+ <string name="screen_zoom_summary_very_large" msgid="6853699118427019538">"極大"</string>
+ <string name="screen_zoom_summary_extra_large" msgid="4898389280769475551">"特大"</string>
+ <string name="screen_zoom_summary_largest" msgid="5120290329622591371">"最大"</string>
<string name="screen_zoom_summary_custom" msgid="3468154096832912210">"カスタム(<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
<string name="content_description_menu_button" msgid="6254844309171779931">"メニュー"</string>
<string name="retail_demo_reset_message" msgid="5392824901108195463">"デモモードで初期状態にリセットするには、パスワードを入力してください"</string>
diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml
index fe7e65c..5485e6c 100644
--- a/packages/SettingsLib/res/values-ka/strings.xml
+++ b/packages/SettingsLib/res/values-ka/strings.xml
@@ -551,11 +551,15 @@
</string-array>
<string name="charge_length_format" msgid="6941645744588690932">"გავიდა <xliff:g id="ID_1">%1$s</xliff:g>"</string>
<string name="remaining_length_format" msgid="4310625772926171089">"დარჩენილია <xliff:g id="ID_1">%1$s</xliff:g>"</string>
+ <string name="screen_zoom_summary_smallest" msgid="1042159093382395789">"ყველაზე პატარა"</string>
+ <string name="screen_zoom_summary_smaller" msgid="7452722393802683776">"უფრო პატარა"</string>
<string name="screen_zoom_summary_small" msgid="6050633151263074260">"პატარა"</string>
<string name="screen_zoom_summary_default" msgid="1888865694033865408">"ნაგულისხმევი"</string>
<string name="screen_zoom_summary_large" msgid="4706951482598978984">"დიდი"</string>
- <string name="screen_zoom_summary_very_large" msgid="7317423942896999029">"უფრო დიდი"</string>
- <string name="screen_zoom_summary_extremely_large" msgid="1438045624562358554">"უდიდესი"</string>
+ <string name="screen_zoom_summary_larger" msgid="7346406528348409139">"უფრო დიდი"</string>
+ <string name="screen_zoom_summary_very_large" msgid="6853699118427019538">"ძალიან დიდი"</string>
+ <string name="screen_zoom_summary_extra_large" msgid="4898389280769475551">"ძალიან დიდი"</string>
+ <string name="screen_zoom_summary_largest" msgid="5120290329622591371">"უდიდესი"</string>
<string name="screen_zoom_summary_custom" msgid="3468154096832912210">"მორგებული (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
<string name="content_description_menu_button" msgid="6254844309171779931">"მენიუ"</string>
<string name="retail_demo_reset_message" msgid="5392824901108195463">"შეიყვანეთ პაროლი დემო-რეჟიმში ქარხნულ მდგომარეობაზე დასაბრუნებლად"</string>
diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml
index 724c9cd..6f96620 100644
--- a/packages/SettingsLib/res/values-kk/strings.xml
+++ b/packages/SettingsLib/res/values-kk/strings.xml
@@ -94,7 +94,7 @@
<string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"Жалғанды (телефонсыз), батарея заряды: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
<string name="bluetooth_connected_no_a2dp_battery_level" msgid="6499078454894324287">"Жалғанды (аудиосыз), батарея заряды: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_no_a2dp_battery_level" msgid="8477440576953067242">"Жалғанды (телефонсыз не аудиосыз), батарея заряды: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
- <string name="bluetooth_active_battery_level" msgid="2685517576209066008">"Істеп тұр. Батарея зарядының деңгейі – <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_battery_level" msgid="2685517576209066008">"Істеп тұр. Батарея зарядының деңгейі – <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>."</string>
<string name="bluetooth_active_battery_level_untethered" msgid="4961338936672922617">"Істеп тұр. Сол жақ: батарея зарядының деңгейі – <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>. Оң жақ: батарея зарядының деңгейі — <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>."</string>
<string name="bluetooth_active_battery_level_untethered_left" msgid="2895644748625343977">"Істеп тұр. Сол жақ: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> қуат."</string>
<string name="bluetooth_active_battery_level_untethered_right" msgid="7407517998880370179">"Істеп тұр. Оң жақ: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> қуат."</string>
@@ -138,7 +138,7 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Файл жіберу"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Енгізу құрылғысы"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Интернетке қосылу"</string>
- <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"Контакт пен қоңырау тарихына рұқсат беру"</string>
+ <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"Контактілерге және қоңыраулар тарихына рұқсат беру"</string>
<string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"Ақпарат қоңырау туралы хабарландыру, т.б. үшін қолданылады."</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Интернет байланысын ортақ қолдану"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Мәтіндік хабарлар"</string>
@@ -551,11 +551,15 @@
</string-array>
<string name="charge_length_format" msgid="6941645744588690932">"<xliff:g id="ID_1">%1$s</xliff:g> бұрын"</string>
<string name="remaining_length_format" msgid="4310625772926171089">"<xliff:g id="ID_1">%1$s</xliff:g> қалды"</string>
+ <string name="screen_zoom_summary_smallest" msgid="1042159093382395789">"Ең кішкентай"</string>
+ <string name="screen_zoom_summary_smaller" msgid="7452722393802683776">"Кішірек"</string>
<string name="screen_zoom_summary_small" msgid="6050633151263074260">"Кішкентай"</string>
<string name="screen_zoom_summary_default" msgid="1888865694033865408">"Әдепкі"</string>
<string name="screen_zoom_summary_large" msgid="4706951482598978984">"Үлкен"</string>
- <string name="screen_zoom_summary_very_large" msgid="7317423942896999029">"Үлкенірек"</string>
- <string name="screen_zoom_summary_extremely_large" msgid="1438045624562358554">"Ең үлкен"</string>
+ <string name="screen_zoom_summary_larger" msgid="7346406528348409139">"Үлкенірек"</string>
+ <string name="screen_zoom_summary_very_large" msgid="6853699118427019538">"Өте үлкен"</string>
+ <string name="screen_zoom_summary_extra_large" msgid="4898389280769475551">"Өте қатты үлкен"</string>
+ <string name="screen_zoom_summary_largest" msgid="5120290329622591371">"Ең үлкен"</string>
<string name="screen_zoom_summary_custom" msgid="3468154096832912210">"Басқа (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
<string name="content_description_menu_button" msgid="6254844309171779931">"Mәзір"</string>
<string name="retail_demo_reset_message" msgid="5392824901108195463">"Демо режимде зауыттық мәндерге қайтару үшін құпия сөзді енгізу"</string>
@@ -586,7 +590,7 @@
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Бұл қолданбаға оятқыштарды орнатуға және уақытқа байланысты әрекеттерді жоспарлауға рұқсат береді. Мұндайда қолданба фондық режимде жұмыс істейді, сондықтан батарея шығыны артуы мүмкін.\n\nБұл рұқсат өшірулі болса, осы қолданбада жоспарланған ағымдағы оятқыштар мен уақытқа байланысты іс-шаралар жұмыс істемейді."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"кесте, оятқыш, еске салғыш, сағат"</string>
<string name="zen_mode_do_not_disturb_name" msgid="6798711401734798283">"Мазаламау"</string>
- <string name="zen_mode_settings_title" msgid="7374070457626419755">"Мазаламау"</string>
+ <string name="zen_mode_settings_title" msgid="7374070457626419755">"Мазаламау"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Қосу"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Мазаламау режимін қосу"</string>
<string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"Ешқашан"</string>
diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml
index 6053ff7..6ad52b3 100644
--- a/packages/SettingsLib/res/values-km/strings.xml
+++ b/packages/SettingsLib/res/values-km/strings.xml
@@ -551,11 +551,15 @@
</string-array>
<string name="charge_length_format" msgid="6941645744588690932">"<xliff:g id="ID_1">%1$s</xliff:g> មុន"</string>
<string name="remaining_length_format" msgid="4310625772926171089">"នៅសល់ <xliff:g id="ID_1">%1$s</xliff:g>"</string>
+ <string name="screen_zoom_summary_smallest" msgid="1042159093382395789">"តូចបំផុត"</string>
+ <string name="screen_zoom_summary_smaller" msgid="7452722393802683776">"តូចជាង"</string>
<string name="screen_zoom_summary_small" msgid="6050633151263074260">"តូច"</string>
<string name="screen_zoom_summary_default" msgid="1888865694033865408">"លំនាំដើម"</string>
<string name="screen_zoom_summary_large" msgid="4706951482598978984">"ធំ"</string>
- <string name="screen_zoom_summary_very_large" msgid="7317423942896999029">"ធំជាង"</string>
- <string name="screen_zoom_summary_extremely_large" msgid="1438045624562358554">"ធំបំផុត"</string>
+ <string name="screen_zoom_summary_larger" msgid="7346406528348409139">"ធំជាង"</string>
+ <string name="screen_zoom_summary_very_large" msgid="6853699118427019538">"ធំខ្លាំង"</string>
+ <string name="screen_zoom_summary_extra_large" msgid="4898389280769475551">"ធំលើសធម្មតា"</string>
+ <string name="screen_zoom_summary_largest" msgid="5120290329622591371">"ធំបំផុត"</string>
<string name="screen_zoom_summary_custom" msgid="3468154096832912210">"ផ្ទាល់ខ្លួន (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
<string name="content_description_menu_button" msgid="6254844309171779931">"ម៉ឺនុយ"</string>
<string name="retail_demo_reset_message" msgid="5392824901108195463">"បញ្ចូលពាក្យសម្ងាត់ ដើម្បីកំណត់ឧបករណ៍ឡើងវិញដូចពេលចេញពីរោងចក្រ នៅក្នុងមុខងារសាកល្បង"</string>
diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml
index 61b235e..fd509ca 100644
--- a/packages/SettingsLib/res/values-kn/strings.xml
+++ b/packages/SettingsLib/res/values-kn/strings.xml
@@ -478,7 +478,7 @@
<string name="transcode_disable_cache" msgid="3160069309377467045">"ಟ್ರಾನ್ಸ್ಕೋಡಿಂಗ್ ಕ್ಯಾಷ್ ಅನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಿ"</string>
<string name="runningservices_settings_title" msgid="6460099290493086515">"ರನ್ ಆಗುತ್ತಿರುವ ಸೇವೆಗಳು"</string>
<string name="runningservices_settings_summary" msgid="1046080643262665743">"ಈಗ ರನ್ ಆಗುತ್ತಿರುವ ಸೇವೆಗಳನ್ನು ವೀಕ್ಷಿಸಿ ಮತ್ತು ನಿಯಂತ್ರಿಸಿ"</string>
- <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView ಹೊಂದಿಸಿ"</string>
+ <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView ಅನುಷ್ಠಾನಗೊಳಿಸಿ"</string>
<string name="select_webview_provider_dialog_title" msgid="2444261109877277714">"WebView ಅನುಷ್ಠಾನಗೊಳಿಸುವಿಕೆಯನ್ನು ಸೆಟ್ ಮಾಡಿ"</string>
<string name="select_webview_provider_toast_text" msgid="8512254949169359848">"ಈ ಆಯ್ಕೆಯು ಇನ್ನು ಮುಂದೆ ಮಾನ್ಯವಾಗಿರುವುದಿಲ್ಲ. ಮತ್ತೊಮ್ಮೆ ಪ್ರಯತ್ನಿಸಿ."</string>
<string name="webview_launch_devtools_title" msgid="8009687433555367112">"WebView DevTools"</string>
@@ -551,11 +551,15 @@
</string-array>
<string name="charge_length_format" msgid="6941645744588690932">"<xliff:g id="ID_1">%1$s</xliff:g> ಹಿಂದೆ"</string>
<string name="remaining_length_format" msgid="4310625772926171089">"<xliff:g id="ID_1">%1$s</xliff:g> ಉಳಿದಿದೆ"</string>
+ <string name="screen_zoom_summary_smallest" msgid="1042159093382395789">"ಅತಿ ಚಿಕ್ಕದು"</string>
+ <string name="screen_zoom_summary_smaller" msgid="7452722393802683776">"ಚಿಕ್ಕದು"</string>
<string name="screen_zoom_summary_small" msgid="6050633151263074260">"ಸಣ್ಣದು"</string>
<string name="screen_zoom_summary_default" msgid="1888865694033865408">"ಡಿಫಾಲ್ಟ್"</string>
<string name="screen_zoom_summary_large" msgid="4706951482598978984">"ದೊಡ್ಡದು"</string>
- <string name="screen_zoom_summary_very_large" msgid="7317423942896999029">"ಸ್ವಲ್ಪ ದೊಡ್ಡ"</string>
- <string name="screen_zoom_summary_extremely_large" msgid="1438045624562358554">"ದೊಡ್ಡ"</string>
+ <string name="screen_zoom_summary_larger" msgid="7346406528348409139">"ದೊಡ್ಡದು"</string>
+ <string name="screen_zoom_summary_very_large" msgid="6853699118427019538">"ಅತ್ಯಂತ ದೊಡ್ಡದು"</string>
+ <string name="screen_zoom_summary_extra_large" msgid="4898389280769475551">"ತುಂಬಾ ದೊಡ್ಡದು"</string>
+ <string name="screen_zoom_summary_largest" msgid="5120290329622591371">"ಅತಿ ದೊಡ್ಡದು"</string>
<string name="screen_zoom_summary_custom" msgid="3468154096832912210">"ಕಸ್ಟಮ್ (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
<string name="content_description_menu_button" msgid="6254844309171779931">"ಮೆನು"</string>
<string name="retail_demo_reset_message" msgid="5392824901108195463">"ಫ್ಯಾಕ್ಟರಿ ರಿಸೆಟ್ಗೆ ಪಾಸ್ವರ್ಡ್ ನಮೂದಿಸಿ"</string>
diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml
index d0a583bd..fc3b968 100644
--- a/packages/SettingsLib/res/values-ko/strings.xml
+++ b/packages/SettingsLib/res/values-ko/strings.xml
@@ -551,11 +551,15 @@
</string-array>
<string name="charge_length_format" msgid="6941645744588690932">"<xliff:g id="ID_1">%1$s</xliff:g> 전"</string>
<string name="remaining_length_format" msgid="4310625772926171089">"<xliff:g id="ID_1">%1$s</xliff:g> 남음"</string>
+ <string name="screen_zoom_summary_smallest" msgid="1042159093382395789">"가장 작게"</string>
+ <string name="screen_zoom_summary_smaller" msgid="7452722393802683776">"더 작게"</string>
<string name="screen_zoom_summary_small" msgid="6050633151263074260">"작게"</string>
<string name="screen_zoom_summary_default" msgid="1888865694033865408">"기본"</string>
<string name="screen_zoom_summary_large" msgid="4706951482598978984">"크게"</string>
- <string name="screen_zoom_summary_very_large" msgid="7317423942896999029">"더 크게"</string>
- <string name="screen_zoom_summary_extremely_large" msgid="1438045624562358554">"가장 크게"</string>
+ <string name="screen_zoom_summary_larger" msgid="7346406528348409139">"더 크게"</string>
+ <string name="screen_zoom_summary_very_large" msgid="6853699118427019538">"아주 크게"</string>
+ <string name="screen_zoom_summary_extra_large" msgid="4898389280769475551">"매우 크게"</string>
+ <string name="screen_zoom_summary_largest" msgid="5120290329622591371">"가장 크게"</string>
<string name="screen_zoom_summary_custom" msgid="3468154096832912210">"맞춤(<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
<string name="content_description_menu_button" msgid="6254844309171779931">"메뉴"</string>
<string name="retail_demo_reset_message" msgid="5392824901108195463">"데모 모드에서 초기화하려면 비밀번호 입력"</string>
diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml
index c68fb70..930427b 100644
--- a/packages/SettingsLib/res/values-ky/strings.xml
+++ b/packages/SettingsLib/res/values-ky/strings.xml
@@ -551,11 +551,15 @@
</string-array>
<string name="charge_length_format" msgid="6941645744588690932">"<xliff:g id="ID_1">%1$s</xliff:g> мурун"</string>
<string name="remaining_length_format" msgid="4310625772926171089">"<xliff:g id="ID_1">%1$s</xliff:g> калды"</string>
+ <string name="screen_zoom_summary_smallest" msgid="1042159093382395789">"Эң кичине"</string>
+ <string name="screen_zoom_summary_smaller" msgid="7452722393802683776">"Кичирээк"</string>
<string name="screen_zoom_summary_small" msgid="6050633151263074260">"Кичине"</string>
<string name="screen_zoom_summary_default" msgid="1888865694033865408">"Демейки"</string>
<string name="screen_zoom_summary_large" msgid="4706951482598978984">"Чоң"</string>
- <string name="screen_zoom_summary_very_large" msgid="7317423942896999029">"Чоңураак"</string>
- <string name="screen_zoom_summary_extremely_large" msgid="1438045624562358554">"Эң чоң"</string>
+ <string name="screen_zoom_summary_larger" msgid="7346406528348409139">"Чоңураак"</string>
+ <string name="screen_zoom_summary_very_large" msgid="6853699118427019538">"Өтө чоң"</string>
+ <string name="screen_zoom_summary_extra_large" msgid="4898389280769475551">"Абдан чоң"</string>
+ <string name="screen_zoom_summary_largest" msgid="5120290329622591371">"Эң чоң"</string>
<string name="screen_zoom_summary_custom" msgid="3468154096832912210">"Ыңгайлаштырылган (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
<string name="content_description_menu_button" msgid="6254844309171779931">"Меню"</string>
<string name="retail_demo_reset_message" msgid="5392824901108195463">"Демо режиминде баштапкы абалга кайтаруу үчүн сырсөздү киргизиңиз"</string>
diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml
index c4e25f8..b7902b6f 100644
--- a/packages/SettingsLib/res/values-lo/strings.xml
+++ b/packages/SettingsLib/res/values-lo/strings.xml
@@ -551,11 +551,15 @@
</string-array>
<string name="charge_length_format" msgid="6941645744588690932">"<xliff:g id="ID_1">%1$s</xliff:g> ກ່ອນນີ້"</string>
<string name="remaining_length_format" msgid="4310625772926171089">"ຍັງເຫຼືອ <xliff:g id="ID_1">%1$s</xliff:g>"</string>
+ <string name="screen_zoom_summary_smallest" msgid="1042159093382395789">"ນ້ອຍທີ່ສຸດ"</string>
+ <string name="screen_zoom_summary_smaller" msgid="7452722393802683776">"ນ້ອຍລົງ"</string>
<string name="screen_zoom_summary_small" msgid="6050633151263074260">"ນ້ອຍ"</string>
<string name="screen_zoom_summary_default" msgid="1888865694033865408">"ຄ່າເລີ່ມຕົ້ນ"</string>
<string name="screen_zoom_summary_large" msgid="4706951482598978984">"ໃຫຍ່"</string>
- <string name="screen_zoom_summary_very_large" msgid="7317423942896999029">"ໃຫຍ່ກວ່າ"</string>
- <string name="screen_zoom_summary_extremely_large" msgid="1438045624562358554">"ໃຫຍ່ທີ່ສຸດ"</string>
+ <string name="screen_zoom_summary_larger" msgid="7346406528348409139">"ໃຫຍ່ຂຶ້ນ"</string>
+ <string name="screen_zoom_summary_very_large" msgid="6853699118427019538">"ໃຫຍ່ຫຼາຍ"</string>
+ <string name="screen_zoom_summary_extra_large" msgid="4898389280769475551">"ໃຫຍ່ພິເສດ"</string>
+ <string name="screen_zoom_summary_largest" msgid="5120290329622591371">"ໃຫຍ່ທີ່ສຸດ"</string>
<string name="screen_zoom_summary_custom" msgid="3468154096832912210">"ປັບແຕ່ງເອງ (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
<string name="content_description_menu_button" msgid="6254844309171779931">"ເມນູ"</string>
<string name="retail_demo_reset_message" msgid="5392824901108195463">"Enter password to perform factory reset in demo mode"</string>
diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml
index 0379d5c..befae40 100644
--- a/packages/SettingsLib/res/values-lt/strings.xml
+++ b/packages/SettingsLib/res/values-lt/strings.xml
@@ -94,7 +94,7 @@
<string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"Prisijungta (<xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>), (telefono nėra), akumuliatoriaus įkrovos lygis: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_a2dp_battery_level" msgid="6499078454894324287">"Prisijungta (<xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>), (medijos nėra), akumuliatoriaus įkrovos lygis: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_no_a2dp_battery_level" msgid="8477440576953067242">"Prisijungta (<xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>), (telefono ar medijos nėra), akumuliatoriaus įkrovos lygis: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
- <string name="bluetooth_active_battery_level" msgid="2685517576209066008">"Aktyvus. Akumuliatorius lygis: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>."</string>
+ <string name="bluetooth_active_battery_level" msgid="2685517576209066008">"Aktyvus. Akumuliatoriaus lygis: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>."</string>
<string name="bluetooth_active_battery_level_untethered" msgid="4961338936672922617">"Aktyvus. Akumuliatoriaus lygis kairėje: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, dešinėje: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>."</string>
<string name="bluetooth_active_battery_level_untethered_left" msgid="2895644748625343977">"Aktyvus. K.: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> akumuliatoriaus įkrova."</string>
<string name="bluetooth_active_battery_level_untethered_right" msgid="7407517998880370179">"Aktyvus. D.: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> akumuliatoriaus įkrova."</string>
@@ -551,11 +551,15 @@
</string-array>
<string name="charge_length_format" msgid="6941645744588690932">"Prieš <xliff:g id="ID_1">%1$s</xliff:g>"</string>
<string name="remaining_length_format" msgid="4310625772926171089">"Liko <xliff:g id="ID_1">%1$s</xliff:g>"</string>
+ <string name="screen_zoom_summary_smallest" msgid="1042159093382395789">"Mažiausias"</string>
+ <string name="screen_zoom_summary_smaller" msgid="7452722393802683776">"Mažesnis"</string>
<string name="screen_zoom_summary_small" msgid="6050633151263074260">"Mažas"</string>
<string name="screen_zoom_summary_default" msgid="1888865694033865408">"Numatytasis"</string>
<string name="screen_zoom_summary_large" msgid="4706951482598978984">"Didelis"</string>
- <string name="screen_zoom_summary_very_large" msgid="7317423942896999029">"Didesnis"</string>
- <string name="screen_zoom_summary_extremely_large" msgid="1438045624562358554">"Didžiausias"</string>
+ <string name="screen_zoom_summary_larger" msgid="7346406528348409139">"Didesnis"</string>
+ <string name="screen_zoom_summary_very_large" msgid="6853699118427019538">"Labai didelis"</string>
+ <string name="screen_zoom_summary_extra_large" msgid="4898389280769475551">"Itin didelis"</string>
+ <string name="screen_zoom_summary_largest" msgid="5120290329622591371">"Didžiausias"</string>
<string name="screen_zoom_summary_custom" msgid="3468154096832912210">"Tinkintas (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
<string name="content_description_menu_button" msgid="6254844309171779931">"Meniu"</string>
<string name="retail_demo_reset_message" msgid="5392824901108195463">"Įv. slapt. ir atk. gam. nust. dem. rež."</string>
diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml
index 756b119..e90e926 100644
--- a/packages/SettingsLib/res/values-lv/strings.xml
+++ b/packages/SettingsLib/res/values-lv/strings.xml
@@ -86,7 +86,7 @@
<string name="bluetooth_disconnecting" msgid="7638892134401574338">"Notiek atvienošana..."</string>
<string name="bluetooth_connecting" msgid="5871702668260192755">"Notiek savienojuma izveide…"</string>
<string name="bluetooth_connected" msgid="8065345572198502293">"Savienojums izveidots: <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
- <string name="bluetooth_pairing" msgid="4269046942588193600">"Notiek pāra izveide..."</string>
+ <string name="bluetooth_pairing" msgid="4269046942588193600">"Notiek savienošana pārī..."</string>
<string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"Savienojums izveidots (nav tālrunis): <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"Savienojums izveidots (nav multivide): <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"Savienots (nav tālrunis vai multivide): <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
@@ -207,7 +207,7 @@
<string name="launch_defaults_some" msgid="3631650616557252926">"Iestatīti daži noklusējumi"</string>
<string name="launch_defaults_none" msgid="8049374306261262709">"Nav iestatīti noklusējumi"</string>
<string name="tts_settings" msgid="8130616705989351312">"Teksta-runas iestatījumi"</string>
- <string name="tts_settings_title" msgid="7602210956640483039">"Teksta-runas izvade"</string>
+ <string name="tts_settings_title" msgid="7602210956640483039">"Teksta pārvēršanas runā izvade"</string>
<string name="tts_default_rate_title" msgid="3964187817364304022">"Runas ātrums"</string>
<string name="tts_default_rate_summary" msgid="3781937042151716987">"Teksta ierunāšanas ātrums"</string>
<string name="tts_default_pitch_title" msgid="6988592215554485479">"Tonis"</string>
@@ -328,7 +328,7 @@
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="7274396574659784285">"Aktivizēt Bluetooth audio LDAC\nkodeka atlasi: atskaņošanas kvalitāte"</string>
<string name="bluetooth_select_a2dp_codec_streaming_label" msgid="2040810756832027227">"Straumēšana: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
<string name="select_private_dns_configuration_title" msgid="7887550926056143018">"Privāts DNS"</string>
- <string name="select_private_dns_configuration_dialog_title" msgid="3731422918335951912">"Atlasiet privāta DNS režīmu"</string>
+ <string name="select_private_dns_configuration_dialog_title" msgid="3731422918335951912">"Atlasiet privātā DNS režīmu"</string>
<string name="private_dns_mode_off" msgid="7065962499349997041">"Izslēgts"</string>
<string name="private_dns_mode_opportunistic" msgid="1947864819060442354">"Automātiski"</string>
<string name="private_dns_mode_provider" msgid="3619040641762557028">"Privātā DNS pakalpojumu sniedzēja saimniekdatora nosaukums"</string>
@@ -551,11 +551,15 @@
</string-array>
<string name="charge_length_format" msgid="6941645744588690932">"Pirms šāda laika: <xliff:g id="ID_1">%1$s</xliff:g>"</string>
<string name="remaining_length_format" msgid="4310625772926171089">"Atlikušais laiks: <xliff:g id="ID_1">%1$s</xliff:g>"</string>
+ <string name="screen_zoom_summary_smallest" msgid="1042159093382395789">"Vismazākais"</string>
+ <string name="screen_zoom_summary_smaller" msgid="7452722393802683776">"Mazāks"</string>
<string name="screen_zoom_summary_small" msgid="6050633151263074260">"Mazs"</string>
<string name="screen_zoom_summary_default" msgid="1888865694033865408">"Noklusējuma"</string>
<string name="screen_zoom_summary_large" msgid="4706951482598978984">"Liels"</string>
- <string name="screen_zoom_summary_very_large" msgid="7317423942896999029">"Lielāks"</string>
- <string name="screen_zoom_summary_extremely_large" msgid="1438045624562358554">"Vislielākais"</string>
+ <string name="screen_zoom_summary_larger" msgid="7346406528348409139">"Lielāks"</string>
+ <string name="screen_zoom_summary_very_large" msgid="6853699118427019538">"Ļoti liels"</string>
+ <string name="screen_zoom_summary_extra_large" msgid="4898389280769475551">"Īpaši liels"</string>
+ <string name="screen_zoom_summary_largest" msgid="5120290329622591371">"Vislielākais"</string>
<string name="screen_zoom_summary_custom" msgid="3468154096832912210">"Pielāgots (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
<string name="content_description_menu_button" msgid="6254844309171779931">"Izvēlne"</string>
<string name="retail_demo_reset_message" msgid="5392824901108195463">"Iev. paroli, lai atiest. rūpnīcas iest. dem. režīmā"</string>
diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml
index a181249..14a89e3 100644
--- a/packages/SettingsLib/res/values-mk/strings.xml
+++ b/packages/SettingsLib/res/values-mk/strings.xml
@@ -209,7 +209,7 @@
<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>
+ <string name="tts_default_rate_summary" msgid="3781937042151716987">"Брзина со која се изговара текстот"</string>
<string name="tts_default_pitch_title" msgid="6988592215554485479">"Висина на тонот"</string>
<string name="tts_default_pitch_summary" msgid="9132719475281551884">"Влијае на тонот на синтетизираниот говор"</string>
<string name="tts_default_lang_title" msgid="4698933575028098940">"Јазик"</string>
@@ -246,9 +246,9 @@
<item msgid="6946761421234586000">"400 %"</item>
</string-array>
<string name="choose_profile" msgid="343803890897657450">"Изберете профил"</string>
- <string name="category_personal" msgid="6236798763159385225">"Личен"</string>
+ <string name="category_personal" msgid="6236798763159385225">"Лично"</string>
<string name="category_work" msgid="4014193632325996115">"Работа"</string>
- <string name="category_private" msgid="4244892185452788977">"Приватен"</string>
+ <string name="category_private" msgid="4244892185452788977">"Приватно"</string>
<string name="category_clone" msgid="1554511758987195974">"Клон"</string>
<string name="development_settings_title" msgid="140296922921597393">"Програмерски опции"</string>
<string name="development_settings_enable" msgid="4285094651288242183">"Овозможете ги програмерските опции"</string>
@@ -551,11 +551,15 @@
</string-array>
<string name="charge_length_format" msgid="6941645744588690932">"Пред <xliff:g id="ID_1">%1$s</xliff:g>"</string>
<string name="remaining_length_format" msgid="4310625772926171089">"Преостануваат <xliff:g id="ID_1">%1$s</xliff:g>"</string>
+ <string name="screen_zoom_summary_smallest" msgid="1042159093382395789">"Најмало"</string>
+ <string name="screen_zoom_summary_smaller" msgid="7452722393802683776">"Помало"</string>
<string name="screen_zoom_summary_small" msgid="6050633151263074260">"Мал"</string>
<string name="screen_zoom_summary_default" msgid="1888865694033865408">"Стандардно"</string>
<string name="screen_zoom_summary_large" msgid="4706951482598978984">"Голем"</string>
- <string name="screen_zoom_summary_very_large" msgid="7317423942896999029">"Поголем"</string>
- <string name="screen_zoom_summary_extremely_large" msgid="1438045624562358554">"Најголем"</string>
+ <string name="screen_zoom_summary_larger" msgid="7346406528348409139">"Поголемо"</string>
+ <string name="screen_zoom_summary_very_large" msgid="6853699118427019538">"Многу големо"</string>
+ <string name="screen_zoom_summary_extra_large" msgid="4898389280769475551">"Особено големо"</string>
+ <string name="screen_zoom_summary_largest" msgid="5120290329622591371">"Најголемо"</string>
<string name="screen_zoom_summary_custom" msgid="3468154096832912210">"Приспособен (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
<string name="content_description_menu_button" msgid="6254844309171779931">"Мени"</string>
<string name="retail_demo_reset_message" msgid="5392824901108195463">"Внесете лозинка за фаб. ресет. во демо"</string>
diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml
index b1d1d4f..c598543 100644
--- a/packages/SettingsLib/res/values-ml/strings.xml
+++ b/packages/SettingsLib/res/values-ml/strings.xml
@@ -551,11 +551,15 @@
</string-array>
<string name="charge_length_format" msgid="6941645744588690932">"<xliff:g id="ID_1">%1$s</xliff:g> മുമ്പ്"</string>
<string name="remaining_length_format" msgid="4310625772926171089">"<xliff:g id="ID_1">%1$s</xliff:g> ശേഷിക്കുന്നു"</string>
+ <string name="screen_zoom_summary_smallest" msgid="1042159093382395789">"ഏറ്റവും ചെറുത്"</string>
+ <string name="screen_zoom_summary_smaller" msgid="7452722393802683776">"വളരെ ചെറുത്"</string>
<string name="screen_zoom_summary_small" msgid="6050633151263074260">"ചെറുത്"</string>
<string name="screen_zoom_summary_default" msgid="1888865694033865408">"ഡിഫോൾട്ട്"</string>
<string name="screen_zoom_summary_large" msgid="4706951482598978984">"വലുത്"</string>
- <string name="screen_zoom_summary_very_large" msgid="7317423942896999029">"കൂടുതൽ വലുത്"</string>
- <string name="screen_zoom_summary_extremely_large" msgid="1438045624562358554">"ഏറ്റവും വലുത്"</string>
+ <string name="screen_zoom_summary_larger" msgid="7346406528348409139">"കൂടുതൽ വലുത്"</string>
+ <string name="screen_zoom_summary_very_large" msgid="6853699118427019538">"വളരെയധികം വലുത്"</string>
+ <string name="screen_zoom_summary_extra_large" msgid="4898389280769475551">"വളരെ വലുത്"</string>
+ <string name="screen_zoom_summary_largest" msgid="5120290329622591371">"ഏറ്റവും വലുത്"</string>
<string name="screen_zoom_summary_custom" msgid="3468154096832912210">"ഇഷ്ടാനുസൃതം ( <xliff:g id="DENSITYDPI">%d</xliff:g> )"</string>
<string name="content_description_menu_button" msgid="6254844309171779931">"മെനു"</string>
<string name="retail_demo_reset_message" msgid="5392824901108195463">"ഡെമോ മോഡിൽ ഫാക്ടറി റീസെറ്റിന് പാസ്വേഡ് നൽകുക"</string>
diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml
index 9b128bd..943cf44 100644
--- a/packages/SettingsLib/res/values-mn/strings.xml
+++ b/packages/SettingsLib/res/values-mn/strings.xml
@@ -491,7 +491,7 @@
<string name="daltonizer_mode_deuteranomaly" msgid="3507284319584683963">"Дьютераномаль (улаан-ногоон)"</string>
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Протаномаль (улаан-ногоон)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Тританомаль (цэнхэр-шар)"</string>
- <string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Өнгө тохируулга"</string>
+ <string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Өнгө засах"</string>
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"Өнгө засах нь таныг дараахыг хийхийг хүсэх үед хэрэгтэй байж болно:<br/> <ol> <li> Өнгөнүүдийг илүү нарийвчилж харах</li> <li> Төвлөрөхийн тулд өнгөнүүдийг хасах</li> </ol>"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Давхарласан <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
@@ -551,11 +551,15 @@
</string-array>
<string name="charge_length_format" msgid="6941645744588690932">"<xliff:g id="ID_1">%1$s</xliff:g> өмнө"</string>
<string name="remaining_length_format" msgid="4310625772926171089">"<xliff:g id="ID_1">%1$s</xliff:g> үлдсэн"</string>
+ <string name="screen_zoom_summary_smallest" msgid="1042159093382395789">"Хамгийн жижиг"</string>
+ <string name="screen_zoom_summary_smaller" msgid="7452722393802683776">"Илүү жижиг"</string>
<string name="screen_zoom_summary_small" msgid="6050633151263074260">"Жижиг"</string>
<string name="screen_zoom_summary_default" msgid="1888865694033865408">"Өгөгдмөл"</string>
<string name="screen_zoom_summary_large" msgid="4706951482598978984">"Том"</string>
- <string name="screen_zoom_summary_very_large" msgid="7317423942896999029">"Илүү том"</string>
- <string name="screen_zoom_summary_extremely_large" msgid="1438045624562358554">"Хамгийн том"</string>
+ <string name="screen_zoom_summary_larger" msgid="7346406528348409139">"Илүү том"</string>
+ <string name="screen_zoom_summary_very_large" msgid="6853699118427019538">"Маш том"</string>
+ <string name="screen_zoom_summary_extra_large" msgid="4898389280769475551">"Асар том"</string>
+ <string name="screen_zoom_summary_largest" msgid="5120290329622591371">"Хамгийн том"</string>
<string name="screen_zoom_summary_custom" msgid="3468154096832912210">"Тогтмол утга (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
<string name="content_description_menu_button" msgid="6254844309171779931">"Цэс"</string>
<string name="retail_demo_reset_message" msgid="5392824901108195463">"Үйлдвэрийн тохиргоог демо горимд ажиллуулахын тулд нууц үг оруулна уу"</string>
diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml
index 7c8c6aa..f61ca8a 100644
--- a/packages/SettingsLib/res/values-mr/strings.xml
+++ b/packages/SettingsLib/res/values-mr/strings.xml
@@ -551,11 +551,15 @@
</string-array>
<string name="charge_length_format" msgid="6941645744588690932">"<xliff:g id="ID_1">%1$s</xliff:g> पूर्वी"</string>
<string name="remaining_length_format" msgid="4310625772926171089">"<xliff:g id="ID_1">%1$s</xliff:g> शिल्लक"</string>
+ <string name="screen_zoom_summary_smallest" msgid="1042159093382395789">"सर्वाधिक लहान"</string>
+ <string name="screen_zoom_summary_smaller" msgid="7452722393802683776">"अधिक लहान"</string>
<string name="screen_zoom_summary_small" msgid="6050633151263074260">"लहान"</string>
<string name="screen_zoom_summary_default" msgid="1888865694033865408">"डीफॉल्ट"</string>
<string name="screen_zoom_summary_large" msgid="4706951482598978984">"मोठा"</string>
- <string name="screen_zoom_summary_very_large" msgid="7317423942896999029">"आणखी मोठा"</string>
- <string name="screen_zoom_summary_extremely_large" msgid="1438045624562358554">"सर्वात मोठा"</string>
+ <string name="screen_zoom_summary_larger" msgid="7346406528348409139">"अधिक मोठी"</string>
+ <string name="screen_zoom_summary_very_large" msgid="6853699118427019538">"खूप मोठी"</string>
+ <string name="screen_zoom_summary_extra_large" msgid="4898389280769475551">"अतिरिक्त मोठी"</string>
+ <string name="screen_zoom_summary_largest" msgid="5120290329622591371">"सर्वाधिक मोठी"</string>
<string name="screen_zoom_summary_custom" msgid="3468154096832912210">"कस्टम करा (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
<string name="content_description_menu_button" msgid="6254844309171779931">"मेनू"</string>
<string name="retail_demo_reset_message" msgid="5392824901108195463">"डेमो मोडमध्ये फॅक्टरी रीसेट करण्यासाठी पासवर्ड एंटर करा"</string>
diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml
index 52d1d5a..d305dfdb 100644
--- a/packages/SettingsLib/res/values-ms/strings.xml
+++ b/packages/SettingsLib/res/values-ms/strings.xml
@@ -139,7 +139,7 @@
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Peranti input"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Akses Internet"</string>
<string name="bluetooth_profile_pbap" msgid="2103406516858653017">"Benarkan akses kepada kenalan dan sejarah panggilan"</string>
- <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"Maklumat digunakan untuk makluman panggilan dan banyak lagi"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"Maklumat akan digunakan untuk pengumuman panggilan dan lain-lain"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Perkongsian sambungan Internet"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Mesej Teks"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Akses SIM"</string>
@@ -551,11 +551,15 @@
</string-array>
<string name="charge_length_format" msgid="6941645744588690932">"<xliff:g id="ID_1">%1$s</xliff:g> yang lalu"</string>
<string name="remaining_length_format" msgid="4310625772926171089">"<xliff:g id="ID_1">%1$s</xliff:g> lagi"</string>
+ <string name="screen_zoom_summary_smallest" msgid="1042159093382395789">"Paling kecil"</string>
+ <string name="screen_zoom_summary_smaller" msgid="7452722393802683776">"Lebih kecil"</string>
<string name="screen_zoom_summary_small" msgid="6050633151263074260">"Kecil"</string>
<string name="screen_zoom_summary_default" msgid="1888865694033865408">"Lalai"</string>
<string name="screen_zoom_summary_large" msgid="4706951482598978984">"Besar"</string>
- <string name="screen_zoom_summary_very_large" msgid="7317423942896999029">"Lebih besar"</string>
- <string name="screen_zoom_summary_extremely_large" msgid="1438045624562358554">"Terbesar"</string>
+ <string name="screen_zoom_summary_larger" msgid="7346406528348409139">"Lebih besar"</string>
+ <string name="screen_zoom_summary_very_large" msgid="6853699118427019538">"Amat Besar"</string>
+ <string name="screen_zoom_summary_extra_large" msgid="4898389280769475551">"Teramat Besar"</string>
+ <string name="screen_zoom_summary_largest" msgid="5120290329622591371">"Terbesar"</string>
<string name="screen_zoom_summary_custom" msgid="3468154096832912210">"Tersuai (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
<string name="content_description_menu_button" msgid="6254844309171779931">"Menu"</string>
<string name="retail_demo_reset_message" msgid="5392824901108195463">"Mskkn kta laluan utk ttpn sml kilang dlm mod demo"</string>
diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml
index e3d1005..950a7c8 100644
--- a/packages/SettingsLib/res/values-my/strings.xml
+++ b/packages/SettingsLib/res/values-my/strings.xml
@@ -209,9 +209,9 @@
<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>
+ <string name="tts_default_rate_summary" msgid="3781937042151716987">"စာသားကို ဖတ်ပြသော အမြန်နှုန်း"</string>
<string name="tts_default_pitch_title" msgid="6988592215554485479">"အသံအနိမ့်အမြင့်"</string>
- <string name="tts_default_pitch_summary" msgid="9132719475281551884">"စက်ဖြင့်ထုတ်လုပ်ထားသည့် စကားသံကို အကျိုးသက်ရောက်မှုရှိပါမည်။"</string>
+ <string name="tts_default_pitch_summary" msgid="9132719475281551884">"စက်ဖြင့်ထုတ်လုပ်သည့် စကားသံအပေါ် သက်ရောက်သည်"</string>
<string name="tts_default_lang_title" msgid="4698933575028098940">"ဘာသာစကား"</string>
<string name="tts_lang_use_system" msgid="6312945299804012406">"စနစ်၏ ဘာသာစကားကို အသုံးပြုရန်"</string>
<string name="tts_lang_not_selected" msgid="7927823081096056147">"ဘာသာစကား မရွေးချယ်ထားပါ။"</string>
@@ -551,11 +551,15 @@
</string-array>
<string name="charge_length_format" msgid="6941645744588690932">"ပြီးခဲ့သည့် <xliff:g id="ID_1">%1$s</xliff:g> က"</string>
<string name="remaining_length_format" msgid="4310625772926171089">"<xliff:g id="ID_1">%1$s</xliff:g> ကျန်"</string>
+ <string name="screen_zoom_summary_smallest" msgid="1042159093382395789">"အသေးဆုံး"</string>
+ <string name="screen_zoom_summary_smaller" msgid="7452722393802683776">"ပိုသေး"</string>
<string name="screen_zoom_summary_small" msgid="6050633151263074260">"သေး"</string>
<string name="screen_zoom_summary_default" msgid="1888865694033865408">"မူရင်း"</string>
<string name="screen_zoom_summary_large" msgid="4706951482598978984">"ကြီး"</string>
- <string name="screen_zoom_summary_very_large" msgid="7317423942896999029">"ပိုကြီး"</string>
- <string name="screen_zoom_summary_extremely_large" msgid="1438045624562358554">"အကြီးဆုံး"</string>
+ <string name="screen_zoom_summary_larger" msgid="7346406528348409139">"ပိုကြီး"</string>
+ <string name="screen_zoom_summary_very_large" msgid="6853699118427019538">"အလွန်ကြီး"</string>
+ <string name="screen_zoom_summary_extra_large" msgid="4898389280769475551">"အလွန်ကြီး"</string>
+ <string name="screen_zoom_summary_largest" msgid="5120290329622591371">"အကြီးဆုံး"</string>
<string name="screen_zoom_summary_custom" msgid="3468154096832912210">"စိတ်ကြိုက် (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
<string name="content_description_menu_button" msgid="6254844309171779931">"မီနူး"</string>
<string name="retail_demo_reset_message" msgid="5392824901108195463">"ဒီမိုမုဒ်၌မူလဆက်တင်ထားရန် စကားဝှက်ထည့်ပါ"</string>
diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml
index 9b2fbd8..11fc906 100644
--- a/packages/SettingsLib/res/values-nb/strings.xml
+++ b/packages/SettingsLib/res/values-nb/strings.xml
@@ -551,11 +551,15 @@
</string-array>
<string name="charge_length_format" msgid="6941645744588690932">"<xliff:g id="ID_1">%1$s</xliff:g> siden"</string>
<string name="remaining_length_format" msgid="4310625772926171089">"<xliff:g id="ID_1">%1$s</xliff:g> gjenstår"</string>
+ <string name="screen_zoom_summary_smallest" msgid="1042159093382395789">"Minst"</string>
+ <string name="screen_zoom_summary_smaller" msgid="7452722393802683776">"Mindre"</string>
<string name="screen_zoom_summary_small" msgid="6050633151263074260">"Liten"</string>
<string name="screen_zoom_summary_default" msgid="1888865694033865408">"Standard"</string>
<string name="screen_zoom_summary_large" msgid="4706951482598978984">"Stor"</string>
- <string name="screen_zoom_summary_very_large" msgid="7317423942896999029">"Større"</string>
- <string name="screen_zoom_summary_extremely_large" msgid="1438045624562358554">"Størst"</string>
+ <string name="screen_zoom_summary_larger" msgid="7346406528348409139">"Større"</string>
+ <string name="screen_zoom_summary_very_large" msgid="6853699118427019538">"Veldig stor"</string>
+ <string name="screen_zoom_summary_extra_large" msgid="4898389280769475551">"Ekstra stor"</string>
+ <string name="screen_zoom_summary_largest" msgid="5120290329622591371">"Størst"</string>
<string name="screen_zoom_summary_custom" msgid="3468154096832912210">"Egendefinert (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
<string name="content_description_menu_button" msgid="6254844309171779931">"Meny"</string>
<string name="retail_demo_reset_message" msgid="5392824901108195463">"Skriv inn passordet for å tilbakestille til fabrikkstandard i demomodus"</string>
diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml
index fa8347e..bc4c1b9 100644
--- a/packages/SettingsLib/res/values-ne/strings.xml
+++ b/packages/SettingsLib/res/values-ne/strings.xml
@@ -209,7 +209,7 @@
<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>
+ <string name="tts_default_rate_summary" msgid="3781937042151716987">"टेक्स्ट वाचन हुने गति"</string>
<string name="tts_default_pitch_title" msgid="6988592215554485479">"पिच"</string>
<string name="tts_default_pitch_summary" msgid="9132719475281551884">"संश्लेषित बोलीको टोनमा प्रभाव पार्छ"</string>
<string name="tts_default_lang_title" msgid="4698933575028098940">"भाषा"</string>
@@ -551,11 +551,15 @@
</string-array>
<string name="charge_length_format" msgid="6941645744588690932">"<xliff:g id="ID_1">%1$s</xliff:g> पहिले"</string>
<string name="remaining_length_format" msgid="4310625772926171089">"<xliff:g id="ID_1">%1$s</xliff:g> बाँकी"</string>
+ <string name="screen_zoom_summary_smallest" msgid="1042159093382395789">"सबैभन्दा सानो"</string>
+ <string name="screen_zoom_summary_smaller" msgid="7452722393802683776">"अझ सानो"</string>
<string name="screen_zoom_summary_small" msgid="6050633151263074260">"सानो"</string>
<string name="screen_zoom_summary_default" msgid="1888865694033865408">"डिफल्ट"</string>
<string name="screen_zoom_summary_large" msgid="4706951482598978984">"ठुलो"</string>
- <string name="screen_zoom_summary_very_large" msgid="7317423942896999029">"अझ ठुलो"</string>
- <string name="screen_zoom_summary_extremely_large" msgid="1438045624562358554">"सबैभन्दा ठुलो"</string>
+ <string name="screen_zoom_summary_larger" msgid="7346406528348409139">"अझ ठुलो"</string>
+ <string name="screen_zoom_summary_very_large" msgid="6853699118427019538">"धेरै ठुलो"</string>
+ <string name="screen_zoom_summary_extra_large" msgid="4898389280769475551">"एकदमै ठुलो"</string>
+ <string name="screen_zoom_summary_largest" msgid="5120290329622591371">"सबैभन्दा ठुलो"</string>
<string name="screen_zoom_summary_custom" msgid="3468154096832912210">" कस्टम (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
<string name="content_description_menu_button" msgid="6254844309171779931">"मेनु"</string>
<string name="retail_demo_reset_message" msgid="5392824901108195463">"डेमो मोडमा फ्याक्ट्री रिसेट गर्न पासवर्ड प्रविष्टि गर्नुहोस्"</string>
@@ -673,8 +677,8 @@
<string name="add_guest_failed" msgid="8074548434469843443">"नयाँ अतिथि बनाउन सकिएन"</string>
<string name="user_nickname" msgid="262624187455825083">"उपनाम"</string>
<string name="edit_user_info_message" msgid="6677556031419002895">"यो डिभाइस प्रयोग गर्ने सबै जना तपाईंले छनौट गर्ने नाम र फोटो देख्न सक्ने छन्।"</string>
- <string name="user_add_user" msgid="7876449291500212468">"प्रयोगकर्ता थप्नुहोस्"</string>
- <string name="guest_new_guest" msgid="3482026122932643557">"अतिथि थप्नुहोस्"</string>
+ <string name="user_add_user" msgid="7876449291500212468">"प्रयोगकर्ता कनेक्ट गर्नुहोस्"</string>
+ <string name="guest_new_guest" msgid="3482026122932643557">"अतिथि कनेक्ट गर्नुहोस्"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"अथिति हटाउनुहोस्"</string>
<string name="guest_reset_guest" msgid="6110013010356013758">"अतिथि सत्र रिसेट गर्नुहोस्"</string>
<string name="guest_reset_guest_dialog_title" msgid="8047270010895437534">"अतिथिका रूपमा ब्राउज गर्ने सेसन रिसेट गर्ने हो?"</string>
diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml
index ffde98a..b25414b 100644
--- a/packages/SettingsLib/res/values-nl/strings.xml
+++ b/packages/SettingsLib/res/values-nl/strings.xml
@@ -551,11 +551,15 @@
</string-array>
<string name="charge_length_format" msgid="6941645744588690932">"<xliff:g id="ID_1">%1$s</xliff:g> geleden"</string>
<string name="remaining_length_format" msgid="4310625772926171089">"Nog <xliff:g id="ID_1">%1$s</xliff:g>"</string>
+ <string name="screen_zoom_summary_smallest" msgid="1042159093382395789">"Kleinst"</string>
+ <string name="screen_zoom_summary_smaller" msgid="7452722393802683776">"Kleiner"</string>
<string name="screen_zoom_summary_small" msgid="6050633151263074260">"Klein"</string>
<string name="screen_zoom_summary_default" msgid="1888865694033865408">"Standaard"</string>
<string name="screen_zoom_summary_large" msgid="4706951482598978984">"Groot"</string>
- <string name="screen_zoom_summary_very_large" msgid="7317423942896999029">"Groter"</string>
- <string name="screen_zoom_summary_extremely_large" msgid="1438045624562358554">"Grootst"</string>
+ <string name="screen_zoom_summary_larger" msgid="7346406528348409139">"Groter"</string>
+ <string name="screen_zoom_summary_very_large" msgid="6853699118427019538">"Zeer groot"</string>
+ <string name="screen_zoom_summary_extra_large" msgid="4898389280769475551">"Extra groot"</string>
+ <string name="screen_zoom_summary_largest" msgid="5120290329622591371">"Grootst"</string>
<string name="screen_zoom_summary_custom" msgid="3468154096832912210">"Aangepast (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
<string name="content_description_menu_button" msgid="6254844309171779931">"Menu"</string>
<string name="retail_demo_reset_message" msgid="5392824901108195463">"Geef wachtwoord op om terug te zetten op fabrieksinstellingen in demomodus"</string>
diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml
index 0ba9cb6..938e0e0 100644
--- a/packages/SettingsLib/res/values-or/strings.xml
+++ b/packages/SettingsLib/res/values-or/strings.xml
@@ -95,7 +95,7 @@
<string name="bluetooth_connected_no_a2dp_battery_level" msgid="6499078454894324287">"ସଂଯୁକ୍ତ ହେଲା (ମିଡିଆ ନୁହେଁ), ବ୍ୟାଟେରୀ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_no_a2dp_battery_level" msgid="8477440576953067242">"ସଂଯୁକ୍ତ ହେଲା (ଫୋନ୍ କିମ୍ବା ମେଡିଆ ନୁହେଁ), ବ୍ୟାଟେରୀ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
<string name="bluetooth_active_battery_level" msgid="2685517576209066008">"ସକ୍ରିୟ ଅଛି। <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> ବେଟେରୀ।"</string>
- <string name="bluetooth_active_battery_level_untethered" msgid="4961338936672922617">"ସକ୍ରିୟ ଅଛନ୍ତି। ବାମ: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, ଡାହାଣ: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> ବେଟେରୀ।"</string>
+ <string name="bluetooth_active_battery_level_untethered" msgid="4961338936672922617">"ସକ୍ରିୟ ଅଛି। ବାମ: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, ଡାହାଣ: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> ବେଟେରୀ।"</string>
<string name="bluetooth_active_battery_level_untethered_left" msgid="2895644748625343977">"ସକ୍ରିୟ ଅଛି। ବାମ: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> ବେଟେରୀ।"</string>
<string name="bluetooth_active_battery_level_untethered_right" msgid="7407517998880370179">"ସକ୍ରିୟ ଅଛି। ଡାହାଣ: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> ବେଟେରୀ।"</string>
<string name="bluetooth_battery_level" msgid="2893696778200201555">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> ବେଟେରୀ"</string>
@@ -167,7 +167,7 @@
<string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"ଇନ୍ପୁଟ୍ ପାଇଁ ବ୍ୟବହାର କରନ୍ତୁ"</string>
<string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="3374057355721486932">"ଶ୍ରବଣ ଯନ୍ତ୍ର ପାଇଁ ବ୍ୟବହାର କରନ୍ତୁ"</string>
<string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"LE_AUDIO ପାଇଁ ବ୍ୟବହାର କରନ୍ତୁ"</string>
- <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"ପେୟାର୍"</string>
+ <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"ପେୟାର କରନ୍ତୁ"</string>
<string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"ପେୟାର୍"</string>
<string name="bluetooth_pairing_decline" msgid="6483118841204885890">"ବାତିଲ କରନ୍ତୁ"</string>
<string name="bluetooth_pairing_will_share_phonebook" msgid="3064334458659165176">"ପେୟାରିଂ ଫଳରେ ସଂଯୁକ୍ତ ଥିବା ବେଳେ ଆପଣଙ୍କ ସମ୍ପର୍କଗୁଡ଼ିକୁ ଏବଂ କଲ୍ର ଇତିବୃତିକୁ ଆକସେସ୍ ମଞ୍ଜୁର ହୁଏ।"</string>
@@ -492,7 +492,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"ପ୍ରୋଟାନୋମାଲି (ଲାଲ୍-ସବୁଜ)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomaly (ନୀଳ-ହଳଦିଆ)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"ରଙ୍ଗ ସଂଶୋଧନ"</string>
- <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"ଆପଣ ଏସବୁ କରିବାକୁ ଚାହିଁଲେ ରଙ୍ଗ ସଂଶୋଧନ ଉପଯୋଗୀ ହୋଇପାରିବ:<br/> <ol> <li>&nbsp;ଆହୁରି ସଠିକ୍ ଭାବେ ରଙ୍ଗଗୁଡ଼ିକ ଦେଖିବା</li> <li>&nbsp;ଆପଣଙ୍କୁ ଫୋକସ କରିବାରେ ସାହାଯ୍ୟ କରିବା ପାଇଁ ରଙ୍ଗଗୁଡ଼ିକୁ କାଢ଼ିବା</li> </ol>"</string>
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"ଆପଣ ଏସବୁ କରିବାକୁ ଚାହିଁଲେ ରଙ୍ଗ ସଂଶୋଧନ ଉପଯୋଗୀ ହୋଇପାରିବ:<br/> <ol> <li>&nbsp;ଆହୁରି ସଠିକ ଭାବେ ରଙ୍ଗଗୁଡ଼ିକ ଦେଖିବା</li> <li>&nbsp;ଆପଣଙ୍କୁ ଫୋକସ କରିବାରେ ସାହାଯ୍ୟ କରିବା ପାଇଁ ରଙ୍ଗଗୁଡ଼ିକୁ କାଢ଼ିବା</li> </ol>"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> ଦ୍ୱାରା ଓଭର୍ରାଇଡ୍ କରାଯାଇଛି"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - ବେଟେରୀକୁ ସୁରକ୍ଷିତ ରଖିବା ପାଇଁ ଚାର୍ଜିଂ ହୋଲ୍ଡରେ ଅଛି"</string>
@@ -551,11 +551,15 @@
</string-array>
<string name="charge_length_format" msgid="6941645744588690932">"<xliff:g id="ID_1">%1$s</xliff:g> ପୂର୍ବେ"</string>
<string name="remaining_length_format" msgid="4310625772926171089">"<xliff:g id="ID_1">%1$s</xliff:g> ଅବଶିଷ୍ଟ"</string>
+ <string name="screen_zoom_summary_smallest" msgid="1042159093382395789">"ସବୁଠୁ ଛୋଟ"</string>
+ <string name="screen_zoom_summary_smaller" msgid="7452722393802683776">"ଛୋଟ"</string>
<string name="screen_zoom_summary_small" msgid="6050633151263074260">"ଛୋଟ"</string>
<string name="screen_zoom_summary_default" msgid="1888865694033865408">"ଡିଫଲ୍ଟ"</string>
<string name="screen_zoom_summary_large" msgid="4706951482598978984">"ବଡ"</string>
- <string name="screen_zoom_summary_very_large" msgid="7317423942896999029">"ବୃହତ୍ତମ"</string>
- <string name="screen_zoom_summary_extremely_large" msgid="1438045624562358554">"ବୃହତ୍ତମ"</string>
+ <string name="screen_zoom_summary_larger" msgid="7346406528348409139">"ବଡ଼"</string>
+ <string name="screen_zoom_summary_very_large" msgid="6853699118427019538">"ବହୁତ ବଡ଼"</string>
+ <string name="screen_zoom_summary_extra_large" msgid="4898389280769475551">"ଅତିରିକ୍ତ ବଡ଼"</string>
+ <string name="screen_zoom_summary_largest" msgid="5120290329622591371">"ସବୁଠୁ ବଡ଼"</string>
<string name="screen_zoom_summary_custom" msgid="3468154096832912210">"କଷ୍ଟମ୍ (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
<string name="content_description_menu_button" msgid="6254844309171779931">"ମେନୁ"</string>
<string name="retail_demo_reset_message" msgid="5392824901108195463">"ଡେମୋ ମୋଡ୍ରେ ଫ୍ୟାକ୍ଟୋରୀ ରିସେଟ୍ କରିବାକୁ ପାସ୍ୱାର୍ଡ ଲେଖନ୍ତୁ"</string>
diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml
index 08106e4..89520d0 100644
--- a/packages/SettingsLib/res/values-pa/strings.xml
+++ b/packages/SettingsLib/res/values-pa/strings.xml
@@ -551,11 +551,15 @@
</string-array>
<string name="charge_length_format" msgid="6941645744588690932">"<xliff:g id="ID_1">%1$s</xliff:g> ਪਹਿਲਾਂ"</string>
<string name="remaining_length_format" msgid="4310625772926171089">"<xliff:g id="ID_1">%1$s</xliff:g> ਬਾਕੀ"</string>
+ <string name="screen_zoom_summary_smallest" msgid="1042159093382395789">"ਸਭ ਤੋਂ ਛੋਟਾ"</string>
+ <string name="screen_zoom_summary_smaller" msgid="7452722393802683776">"ਜ਼ਿਆਦਾ ਛੋਟਾ"</string>
<string name="screen_zoom_summary_small" msgid="6050633151263074260">"ਛੋਟਾ"</string>
<string name="screen_zoom_summary_default" msgid="1888865694033865408">"ਪੂਰਵ-ਨਿਰਧਾਰਿਤ"</string>
<string name="screen_zoom_summary_large" msgid="4706951482598978984">"ਵੱਡਾ"</string>
- <string name="screen_zoom_summary_very_large" msgid="7317423942896999029">"ਜ਼ਿਆਦਾ ਵੱਡਾ"</string>
- <string name="screen_zoom_summary_extremely_large" msgid="1438045624562358554">"ਸਭ ਤੋਂ ਵੱਡੀ"</string>
+ <string name="screen_zoom_summary_larger" msgid="7346406528348409139">"ਕਾਫ਼ੀ ਵੱਡਾ"</string>
+ <string name="screen_zoom_summary_very_large" msgid="6853699118427019538">"ਬਹੁਤ ਵੱਡਾ"</string>
+ <string name="screen_zoom_summary_extra_large" msgid="4898389280769475551">"ਜ਼ਿਆਦਾ ਵੱਡਾ"</string>
+ <string name="screen_zoom_summary_largest" msgid="5120290329622591371">"ਸਭ ਤੋਂ ਵੱਡਾ"</string>
<string name="screen_zoom_summary_custom" msgid="3468154096832912210">"ਵਿਉਂਂਤੀ (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
<string name="content_description_menu_button" msgid="6254844309171779931">"ਮੀਨੂ"</string>
<string name="retail_demo_reset_message" msgid="5392824901108195463">"ਡੈਮੋ ਮੋਡ \'ਚ ਫੈਕਟਰੀ ਰੀਸੈੱਟ ਲਈ ਪਾਸਵਰਡ ਦਿਓ"</string>
diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml
index d8d2b0b..35e1251 100644
--- a/packages/SettingsLib/res/values-pl/strings.xml
+++ b/packages/SettingsLib/res/values-pl/strings.xml
@@ -551,11 +551,15 @@
</string-array>
<string name="charge_length_format" msgid="6941645744588690932">"<xliff:g id="ID_1">%1$s</xliff:g> temu"</string>
<string name="remaining_length_format" msgid="4310625772926171089">"Pozostało: <xliff:g id="ID_1">%1$s</xliff:g>"</string>
+ <string name="screen_zoom_summary_smallest" msgid="1042159093382395789">"Najmniejsze"</string>
+ <string name="screen_zoom_summary_smaller" msgid="7452722393802683776">"Mniejsze"</string>
<string name="screen_zoom_summary_small" msgid="6050633151263074260">"Mały"</string>
<string name="screen_zoom_summary_default" msgid="1888865694033865408">"Domyślny"</string>
<string name="screen_zoom_summary_large" msgid="4706951482598978984">"Duży"</string>
- <string name="screen_zoom_summary_very_large" msgid="7317423942896999029">"Większy"</string>
- <string name="screen_zoom_summary_extremely_large" msgid="1438045624562358554">"Największy"</string>
+ <string name="screen_zoom_summary_larger" msgid="7346406528348409139">"Większe"</string>
+ <string name="screen_zoom_summary_very_large" msgid="6853699118427019538">"Bardzo duże"</string>
+ <string name="screen_zoom_summary_extra_large" msgid="4898389280769475551">"Ekstra duże"</string>
+ <string name="screen_zoom_summary_largest" msgid="5120290329622591371">"Największe"</string>
<string name="screen_zoom_summary_custom" msgid="3468154096832912210">"Niestandardowe (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
<string name="content_description_menu_button" msgid="6254844309171779931">"Menu"</string>
<string name="retail_demo_reset_message" msgid="5392824901108195463">"Wpisz hasło, by przywrócić ustawienia fabryczne w trybie demonstracyjnym"</string>
diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml
index a3c8ae6..db18caa 100644
--- a/packages/SettingsLib/res/values-pt-rBR/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml
@@ -520,7 +520,7 @@
<string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> (carregando)"</string>
<string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - Carregado até <xliff:g id="TIME">%3$s</xliff:g>"</string>
<string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - Totalmente carregado até <xliff:g id="TIME">%2$s</xliff:g>"</string>
- <string name="power_remaining_charging_duration_only_v2" msgid="5358176435722950193">"Totalmente carregado até <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_remaining_charging_duration_only_v2" msgid="5358176435722950193">"Previsão de carga total: <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_fast_charging_duration_only_v2" msgid="6270950195810579563">"Carregado até <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"Desconhecido"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Carregando"</string>
@@ -551,11 +551,15 @@
</string-array>
<string name="charge_length_format" msgid="6941645744588690932">"<xliff:g id="ID_1">%1$s</xliff:g> atrás"</string>
<string name="remaining_length_format" msgid="4310625772926171089">"<xliff:g id="ID_1">%1$s</xliff:g> restante(s)"</string>
+ <string name="screen_zoom_summary_smallest" msgid="1042159093382395789">"Mínimo"</string>
+ <string name="screen_zoom_summary_smaller" msgid="7452722393802683776">"Menor"</string>
<string name="screen_zoom_summary_small" msgid="6050633151263074260">"Pequena"</string>
<string name="screen_zoom_summary_default" msgid="1888865694033865408">"Padrão"</string>
<string name="screen_zoom_summary_large" msgid="4706951482598978984">"Grande"</string>
- <string name="screen_zoom_summary_very_large" msgid="7317423942896999029">"Muito grande"</string>
- <string name="screen_zoom_summary_extremely_large" msgid="1438045624562358554">"Maior"</string>
+ <string name="screen_zoom_summary_larger" msgid="7346406528348409139">"Maior"</string>
+ <string name="screen_zoom_summary_very_large" msgid="6853699118427019538">"Muito grande"</string>
+ <string name="screen_zoom_summary_extra_large" msgid="4898389280769475551">"Extragrande"</string>
+ <string name="screen_zoom_summary_largest" msgid="5120290329622591371">"Máximo"</string>
<string name="screen_zoom_summary_custom" msgid="3468154096832912210">"Personalizada (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
<string name="content_description_menu_button" msgid="6254844309171779931">"Menu"</string>
<string name="retail_demo_reset_message" msgid="5392824901108195463">"Digite a senha para redef. p/ configuração original em modo demo"</string>
diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml
index 194af5b..4b1fdc8 100644
--- a/packages/SettingsLib/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml
@@ -551,11 +551,15 @@
</string-array>
<string name="charge_length_format" msgid="6941645744588690932">"Há <xliff:g id="ID_1">%1$s</xliff:g>"</string>
<string name="remaining_length_format" msgid="4310625772926171089">"Resta(m) <xliff:g id="ID_1">%1$s</xliff:g>"</string>
+ <string name="screen_zoom_summary_smallest" msgid="1042159093382395789">"O menor"</string>
+ <string name="screen_zoom_summary_smaller" msgid="7452722393802683776">"Menor"</string>
<string name="screen_zoom_summary_small" msgid="6050633151263074260">"Pequeno"</string>
<string name="screen_zoom_summary_default" msgid="1888865694033865408">"Predefinição"</string>
<string name="screen_zoom_summary_large" msgid="4706951482598978984">"Grande"</string>
- <string name="screen_zoom_summary_very_large" msgid="7317423942896999029">"Maior"</string>
- <string name="screen_zoom_summary_extremely_large" msgid="1438045624562358554">"O maior"</string>
+ <string name="screen_zoom_summary_larger" msgid="7346406528348409139">"Maior"</string>
+ <string name="screen_zoom_summary_very_large" msgid="6853699118427019538">"Muito grande"</string>
+ <string name="screen_zoom_summary_extra_large" msgid="4898389280769475551">"Extra grande"</string>
+ <string name="screen_zoom_summary_largest" msgid="5120290329622591371">"O maior"</string>
<string name="screen_zoom_summary_custom" msgid="3468154096832912210">"Personalizado (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
<string name="content_description_menu_button" msgid="6254844309171779931">"Menu"</string>
<string name="retail_demo_reset_message" msgid="5392824901108195463">"Introduzir palavra-passe para efetuar a reposição de fábrica no modo demo"</string>
diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml
index a3c8ae6..db18caa 100644
--- a/packages/SettingsLib/res/values-pt/strings.xml
+++ b/packages/SettingsLib/res/values-pt/strings.xml
@@ -520,7 +520,7 @@
<string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> (carregando)"</string>
<string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - Carregado até <xliff:g id="TIME">%3$s</xliff:g>"</string>
<string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - Totalmente carregado até <xliff:g id="TIME">%2$s</xliff:g>"</string>
- <string name="power_remaining_charging_duration_only_v2" msgid="5358176435722950193">"Totalmente carregado até <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_remaining_charging_duration_only_v2" msgid="5358176435722950193">"Previsão de carga total: <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_fast_charging_duration_only_v2" msgid="6270950195810579563">"Carregado até <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"Desconhecido"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Carregando"</string>
@@ -551,11 +551,15 @@
</string-array>
<string name="charge_length_format" msgid="6941645744588690932">"<xliff:g id="ID_1">%1$s</xliff:g> atrás"</string>
<string name="remaining_length_format" msgid="4310625772926171089">"<xliff:g id="ID_1">%1$s</xliff:g> restante(s)"</string>
+ <string name="screen_zoom_summary_smallest" msgid="1042159093382395789">"Mínimo"</string>
+ <string name="screen_zoom_summary_smaller" msgid="7452722393802683776">"Menor"</string>
<string name="screen_zoom_summary_small" msgid="6050633151263074260">"Pequena"</string>
<string name="screen_zoom_summary_default" msgid="1888865694033865408">"Padrão"</string>
<string name="screen_zoom_summary_large" msgid="4706951482598978984">"Grande"</string>
- <string name="screen_zoom_summary_very_large" msgid="7317423942896999029">"Muito grande"</string>
- <string name="screen_zoom_summary_extremely_large" msgid="1438045624562358554">"Maior"</string>
+ <string name="screen_zoom_summary_larger" msgid="7346406528348409139">"Maior"</string>
+ <string name="screen_zoom_summary_very_large" msgid="6853699118427019538">"Muito grande"</string>
+ <string name="screen_zoom_summary_extra_large" msgid="4898389280769475551">"Extragrande"</string>
+ <string name="screen_zoom_summary_largest" msgid="5120290329622591371">"Máximo"</string>
<string name="screen_zoom_summary_custom" msgid="3468154096832912210">"Personalizada (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
<string name="content_description_menu_button" msgid="6254844309171779931">"Menu"</string>
<string name="retail_demo_reset_message" msgid="5392824901108195463">"Digite a senha para redef. p/ configuração original em modo demo"</string>
diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml
index 7d07d54..b55f3ae 100644
--- a/packages/SettingsLib/res/values-ro/strings.xml
+++ b/packages/SettingsLib/res/values-ro/strings.xml
@@ -551,11 +551,15 @@
</string-array>
<string name="charge_length_format" msgid="6941645744588690932">"Acum <xliff:g id="ID_1">%1$s</xliff:g>"</string>
<string name="remaining_length_format" msgid="4310625772926171089">"Timp rămas: <xliff:g id="ID_1">%1$s</xliff:g>"</string>
+ <string name="screen_zoom_summary_smallest" msgid="1042159093382395789">"Cel mai mic"</string>
+ <string name="screen_zoom_summary_smaller" msgid="7452722393802683776">"Mai mic"</string>
<string name="screen_zoom_summary_small" msgid="6050633151263074260">"Mică"</string>
<string name="screen_zoom_summary_default" msgid="1888865694033865408">"Prestabilită"</string>
<string name="screen_zoom_summary_large" msgid="4706951482598978984">"Mare"</string>
- <string name="screen_zoom_summary_very_large" msgid="7317423942896999029">"Mai mare"</string>
- <string name="screen_zoom_summary_extremely_large" msgid="1438045624562358554">"Cel mai mare"</string>
+ <string name="screen_zoom_summary_larger" msgid="7346406528348409139">"Mai mare"</string>
+ <string name="screen_zoom_summary_very_large" msgid="6853699118427019538">"Foarte mare"</string>
+ <string name="screen_zoom_summary_extra_large" msgid="4898389280769475551">"Extra mare"</string>
+ <string name="screen_zoom_summary_largest" msgid="5120290329622591371">"Cel mai mare"</string>
<string name="screen_zoom_summary_custom" msgid="3468154096832912210">"Personalizat (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
<string name="content_description_menu_button" msgid="6254844309171779931">"Meniu"</string>
<string name="retail_demo_reset_message" msgid="5392824901108195463">"Introdu parola pentru a reveni la setările din fabrică în modul demo"</string>
diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml
index 6c6a0da..da35434 100644
--- a/packages/SettingsLib/res/values-ru/strings.xml
+++ b/packages/SettingsLib/res/values-ru/strings.xml
@@ -133,7 +133,7 @@
<string name="bluetooth_hearing_aid_media_only_left_active" msgid="1632152540901488645">"Используется (только для медиа), только левый"</string>
<string name="bluetooth_hearing_aid_media_only_right_active" msgid="3854140683042617230">"Используется (только для медиа), правый наушник"</string>
<string name="bluetooth_hearing_aid_media_only_left_and_right_active" msgid="1299913413062528417">"Используется (только для медиа), левый и правый наушники"</string>
- <string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"Профиль A2DP"</string>
+ <string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"Аудио мультимедиа"</string>
<string name="bluetooth_profile_headset" msgid="5395952236133499331">"Звонки"</string>
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Профиль OPP"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Устройство ввода"</string>
@@ -551,11 +551,15 @@
</string-array>
<string name="charge_length_format" msgid="6941645744588690932">"<xliff:g id="ID_1">%1$s</xliff:g> назад"</string>
<string name="remaining_length_format" msgid="4310625772926171089">"Осталось <xliff:g id="ID_1">%1$s</xliff:g>"</string>
+ <string name="screen_zoom_summary_smallest" msgid="1042159093382395789">"Самый мелкий"</string>
+ <string name="screen_zoom_summary_smaller" msgid="7452722393802683776">"Более мелкий"</string>
<string name="screen_zoom_summary_small" msgid="6050633151263074260">"Мелкий"</string>
<string name="screen_zoom_summary_default" msgid="1888865694033865408">"По умолчанию"</string>
<string name="screen_zoom_summary_large" msgid="4706951482598978984">"Крупный"</string>
- <string name="screen_zoom_summary_very_large" msgid="7317423942896999029">"Очень крупный"</string>
- <string name="screen_zoom_summary_extremely_large" msgid="1438045624562358554">"Максимальный"</string>
+ <string name="screen_zoom_summary_larger" msgid="7346406528348409139">"Более крупный"</string>
+ <string name="screen_zoom_summary_very_large" msgid="6853699118427019538">"Очень крупный"</string>
+ <string name="screen_zoom_summary_extra_large" msgid="4898389280769475551">"Ещё более крупный"</string>
+ <string name="screen_zoom_summary_largest" msgid="5120290329622591371">"Самый крупный"</string>
<string name="screen_zoom_summary_custom" msgid="3468154096832912210">"Другой (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
<string name="content_description_menu_button" msgid="6254844309171779931">"Меню"</string>
<string name="retail_demo_reset_message" msgid="5392824901108195463">"Чтобы сбросить настройки в деморежиме, введите пароль."</string>
diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml
index 4546f4b..2bbb35a 100644
--- a/packages/SettingsLib/res/values-si/strings.xml
+++ b/packages/SettingsLib/res/values-si/strings.xml
@@ -551,11 +551,15 @@
</string-array>
<string name="charge_length_format" msgid="6941645744588690932">"<xliff:g id="ID_1">%1$s</xliff:g>කට පෙර"</string>
<string name="remaining_length_format" msgid="4310625772926171089">"<xliff:g id="ID_1">%1$s</xliff:g>ක් ඉතිරිය"</string>
+ <string name="screen_zoom_summary_smallest" msgid="1042159093382395789">"කුඩාම"</string>
+ <string name="screen_zoom_summary_smaller" msgid="7452722393802683776">"වඩා කුඩා"</string>
<string name="screen_zoom_summary_small" msgid="6050633151263074260">"කුඩා"</string>
<string name="screen_zoom_summary_default" msgid="1888865694033865408">"පෙරනිමි"</string>
<string name="screen_zoom_summary_large" msgid="4706951482598978984">"විශාල"</string>
- <string name="screen_zoom_summary_very_large" msgid="7317423942896999029">"වඩා විශාල"</string>
- <string name="screen_zoom_summary_extremely_large" msgid="1438045624562358554">"විශාලතම"</string>
+ <string name="screen_zoom_summary_larger" msgid="7346406528348409139">"වඩා විශාල"</string>
+ <string name="screen_zoom_summary_very_large" msgid="6853699118427019538">"ඉතා විශාල"</string>
+ <string name="screen_zoom_summary_extra_large" msgid="4898389280769475551">"අති විශාල"</string>
+ <string name="screen_zoom_summary_largest" msgid="5120290329622591371">"විශාලතම"</string>
<string name="screen_zoom_summary_custom" msgid="3468154096832912210">"අභිරුචි (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
<string name="content_description_menu_button" msgid="6254844309171779931">"මෙනුව"</string>
<string name="retail_demo_reset_message" msgid="5392824901108195463">"ආදර්ශන ප්රකාර කර්මාන්තශාලා යළි සැකසීමට මුරපදය ඇ. ක."</string>
diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml
index 7578d06..831428c 100644
--- a/packages/SettingsLib/res/values-sk/strings.xml
+++ b/packages/SettingsLib/res/values-sk/strings.xml
@@ -551,11 +551,15 @@
</string-array>
<string name="charge_length_format" msgid="6941645744588690932">"pred <xliff:g id="ID_1">%1$s</xliff:g>"</string>
<string name="remaining_length_format" msgid="4310625772926171089">"Zostáva <xliff:g id="ID_1">%1$s</xliff:g>"</string>
+ <string name="screen_zoom_summary_smallest" msgid="1042159093382395789">"Najmenšie"</string>
+ <string name="screen_zoom_summary_smaller" msgid="7452722393802683776">"Menšie"</string>
<string name="screen_zoom_summary_small" msgid="6050633151263074260">"Malé"</string>
<string name="screen_zoom_summary_default" msgid="1888865694033865408">"Predvolené"</string>
<string name="screen_zoom_summary_large" msgid="4706951482598978984">"Veľké"</string>
- <string name="screen_zoom_summary_very_large" msgid="7317423942896999029">"Väčšie"</string>
- <string name="screen_zoom_summary_extremely_large" msgid="1438045624562358554">"Najväčšie"</string>
+ <string name="screen_zoom_summary_larger" msgid="7346406528348409139">"Väčšie"</string>
+ <string name="screen_zoom_summary_very_large" msgid="6853699118427019538">"Veľmi veľké"</string>
+ <string name="screen_zoom_summary_extra_large" msgid="4898389280769475551">"Extra veľké"</string>
+ <string name="screen_zoom_summary_largest" msgid="5120290329622591371">"Najväčšie"</string>
<string name="screen_zoom_summary_custom" msgid="3468154096832912210">"Vlastné (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
<string name="content_description_menu_button" msgid="6254844309171779931">"Ponuka"</string>
<string name="retail_demo_reset_message" msgid="5392824901108195463">"Zadajte heslo na obnovenie továrenských nastavení v režime ukážky"</string>
diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml
index 4b268cb..eb81532 100644
--- a/packages/SettingsLib/res/values-sl/strings.xml
+++ b/packages/SettingsLib/res/values-sl/strings.xml
@@ -551,11 +551,15 @@
</string-array>
<string name="charge_length_format" msgid="6941645744588690932">"Pred <xliff:g id="ID_1">%1$s</xliff:g>"</string>
<string name="remaining_length_format" msgid="4310625772926171089">"Še <xliff:g id="ID_1">%1$s</xliff:g>"</string>
+ <string name="screen_zoom_summary_smallest" msgid="1042159093382395789">"Najmanjša"</string>
+ <string name="screen_zoom_summary_smaller" msgid="7452722393802683776">"Manjša"</string>
<string name="screen_zoom_summary_small" msgid="6050633151263074260">"Majhno"</string>
<string name="screen_zoom_summary_default" msgid="1888865694033865408">"Privzeto"</string>
<string name="screen_zoom_summary_large" msgid="4706951482598978984">"Veliko"</string>
- <string name="screen_zoom_summary_very_large" msgid="7317423942896999029">"Večje"</string>
- <string name="screen_zoom_summary_extremely_large" msgid="1438045624562358554">"Največje"</string>
+ <string name="screen_zoom_summary_larger" msgid="7346406528348409139">"Večja"</string>
+ <string name="screen_zoom_summary_very_large" msgid="6853699118427019538">"Zelo velika"</string>
+ <string name="screen_zoom_summary_extra_large" msgid="4898389280769475551">"Izredno velika"</string>
+ <string name="screen_zoom_summary_largest" msgid="5120290329622591371">"Največja"</string>
<string name="screen_zoom_summary_custom" msgid="3468154096832912210">"Po meri (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
<string name="content_description_menu_button" msgid="6254844309171779931">"Meni"</string>
<string name="retail_demo_reset_message" msgid="5392824901108195463">"Geslo za tovar. nast. v predstav. načinu"</string>
diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml
index a296f87..6919799 100644
--- a/packages/SettingsLib/res/values-sq/strings.xml
+++ b/packages/SettingsLib/res/values-sq/strings.xml
@@ -196,7 +196,7 @@
<string name="data_usage_uninstalled_apps" msgid="1933665711856171491">"Aplikacionet e hequra"</string>
<string name="data_usage_uninstalled_apps_users" msgid="5533981546921913295">"Aplikacionet dhe përdoruesit e hequr"</string>
<string name="data_usage_ota" msgid="7984667793701597001">"Përditësimet e sistemit"</string>
- <string name="tether_settings_title_usb" msgid="3728686573430917722">"Ndarje përmes USB-së"</string>
+ <string name="tether_settings_title_usb" msgid="3728686573430917722">"Ndarje interneti përmes USB-së"</string>
<string name="tether_settings_title_wifi" msgid="4803402057533895526">"Zona e qasjes e lëvizshme"</string>
<string name="tether_settings_title_bluetooth" msgid="916519902721399656">"Ndarje interneti përmes Bluetooth-it"</string>
<string name="tether_settings_title_usb_bluetooth" msgid="1727111807207577322">"Ndarja e internetit"</string>
@@ -551,11 +551,15 @@
</string-array>
<string name="charge_length_format" msgid="6941645744588690932">"<xliff:g id="ID_1">%1$s</xliff:g> më parë"</string>
<string name="remaining_length_format" msgid="4310625772926171089">"<xliff:g id="ID_1">%1$s</xliff:g> të mbetura"</string>
+ <string name="screen_zoom_summary_smallest" msgid="1042159093382395789">"Më të voglat"</string>
+ <string name="screen_zoom_summary_smaller" msgid="7452722393802683776">"Më të vogla"</string>
<string name="screen_zoom_summary_small" msgid="6050633151263074260">"I vogël"</string>
<string name="screen_zoom_summary_default" msgid="1888865694033865408">"E parazgjedhur"</string>
<string name="screen_zoom_summary_large" msgid="4706951482598978984">"I madh"</string>
- <string name="screen_zoom_summary_very_large" msgid="7317423942896999029">"Më i madh"</string>
- <string name="screen_zoom_summary_extremely_large" msgid="1438045624562358554">"Më i madhi"</string>
+ <string name="screen_zoom_summary_larger" msgid="7346406528348409139">"Më të mëdha"</string>
+ <string name="screen_zoom_summary_very_large" msgid="6853699118427019538">"Shumë të mëdha"</string>
+ <string name="screen_zoom_summary_extra_large" msgid="4898389280769475551">"Shumë të mëdha"</string>
+ <string name="screen_zoom_summary_largest" msgid="5120290329622591371">"Më të mëdhatë"</string>
<string name="screen_zoom_summary_custom" msgid="3468154096832912210">"I personalizuar (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
<string name="content_description_menu_button" msgid="6254844309171779931">"Menyja"</string>
<string name="retail_demo_reset_message" msgid="5392824901108195463">"Fut fjalëkalimin për të kryer rivendosje në gjendje fabrike në modalitetin e demonstrimit"</string>
diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml
index defea3a..aa03275 100644
--- a/packages/SettingsLib/res/values-sr/strings.xml
+++ b/packages/SettingsLib/res/values-sr/strings.xml
@@ -551,11 +551,15 @@
</string-array>
<string name="charge_length_format" msgid="6941645744588690932">"Пре <xliff:g id="ID_1">%1$s</xliff:g>"</string>
<string name="remaining_length_format" msgid="4310625772926171089">"Још <xliff:g id="ID_1">%1$s</xliff:g>"</string>
+ <string name="screen_zoom_summary_smallest" msgid="1042159093382395789">"Најмање"</string>
+ <string name="screen_zoom_summary_smaller" msgid="7452722393802683776">"Мање"</string>
<string name="screen_zoom_summary_small" msgid="6050633151263074260">"Мали"</string>
<string name="screen_zoom_summary_default" msgid="1888865694033865408">"Подразумевано"</string>
<string name="screen_zoom_summary_large" msgid="4706951482598978984">"Велики"</string>
- <string name="screen_zoom_summary_very_large" msgid="7317423942896999029">"Већи"</string>
- <string name="screen_zoom_summary_extremely_large" msgid="1438045624562358554">"Највећи"</string>
+ <string name="screen_zoom_summary_larger" msgid="7346406528348409139">"Веће"</string>
+ <string name="screen_zoom_summary_very_large" msgid="6853699118427019538">"Веома велико"</string>
+ <string name="screen_zoom_summary_extra_large" msgid="4898389280769475551">"Веома велико"</string>
+ <string name="screen_zoom_summary_largest" msgid="5120290329622591371">"Највеће"</string>
<string name="screen_zoom_summary_custom" msgid="3468154096832912210">"Прилагођени (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
<string name="content_description_menu_button" msgid="6254844309171779931">"Мени"</string>
<string name="retail_demo_reset_message" msgid="5392824901108195463">"Унесите лозинку да бисте обавили ресетовање на фабричка подешавања у режиму демонстрације"</string>
diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml
index d03b99b..fd094d3 100644
--- a/packages/SettingsLib/res/values-sv/strings.xml
+++ b/packages/SettingsLib/res/values-sv/strings.xml
@@ -551,11 +551,15 @@
</string-array>
<string name="charge_length_format" msgid="6941645744588690932">"för <xliff:g id="ID_1">%1$s</xliff:g> sedan"</string>
<string name="remaining_length_format" msgid="4310625772926171089">"<xliff:g id="ID_1">%1$s</xliff:g> kvar"</string>
+ <string name="screen_zoom_summary_smallest" msgid="1042159093382395789">"Minst"</string>
+ <string name="screen_zoom_summary_smaller" msgid="7452722393802683776">"Mindre"</string>
<string name="screen_zoom_summary_small" msgid="6050633151263074260">"Små"</string>
<string name="screen_zoom_summary_default" msgid="1888865694033865408">"Standardinställning"</string>
<string name="screen_zoom_summary_large" msgid="4706951482598978984">"Stor"</string>
- <string name="screen_zoom_summary_very_large" msgid="7317423942896999029">"Större"</string>
- <string name="screen_zoom_summary_extremely_large" msgid="1438045624562358554">"Störst"</string>
+ <string name="screen_zoom_summary_larger" msgid="7346406528348409139">"Större"</string>
+ <string name="screen_zoom_summary_very_large" msgid="6853699118427019538">"Mycket stor"</string>
+ <string name="screen_zoom_summary_extra_large" msgid="4898389280769475551">"Extra stor"</string>
+ <string name="screen_zoom_summary_largest" msgid="5120290329622591371">"Störst"</string>
<string name="screen_zoom_summary_custom" msgid="3468154096832912210">"Anpassad (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
<string name="content_description_menu_button" msgid="6254844309171779931">"Meny"</string>
<string name="retail_demo_reset_message" msgid="5392824901108195463">"Ange lösenord och utför fabriksåterställning i demoläge"</string>
diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml
index 0755ce8..c8b46ef 100644
--- a/packages/SettingsLib/res/values-sw/strings.xml
+++ b/packages/SettingsLib/res/values-sw/strings.xml
@@ -95,14 +95,14 @@
<string name="bluetooth_connected_no_a2dp_battery_level" msgid="6499078454894324287">"Imeunganishwa (hamna kifaa cha sauti), kiasi cha chaji ni <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_no_a2dp_battery_level" msgid="8477440576953067242">"Imeunganishwa (hamna simu au kifaa cha sauti), kiasi cha chaji ni <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
<string name="bluetooth_active_battery_level" msgid="2685517576209066008">"Inatumika. Chaji imefika <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>."</string>
- <string name="bluetooth_active_battery_level_untethered" msgid="4961338936672922617">"Inatumika. Kushoto: chaji ya betri imefika <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, Kulia: chaji ya betri imefika <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>."</string>
+ <string name="bluetooth_active_battery_level_untethered" msgid="4961338936672922617">"Inatumika. Kushoto: chaji <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, Kulia: chaji <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>."</string>
<string name="bluetooth_active_battery_level_untethered_left" msgid="2895644748625343977">"Inatumika. Kushoto: chaji <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>."</string>
- <string name="bluetooth_active_battery_level_untethered_right" msgid="7407517998880370179">"Inatumika. Kulia: chaji ya betri imefika <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>."</string>
+ <string name="bluetooth_active_battery_level_untethered_right" msgid="7407517998880370179">"Inatumika. Kulia: chaji <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>."</string>
<string name="bluetooth_battery_level" msgid="2893696778200201555">"Chaji ya betri ni <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="tv_bluetooth_battery_level" msgid="8786353985605532846">"Chaji ya betri ni <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_battery_level_untethered" msgid="1616774716076301755">"Kushoto: chaji imefika <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, Kulia: chaji imefika <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>."</string>
- <string name="bluetooth_battery_level_untethered_left" msgid="5725764679536058365">"Kushoto: chaji ya betri imefika <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
- <string name="bluetooth_battery_level_untethered_right" msgid="8377995536997790142">"Kulia: chaji ya betri imefika <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_left" msgid="5725764679536058365">"Kushoto: chaji <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right" msgid="8377995536997790142">"Kulia: chaji <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="tv_bluetooth_battery_level_untethered_left" msgid="337629670583744410">"Kushoto <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="tv_bluetooth_battery_level_untethered_right" msgid="8610019317279155595">"Kulia <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"Kimeunganishwa"</string>
@@ -111,15 +111,15 @@
<string name="bluetooth_hearing_aid_right_active" msgid="2244728507170385397">"Inatumika (kulia pekee)"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="4294571497939983181">"Inatumika (kushoto na kulia)"</string>
<string name="bluetooth_hearing_device_ambient_error" msgid="6035857289108813878">"Imeshindwa kusasisha mazingira"</string>
- <string name="bluetooth_active_media_only_battery_level" msgid="7772517511061834073">"Inatumika (maudhui pekee). Chaji ya betri imefika <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>."</string>
- <string name="bluetooth_active_media_only_battery_level_untethered" msgid="7444753133664620926">"Inatumika (maudhui pekee), Kushoto: chaji ya betri imefika <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, Kulia: chaji ya betri imefika <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>."</string>
+ <string name="bluetooth_active_media_only_battery_level" msgid="7772517511061834073">"Inatumika (maudhui pekee). Chaji <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>."</string>
+ <string name="bluetooth_active_media_only_battery_level_untethered" msgid="7444753133664620926">"Inatumika (maudhui pekee), Kushoto: chaji <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, Kulia: chaji <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>."</string>
<string name="bluetooth_guest_battery_level" msgid="2820003593899467676">"Kifaa kingeni. Chaji ya betri imefika <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>."</string>
<string name="bluetooth_guest_battery_level_untethered" msgid="5404013822067644960">"Kifaa kingeni. Kushoto: chaji ya betri imefika <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, Kulia: chaji ya betri imefika <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>."</string>
<string name="bluetooth_guest_media_only_battery_level" msgid="7928347900623812299">"Kifaa kingeni (sauti/video/picha pekee). Chaji ya betri imefika <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>."</string>
<string name="bluetooth_guest_media_only_battery_level_untethered" msgid="4458143141394300892">"Kifaa kingeni (sauti/video/picha pekee). Kushoto: chaji ya betri imefika <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, Kulia: chaji ya betri imefika <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>."</string>
<string name="bluetooth_battery_level_lea_support" msgid="5968584103507988820">"Imeunganishwa (inaweza kutumia kipengele cha kusikiliza pamoja). Chaji imefika <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>."</string>
<string name="bluetooth_battery_level_untethered_lea_support" msgid="803110681688633362">"Imeunganishwa (inaweza kutumia kipengele cha kusikiliza pamoja). Kushoto: chaji ya betri imefika <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, Kulia: chaji ya betri imefika <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>."</string>
- <string name="bluetooth_battery_level_untethered_left_lea_support" msgid="7707464334346454950">"Imeunganishwa (inaweza kutumia kipengele cha kusikiliza pamoja). Kushoto: chaji ya betri imefika <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>."</string>
+ <string name="bluetooth_battery_level_untethered_left_lea_support" msgid="7707464334346454950">"Imeunganishwa (inaweza kutumia kipengele cha kusikiliza pamoja). Kushoto: chaji <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>."</string>
<string name="bluetooth_battery_level_untethered_right_lea_support" msgid="8941549024377771038">"Imeunganishwa (inaweza kutumia kipengele cha kusikiliza pamoja). Kulia: chaji ya betri imefika <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>."</string>
<string name="bluetooth_no_battery_level_lea_support" msgid="5721725041048434075">"Imeunganishwa (inaweza kusikiliza pamoja)"</string>
<string name="bluetooth_guest_battery_level_lea_support" msgid="8098327939585013928">"Kifaa kingeni (kinatumia kipengele cha kusikiliza pamoja). Chaji ya betri imefika <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>."</string>
@@ -139,7 +139,7 @@
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Kifaa cha kuingiza"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Ufikiaji wa intaneti"</string>
<string name="bluetooth_profile_pbap" msgid="2103406516858653017">"Ruhusu ufikiaji wa anwani na rekodi ya simu zilizopigwa"</string>
- <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"Maelezo yatatumiwa kwa matangazo ya simu na mengine"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"Maelezo yatatumiwa kutaarifu kuhusu simu na zaidi"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Kushiriki muunganisho wa tovuti"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"SMS"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Ufikiaji wa SIM"</string>
@@ -248,7 +248,7 @@
<string name="choose_profile" msgid="343803890897657450">"Chagua wasifu"</string>
<string name="category_personal" msgid="6236798763159385225">"Binafsi"</string>
<string name="category_work" msgid="4014193632325996115">"Kazini"</string>
- <string name="category_private" msgid="4244892185452788977">"Sehemu ya Faragha"</string>
+ <string name="category_private" msgid="4244892185452788977">"Faragha"</string>
<string name="category_clone" msgid="1554511758987195974">"Kloni"</string>
<string name="development_settings_title" msgid="140296922921597393">"Chaguo za wasanidi"</string>
<string name="development_settings_enable" msgid="4285094651288242183">"Washa chaguo za wasanidi programu"</string>
@@ -551,11 +551,15 @@
</string-array>
<string name="charge_length_format" msgid="6941645744588690932">"<xliff:g id="ID_1">%1$s</xliff:g> zilizopita"</string>
<string name="remaining_length_format" msgid="4310625772926171089">"Zimesalia <xliff:g id="ID_1">%1$s</xliff:g>"</string>
+ <string name="screen_zoom_summary_smallest" msgid="1042159093382395789">"Ndogo zaidi"</string>
+ <string name="screen_zoom_summary_smaller" msgid="7452722393802683776">"Ndogo kiasi"</string>
<string name="screen_zoom_summary_small" msgid="6050633151263074260">"Ndogo"</string>
<string name="screen_zoom_summary_default" msgid="1888865694033865408">"Chaguomsingi"</string>
<string name="screen_zoom_summary_large" msgid="4706951482598978984">"Kubwa"</string>
- <string name="screen_zoom_summary_very_large" msgid="7317423942896999029">"Kubwa kiasi"</string>
- <string name="screen_zoom_summary_extremely_large" msgid="1438045624562358554">"Kubwa zaidi"</string>
+ <string name="screen_zoom_summary_larger" msgid="7346406528348409139">"Kubwa kiasi"</string>
+ <string name="screen_zoom_summary_very_large" msgid="6853699118427019538">"Kubwa Sana"</string>
+ <string name="screen_zoom_summary_extra_large" msgid="4898389280769475551">"Kubwa Zaidi"</string>
+ <string name="screen_zoom_summary_largest" msgid="5120290329622591371">"Kubwa zaidi"</string>
<string name="screen_zoom_summary_custom" msgid="3468154096832912210">"Kiwango maalum (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
<string name="content_description_menu_button" msgid="6254844309171779931">"Menyu"</string>
<string name="retail_demo_reset_message" msgid="5392824901108195463">"Weka nenosiri ili urejeshe mipangilio ya kiwandani ikiwa katika hali ya onyesho."</string>
@@ -574,7 +578,7 @@
<string name="wifi_tether_connected_summary" msgid="5100712926640492336">"{count,plural, =1{Kifaa 1 kimeunganishwa}other{Vifaa # vimeunganishwa}}"</string>
<string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"Muda zaidi."</string>
<string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"Muda kidogo."</string>
- <string name="cancel" msgid="5665114069455378395">"Ghairi"</string>
+ <string name="cancel" msgid="5665114069455378395">"Acha"</string>
<string name="next" msgid="2699398661093607009">"Endelea"</string>
<string name="back" msgid="5554327870352703710">"Rudi nyuma"</string>
<string name="save" msgid="3745809743277153149">"Hifadhi"</string>
diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml
index a67f35e..ec7951c 100644
--- a/packages/SettingsLib/res/values-ta/strings.xml
+++ b/packages/SettingsLib/res/values-ta/strings.xml
@@ -542,7 +542,7 @@
<string name="enabled" msgid="3997122818554810678">"இயக்கப்பட்டது"</string>
<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="install_other_apps" msgid="3232595082023199454">"தெரியாத ஆப்ஸை நிறுவுதல்"</string>
<string name="home" msgid="973834627243661438">"அமைப்புகள் முகப்பு"</string>
<string-array name="battery_labels">
<item msgid="7878690469765357158">"0%"</item>
@@ -551,11 +551,15 @@
</string-array>
<string name="charge_length_format" msgid="6941645744588690932">"<xliff:g id="ID_1">%1$s</xliff:g> முன்"</string>
<string name="remaining_length_format" msgid="4310625772926171089">"<xliff:g id="ID_1">%1$s</xliff:g> உள்ளது"</string>
+ <string name="screen_zoom_summary_smallest" msgid="1042159093382395789">"மிகச் சிறியது"</string>
+ <string name="screen_zoom_summary_smaller" msgid="7452722393802683776">"சற்று சிறியது"</string>
<string name="screen_zoom_summary_small" msgid="6050633151263074260">"சிறியது"</string>
<string name="screen_zoom_summary_default" msgid="1888865694033865408">"இயல்பு"</string>
<string name="screen_zoom_summary_large" msgid="4706951482598978984">"பெரியது"</string>
- <string name="screen_zoom_summary_very_large" msgid="7317423942896999029">"கொஞ்சம் பெரியது"</string>
- <string name="screen_zoom_summary_extremely_large" msgid="1438045624562358554">"மிகப் பெரியது"</string>
+ <string name="screen_zoom_summary_larger" msgid="7346406528348409139">"சற்று பெரியது"</string>
+ <string name="screen_zoom_summary_very_large" msgid="6853699118427019538">"இன்னும் பெரியது"</string>
+ <string name="screen_zoom_summary_extra_large" msgid="4898389280769475551">"மிகப் பெரியது"</string>
+ <string name="screen_zoom_summary_largest" msgid="5120290329622591371">"மிக மிகப் பெரியது"</string>
<string name="screen_zoom_summary_custom" msgid="3468154096832912210">"பிரத்தியேக (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
<string name="content_description_menu_button" msgid="6254844309171779931">"மெனு"</string>
<string name="retail_demo_reset_message" msgid="5392824901108195463">"டெமோ பயன்முறையில் ஆரம்பநிலை மீட்டமைவைச் செயல்படுத்த, கடவுச்சொல்லை உள்ளிடவும்"</string>
diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml
index ffca18d..ca50cd1 100644
--- a/packages/SettingsLib/res/values-te/strings.xml
+++ b/packages/SettingsLib/res/values-te/strings.xml
@@ -271,7 +271,7 @@
<string name="adb_pair_method_code_summary" msgid="6370414511333685185">"ఆరు అంకెల కోడ్ను ఉపయోగించి కొత్త పరికరాలను పెయిర్ చేయండి"</string>
<string name="adb_paired_devices_title" msgid="5268997341526217362">"పెయిర్ చేయబడిన పరికరాలు"</string>
<string name="adb_wireless_device_connected_summary" msgid="3039660790249148713">"ప్రస్తుతం కనెక్ట్ చేయబడింది"</string>
- <string name="adb_wireless_device_details_title" msgid="7129369670526565786">"పరికర వివరాలు"</string>
+ <string name="adb_wireless_device_details_title" msgid="7129369670526565786">"డివైజ్ వివరాలు"</string>
<string name="adb_device_forget" msgid="193072400783068417">"విస్మరించు"</string>
<string name="adb_device_fingerprint_title_format" msgid="291504822917843701">"పరికరం వేలిముద్ర: <xliff:g id="FINGERPRINT_PARAM">%1$s</xliff:g>"</string>
<string name="adb_wireless_connection_failed_title" msgid="664211177427438438">"కనెక్షన్ విఫలమైంది"</string>
@@ -551,11 +551,15 @@
</string-array>
<string name="charge_length_format" msgid="6941645744588690932">"<xliff:g id="ID_1">%1$s</xliff:g> క్రితం"</string>
<string name="remaining_length_format" msgid="4310625772926171089">"<xliff:g id="ID_1">%1$s</xliff:g> మిగిలి ఉంది"</string>
+ <string name="screen_zoom_summary_smallest" msgid="1042159093382395789">"అత్యంత చిన్నది"</string>
+ <string name="screen_zoom_summary_smaller" msgid="7452722393802683776">"చాలా చిన్నది"</string>
<string name="screen_zoom_summary_small" msgid="6050633151263074260">"చిన్నగా"</string>
<string name="screen_zoom_summary_default" msgid="1888865694033865408">"ఆటోమేటిక్"</string>
<string name="screen_zoom_summary_large" msgid="4706951482598978984">"పెద్దగా"</string>
- <string name="screen_zoom_summary_very_large" msgid="7317423942896999029">"చాలా పెద్దగా"</string>
- <string name="screen_zoom_summary_extremely_large" msgid="1438045624562358554">"అతి పెద్దగా"</string>
+ <string name="screen_zoom_summary_larger" msgid="7346406528348409139">"చాలా పెద్దది"</string>
+ <string name="screen_zoom_summary_very_large" msgid="6853699118427019538">"చాలా పెద్దది"</string>
+ <string name="screen_zoom_summary_extra_large" msgid="4898389280769475551">"అతి పెద్దది"</string>
+ <string name="screen_zoom_summary_largest" msgid="5120290329622591371">"అత్యంత పెద్దది"</string>
<string name="screen_zoom_summary_custom" msgid="3468154096832912210">"అనుకూలం (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
<string name="content_description_menu_button" msgid="6254844309171779931">"మెనూ"</string>
<string name="retail_demo_reset_message" msgid="5392824901108195463">"డెమో మోడ్లో ఫ్యాక్టరీ రీసెట్ను మేనేజ్ చేయడానికి పాస్వర్డ్ను నమోదు చేయండి"</string>
diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml
index 7b4c162..fd85f87 100644
--- a/packages/SettingsLib/res/values-th/strings.xml
+++ b/packages/SettingsLib/res/values-th/strings.xml
@@ -551,11 +551,15 @@
</string-array>
<string name="charge_length_format" msgid="6941645744588690932">"<xliff:g id="ID_1">%1$s</xliff:g>ที่ผ่านมา"</string>
<string name="remaining_length_format" msgid="4310625772926171089">"เหลือ <xliff:g id="ID_1">%1$s</xliff:g>"</string>
+ <string name="screen_zoom_summary_smallest" msgid="1042159093382395789">"เล็กที่สุด"</string>
+ <string name="screen_zoom_summary_smaller" msgid="7452722393802683776">"เล็กลง"</string>
<string name="screen_zoom_summary_small" msgid="6050633151263074260">"เล็ก"</string>
<string name="screen_zoom_summary_default" msgid="1888865694033865408">"ค่าเริ่มต้น"</string>
<string name="screen_zoom_summary_large" msgid="4706951482598978984">"ใหญ่"</string>
- <string name="screen_zoom_summary_very_large" msgid="7317423942896999029">"ใหญ่ขึ้น"</string>
- <string name="screen_zoom_summary_extremely_large" msgid="1438045624562358554">"ใหญ่ที่สุด"</string>
+ <string name="screen_zoom_summary_larger" msgid="7346406528348409139">"ใหญ่ขึ้น"</string>
+ <string name="screen_zoom_summary_very_large" msgid="6853699118427019538">"ใหญ่มาก"</string>
+ <string name="screen_zoom_summary_extra_large" msgid="4898389280769475551">"ใหญ่พิเศษ"</string>
+ <string name="screen_zoom_summary_largest" msgid="5120290329622591371">"ใหญ่ที่สุด"</string>
<string name="screen_zoom_summary_custom" msgid="3468154096832912210">"กำหนดเอง (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
<string name="content_description_menu_button" msgid="6254844309171779931">"เมนู"</string>
<string name="retail_demo_reset_message" msgid="5392824901108195463">"ป้อนรหัสผ่านเพื่อรีเซ็ตค่าในโหมดสาธิต"</string>
diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml
index 011c8d7..88a6557 100644
--- a/packages/SettingsLib/res/values-tl/strings.xml
+++ b/packages/SettingsLib/res/values-tl/strings.xml
@@ -230,7 +230,7 @@
<string name="tts_status_checking" msgid="8026559918948285013">"Sinusuri…"</string>
<string name="tts_engine_settings_title" msgid="7849477533103566291">"Mga setting para sa <xliff:g id="TTS_ENGINE_NAME">%s</xliff:g>"</string>
<string name="tts_engine_settings_button" msgid="477155276199968948">"Ilunsad ang mga setting ng engine"</string>
- <string name="tts_engine_preference_section_title" msgid="3861562305498624904">"Ginustong engine"</string>
+ <string name="tts_engine_preference_section_title" msgid="3861562305498624904">"Prefer na engine"</string>
<string name="tts_general_section_title" msgid="8919671529502364567">"Pangkalahatan"</string>
<string name="tts_reset_speech_pitch_title" msgid="7149398585468413246">"I-reset ang pitch ng pagsasalita"</string>
<string name="tts_reset_speech_pitch_summary" msgid="6822904157021406449">"I-reset ang pitch sa default na pagbigkas ng text."</string>
@@ -551,11 +551,15 @@
</string-array>
<string name="charge_length_format" msgid="6941645744588690932">"<xliff:g id="ID_1">%1$s</xliff:g> na ang nakalipas"</string>
<string name="remaining_length_format" msgid="4310625772926171089">"<xliff:g id="ID_1">%1$s</xliff:g> na lang"</string>
+ <string name="screen_zoom_summary_smallest" msgid="1042159093382395789">"Pinakamaliit"</string>
+ <string name="screen_zoom_summary_smaller" msgid="7452722393802683776">"Mas maliit"</string>
<string name="screen_zoom_summary_small" msgid="6050633151263074260">"Maliit"</string>
<string name="screen_zoom_summary_default" msgid="1888865694033865408">"Default"</string>
<string name="screen_zoom_summary_large" msgid="4706951482598978984">"Malaki"</string>
- <string name="screen_zoom_summary_very_large" msgid="7317423942896999029">"Mas malaki"</string>
- <string name="screen_zoom_summary_extremely_large" msgid="1438045624562358554">"Pinakamalaki"</string>
+ <string name="screen_zoom_summary_larger" msgid="7346406528348409139">"Mas malaki"</string>
+ <string name="screen_zoom_summary_very_large" msgid="6853699118427019538">"Napakalaki"</string>
+ <string name="screen_zoom_summary_extra_large" msgid="4898389280769475551">"Sobrang Laki"</string>
+ <string name="screen_zoom_summary_largest" msgid="5120290329622591371">"Pinakamalaki"</string>
<string name="screen_zoom_summary_custom" msgid="3468154096832912210">"Custom (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
<string name="content_description_menu_button" msgid="6254844309171779931">"Menu"</string>
<string name="retail_demo_reset_message" msgid="5392824901108195463">"Ilagay ang password upang mag-factory reset sa demo mode"</string>
diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml
index 4285230..277122a 100644
--- a/packages/SettingsLib/res/values-tr/strings.xml
+++ b/packages/SettingsLib/res/values-tr/strings.xml
@@ -551,11 +551,15 @@
</string-array>
<string name="charge_length_format" msgid="6941645744588690932">"<xliff:g id="ID_1">%1$s</xliff:g> önce"</string>
<string name="remaining_length_format" msgid="4310625772926171089">"<xliff:g id="ID_1">%1$s</xliff:g> kaldı"</string>
+ <string name="screen_zoom_summary_smallest" msgid="1042159093382395789">"En küçük"</string>
+ <string name="screen_zoom_summary_smaller" msgid="7452722393802683776">"Daha küçük"</string>
<string name="screen_zoom_summary_small" msgid="6050633151263074260">"Küçük"</string>
<string name="screen_zoom_summary_default" msgid="1888865694033865408">"Varsayılan"</string>
<string name="screen_zoom_summary_large" msgid="4706951482598978984">"Büyük"</string>
- <string name="screen_zoom_summary_very_large" msgid="7317423942896999029">"Daha büyük"</string>
- <string name="screen_zoom_summary_extremely_large" msgid="1438045624562358554">"En büyük"</string>
+ <string name="screen_zoom_summary_larger" msgid="7346406528348409139">"Daha büyük"</string>
+ <string name="screen_zoom_summary_very_large" msgid="6853699118427019538">"Çok Büyük"</string>
+ <string name="screen_zoom_summary_extra_large" msgid="4898389280769475551">"Ekstra Büyük"</string>
+ <string name="screen_zoom_summary_largest" msgid="5120290329622591371">"En büyük"</string>
<string name="screen_zoom_summary_custom" msgid="3468154096832912210">"Özel (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
<string name="content_description_menu_button" msgid="6254844309171779931">"Menü"</string>
<string name="retail_demo_reset_message" msgid="5392824901108195463">"Demo modunda sıfırlamak için şifreyi girin"</string>
diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml
index d3e1b41..1bff3cd 100644
--- a/packages/SettingsLib/res/values-uk/strings.xml
+++ b/packages/SettingsLib/res/values-uk/strings.xml
@@ -81,7 +81,7 @@
<string name="speed_label_fast" msgid="2677719134596044051">"Швидка"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Дуже швидка"</string>
<string name="wifi_passpoint_expired" msgid="6540867261754427561">"Термін дії минув"</string>
- <string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g>: <xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
+ <string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
<string name="bluetooth_disconnected" msgid="7739366554710388701">"Роз’єднано"</string>
<string name="bluetooth_disconnecting" msgid="7638892134401574338">"Відключення..."</string>
<string name="bluetooth_connecting" msgid="5871702668260192755">"Підключення…"</string>
@@ -331,8 +331,8 @@
<string name="select_private_dns_configuration_dialog_title" msgid="3731422918335951912">"Режим приватного DNS-сервера"</string>
<string name="private_dns_mode_off" msgid="7065962499349997041">"Вимкнено"</string>
<string name="private_dns_mode_opportunistic" msgid="1947864819060442354">"Автоматично"</string>
- <string name="private_dns_mode_provider" msgid="3619040641762557028">"Ім’я хосту постачальника приватного DNS-сервера"</string>
- <string name="private_dns_mode_provider_hostname_hint" msgid="6564868953748514595">"Введіть ім’я хосту постачальника послуг DNS"</string>
+ <string name="private_dns_mode_provider" msgid="3619040641762557028">"Ім’я хоста постачальника приватного DNS-сервера"</string>
+ <string name="private_dns_mode_provider_hostname_hint" msgid="6564868953748514595">"Введіть ім’я хоста постачальника послуг DNS"</string>
<string name="private_dns_mode_provider_failure" msgid="8356259467861515108">"Не вдалося під’єднатися"</string>
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"Показати параметри сертифікації бездротового екрана"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Показувати в журналі RSSI для кожного SSID під час вибору Wi-Fi"</string>
@@ -551,11 +551,15 @@
</string-array>
<string name="charge_length_format" msgid="6941645744588690932">"<xliff:g id="ID_1">%1$s</xliff:g> тому"</string>
<string name="remaining_length_format" msgid="4310625772926171089">"Залишилося <xliff:g id="ID_1">%1$s</xliff:g>"</string>
+ <string name="screen_zoom_summary_smallest" msgid="1042159093382395789">"Найменший"</string>
+ <string name="screen_zoom_summary_smaller" msgid="7452722393802683776">"Менший"</string>
<string name="screen_zoom_summary_small" msgid="6050633151263074260">"Малі елементи"</string>
<string name="screen_zoom_summary_default" msgid="1888865694033865408">"За умовчанням"</string>
<string name="screen_zoom_summary_large" msgid="4706951482598978984">"Великі елементи"</string>
- <string name="screen_zoom_summary_very_large" msgid="7317423942896999029">"Більші елементи"</string>
- <string name="screen_zoom_summary_extremely_large" msgid="1438045624562358554">"Найбільші елементи"</string>
+ <string name="screen_zoom_summary_larger" msgid="7346406528348409139">"Більший"</string>
+ <string name="screen_zoom_summary_very_large" msgid="6853699118427019538">"Дуже великий"</string>
+ <string name="screen_zoom_summary_extra_large" msgid="4898389280769475551">"Надвеликий"</string>
+ <string name="screen_zoom_summary_largest" msgid="5120290329622591371">"Найбільший"</string>
<string name="screen_zoom_summary_custom" msgid="3468154096832912210">"Спеціальний масштаб (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
<string name="content_description_menu_button" msgid="6254844309171779931">"Меню"</string>
<string name="retail_demo_reset_message" msgid="5392824901108195463">"Введіть пароль, щоб скинути налаштування в демо-режимі"</string>
diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml
index 48aa063..ef4c981 100644
--- a/packages/SettingsLib/res/values-ur/strings.xml
+++ b/packages/SettingsLib/res/values-ur/strings.xml
@@ -209,7 +209,7 @@
<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>
+ <string name="tts_default_rate_summary" msgid="3781937042151716987">"ٹیکسٹ بولے جانے کی رفتار"</string>
<string name="tts_default_pitch_title" msgid="6988592215554485479">"پچ"</string>
<string name="tts_default_pitch_summary" msgid="9132719475281551884">"تصنعی اسپیچ کی ٹون کو متاثر کرتا ہے"</string>
<string name="tts_default_lang_title" msgid="4698933575028098940">"زبان"</string>
@@ -551,11 +551,15 @@
</string-array>
<string name="charge_length_format" msgid="6941645744588690932">"<xliff:g id="ID_1">%1$s</xliff:g> قبل"</string>
<string name="remaining_length_format" msgid="4310625772926171089">"<xliff:g id="ID_1">%1$s</xliff:g> باقی ہیں"</string>
+ <string name="screen_zoom_summary_smallest" msgid="1042159093382395789">"سب سے چھوٹا"</string>
+ <string name="screen_zoom_summary_smaller" msgid="7452722393802683776">"قدرے چھوٹا"</string>
<string name="screen_zoom_summary_small" msgid="6050633151263074260">"چھوٹا"</string>
<string name="screen_zoom_summary_default" msgid="1888865694033865408">"ڈیفالٹ"</string>
<string name="screen_zoom_summary_large" msgid="4706951482598978984">"بڑا"</string>
- <string name="screen_zoom_summary_very_large" msgid="7317423942896999029">"قدرے بڑا"</string>
- <string name="screen_zoom_summary_extremely_large" msgid="1438045624562358554">"سب سے بڑا"</string>
+ <string name="screen_zoom_summary_larger" msgid="7346406528348409139">"قدرے بڑا"</string>
+ <string name="screen_zoom_summary_very_large" msgid="6853699118427019538">"کافی بڑا"</string>
+ <string name="screen_zoom_summary_extra_large" msgid="4898389280769475551">"مزید بڑا"</string>
+ <string name="screen_zoom_summary_largest" msgid="5120290329622591371">"سب سے بڑا"</string>
<string name="screen_zoom_summary_custom" msgid="3468154096832912210">"حسب ضرورت (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
<string name="content_description_menu_button" msgid="6254844309171779931">"مینیو"</string>
<string name="retail_demo_reset_message" msgid="5392824901108195463">"ڈیمو موڈ میں فیکٹری ری سیٹ کیلئے پاس ورڈ درج کریں"</string>
diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml
index 8969c0d..827d1ca 100644
--- a/packages/SettingsLib/res/values-uz/strings.xml
+++ b/packages/SettingsLib/res/values-uz/strings.xml
@@ -551,11 +551,15 @@
</string-array>
<string name="charge_length_format" msgid="6941645744588690932">"<xliff:g id="ID_1">%1$s</xliff:g> oldin"</string>
<string name="remaining_length_format" msgid="4310625772926171089">"<xliff:g id="ID_1">%1$s</xliff:g> qoldi"</string>
+ <string name="screen_zoom_summary_smallest" msgid="1042159093382395789">"Eng kichik"</string>
+ <string name="screen_zoom_summary_smaller" msgid="7452722393802683776">"Kichikroq"</string>
<string name="screen_zoom_summary_small" msgid="6050633151263074260">"Kichkina"</string>
<string name="screen_zoom_summary_default" msgid="1888865694033865408">"Standart"</string>
<string name="screen_zoom_summary_large" msgid="4706951482598978984">"Katta"</string>
- <string name="screen_zoom_summary_very_large" msgid="7317423942896999029">"Kattaroq"</string>
- <string name="screen_zoom_summary_extremely_large" msgid="1438045624562358554">"Eng katta"</string>
+ <string name="screen_zoom_summary_larger" msgid="7346406528348409139">"Kattaroq"</string>
+ <string name="screen_zoom_summary_very_large" msgid="6853699118427019538">"Juda yirik"</string>
+ <string name="screen_zoom_summary_extra_large" msgid="4898389280769475551">"Juda katta"</string>
+ <string name="screen_zoom_summary_largest" msgid="5120290329622591371">"Eng katta"</string>
<string name="screen_zoom_summary_custom" msgid="3468154096832912210">"Moslashtirilgan (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
<string name="content_description_menu_button" msgid="6254844309171779931">"Menyu"</string>
<string name="retail_demo_reset_message" msgid="5392824901108195463">"Demo rejimda zavod holatiga qaytarish uchun parolni kiriting"</string>
diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml
index e456617..cdd0214 100644
--- a/packages/SettingsLib/res/values-vi/strings.xml
+++ b/packages/SettingsLib/res/values-vi/strings.xml
@@ -551,11 +551,15 @@
</string-array>
<string name="charge_length_format" msgid="6941645744588690932">"<xliff:g id="ID_1">%1$s</xliff:g> trước"</string>
<string name="remaining_length_format" msgid="4310625772926171089">"Còn <xliff:g id="ID_1">%1$s</xliff:g>"</string>
+ <string name="screen_zoom_summary_smallest" msgid="1042159093382395789">"Nhỏ nhất"</string>
+ <string name="screen_zoom_summary_smaller" msgid="7452722393802683776">"Nhỏ hơn"</string>
<string name="screen_zoom_summary_small" msgid="6050633151263074260">"Nhỏ"</string>
<string name="screen_zoom_summary_default" msgid="1888865694033865408">"Mặc định"</string>
<string name="screen_zoom_summary_large" msgid="4706951482598978984">"Lớn"</string>
- <string name="screen_zoom_summary_very_large" msgid="7317423942896999029">"Lớn hơn"</string>
- <string name="screen_zoom_summary_extremely_large" msgid="1438045624562358554">"Lớn nhất"</string>
+ <string name="screen_zoom_summary_larger" msgid="7346406528348409139">"Lớn hơn"</string>
+ <string name="screen_zoom_summary_very_large" msgid="6853699118427019538">"Rất lớn"</string>
+ <string name="screen_zoom_summary_extra_large" msgid="4898389280769475551">"Rất lớn"</string>
+ <string name="screen_zoom_summary_largest" msgid="5120290329622591371">"Lớn nhất"</string>
<string name="screen_zoom_summary_custom" msgid="3468154096832912210">"Tùy chỉnh (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
<string name="content_description_menu_button" msgid="6254844309171779931">"Menu"</string>
<string name="retail_demo_reset_message" msgid="5392824901108195463">"Nhập mật khẩu để tiến hành khôi phục cài đặt gốc ở chế độ trình diễn"</string>
diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml
index fb966ee..a2cca88 100644
--- a/packages/SettingsLib/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml
@@ -139,7 +139,7 @@
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"输入设备"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"互联网连接"</string>
<string name="bluetooth_profile_pbap" msgid="2103406516858653017">"允许访问通讯录和通话记录"</string>
- <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"这些信息将用于来电通知等用途"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"相关信息将用于来电通知等用途"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"共享互联网连接"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"短信"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM 卡访问权限"</string>
@@ -551,11 +551,15 @@
</string-array>
<string name="charge_length_format" msgid="6941645744588690932">"<xliff:g id="ID_1">%1$s</xliff:g>前"</string>
<string name="remaining_length_format" msgid="4310625772926171089">"还可以使用 <xliff:g id="ID_1">%1$s</xliff:g>"</string>
+ <string name="screen_zoom_summary_smallest" msgid="1042159093382395789">"最小"</string>
+ <string name="screen_zoom_summary_smaller" msgid="7452722393802683776">"较小"</string>
<string name="screen_zoom_summary_small" msgid="6050633151263074260">"小"</string>
<string name="screen_zoom_summary_default" msgid="1888865694033865408">"默认"</string>
<string name="screen_zoom_summary_large" msgid="4706951482598978984">"大"</string>
- <string name="screen_zoom_summary_very_large" msgid="7317423942896999029">"较大"</string>
- <string name="screen_zoom_summary_extremely_large" msgid="1438045624562358554">"最大"</string>
+ <string name="screen_zoom_summary_larger" msgid="7346406528348409139">"较大"</string>
+ <string name="screen_zoom_summary_very_large" msgid="6853699118427019538">"非常大"</string>
+ <string name="screen_zoom_summary_extra_large" msgid="4898389280769475551">"超大"</string>
+ <string name="screen_zoom_summary_largest" msgid="5120290329622591371">"最大"</string>
<string name="screen_zoom_summary_custom" msgid="3468154096832912210">"自定义 (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
<string name="content_description_menu_button" msgid="6254844309171779931">"菜单"</string>
<string name="retail_demo_reset_message" msgid="5392824901108195463">"输入密码即可在演示模式下恢复出厂设置"</string>
@@ -586,7 +590,7 @@
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"允许该应用设置闹钟以及安排在特定时间执行某些操作。这项权限开启后,该应用将在后台运行,可能会消耗更多电池电量。\n\n如果您关闭此权限,该应用设置的现有闹钟将不会响起,而且该应用安排在特定时间执行的现有活动也不会执行。"</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"设置, 闹钟, 提醒, 时钟, schedule, alarm, reminder, clock"</string>
<string name="zen_mode_do_not_disturb_name" msgid="6798711401734798283">"勿扰"</string>
- <string name="zen_mode_settings_title" msgid="7374070457626419755">"勿扰模式"</string>
+ <string name="zen_mode_settings_title" msgid="7374070457626419755">"勿扰"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"开启"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"开启勿扰模式"</string>
<string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"永不"</string>
@@ -601,14 +605,14 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"直到您将其关闭"</string>
<string name="zen_mode_starred_contacts_empty_name" msgid="933552939706125937">"(无姓名)"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"刚刚"</string>
- <string name="media_transfer_this_device_name" msgid="2357329267148436433">"这部手机"</string>
+ <string name="media_transfer_this_device_name" msgid="2357329267148436433">"本机"</string>
<string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"这部平板电脑"</string>
<string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"此计算机(内部)"</string>
<string name="media_transfer_this_device_name_tv" msgid="8508713779441163887">"此电视"</string>
<string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"基座音箱"</string>
<string name="media_transfer_external_device_name" msgid="2588672258721846418">"外部设备"</string>
<string name="media_transfer_default_device_name" msgid="4315604017399871828">"连接的设备"</string>
- <string name="media_transfer_this_phone" msgid="7194341457812151531">"这部手机"</string>
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"本机"</string>
<string name="media_transfer_digital_line_name" msgid="312091711951124301">"S/PDIF"</string>
<string name="media_transfer_analog_line_name" msgid="1841163866716302104">"模拟"</string>
<string name="media_transfer_aux_line_name" msgid="894135835967856689">"AUX"</string>
diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml
index 23ee3c9..fe6a732 100644
--- a/packages/SettingsLib/res/values-zh-rHK/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml
@@ -551,11 +551,15 @@
</string-array>
<string name="charge_length_format" msgid="6941645744588690932">"<xliff:g id="ID_1">%1$s</xliff:g>前"</string>
<string name="remaining_length_format" msgid="4310625772926171089">"尚餘 <xliff:g id="ID_1">%1$s</xliff:g>"</string>
+ <string name="screen_zoom_summary_smallest" msgid="1042159093382395789">"最小"</string>
+ <string name="screen_zoom_summary_smaller" msgid="7452722393802683776">"較小"</string>
<string name="screen_zoom_summary_small" msgid="6050633151263074260">"小"</string>
<string name="screen_zoom_summary_default" msgid="1888865694033865408">"預設"</string>
<string name="screen_zoom_summary_large" msgid="4706951482598978984">"大"</string>
- <string name="screen_zoom_summary_very_large" msgid="7317423942896999029">"較大"</string>
- <string name="screen_zoom_summary_extremely_large" msgid="1438045624562358554">"最大"</string>
+ <string name="screen_zoom_summary_larger" msgid="7346406528348409139">"較大"</string>
+ <string name="screen_zoom_summary_very_large" msgid="6853699118427019538">"非常大"</string>
+ <string name="screen_zoom_summary_extra_large" msgid="4898389280769475551">"特大"</string>
+ <string name="screen_zoom_summary_largest" msgid="5120290329622591371">"最大"</string>
<string name="screen_zoom_summary_custom" msgid="3468154096832912210">"自訂 (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
<string name="content_description_menu_button" msgid="6254844309171779931">"選單"</string>
<string name="retail_demo_reset_message" msgid="5392824901108195463">"輸入密碼即可在示範模式下重設原廠設定"</string>
diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml
index 32151eea..45c7447 100644
--- a/packages/SettingsLib/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml
@@ -551,11 +551,15 @@
</string-array>
<string name="charge_length_format" msgid="6941645744588690932">"<xliff:g id="ID_1">%1$s</xliff:g>前"</string>
<string name="remaining_length_format" msgid="4310625772926171089">"還剩 <xliff:g id="ID_1">%1$s</xliff:g>"</string>
+ <string name="screen_zoom_summary_smallest" msgid="1042159093382395789">"最小"</string>
+ <string name="screen_zoom_summary_smaller" msgid="7452722393802683776">"較小"</string>
<string name="screen_zoom_summary_small" msgid="6050633151263074260">"小"</string>
<string name="screen_zoom_summary_default" msgid="1888865694033865408">"預設"</string>
<string name="screen_zoom_summary_large" msgid="4706951482598978984">"大"</string>
- <string name="screen_zoom_summary_very_large" msgid="7317423942896999029">"較大"</string>
- <string name="screen_zoom_summary_extremely_large" msgid="1438045624562358554">"最大"</string>
+ <string name="screen_zoom_summary_larger" msgid="7346406528348409139">"較大"</string>
+ <string name="screen_zoom_summary_very_large" msgid="6853699118427019538">"很大"</string>
+ <string name="screen_zoom_summary_extra_large" msgid="4898389280769475551">"特大"</string>
+ <string name="screen_zoom_summary_largest" msgid="5120290329622591371">"最大"</string>
<string name="screen_zoom_summary_custom" msgid="3468154096832912210">"自訂 (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
<string name="content_description_menu_button" msgid="6254844309171779931">"選單"</string>
<string name="retail_demo_reset_message" msgid="5392824901108195463">"如要在展示模式中恢復原廠設定,請輸入密碼"</string>
diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml
index e4f3c57..6866f33 100644
--- a/packages/SettingsLib/res/values-zu/strings.xml
+++ b/packages/SettingsLib/res/values-zu/strings.xml
@@ -551,11 +551,15 @@
</string-array>
<string name="charge_length_format" msgid="6941645744588690932">"<xliff:g id="ID_1">%1$s</xliff:g> edlule"</string>
<string name="remaining_length_format" msgid="4310625772926171089">"<xliff:g id="ID_1">%1$s</xliff:g> osele"</string>
+ <string name="screen_zoom_summary_smallest" msgid="1042159093382395789">"Okuncane kakhulu"</string>
+ <string name="screen_zoom_summary_smaller" msgid="7452722393802683776">"Omncanyana"</string>
<string name="screen_zoom_summary_small" msgid="6050633151263074260">"Okuncane"</string>
<string name="screen_zoom_summary_default" msgid="1888865694033865408">"Okuzenzakalelayo"</string>
<string name="screen_zoom_summary_large" msgid="4706951482598978984">"Okukhulu"</string>
- <string name="screen_zoom_summary_very_large" msgid="7317423942896999029">"Okukhulu kakhulu"</string>
- <string name="screen_zoom_summary_extremely_large" msgid="1438045624562358554">"Okukhulu kakhulu"</string>
+ <string name="screen_zoom_summary_larger" msgid="7346406528348409139">"Obukhudlwana"</string>
+ <string name="screen_zoom_summary_very_large" msgid="6853699118427019538">"Okukhulu Kakhulu"</string>
+ <string name="screen_zoom_summary_extra_large" msgid="4898389280769475551">"Kukhulu Kakhulu"</string>
+ <string name="screen_zoom_summary_largest" msgid="5120290329622591371">"Obukhulukazi"</string>
<string name="screen_zoom_summary_custom" msgid="3468154096832912210">"Ngokwezifiso (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
<string name="content_description_menu_button" msgid="6254844309171779931">"Imenyu"</string>
<string name="retail_demo_reset_message" msgid="5392824901108195463">"Faka iphasiwedi ukuze wenze ukusetha kwefekthri kumodi yedemo"</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/PrimarySwitchPreference.java b/packages/SettingsLib/src/com/android/settingslib/PrimarySwitchPreference.java
index 2475c8e..a3e92a8 100644
--- a/packages/SettingsLib/src/com/android/settingslib/PrimarySwitchPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/PrimarySwitchPreference.java
@@ -98,6 +98,7 @@
mSwitch.setContentDescription(getTitle());
mSwitch.setChecked(mChecked);
mSwitch.setEnabled(mEnableSwitch);
+ mSwitch.setFocusable(mEnableSwitch);
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/AmbientVolumeUi.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/AmbientVolumeUi.java
index 881a97b..85e9182 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/AmbientVolumeUi.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/AmbientVolumeUi.java
@@ -94,16 +94,16 @@
*
* <p> If the UI is not expandable, it implies the UI will always stay in collapsed mode
*/
- void setExpandable(boolean expandable);
+ void setControlExpandable(boolean expandable);
/** @return if the UI is expandable. */
- boolean isExpandable();
+ boolean isControlExpandable();
/** Sets if the UI is in expanded mode. */
- void setExpanded(boolean expanded);
+ void setControlExpanded(boolean expanded);
/** @return if the UI is in expanded mode. */
- boolean isExpanded();
+ boolean isControlExpanded();
/**
* Sets if the UI is capable to mute the ambient of the remote device.
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/AmbientVolumeUiController.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/AmbientVolumeUiController.java
index f7da8a5..23a72e0 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/AmbientVolumeUiController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/AmbientVolumeUiController.java
@@ -128,7 +128,7 @@
Log.d(TAG, "onAmbientChanged, value:" + gainSettings + ", device:" + device);
}
HearingDeviceLocalDataManager.Data data = mLocalDataManager.get(device);
- final boolean expanded = mAmbientLayout.isExpanded();
+ final boolean expanded = mAmbientLayout.isControlExpanded();
final boolean isInitiatedFromUi = (expanded && data.ambient() == gainSettings)
|| (!expanded && data.groupAmbient() == gainSettings);
if (isInitiatedFromUi) {
@@ -176,13 +176,13 @@
if (!mAmbientLayout.isMuted()) {
// Apply previous collapsed/expanded volume to remote device
HearingDeviceLocalDataManager.Data data = mLocalDataManager.get(d);
- int volume = mAmbientLayout.isExpanded()
+ int volume = mAmbientLayout.isControlExpanded()
? data.ambient() : data.groupAmbient();
mVolumeController.setAmbient(d, volume);
}
// Update new value to local data
mLocalDataManager.updateAmbientControlExpanded(d,
- mAmbientLayout.isExpanded());
+ mAmbientLayout.isControlExpanded());
});
mLocalDataManager.flush();
}
@@ -322,7 +322,7 @@
}
}
- mAmbientLayout.setExpandable(mSideToDeviceMap.size() > 1);
+ mAmbientLayout.setControlExpandable(mSideToDeviceMap.size() > 1);
mAmbientLayout.setupSliders(mSideToDeviceMap);
refresh();
}
@@ -412,7 +412,7 @@
// Update ambient volume
final int leftAmbient = leftState != null ? leftState.gainSetting() : INVALID_VOLUME;
final int rightAmbient = rightState != null ? rightState.gainSetting() : INVALID_VOLUME;
- if (mAmbientLayout.isExpanded()) {
+ if (mAmbientLayout.isControlExpanded()) {
setVolumeIfValid(SIDE_LEFT, leftAmbient);
setVolumeIfValid(SIDE_RIGHT, rightAmbient);
} else {
@@ -451,7 +451,7 @@
}
private void setAmbientControlExpanded(boolean expanded) {
- mAmbientLayout.setExpanded(expanded);
+ mAmbientLayout.setControlExpanded(expanded);
mSideToDeviceMap.forEach((s, d) -> {
// Update new value to local data
mLocalDataManager.updateAmbientControlExpanded(d, expanded);
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
index 29f99cb..aad7919 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
@@ -1,15 +1,21 @@
package com.android.settingslib.bluetooth;
import static com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast.UNKNOWN_VALUE_PLACEHOLDER;
+import static com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant.UNKNOWN_CHANNEL;
import static com.android.settingslib.flags.Flags.audioSharingHysteresisModeFix;
import static com.android.settingslib.widget.AdaptiveOutlineDrawable.ICON_TYPE_ADVANCED;
+import static java.util.stream.Collectors.toSet;
+
import android.annotation.SuppressLint;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothCsipSetCoordinator;
import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothLeBroadcastChannel;
+import android.bluetooth.BluetoothLeBroadcastMetadata;
import android.bluetooth.BluetoothLeBroadcastReceiveState;
+import android.bluetooth.BluetoothLeBroadcastSubgroup;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothStatusCodes;
import android.content.ComponentName;
@@ -38,6 +44,7 @@
import android.view.InputDevice;
import androidx.annotation.DrawableRes;
+import androidx.annotation.IntRange;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;
@@ -53,6 +60,7 @@
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
+import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
@@ -60,7 +68,6 @@
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
-import java.util.stream.Collectors;
public class BluetoothUtils {
private static final String TAG = "BluetoothUtils";
@@ -652,13 +659,13 @@
.map(deviceManager::findDevice)
.filter(Objects::nonNull)
.map(BluetoothUtils::getGroupId)
- .collect(Collectors.toSet());
+ .collect(toSet());
Set<Integer> activeGroupIds =
leAudioProfile.getActiveDevices().stream()
.map(deviceManager::findDevice)
.filter(Objects::nonNull)
.map(BluetoothUtils::getGroupId)
- .collect(Collectors.toSet());
+ .collect(toSet());
int groupId = getGroupId(cachedDevice);
return activeGroupIds.size() == 1
&& !activeGroupIds.contains(groupId)
@@ -1305,4 +1312,87 @@
return null;
}
}
+
+ /**
+ * Gets the index of the first selected channel for the first subgroup for a given broadcast
+ * source Id on a Bluetooth sink device.
+ */
+ @NonNull
+ public static Set<Integer> getSelectedChannelIndex(
+ @NonNull LocalBluetoothProfileManager profileManager,
+ @NonNull BluetoothDevice sink, @IntRange(from = 0x00, to = 0xFF) int sourceId) {
+ LocalBluetoothLeBroadcastAssistant assistant =
+ profileManager.getLeAudioBroadcastAssistantProfile();
+ if (assistant == null) {
+ Log.w(TAG, "getSelectedChannelIndex(): assistant is null");
+ return UNKNOWN_CHANNEL;
+ }
+ BluetoothLeBroadcastMetadata metadata = assistant.getSourceMetadata(sink, sourceId);
+ if (metadata == null) {
+ Log.w(TAG, "getSelectedChannelIndex(): metadata is null");
+ return UNKNOWN_CHANNEL;
+ }
+ List<BluetoothLeBroadcastSubgroup> subgroups = metadata.getSubgroups();
+ if (subgroups == null || subgroups.isEmpty()) {
+ Log.d(TAG, "getSelectedChannelIndex(): subgroup is null or empty");
+ return UNKNOWN_CHANNEL;
+ }
+ return subgroups.getFirst().getChannels().stream().filter(
+ BluetoothLeBroadcastChannel::isSelected).map(
+ BluetoothLeBroadcastChannel::getChannelIndex).collect(toSet());
+ }
+
+ /**
+ * Sets the selected state of a specific channel index for a given broadcast source Id on a
+ * Bluetooth sink device by modifying the broadcast metadata. This method assumes the channel
+ * belongs to the first subgroup in the metadata.
+ */
+ public static void modifySelectedChannelIndex(
+ @NonNull LocalBluetoothProfileManager profileManager,
+ @NonNull BluetoothDevice sink, @IntRange(from = 0x00, to = 0xFF) int sourceId,
+ @NonNull Set<Integer> channelIndex, boolean shouldSelect) {
+ LocalBluetoothLeBroadcastAssistant assistant =
+ profileManager.getLeAudioBroadcastAssistantProfile();
+ if (assistant == null) {
+ Log.w(TAG, "modifySelectedChannelIndex(): assistant is null");
+ return;
+ }
+ BluetoothLeBroadcastMetadata original = assistant.getSourceMetadata(sink, sourceId);
+ if (original == null) {
+ Log.w(TAG, "modifySelectedChannelIndex(): metadata is null");
+ return;
+ }
+ List<BluetoothLeBroadcastSubgroup> subgroups = new ArrayList<>(original.getSubgroups());
+ if (subgroups == null || subgroups.isEmpty()) {
+ Log.d(TAG, "modifySelectedChannelIndex(): subgroup is null or empty");
+ return;
+ }
+ BluetoothLeBroadcastSubgroup firstSubgroup = subgroups.getFirst();
+ List<BluetoothLeBroadcastChannel> channels = firstSubgroup.getChannels();
+ if (!channels.stream().map(BluetoothLeBroadcastChannel::getChannelIndex).collect(
+ toSet()).containsAll(channelIndex)) {
+ Log.d(TAG, "modifySelectedChannelIndex(): no channel found for given index");
+ return;
+ }
+ List<BluetoothLeBroadcastChannel> updatedChannels = channels.stream()
+ .map(c ->
+ channelIndex.contains(c.getChannelIndex()) && c.isSelected() != shouldSelect
+ ? new BluetoothLeBroadcastChannel.Builder(c).setSelected(
+ shouldSelect).build() : c).toList();
+ if (updatedChannels.equals(channels)) {
+ Log.d(TAG, "modifySelectedChannelIndex(): no change needed");
+ return;
+ }
+ BluetoothLeBroadcastSubgroup.Builder updatedSubgroupBuilder =
+ new BluetoothLeBroadcastSubgroup.Builder(firstSubgroup);
+ updatedSubgroupBuilder.clearChannel();
+ updatedChannels.forEach(updatedSubgroupBuilder::addChannel);
+ subgroups.set(0, updatedSubgroupBuilder.build());
+ var updatedBuilder = new BluetoothLeBroadcastMetadata.Builder(original).clearSubgroup();
+ subgroups.forEach(updatedBuilder::addSubgroup);
+ BluetoothLeBroadcastMetadata updated = updatedBuilder.build();
+ Log.d(TAG, "modifySelectedChannelIndex(): existedMetadata = " + original
+ + " updatedMetadata = " + updated);
+ assistant.modifySource(sink, sourceId, updated);
+ }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java
index 3ec4bb8..d36a194 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java
@@ -21,7 +21,9 @@
import static com.android.settingslib.Utils.isAudioModeOngoingCall;
import static com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant.LocalBluetoothLeBroadcastSourceState.DECRYPTION_FAILED;
import static com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant.LocalBluetoothLeBroadcastSourceState.PAUSED;
+import static com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant.LocalBluetoothLeBroadcastSourceState.PAUSED_BY_RECEIVER;
import static com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant.LocalBluetoothLeBroadcastSourceState.STREAMING;
+import static com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant.getLocalSourceStateWithSelectedChannel;
import static java.util.stream.Collectors.toList;
@@ -108,6 +110,7 @@
public static final int BROADCAST_STATE_ON = 1;
public static final int BROADCAST_STATE_OFF = 2;
private static final int BROADCAST_NAME_PREFIX_MAX_LENGTH = 27;
+ private static final String DEFAULT_BROADCAST_NAME_PREFIX = "Broadcast";
@Retention(RetentionPolicy.SOURCE)
@IntDef(
@@ -144,6 +147,7 @@
};
private final Context mContext;
private final CachedBluetoothDeviceManager mDeviceManager;
+ private final LocalBluetoothProfileManager mProfileManager;
private final boolean mHysteresisModeFixAvailable;
private final boolean mIsWorkProfile;
private BluetoothLeBroadcast mServiceBroadcast;
@@ -426,6 +430,12 @@
Log.d(TAG, "Skip notifyPrivateBroadcastReceived for work profile.");
return;
}
+ String packageName = mContext.getPackageName();
+ if (!packageName.equals(SYSUI_PKG)) {
+ Log.d(TAG,
+ "Skip notifyPrivateBroadcastReceived, not triggered by SystemUI.");
+ return;
+ }
if (state.getBroadcastId() == mBroadcastId
|| !mLocalSinksPendingSourceRemoval.isEmpty()) {
Log.d(TAG,
@@ -433,9 +443,14 @@
+ "triggered by personal audio sharing.");
return;
}
- var sourceState = LocalBluetoothLeBroadcastAssistant.getLocalSourceState(state);
+ var sourceStateAndSelectedChannel = getLocalSourceStateWithSelectedChannel(
+ mProfileManager, sink, sourceId, state);
+ var sourceState = sourceStateAndSelectedChannel.first;
+ var selectedChannel = sourceStateAndSelectedChannel.second;
if (sourceState == STREAMING || sourceState == DECRYPTION_FAILED
- || (mHysteresisModeFixAvailable && sourceState == PAUSED)) {
+ || (mHysteresisModeFixAvailable && sourceState == PAUSED)
+ || (Flags.audioStreamPlayPauseByModifySource()
+ && sourceState == PAUSED_BY_RECEIVER)) {
List<BluetoothLeAudioContentMetadata> subgroupMetadata =
state.getSubgroupMetadata();
String programInfo = subgroupMetadata.isEmpty() ? ""
@@ -445,7 +460,8 @@
sourceId,
state.getBroadcastId(),
programInfo == null ? "" : programInfo,
- sourceState);
+ sourceState,
+ selectedChannel);
}
}
};
@@ -462,9 +478,11 @@
}
}
- LocalBluetoothLeBroadcast(Context context, CachedBluetoothDeviceManager deviceManager) {
+ LocalBluetoothLeBroadcast(Context context, CachedBluetoothDeviceManager deviceManager,
+ LocalBluetoothProfileManager profileManager) {
mContext = context;
mDeviceManager = deviceManager;
+ mProfileManager = profileManager;
mExecutor = Executors.newSingleThreadExecutor();
mBuilder = new BluetoothLeAudioContentMetadata.Builder();
mContentResolver = context.getContentResolver();
@@ -1118,6 +1136,9 @@
// set the default value;
int postfix = ThreadLocalRandom.current().nextInt(DEFAULT_CODE_MIN, DEFAULT_CODE_MAX);
String name = BluetoothAdapter.getDefaultAdapter().getName();
+ if (name == null || name.isEmpty()) {
+ name = DEFAULT_BROADCAST_NAME_PREFIX;
+ }
return (name.length() < BROADCAST_NAME_PREFIX_MAX_LENGTH ? name : name.substring(0,
BROADCAST_NAME_PREFIX_MAX_LENGTH)) + UNDERLINE + postfix;
}
@@ -1126,6 +1147,9 @@
// set the default value;
int postfix = ThreadLocalRandom.current().nextInt(DEFAULT_CODE_MIN, DEFAULT_CODE_MAX);
String name = BluetoothAdapter.getDefaultAdapter().getName();
+ if (name == null || name.isEmpty()) {
+ name = DEFAULT_BROADCAST_NAME_PREFIX;
+ }
return (name.length() < BROADCAST_NAME_PREFIX_MAX_LENGTH ? name : name.substring(0,
BROADCAST_NAME_PREFIX_MAX_LENGTH)) + UNDERLINE + postfix;
}
@@ -1340,20 +1364,17 @@
private void notifyPrivateBroadcastReceived(BluetoothDevice sink, int sourceId, int broadcastId,
String programInfo,
- LocalBluetoothLeBroadcastAssistant.LocalBluetoothLeBroadcastSourceState state) {
- String packageName = mContext.getPackageName();
- if (!packageName.equals(SYSUI_PKG)) {
- Log.d(TAG, "Skip notifyPrivateBroadcastReceived, not triggered by SystemUI.");
- return;
- }
- var data = new PrivateBroadcastReceiveData(sink, sourceId, broadcastId, programInfo, state);
+ LocalBluetoothLeBroadcastAssistant.LocalBluetoothLeBroadcastSourceState state,
+ Set<Integer> selectedChannelIndex) {
+ var data = new PrivateBroadcastReceiveData(sink, sourceId, broadcastId, programInfo, state,
+ selectedChannelIndex);
Intent intent = new Intent(ACTION_LE_AUDIO_PRIVATE_BROADCAST_RECEIVED);
intent.putExtra(EXTRA_PRIVATE_BROADCAST_RECEIVE_DATA, data);
intent.setPackage(SETTINGS_PKG);
Log.d(TAG,
"notifyPrivateBroadcastReceived for sink = " + sink + " with sourceId = " + sourceId
- + " state = " + state
- + " programInfo =" + programInfo
+ + " state = " + state + " selectedChannelIndex = "
+ + selectedChannelIndex + " programInfo = " + programInfo
+ " broadcastId = " + broadcastId);
mContext.sendBroadcast(intent);
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcastAssistant.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcastAssistant.java
index 223035a..d1a9ae7 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcastAssistant.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcastAssistant.java
@@ -31,6 +31,7 @@
import android.content.Context;
import android.os.Build;
import android.util.Log;
+import android.util.Pair;
import androidx.annotation.IntRange;
import androidx.annotation.NonNull;
@@ -38,10 +39,13 @@
import androidx.annotation.RequiresApi;
import com.android.settingslib.R;
+import com.android.settingslib.flags.Flags;
import java.util.ArrayList;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
@@ -52,12 +56,15 @@
* BluetoothLeBroadcastAssistant.Callback} to get the result callback.
*/
public class LocalBluetoothLeBroadcastAssistant implements LocalBluetoothProfile {
+ public static final HashSet<Integer> UNKNOWN_CHANNEL = new HashSet<>(List.of(-1));
+
/** A derived source state based on {@link BluetoothLeBroadcastReceiveState}. */
public enum LocalBluetoothLeBroadcastSourceState {
UNKNOWN,
STREAMING,
DECRYPTION_FAILED,
PAUSED,
+ PAUSED_BY_RECEIVER;
}
private static final String TAG = "LocalBluetoothLeBroadcastAssistant";
@@ -620,4 +627,31 @@
}
return LocalBluetoothLeBroadcastSourceState.UNKNOWN;
}
+
+ /**
+ * Returns the source connection status with channel selected based on the provided broadcast
+ * receive state and source metadata retrieved from stack.
+ */
+ public static @NonNull Pair<LocalBluetoothLeBroadcastSourceState, Set<Integer>>
+ getLocalSourceStateWithSelectedChannel(
+ @NonNull LocalBluetoothProfileManager profileManager,
+ @NonNull BluetoothDevice sink,
+ int sourceId,
+ @NonNull BluetoothLeBroadcastReceiveState state) {
+ var localSourceState = getLocalSourceState(state);
+ if (!Flags.audioStreamPlayPauseByModifySource()) {
+ return Pair.create(localSourceState, UNKNOWN_CHANNEL);
+ }
+ Set<Integer> selectedChannelIndex = BluetoothUtils.getSelectedChannelIndex(
+ profileManager, sink, sourceId);
+ if (localSourceState == LocalBluetoothLeBroadcastSourceState.PAUSED
+ && selectedChannelIndex.isEmpty()) {
+ // No channel selected meaning the user decided to de-sync to the source, we return
+ // `PAUSED_BY_RECEIVER`. In contrast, having any channel selected meaning the source
+ // paused itself, we return `PAUSED`.
+ return Pair.create(LocalBluetoothLeBroadcastSourceState.PAUSED_BY_RECEIVER,
+ selectedChannelIndex);
+ }
+ return Pair.create(localSourceState, selectedChannelIndex);
+ }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
index ff5e9e6..d0f2e0b 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
@@ -259,7 +259,7 @@
if (DEBUG) {
Log.d(TAG, "Adding local LE_AUDIO_BROADCAST profile");
}
- mLeAudioBroadcast = new LocalBluetoothLeBroadcast(mContext, mDeviceManager);
+ mLeAudioBroadcast = new LocalBluetoothLeBroadcast(mContext, mDeviceManager, this);
// no event handler for the LE boradcast.
mProfileNameMap.put(LocalBluetoothLeBroadcast.NAME, mLeAudioBroadcast);
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PrivateBroadcastReceiveData.kt b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PrivateBroadcastReceiveData.kt
index a284d20..c711722 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PrivateBroadcastReceiveData.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PrivateBroadcastReceiveData.kt
@@ -20,9 +20,11 @@
import android.os.Parcel
import android.os.Parcelable
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant.LocalBluetoothLeBroadcastSourceState
-import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant.LocalBluetoothLeBroadcastSourceState.PAUSED
-import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant.LocalBluetoothLeBroadcastSourceState.STREAMING
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant.LocalBluetoothLeBroadcastSourceState.DECRYPTION_FAILED
+import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant.LocalBluetoothLeBroadcastSourceState.PAUSED
+import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant.LocalBluetoothLeBroadcastSourceState.PAUSED_BY_RECEIVER
+import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant.LocalBluetoothLeBroadcastSourceState.STREAMING
+import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant.UNKNOWN_CHANNEL
/**
* Data class representing information received in a private broadcast.
@@ -41,6 +43,7 @@
val broadcastId: Int = -1,
val programInfo: String = "",
val state: LocalBluetoothLeBroadcastSourceState?,
+ val selectedChannelIndex: Set<Int> = UNKNOWN_CHANNEL
) : Parcelable {
override fun describeContents(): Int = 0
@@ -51,6 +54,7 @@
parcel.writeInt(broadcastId)
parcel.writeString(programInfo)
parcel.writeSerializable(state)
+ parcel.writeSerializable(java.util.HashSet(selectedChannelIndex))
}
companion object {
@@ -70,7 +74,11 @@
state = readSerializable(
LocalBluetoothLeBroadcastSourceState::class.java.classLoader,
LocalBluetoothLeBroadcastSourceState::class.java
- )
+ ),
+ selectedChannelIndex = readSerializable(
+ HashSet::class.java.classLoader,
+ HashSet::class.java
+ )?.filterIsInstance<Int>()?.toHashSet() ?: UNKNOWN_CHANNEL
)
}
override fun newArray(size: Int): Array<PrivateBroadcastReceiveData?> {
@@ -84,7 +92,8 @@
&& broadcastId != -1
&& (state == STREAMING
|| state == PAUSED
- || state == DECRYPTION_FAILED)
+ || state == DECRYPTION_FAILED
+ || state == PAUSED_BY_RECEIVER)
}
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingRepository.kt b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingRepository.kt
index c68dbee..400af40 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingRepository.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingRepository.kt
@@ -16,7 +16,6 @@
package com.android.settingslib.bluetooth.devicesettings.data.repository
-import android.bluetooth.BluetoothAdapter
import android.content.Context
import android.text.TextUtils
import com.android.settingslib.bluetooth.CachedBluetoothDevice
@@ -68,7 +67,6 @@
class DeviceSettingRepositoryImpl(
private val context: Context,
- private val bluetoothAdaptor: BluetoothAdapter,
private val coroutineScope: CoroutineScope,
private val backgroundCoroutineContext: CoroutineContext,
) : DeviceSettingRepository {
@@ -84,7 +82,6 @@
DeviceSettingServiceConnection(
cachedDevice,
context,
- bluetoothAdaptor,
coroutineScope,
backgroundCoroutineContext,
)
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingServiceConnection.kt b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingServiceConnection.kt
index 0474b50..804a066 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingServiceConnection.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingServiceConnection.kt
@@ -16,7 +16,6 @@
package com.android.settingslib.bluetooth.devicesettings.data.repository
-import android.bluetooth.BluetoothAdapter
import android.content.ComponentName
import android.content.Context
import android.content.Intent
@@ -66,6 +65,7 @@
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapNotNull
+import kotlinx.coroutines.flow.retryWhen
import kotlinx.coroutines.flow.shareIn
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
@@ -77,7 +77,6 @@
class DeviceSettingServiceConnection(
private val cachedDevice: CachedBluetoothDevice,
private val context: Context,
- private val bluetoothAdaptor: BluetoothAdapter,
private val coroutineScope: CoroutineScope,
private val backgroundCoroutineContext: CoroutineContext,
) {
@@ -121,6 +120,7 @@
null
}
}
+ .retryWhen { cause, attempt -> cause is DeadObjectException || attempt < 2 }
.catch { e ->
if (e is DeadObjectException) {
Log.e(TAG, "DeadObjectException happens when try to get service status.", e)
@@ -195,20 +195,23 @@
Log.w(TAG, "Service is disabled")
return@flow
}
- getSettingsProviderServices()
- ?.values
- ?.map {
- it.flatMapLatest { status ->
- when (status) {
- is ServiceConnectionStatus.Connected ->
- getDeviceSettingsFromService(cachedDevice, status.service)
- else -> flowOf(emptyList())
+ val services = getSettingsProviderServices()?.values
+ if (services == null || services.isEmpty()) {
+ emit(mapOf())
+ return@flow
+ }
+ services
+ .map {
+ it.filterIsInstance<
+ ServiceConnectionStatus.Connected<IDeviceSettingsProviderService>
+ >()
+ .flatMapLatest { status ->
+ getDeviceSettingsFromService(cachedDevice, status.service)
}
- }
}
- ?.let { items -> combine(items) { it.toList().flatten() } }
- ?.map { items -> items.associateBy { it.settingId } }
- ?.let { emitAll(it) }
+ .let { items -> combine(items) { it.toList().flatten() } }
+ .map { items -> items.associateBy { it.settingId } }
+ .let { emitAll(it) }
}
.shareIn(scope = coroutineScope, started = SharingStarted.WhileSubscribed(), replay = 1)
@@ -224,13 +227,11 @@
Log.w(TAG, "Service is disabled")
return null
}
+ // Wait until all settings providers are ready.
+ settingIdToItemMapping.firstOrNull()
return readConfig()
}
- /** Gets all device settings for the device. */
- fun getDeviceSettingList(): Flow<List<DeviceSetting>> =
- settingIdToItemMapping.map { it.values.toList() }
-
/** Gets the device settings with the ID for the device. */
fun getDeviceSetting(@DeviceSettingId deviceSettingId: Int): Flow<DeviceSetting?> =
settingIdToItemMapping.map { it[deviceSettingId] }
@@ -289,7 +290,9 @@
getService(intent, IDeviceSettingsProviderService.Stub::asInterface)
.stateIn(
coroutineScope.plus(backgroundCoroutineContext),
- SharingStarted.WhileSubscribed(stopTimeoutMillis = SERVICE_CONNECTION_STOP_MILLIS),
+ SharingStarted.WhileSubscribed(
+ stopTimeoutMillis = SERVICE_CONNECTION_STOP_MILLIS
+ ),
ServiceConnectionStatus.Connecting,
)
},
@@ -322,7 +325,7 @@
throw e
}
}
- .stateIn(coroutineScope, SharingStarted.WhileSubscribed(), emptyList())
+ .shareIn(coroutineScope, SharingStarted.WhileSubscribed(), 1)
}
private fun <T : IInterface> getService(
diff --git a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryStatus.java b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryStatus.java
index f659e38..192bad1 100644
--- a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryStatus.java
+++ b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryStatus.java
@@ -16,11 +16,14 @@
package com.android.settingslib.fuelgauge;
+import static android.os.BatteryManager.BATTERY_HEALTH_DEAD;
+import static android.os.BatteryManager.BATTERY_HEALTH_UNKNOWN;
import static android.os.BatteryManager.BATTERY_STATUS_FULL;
import static android.os.BatteryManager.BATTERY_STATUS_UNKNOWN;
import static android.os.BatteryManager.CHARGING_POLICY_ADAPTIVE_LONGLIFE;
import static android.os.BatteryManager.CHARGING_POLICY_DEFAULT;
import static android.os.BatteryManager.EXTRA_CHARGING_STATUS;
+import static android.os.BatteryManager.EXTRA_HEALTH;
import static android.os.BatteryManager.EXTRA_MAX_CHARGING_CURRENT;
import static android.os.BatteryManager.EXTRA_MAX_CHARGING_VOLTAGE;
import static android.os.BatteryManager.EXTRA_PLUGGED;
@@ -55,6 +58,7 @@
public final int status;
public final int level;
public final int plugged;
+ public final int health;
public final int chargingStatus;
public final int maxChargingWattage;
public final boolean present;
@@ -75,6 +79,7 @@
this.maxChargingWattage = maxChargingWattage;
this.present = present;
this.incompatibleCharger = Optional.empty();
+ this.health = BATTERY_HEALTH_UNKNOWN;
}
@@ -90,6 +95,7 @@
status = batteryChangedIntent.getIntExtra(EXTRA_STATUS, BATTERY_STATUS_UNKNOWN);
plugged = batteryChangedIntent.getIntExtra(EXTRA_PLUGGED, 0);
level = getBatteryLevel(batteryChangedIntent);
+ health = batteryChangedIntent.getIntExtra(EXTRA_HEALTH, BATTERY_HEALTH_UNKNOWN);
chargingStatus = batteryChangedIntent.getIntExtra(EXTRA_CHARGING_STATUS,
CHARGING_POLICY_DEFAULT);
present = batteryChangedIntent.getBooleanExtra(EXTRA_PRESENT, true);
@@ -140,6 +146,15 @@
return isBatteryDefender(chargingStatus);
}
+ /**
+ * Whether battery is dead.
+ *
+ * @return true if battery is dead
+ */
+ public boolean isDead() {
+ return health == BATTERY_HEALTH_DEAD;
+ }
+
/** Return current charging speed is fast, slow or normal. */
public final int getChargingSpeed(Context context) {
final int slowThreshold = context.getResources().getInteger(
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
index 125ae5a..c16378f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
@@ -564,6 +564,16 @@
}
/**
+ * Get the current active session's release type.
+ *
+ * @return the release type of the current active session
+ */
+ @RoutingSessionInfo.ReleaseType
+ int getSessionReleaseType() {
+ return getActiveRoutingSession().getReleaseType();
+ }
+
+ /**
* Release session to stop playing media on MediaDevice.
*/
boolean releaseSession() {
@@ -694,6 +704,9 @@
return mSuggestedDeviceState;
}
+ /** Requests a suggestion from other routers. */
+ public abstract void requestDeviceSuggestion();
+
@TargetApi(Build.VERSION_CODES.R)
boolean shouldEnableVolumeSeekBar(RoutingSessionInfo sessionInfo) {
return sessionInfo.isSystemSession() // System sessions are not remote
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InputMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/InputMediaDevice.java
index 80e5e59..2fb52d8 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InputMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InputMediaDevice.java
@@ -42,6 +42,8 @@
private final String mId;
+ private final String mAddress;
+
private final @AudioDeviceType int mAudioDeviceInfoType;
private final int mMaxVolume;
@@ -55,6 +57,7 @@
private InputMediaDevice(
@NonNull Context context,
@NonNull String id,
+ @NonNull String address,
@AudioDeviceType int audioDeviceInfoType,
int maxVolume,
int currentVolume,
@@ -62,6 +65,7 @@
@Nullable String productName) {
super(context, /* info= */ null, /* item= */ null);
mId = id;
+ mAddress = address;
mAudioDeviceInfoType = audioDeviceInfoType;
mMaxVolume = maxVolume;
mCurrentVolume = currentVolume;
@@ -74,6 +78,7 @@
public static InputMediaDevice create(
@NonNull Context context,
@NonNull String id,
+ @NonNull String address,
@AudioDeviceType int audioDeviceInfoType,
int maxVolume,
int currentVolume,
@@ -86,6 +91,7 @@
return new InputMediaDevice(
context,
id,
+ address,
audioDeviceInfoType,
maxVolume,
currentVolume,
@@ -97,6 +103,10 @@
return mAudioDeviceInfoType;
}
+ public @NonNull String getAddress() {
+ return mAddress;
+ }
+
public static boolean isSupportedInputDevice(@AudioDeviceType int audioDeviceInfoType) {
return switch (audioDeviceInfoType) {
case TYPE_BUILTIN_MIC,
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InputRouteManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InputRouteManager.java
index 4e24af3..0b4d21f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InputRouteManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InputRouteManager.java
@@ -27,6 +27,7 @@
import android.media.MediaRecorder;
import android.os.Handler;
import android.os.HandlerExecutor;
+import android.text.TextUtils;
import android.util.Slog;
import androidx.annotation.NonNull;
@@ -67,6 +68,7 @@
@VisibleForTesting final List<MediaDevice> mInputMediaDevices = new CopyOnWriteArrayList<>();
private @AudioDeviceType int mSelectedInputDeviceType;
+ private String mSelectedInputDeviceAddr;
private final Collection<InputDeviceCallback> mCallbacks = new CopyOnWriteArrayList<>();
private final Object mCallbackLock = new Object();
@@ -81,24 +83,30 @@
// Activate the last hot plugged valid input device, to match the output device
// behavior.
@AudioDeviceType int deviceTypeToActivate = mSelectedInputDeviceType;
+ String deviceAddrToActivate = mSelectedInputDeviceAddr;
for (AudioDeviceInfo info : addedDevices) {
@AudioDeviceType int type = info.getType();
+ String addr = info.getAddress();
// Since onAudioDevicesAdded is called not only when new device is hot
// plugged, but also when the switcher dialog is opened, make sure to check
// against existing device list and only activate if the device does not
// exist previously.
if (InputMediaDevice.isSupportedInputDevice(type)
- && findDeviceByType(type) == null) {
+ && findDeviceByTypeAndAddress(type, addr) == null) {
deviceTypeToActivate = type;
+ deviceAddrToActivate = addr;
}
}
// Only activate if we find a different valid input device. e.g. if none of the
// addedDevices is supported input device, we don't need to activate anything.
- if (mSelectedInputDeviceType != deviceTypeToActivate) {
+ if (mSelectedInputDeviceType != deviceTypeToActivate
+ || mSelectedInputDeviceAddr != deviceAddrToActivate) {
mSelectedInputDeviceType = deviceTypeToActivate;
+ mSelectedInputDeviceAddr = deviceAddrToActivate;
AudioDeviceAttributes deviceAttributes =
- createInputDeviceAttributes(mSelectedInputDeviceType);
+ createInputDeviceAttributes(
+ mSelectedInputDeviceType, mSelectedInputDeviceAddr);
setPreferredDeviceForAllPresets(deviceAttributes);
}
}
@@ -151,8 +159,18 @@
}
}
- // TODO(b/355684672): handle edge case where there are two devices with the same type. Only
- // using a single type might not be enough to recognize the correct device.
+ @Nullable
+ private MediaDevice findDeviceByTypeAndAddress(@AudioDeviceType int type, String addr) {
+ for (MediaDevice device : mInputMediaDevices) {
+ if (((InputMediaDevice) device).getAudioDeviceInfoType() == type
+ && (TextUtils.isEmpty(addr)
+ || ((InputMediaDevice) device).getAddress().equals(addr))) {
+ return device;
+ }
+ }
+ return null;
+ }
+
@Nullable
private MediaDevice findDeviceByType(@AudioDeviceType int type) {
for (MediaDevice device : mInputMediaDevices) {
@@ -165,28 +183,44 @@
@Nullable
public MediaDevice getSelectedInputDevice() {
- return findDeviceByType(mSelectedInputDeviceType);
+ MediaDevice exactDevice =
+ findDeviceByTypeAndAddress(mSelectedInputDeviceType, mSelectedInputDeviceAddr);
+
+ // This can happen because the address can sometimes contain surprising strings
+ // such as "bottom" for the default internal mic. In those situations,
+ // ignore the address and search by the type only. In any case, this also
+ // serves as a sane fallback.
+ if (exactDevice == null) {
+ MediaDevice device = findDeviceByType(mSelectedInputDeviceType);
+ return device;
+ }
+
+ return exactDevice;
}
private void applyDefaultSelectedTypeToAllPresets() {
- mSelectedInputDeviceType = retrieveDefaultSelectedDeviceType();
- AudioDeviceAttributes deviceAttributes =
- createInputDeviceAttributes(mSelectedInputDeviceType);
+ AudioDeviceAttributes deviceAttributes = retrieveDefaultSelectedInputDeviceAttrs();
+
+ mSelectedInputDeviceType = deviceAttributes.getType();
+ mSelectedInputDeviceAddr = deviceAttributes.getAddress();
+
setPreferredDeviceForAllPresets(deviceAttributes);
}
- private AudioDeviceAttributes createInputDeviceAttributes(@AudioDeviceType int type) {
- // Address is not used.
- return new AudioDeviceAttributes(AudioDeviceAttributes.ROLE_INPUT, type, /* address= */ "");
+ private AudioDeviceAttributes createInputDeviceAttributes(@AudioDeviceType int type,
+ String address) {
+ return new AudioDeviceAttributes(AudioDeviceAttributes.ROLE_INPUT, type, address);
}
- private @AudioDeviceType int retrieveDefaultSelectedDeviceType() {
+ private AudioDeviceAttributes retrieveDefaultSelectedInputDeviceAttrs() {
List<AudioDeviceAttributes> attributesOfSelectedInputDevices =
mAudioManager.getDevicesForAttributes(INPUT_ATTRIBUTES);
- int selectedInputDeviceAttributesType;
+ @AudioDeviceType int selectedType;
+ String selectedAddr;
if (attributesOfSelectedInputDevices.isEmpty()) {
Slog.e(TAG, "Unexpected empty list of input devices. Using built-in mic.");
- selectedInputDeviceAttributesType = AudioDeviceInfo.TYPE_BUILTIN_MIC;
+ selectedType = AudioDeviceInfo.TYPE_BUILTIN_MIC;
+ selectedAddr = "";
} else {
if (attributesOfSelectedInputDevices.size() > 1) {
Slog.w(
@@ -194,9 +228,10 @@
"AudioManager.getDevicesForAttributes returned more than one element."
+ " Using the first one.");
}
- selectedInputDeviceAttributesType = attributesOfSelectedInputDevices.get(0).getType();
+ selectedType = attributesOfSelectedInputDevices.get(0).getType();
+ selectedAddr = attributesOfSelectedInputDevices.get(0).getAddress();
}
- return selectedInputDeviceAttributesType;
+ return createInputDeviceAttributes(selectedType, selectedAddr);
}
private void dispatchInputDeviceListUpdate() {
@@ -209,13 +244,16 @@
InputMediaDevice.create(
mContext,
String.valueOf(info.getId()),
+ info.getAddress(),
info.getType(),
getMaxInputGain(),
getCurrentInputGain(),
isInputGainFixed(),
getProductNameFromAudioDeviceInfo(info));
if (mediaDevice != null) {
- if (info.getType() == mSelectedInputDeviceType) {
+ if (info.getType() == mSelectedInputDeviceType
+ && (TextUtils.isEmpty(mSelectedInputDeviceAddr)
+ || info.getAddress() == mSelectedInputDeviceAddr)) {
mInfoMediaManager.setDeviceState(mediaDevice, STATE_SELECTED);
}
mInputMediaDevices.add(mediaDevice);
@@ -255,7 +293,9 @@
return;
}
- if (inputMediaDevice.getAudioDeviceInfoType() == mSelectedInputDeviceType) {
+ if (inputMediaDevice.getAudioDeviceInfoType() == mSelectedInputDeviceType
+ && (TextUtils.isEmpty(mSelectedInputDeviceAddr)
+ || inputMediaDevice.getAddress().equals(mSelectedInputDeviceAddr))) {
Slog.w(TAG, "This device is already selected: " + device.getName());
return;
}
@@ -266,11 +306,12 @@
return;
}
- // Update mSelectedInputDeviceType directly based on user action.
+ // Update mSelectedInputDeviceType/Addr directly based on user action.
mSelectedInputDeviceType = inputMediaDevice.getAudioDeviceInfoType();
+ mSelectedInputDeviceAddr = inputMediaDevice.getAddress();
AudioDeviceAttributes deviceAttributes =
- createInputDeviceAttributes(inputMediaDevice.getAudioDeviceInfoType());
+ createInputDeviceAttributes(mSelectedInputDeviceType, mSelectedInputDeviceAddr);
try {
setPreferredDeviceForAllPresets(deviceAttributes);
} catch (IllegalArgumentException e) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
index dffbd60..094aafb 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
@@ -244,6 +244,16 @@
}
}
+ /** Requests a suggestion from other routers. */
+ public void requestDeviceSuggestion() {
+ mInfoMediaManager.requestDeviceSuggestion();
+ }
+
+ @Nullable
+ public SuggestedDeviceState getSuggestedDevice() {
+ return mInfoMediaManager.getSuggestedDevice();
+ }
+
private boolean connectToDeviceIfConnectionPending(MediaDevice device) {
synchronized (mMediaDevicesLock) {
if (mConnectingSuggestedDeviceState != null
@@ -433,6 +443,15 @@
}
/**
+ * Get the current session's release type.
+ *
+ * @return release type of the current session
+ */
+ public @RoutingSessionInfo.ReleaseType int getSessionReleaseType() {
+ return mInfoMediaManager.getSessionReleaseType();
+ }
+
+ /**
* Release session to stop playing media on MediaDevice.
*/
public boolean releaseSession() {
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/ManagerInfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/ManagerInfoMediaManager.java
index 9e511ff..36f3f953 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/ManagerInfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/ManagerInfoMediaManager.java
@@ -187,6 +187,11 @@
return mRouterManager.getTransferableRoutes(packageName);
}
+ @Override
+ public void requestDeviceSuggestion() {
+ // no-op, not supported by ManagerInfoMediaManager
+ }
+
@VisibleForTesting
/* package */ final class RouterManagerCallback implements MediaRouter2Manager.Callback {
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/NoOpInfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/NoOpInfoMediaManager.java
index 9fe5b1d..fd32b46 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/NoOpInfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/NoOpInfoMediaManager.java
@@ -175,4 +175,9 @@
protected List<MediaRoute2Info> getTransferableRoutes(@NonNull String packageName) {
return Collections.emptyList();
}
+
+ @Override
+ public void requestDeviceSuggestion() {
+ // no-op, not supported by NoOpInfoMediaManager
+ }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/RouterInfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/RouterInfoMediaManager.java
index 7728811..f9b0ee8 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/RouterInfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/RouterInfoMediaManager.java
@@ -320,6 +320,11 @@
return getTransferableRoutes(activeController);
}
+ @Override
+ public void requestDeviceSuggestion() {
+ mRouter.notifyDeviceSuggestionRequested();
+ }
+
@NonNull
private List<MediaRoute2Info> getTransferableRoutes(@Nullable RoutingController controller) {
HashMap<String, MediaRoute2Info> transferableRoutes = new HashMap<>();
diff --git a/packages/SettingsLib/src/com/android/settingslib/mobile/TelephonyIcons.java b/packages/SettingsLib/src/com/android/settingslib/mobile/TelephonyIcons.java
index 9ca4623..615c05a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/mobile/TelephonyIcons.java
+++ b/packages/SettingsLib/src/com/android/settingslib/mobile/TelephonyIcons.java
@@ -27,9 +27,6 @@
* Telephony related icons and strings for SysUI and Settings.
*/
public class TelephonyIcons {
- //***** Data connection icons
- public static final int FLIGHT_MODE_ICON = R.drawable.stat_sys_airplane_mode;
-
public static final int ICON_LTE =
flagged(R.drawable.ic_lte_mobiledata, R.drawable.ic_lte_mobiledata_updated);
public static final int ICON_LTE_PLUS =
diff --git a/packages/SettingsLib/src/com/android/settingslib/users/EditUserPhotoController.java b/packages/SettingsLib/src/com/android/settingslib/users/EditUserPhotoController.java
index f38e91a..eb2505b 100644
--- a/packages/SettingsLib/src/com/android/settingslib/users/EditUserPhotoController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/users/EditUserPhotoController.java
@@ -131,20 +131,11 @@
private void showAvatarPicker(boolean isUserNew) {
Intent intent = new Intent(AVATAR_PICKER_ACTION);
intent.addCategory(Intent.CATEGORY_DEFAULT);
- if (Flags.avatarSync()) {
- intent.putExtra(EXTRA_IS_USER_NEW, isUserNew);
- // Fix vulnerability b/341688848 by explicitly set the class name of avatar picker.
- if (Flags.fixAvatarCrossUserLeak()) {
- final String packageName =
- mActivity.getString(R.string.config_avatar_picker_package);
- final String className = mActivity.getString(R.string.config_avatar_picker_class);
- intent.setClassName(packageName, className);
- }
- } else {
- // SettingsLib is used by multiple apps therefore we need to know out of all apps
- // using settingsLib which one is the one we return value to.
- intent.setPackage(mImageView.getContext().getApplicationContext().getPackageName());
- }
+ intent.putExtra(EXTRA_IS_USER_NEW, isUserNew);
+ final String packageName =
+ mActivity.getString(R.string.config_avatar_picker_package);
+ final String className = mActivity.getString(R.string.config_avatar_picker_class);
+ intent.setClassName(packageName, className);
intent.putExtra(EXTRA_FILE_AUTHORITY, mFileAuthority);
mActivityStarter.startActivityForResult(intent, REQUEST_CODE_PICK_AVATAR);
}
diff --git a/packages/SettingsLib/tests/integ/Android.bp b/packages/SettingsLib/tests/integ/Android.bp
index 2678f98..0d1c453 100644
--- a/packages/SettingsLib/tests/integ/Android.bp
+++ b/packages/SettingsLib/tests/integ/Android.bp
@@ -48,7 +48,6 @@
test_suites: ["device-tests"],
static_libs: [
- "SettingsLibAvatarPicker",
"SettingsLibDeviceStateRotationLock",
"SettingsLibSettingsSpinner",
"SettingsLibUsageProgressBarPreference",
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateAutoRotateSettingManagerImplTest.kt b/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateAutoRotateSettingManagerImplTest.kt
index d1ceb4a..38fe325 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateAutoRotateSettingManagerImplTest.kt
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateAutoRotateSettingManagerImplTest.kt
@@ -35,6 +35,7 @@
import com.android.internal.R
import com.android.settingslib.devicestate.DeviceStateAutoRotateSettingManager.DeviceStateAutoRotateSettingListener
import com.google.common.truth.Truth.assertThat
+import org.junit.Assert.assertThrows
import org.junit.Before
import org.junit.Rule
import org.junit.Test
@@ -328,6 +329,111 @@
assertThat(expectedPairs.size).isEqualTo(defaultDeviceStateAutoRotateSetting.size())
}
+ @Test
+ fun loadAutoRotateDeviceStates_missingDeviceStateForPosture_throwsException() {
+ whenever(
+ mMockPostureDeviceStateConverter.postureToDeviceState(
+ eq(
+ DEVICE_STATE_ROTATION_KEY_UNFOLDED
+ )
+ )
+ ).thenReturn(null)
+
+ val exception = assertThrows(IllegalStateException::class.java) {
+ settingManager =
+ DeviceStateAutoRotateSettingManagerImpl(
+ mockContext,
+ executor,
+ fakeSecureSettings,
+ mockHandler,
+ mMockPostureDeviceStateConverter,
+ )
+ }
+ assertThat(exception.message).contains(
+ "No matching device state for posture: "
+ + "$DEVICE_STATE_ROTATION_KEY_UNFOLDED"
+ )
+ }
+
+ @Test
+ fun loadAutoRotateDeviceStates_missingFallbackPosture_throwsException() {
+ whenever(mockResources.getStringArray(R.array.config_perDeviceStateRotationLockDefaults))
+ .thenReturn(
+ arrayOf(
+ "$DEVICE_STATE_ROTATION_KEY_HALF_FOLDED:$DEVICE_STATE_ROTATION_LOCK_IGNORED",
+ "$DEVICE_STATE_ROTATION_KEY_UNFOLDED:$DEVICE_STATE_ROTATION_LOCK_LOCKED"
+ )
+ )
+
+ val exception = assertThrows(IllegalStateException::class.java) {
+ settingManager =
+ DeviceStateAutoRotateSettingManagerImpl(
+ mockContext,
+ executor,
+ fakeSecureSettings,
+ mockHandler,
+ mMockPostureDeviceStateConverter,
+ )
+ }
+ assertThat(exception.message).contains(
+ "Auto rotate setting is IGNORED for posture=" + DEVICE_STATE_ROTATION_KEY_HALF_FOLDED
+ + ", but no fallback-posture defined"
+ )
+ }
+
+ @Test
+ fun loadAutoRotateDeviceStates_invalidNumberOfElementsInEntry_throwsException() {
+ whenever(mockResources.getStringArray(R.array.config_perDeviceStateRotationLockDefaults))
+ .thenReturn(
+ arrayOf(
+ "$DEVICE_STATE_ROTATION_KEY_HALF_FOLDED",
+ "$DEVICE_STATE_ROTATION_KEY_UNFOLDED:$DEVICE_STATE_ROTATION_LOCK_LOCKED",
+ "$DEVICE_STATE_ROTATION_KEY_FOLDED:$DEVICE_STATE_ROTATION_LOCK_LOCKED"
+ )
+ )
+
+ val exception = assertThrows(IllegalStateException::class.java) {
+ settingManager =
+ DeviceStateAutoRotateSettingManagerImpl(
+ mockContext,
+ executor,
+ fakeSecureSettings,
+ mockHandler,
+ mMockPostureDeviceStateConverter,
+ )
+ }
+ assertThat(exception.message).contains(
+ "Invalid number of values in entry: "
+ + "$DEVICE_STATE_ROTATION_KEY_HALF_FOLDED"
+ )
+ }
+
+ @Test
+ fun loadAutoRotateDeviceStates_invalidNumberFormatInEntry_throwsException() {
+ whenever(mockResources.getStringArray(R.array.config_perDeviceStateRotationLockDefaults))
+ .thenReturn(
+ arrayOf(
+ "$DEVICE_STATE_ROTATION_KEY_HALF_FOLDED:two",
+ "$DEVICE_STATE_ROTATION_KEY_UNFOLDED:$DEVICE_STATE_ROTATION_LOCK_LOCKED",
+ "$DEVICE_STATE_ROTATION_KEY_FOLDED:$DEVICE_STATE_ROTATION_LOCK_LOCKED"
+ )
+ )
+
+ val exception = assertThrows(IllegalStateException::class.java) {
+ settingManager =
+ DeviceStateAutoRotateSettingManagerImpl(
+ mockContext,
+ executor,
+ fakeSecureSettings,
+ mockHandler,
+ mMockPostureDeviceStateConverter,
+ )
+ }
+ assertThat(exception.message).contains(
+ "Invalid number format in '$DEVICE_STATE_ROTATION_KEY_HALF_FOLDED:two'"
+ )
+ }
+
private fun persistSettings(devicePosture: Int, autoRotateSetting: Int) {
persistSettings("$devicePosture:$autoRotateSetting")
}
@@ -343,21 +449,47 @@
.thenReturn(DEVICE_STATE_ROTATION_KEY_UNFOLDED)
whenever(mMockPostureDeviceStateConverter.deviceStateToPosture(eq(DEVICE_STATE_FOLDED)))
.thenReturn(DEVICE_STATE_ROTATION_KEY_FOLDED)
- whenever(mMockPostureDeviceStateConverter.deviceStateToPosture(eq(DEVICE_STATE_HALF_FOLDED)))
- .thenReturn(DEVICE_STATE_ROTATION_KEY_HALF_FOLDED)
- whenever(mMockPostureDeviceStateConverter.deviceStateToPosture(eq(DEVICE_STATE_INVALID)))
- .thenReturn(DEVICE_STATE_ROTATION_LOCK_IGNORED)
- whenever(mMockPostureDeviceStateConverter.deviceStateToPosture(eq(DEVICE_STATE_REAR_DISPLAY)))
- .thenReturn(DEVICE_STATE_ROTATION_KEY_REAR_DISPLAY)
+ whenever(
+ mMockPostureDeviceStateConverter
+ .deviceStateToPosture(eq(DEVICE_STATE_HALF_FOLDED))
+ ).thenReturn(DEVICE_STATE_ROTATION_KEY_HALF_FOLDED)
+ whenever(
+ mMockPostureDeviceStateConverter
+ .deviceStateToPosture(eq(DEVICE_STATE_INVALID))
+ ).thenReturn(DEVICE_STATE_ROTATION_LOCK_IGNORED)
+ whenever(
+ mMockPostureDeviceStateConverter
+ .deviceStateToPosture(eq(DEVICE_STATE_REAR_DISPLAY))
+ ).thenReturn(DEVICE_STATE_ROTATION_KEY_REAR_DISPLAY)
- whenever(mMockPostureDeviceStateConverter.postureToDeviceState(eq(DEVICE_STATE_ROTATION_KEY_UNFOLDED)))
- .thenReturn(DEVICE_STATE_UNFOLDED)
- whenever(mMockPostureDeviceStateConverter.postureToDeviceState(eq(DEVICE_STATE_ROTATION_KEY_FOLDED)))
- .thenReturn(DEVICE_STATE_FOLDED)
- whenever(mMockPostureDeviceStateConverter.postureToDeviceState(eq(DEVICE_STATE_ROTATION_KEY_HALF_FOLDED)))
- .thenReturn(DEVICE_STATE_HALF_FOLDED)
- whenever(mMockPostureDeviceStateConverter.postureToDeviceState(eq(DEVICE_STATE_ROTATION_KEY_REAR_DISPLAY)))
- .thenReturn(DEVICE_STATE_REAR_DISPLAY)
+ whenever(
+ mMockPostureDeviceStateConverter.postureToDeviceState(
+ eq(
+ DEVICE_STATE_ROTATION_KEY_UNFOLDED
+ )
+ )
+ ).thenReturn(DEVICE_STATE_UNFOLDED)
+ whenever(
+ mMockPostureDeviceStateConverter.postureToDeviceState(
+ eq(
+ DEVICE_STATE_ROTATION_KEY_FOLDED
+ )
+ )
+ ).thenReturn(DEVICE_STATE_FOLDED)
+ whenever(
+ mMockPostureDeviceStateConverter.postureToDeviceState(
+ eq(
+ DEVICE_STATE_ROTATION_KEY_HALF_FOLDED
+ )
+ )
+ ).thenReturn(DEVICE_STATE_HALF_FOLDED)
+ whenever(
+ mMockPostureDeviceStateConverter.postureToDeviceState(
+ eq(
+ DEVICE_STATE_ROTATION_KEY_REAR_DISPLAY
+ )
+ )
+ ).thenReturn(DEVICE_STATE_REAR_DISPLAY)
}
private companion object {
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/OWNERS b/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/OWNERS
index 98f4123..8967e94 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/OWNERS
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/OWNERS
@@ -1,3 +1,5 @@
# Default reviewers for this and subdirectories.
alexflo@google.com
chrisgollner@google.com
+nickchameyev@google.com
+dshivangi@google.com
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/AvatarPhotoControllerTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/AvatarPhotoControllerTest.java
deleted file mode 100644
index 055487b..0000000
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/AvatarPhotoControllerTest.java
+++ /dev/null
@@ -1,283 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settingslib.avatarpicker;
-
-import static com.android.settingslib.avatarpicker.AvatarPhotoController.REQUEST_CODE_CHOOSE_PHOTO;
-import static com.android.settingslib.avatarpicker.AvatarPhotoController.REQUEST_CODE_CROP_PHOTO;
-import static com.android.settingslib.avatarpicker.AvatarPhotoController.REQUEST_CODE_TAKE_PHOTO;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.anyInt;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.timeout;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.app.Activity;
-import android.content.Context;
-import android.content.Intent;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.net.Uri;
-import android.provider.MediaStore;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-
-@RunWith(AndroidJUnit4.class)
-public class AvatarPhotoControllerTest {
-
- private static final long TIMEOUT_MILLIS = 5000;
- private static final int PHOTO_SIZE = 200;
-
- @Mock AvatarPhotoController.AvatarUi mMockAvatarUi;
-
- private File mImagesDir;
- private AvatarPhotoController mController;
- private Uri mTakePhotoUri = Uri.parse(
- "content://com.android.settingslib.test/my_cache/multi_user/TakeEditUserPhoto.jpg");
- private Uri mCropPhotoUri = Uri.parse(
- "content://com.android.settingslib.test/my_cache/multi_user/CropEditUserPhoto.jpg");
- private Context mContext = InstrumentationRegistry.getTargetContext();
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
- when(mMockAvatarUi.getPhotoSize()).thenReturn(PHOTO_SIZE);
- when(mMockAvatarUi.startSystemActivityForResult(any(), anyInt())).thenReturn(true);
-
- mImagesDir = new File(
- InstrumentationRegistry.getTargetContext().getCacheDir(), "multi_user");
- mImagesDir.mkdir();
-
- AvatarPhotoController.ContextInjector contextInjector =
- new AvatarPhotoController.ContextInjectorImpl(
- InstrumentationRegistry.getTargetContext(), "com.android.settingslib.test");
- mController = new AvatarPhotoController(mMockAvatarUi, contextInjector, false);
- }
-
- @After
- public void tearDown() {
- String[] entries = mImagesDir.list();
- for (String entry : entries) {
- new File(mImagesDir, entry).delete();
- }
- mImagesDir.delete();
- }
-
- @Test
- public void takePhotoHasCorrectIntentAndResultCode() {
- mController.takePhoto();
-
- verifyStartActivityForResult(
- MediaStore.ACTION_IMAGE_CAPTURE_SECURE, REQUEST_CODE_TAKE_PHOTO);
- }
-
- @Test
- public void choosePhotoHasCorrectIntentAndResultCode() {
- mController.choosePhoto();
-
- verifyStartActivityForResult(
- MediaStore.ACTION_PICK_IMAGES, REQUEST_CODE_CHOOSE_PHOTO);
- }
-
- @Test
- public void takePhotoIsFollowedByCrop() throws IOException {
- new File(mImagesDir, "file.txt").createNewFile();
-
- Intent intent = new Intent();
- intent.setData(Uri.parse(
- "content://com.android.settingslib.test/my_cache/multi_user/file.txt"));
- mController.onActivityResult(
- REQUEST_CODE_TAKE_PHOTO, Activity.RESULT_OK, intent);
-
- verifyStartSystemActivityForResult(
- "com.android.camera.action.CROP", REQUEST_CODE_CROP_PHOTO);
- }
-
- @Test
- public void takePhotoIsNotFollowedByCropWhenResultCodeNotOk() throws IOException {
- new File(mImagesDir, "file.txt").createNewFile();
-
- Intent intent = new Intent();
- intent.setData(Uri.parse(
- "content://com.android.settingslib.test/my_cache/multi_user/file.txt"));
- mController.onActivityResult(
- REQUEST_CODE_TAKE_PHOTO, Activity.RESULT_CANCELED, intent);
-
- verify(mMockAvatarUi, never()).startActivityForResult(any(), anyInt());
- verify(mMockAvatarUi, never()).startSystemActivityForResult(any(), anyInt());
- }
-
- @Test
- public void takePhotoIsFollowedByCropWhenTakePhotoUriReturned() throws IOException {
- new File(mImagesDir, "TakeEditUserPhoto.jpg").createNewFile();
-
- Intent intent = new Intent();
- intent.setData(mTakePhotoUri);
- mController.onActivityResult(
- REQUEST_CODE_TAKE_PHOTO, Activity.RESULT_OK, intent);
-
- verifyStartSystemActivityForResult(
- "com.android.camera.action.CROP", REQUEST_CODE_CROP_PHOTO);
- }
-
- @Test
- public void choosePhotoIsFollowedByCrop() throws IOException {
- new File(mImagesDir, "file.txt").createNewFile();
-
- Intent intent = new Intent();
- intent.setData(Uri.parse(
- "content://com.android.settingslib.test/my_cache/multi_user/file.txt"));
- mController.onActivityResult(
- REQUEST_CODE_CHOOSE_PHOTO, Activity.RESULT_OK, intent);
-
- verifyStartSystemActivityForResult(
- "com.android.camera.action.CROP", REQUEST_CODE_CROP_PHOTO);
- }
-
- @Test
- public void choosePhotoIsNotFollowedByCropWhenResultCodeNotOk() throws IOException {
- new File(mImagesDir, "file.txt").createNewFile();
-
- Intent intent = new Intent();
- intent.setData(Uri.parse(
- "content://com.android.settingslib.test/my_cache/multi_user/file.txt"));
- mController.onActivityResult(
- REQUEST_CODE_CHOOSE_PHOTO, Activity.RESULT_CANCELED, intent);
-
- verify(mMockAvatarUi, never()).startActivityForResult(any(), anyInt());
- verify(mMockAvatarUi, never()).startSystemActivityForResult(any(), anyInt());
- }
-
- @Test
- public void choosePhotoIsFollowedByCropWhenTakePhotoUriReturned() throws IOException {
- new File(mImagesDir, "TakeEditUserPhoto.jpg").createNewFile();
-
- Intent intent = new Intent();
- intent.setData(mTakePhotoUri);
- mController.onActivityResult(
- REQUEST_CODE_CHOOSE_PHOTO, Activity.RESULT_OK, intent);
-
- verifyStartSystemActivityForResult(
- "com.android.camera.action.CROP", REQUEST_CODE_CROP_PHOTO);
- }
-
- @Test
- public void cropPhotoResultIsReturnedIfResultOkAndContent() {
- Intent intent = new Intent();
- intent.setData(mCropPhotoUri);
- mController.onActivityResult(REQUEST_CODE_CROP_PHOTO, Activity.RESULT_OK, intent);
- verify(mMockAvatarUi, timeout(TIMEOUT_MILLIS)).returnUriResult(mCropPhotoUri);
- }
-
- @Test
- public void cropPhotoResultIsNotReturnedIfResultCancel() {
- Intent intent = new Intent();
- intent.setData(mCropPhotoUri);
- mController.onActivityResult(REQUEST_CODE_CROP_PHOTO, Activity.RESULT_CANCELED, intent);
- verify(mMockAvatarUi, timeout(TIMEOUT_MILLIS).times(0)).returnUriResult(mCropPhotoUri);
- }
-
- @Test
- public void cropPhotoResultIsNotReturnedIfResultNotContent() {
- Intent intent = new Intent();
- intent.setData(Uri.parse("file://test"));
- mController.onActivityResult(REQUEST_CODE_CROP_PHOTO, Activity.RESULT_OK, intent);
- verify(mMockAvatarUi, timeout(TIMEOUT_MILLIS).times(0)).returnUriResult(mCropPhotoUri);
- }
-
- @Test
- public void cropDoesNotUseTakePhotoUri() throws IOException {
- new File(mImagesDir, "file.txt").createNewFile();
-
- Intent intent = new Intent();
- intent.setData(Uri.parse(
- "content://com.android.settingslib.test/my_cache/multi_user/file.txt"));
- mController.onActivityResult(
- REQUEST_CODE_TAKE_PHOTO, Activity.RESULT_OK, intent);
-
- Intent startIntent = verifyStartSystemActivityForResult(
- "com.android.camera.action.CROP", REQUEST_CODE_CROP_PHOTO);
- assertThat(startIntent.getData()).isNotEqualTo(mTakePhotoUri);
- }
-
- @Test
- public void internalCropUsedIfNoSystemCropperFound() throws IOException {
- when(mMockAvatarUi.startSystemActivityForResult(any(), anyInt())).thenReturn(false);
-
- File file = new File(mImagesDir, "file.txt");
- saveBitmapToFile(file);
-
- Intent intent = new Intent();
- intent.setData(Uri.parse(
- "content://com.android.settingslib.test/my_cache/multi_user/file.txt"));
- mController.onActivityResult(
- REQUEST_CODE_TAKE_PHOTO, Activity.RESULT_OK, intent);
-
- verify(mMockAvatarUi, timeout(TIMEOUT_MILLIS)).returnUriResult(mCropPhotoUri);
-
- InputStream imageStream = mContext.getContentResolver().openInputStream(mCropPhotoUri);
- Bitmap bitmap = BitmapFactory.decodeStream(imageStream);
- assertThat(bitmap.getWidth()).isEqualTo(PHOTO_SIZE);
- assertThat(bitmap.getHeight()).isEqualTo(PHOTO_SIZE);
- }
-
- private Intent verifyStartActivityForResult(String action, int resultCode) {
- ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
- verify(mMockAvatarUi, timeout(TIMEOUT_MILLIS))
- .startActivityForResult(captor.capture(), eq(resultCode));
- Intent intent = captor.getValue();
- assertThat(intent.getAction()).isEqualTo(action);
- return intent;
- }
-
- private Intent verifyStartSystemActivityForResult(String action, int resultCode) {
- ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
- verify(mMockAvatarUi, timeout(TIMEOUT_MILLIS))
- .startSystemActivityForResult(captor.capture(), eq(resultCode));
- Intent intent = captor.getValue();
- assertThat(intent.getAction()).isEqualTo(action);
- return intent;
- }
-
- private void saveBitmapToFile(File file) throws IOException {
- Bitmap bitmap = Bitmap.createBitmap(500, 500, Bitmap.Config.ARGB_8888);
- OutputStream os = new FileOutputStream(file);
- bitmap.compress(Bitmap.CompressFormat.PNG, 100, os);
- os.flush();
- os.close();
- }
-
-}
diff --git a/packages/SettingsLib/tests/robotests/Android.bp b/packages/SettingsLib/tests/robotests/Android.bp
index 54fe40a..702fd16 100644
--- a/packages/SettingsLib/tests/robotests/Android.bp
+++ b/packages/SettingsLib/tests/robotests/Android.bp
@@ -48,6 +48,8 @@
static_libs: [
"Robolectric_shadows_androidx_fragment_upstream",
"SettingsLib-robo-testutils",
+ "SettingsLibGraph",
+ "SettingsLibService",
"Settings_robolectric_meta_service_file",
"androidx.core_core",
"androidx.fragment_fragment",
@@ -57,7 +59,6 @@
"kotlinx_coroutines_test",
"settingslib_illustrationpreference_flags_lib",
"settingslib_media_flags_lib",
- "settingslib_selectorwithwidgetpreference_flags_lib",
"testng", // TODO: remove once JUnit on Android provides assertThrows
],
java_resource_dirs: ["config"],
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/AmbientVolumeUiControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/AmbientVolumeUiControllerTest.java
index 8b606e2..81f4a72 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/AmbientVolumeUiControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/AmbientVolumeUiControllerTest.java
@@ -136,7 +136,7 @@
mController.loadDevice(mCachedDevice);
- verify(mAmbientLayout).setExpandable(false);
+ verify(mAmbientLayout).setControlExpandable(false);
}
@Test
@@ -145,7 +145,7 @@
mController.loadDevice(mCachedDevice);
- verify(mAmbientLayout).setExpandable(true);
+ verify(mAmbientLayout).setControlExpandable(true);
}
@Test
@@ -204,7 +204,7 @@
}
@Test
- public void onDeviceLocalDataChange_verifySetExpandedAndDataUpdated() {
+ public void onDeviceLocalDataChange_verifySetControlExpandedAndDataUpdated() {
final boolean testExpanded = true;
HearingDeviceLocalDataManager.Data data = new HearingDeviceLocalDataManager.Data.Builder()
.ambient(0).groupAmbient(0).ambientControlExpanded(testExpanded).build();
@@ -213,7 +213,7 @@
mController.onDeviceLocalDataChange(TEST_ADDRESS, data);
shadowOf(Looper.getMainLooper()).idle();
- verify(mAmbientLayout).setExpanded(testExpanded);
+ verify(mAmbientLayout).setControlExpanded(testExpanded);
verifyDeviceDataUpdated(mDevice);
}
@@ -222,7 +222,7 @@
HearingDeviceLocalDataManager.Data data = new HearingDeviceLocalDataManager.Data.Builder()
.ambient(10).groupAmbient(10).ambientControlExpanded(true).build();
when(mLocalDataManager.get(mDevice)).thenReturn(data);
- when(mAmbientLayout.isExpanded()).thenReturn(true);
+ when(mAmbientLayout.isControlExpanded()).thenReturn(true);
mController.onAmbientChanged(mDevice, 10);
verify(mController, never()).refresh();
@@ -236,7 +236,7 @@
AmbientVolumeController.RemoteAmbientState state =
new AmbientVolumeController.RemoteAmbientState(MUTE_NOT_MUTED, 0);
when(mVolumeController.refreshAmbientState(mDevice)).thenReturn(state);
- when(mAmbientLayout.isExpanded()).thenReturn(false);
+ when(mAmbientLayout.isControlExpanded()).thenReturn(false);
mController.onMuteChanged(mDevice, MUTE_NOT_MUTED);
verify(mController, never()).refresh();
@@ -249,11 +249,11 @@
public void refresh_leftAndRightDifferentGainSetting_expandControl() {
prepareRemoteData(mDevice, 10, MUTE_NOT_MUTED);
prepareRemoteData(mMemberDevice, 20, MUTE_NOT_MUTED);
- when(mAmbientLayout.isExpanded()).thenReturn(false);
+ when(mAmbientLayout.isControlExpanded()).thenReturn(false);
mController.refresh();
- verify(mAmbientLayout).setExpanded(true);
+ verify(mAmbientLayout).setControlExpanded(true);
}
@Test
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java
index 8fc4aa8..2c52161 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java
@@ -16,14 +16,18 @@
package com.android.settingslib.bluetooth;
import static com.android.settingslib.bluetooth.BluetoothUtils.getInputDevice;
+import static com.android.settingslib.bluetooth.BluetoothUtils.getSelectedChannelIndex;
import static com.android.settingslib.bluetooth.BluetoothUtils.isAvailableAudioSharingMediaBluetoothDevice;
import static com.android.settingslib.bluetooth.BluetoothUtils.isDeviceStylus;
+import static com.android.settingslib.bluetooth.BluetoothUtils.modifySelectedChannelIndex;
import static com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast.UNKNOWN_VALUE_PLACEHOLDER;
+import static com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant.UNKNOWN_CHANNEL;
import static com.android.settingslib.flags.Flags.FLAG_ENABLE_DETERMINING_ADVANCED_DETAILS_HEADER_WITH_METADATA;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
@@ -32,12 +36,23 @@
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
+
+import static java.util.Collections.emptyList;
+import static java.util.Collections.singletonList;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothCsipSetCoordinator;
import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothLeAudioCodecConfigMetadata;
+import android.bluetooth.BluetoothLeAudioContentMetadata;
+import android.bluetooth.BluetoothLeBroadcastChannel;
+import android.bluetooth.BluetoothLeBroadcastMetadata;
import android.bluetooth.BluetoothLeBroadcastReceiveState;
+import android.bluetooth.BluetoothLeBroadcastSubgroup;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothStatusCodes;
import android.content.Context;
@@ -68,6 +83,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Answers;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
@@ -1511,4 +1527,305 @@
assertThat(isDeviceStylus(null, mCachedBluetoothDevice)).isTrue();
}
+
+
+ @Test
+ public void getSelectedChannelIndex_assistantIsNull() {
+ when(mProfileManager.getLeAudioBroadcastAssistantProfile()).thenReturn(null);
+ Set<Integer> result = getSelectedChannelIndex(mProfileManager, mBluetoothDevice, 1);
+ assertEquals(UNKNOWN_CHANNEL, result);
+ }
+
+ @Test
+ public void getSelectedChannelIndex_metadataIsNull() {
+ when(mAssistant.getSourceMetadata(mBluetoothDevice, 1)).thenReturn(null);
+ Set<Integer> result = getSelectedChannelIndex(mProfileManager, mBluetoothDevice, 1);
+ assertEquals(UNKNOWN_CHANNEL, result);
+ }
+
+ @Test
+ public void getSelectedChannelIndex_subgroupsIsNull() {
+ BluetoothLeBroadcastMetadata mockMetadata = mock(BluetoothLeBroadcastMetadata.class);
+ when(mockMetadata.getSubgroups()).thenReturn(null);
+ when(mAssistant.getSourceMetadata(mBluetoothDevice, 1)).thenReturn(mockMetadata);
+ Set<Integer> result = getSelectedChannelIndex(mProfileManager, mBluetoothDevice, 1);
+ assertEquals(UNKNOWN_CHANNEL, result);
+ }
+
+ @Test
+ public void getSelectedChannelIndex_subgroupsIsEmpty() {
+ BluetoothLeBroadcastMetadata mockMetadata = mock(BluetoothLeBroadcastMetadata.class);
+ when(mockMetadata.getSubgroups()).thenReturn(emptyList());
+ when(mAssistant.getSourceMetadata(mBluetoothDevice, 1)).thenReturn(mockMetadata);
+ Set<Integer> result = getSelectedChannelIndex(mProfileManager, mBluetoothDevice, 1);
+ assertEquals(UNKNOWN_CHANNEL, result);
+ }
+
+ @Test
+ public void getSelectedChannelIndex_firstSubgroupChannelsIsEmpty() {
+ BluetoothLeBroadcastMetadata mockMetadata = mock(BluetoothLeBroadcastMetadata.class);
+ BluetoothLeBroadcastSubgroup mockSubgroup = mock(BluetoothLeBroadcastSubgroup.class);
+ when(mockSubgroup.getChannels()).thenReturn(emptyList());
+ when(mockMetadata.getSubgroups()).thenReturn(singletonList(mockSubgroup));
+ when(mAssistant.getSourceMetadata(mBluetoothDevice, 1)).thenReturn(mockMetadata);
+ Set<Integer> result = getSelectedChannelIndex(mProfileManager, mBluetoothDevice, 1);
+ assertThat(result).isEmpty();
+ }
+
+ @Test
+ public void getSelectedChannelIndex_noSelectedChannel() {
+ BluetoothLeBroadcastMetadata mockMetadata = mock(BluetoothLeBroadcastMetadata.class);
+ BluetoothLeBroadcastSubgroup mockSubgroup = mock(BluetoothLeBroadcastSubgroup.class);
+ List<BluetoothLeBroadcastChannel> channels = new ArrayList<>();
+ channels.add(createChannel(0, false));
+ channels.add(createChannel(1, false));
+ when(mockSubgroup.getChannels()).thenReturn(channels);
+ when(mockMetadata.getSubgroups()).thenReturn(singletonList(mockSubgroup));
+ when(mAssistant.getSourceMetadata(mBluetoothDevice, 1)).thenReturn(mockMetadata);
+ Set<Integer> result = getSelectedChannelIndex(mProfileManager, mBluetoothDevice, 1);
+ assertThat(result).isEmpty();
+ }
+
+ @Test
+ public void getSelectedChannelIndex_allSelectedChannelFound() {
+ BluetoothLeBroadcastMetadata mockMetadata = mock(BluetoothLeBroadcastMetadata.class);
+ BluetoothLeBroadcastSubgroup mockSubgroup = mock(BluetoothLeBroadcastSubgroup.class);
+ List<BluetoothLeBroadcastChannel> channels = new ArrayList<>();
+ channels.add(createChannel(0, false));
+ channels.add(createChannel(1, true));
+ channels.add(createChannel(2, true));
+ when(mockSubgroup.getChannels()).thenReturn(channels);
+ when(mockMetadata.getSubgroups()).thenReturn(singletonList(mockSubgroup));
+ when(mAssistant.getSourceMetadata(mBluetoothDevice, 1)).thenReturn(mockMetadata);
+ Set<Integer> result = getSelectedChannelIndex(mProfileManager, mBluetoothDevice, 1);
+ assertThat(result.size()).isEqualTo(2);
+ assertThat(result.contains(1)).isTrue();
+ assertThat(result.contains(2)).isTrue();
+ }
+
+ @Test
+ public void getSelectedChannelIndex_onlySelectedChannel() {
+ BluetoothLeBroadcastMetadata mockMetadata = mock(BluetoothLeBroadcastMetadata.class);
+ BluetoothLeBroadcastSubgroup mockSubgroup = mock(BluetoothLeBroadcastSubgroup.class);
+ List<BluetoothLeBroadcastChannel> channels = new ArrayList<>();
+ channels.add(createChannel(5, true));
+ when(mockSubgroup.getChannels()).thenReturn(channels);
+ when(mockMetadata.getSubgroups()).thenReturn(singletonList(mockSubgroup));
+ when(mAssistant.getSourceMetadata(mBluetoothDevice, 1)).thenReturn(mockMetadata);
+ Set<Integer> result = getSelectedChannelIndex(mProfileManager, mBluetoothDevice, 1);
+ assertThat(result.size()).isEqualTo(1);
+ assertThat(result.contains(5)).isTrue();
+ }
+
+ @Test
+ public void modifySelectedChannelIndex_assistantIsNull() {
+ Set<Integer> channelIndex = Set.of(2);
+ int sourceId = 1;
+ when(mProfileManager.getLeAudioBroadcastAssistantProfile()).thenReturn(null);
+ modifySelectedChannelIndex(mProfileManager, mBluetoothDevice, sourceId,
+ channelIndex, true);
+ verify(mAssistant, never()).getSourceMetadata(any(), anyInt());
+ verify(mAssistant, never()).modifySource(any(), anyInt(), any());
+ }
+
+ @Test
+ public void modifySelectedChannelIndex_metadataIsNull() {
+ Set<Integer> channelIndex = Set.of(2);
+ int sourceId = 1;
+ when(mAssistant.getSourceMetadata(mBluetoothDevice, sourceId)).thenReturn(null);
+ modifySelectedChannelIndex(mProfileManager, mBluetoothDevice, sourceId,
+ channelIndex, true);
+ verify(mAssistant, never()).modifySource(any(), anyInt(), any());
+ }
+
+ @Test
+ public void modifySelectedChannelIndex_subgroupsIsEmpty() {
+ Set<Integer> channelIndex = Set.of(2);
+ int sourceId = 1;
+ BluetoothLeBroadcastMetadata mockMetadata = mock(BluetoothLeBroadcastMetadata.class);
+ when(mockMetadata.getSubgroups()).thenReturn(Collections.emptyList());
+ when(mAssistant.getSourceMetadata(mBluetoothDevice, sourceId)).thenReturn(mockMetadata);
+ modifySelectedChannelIndex(mProfileManager, mBluetoothDevice, sourceId,
+ channelIndex, true);
+ verify(mAssistant, never()).modifySource(any(), anyInt(), any());
+ }
+
+ @Test
+ public void modifySelectedChannelIndex_channelNotFound() {
+ Set<Integer> channelIndex = Set.of(2);
+ int sourceId = 1;
+ BluetoothLeBroadcastMetadata mockMetadata = createMetadataWithChannels(
+ createChannel(0, false), createChannel(1, true));
+ when(mAssistant.getSourceMetadata(mBluetoothDevice, sourceId)).thenReturn(mockMetadata);
+ modifySelectedChannelIndex(mProfileManager, mBluetoothDevice, sourceId,
+ channelIndex, true);
+ verify(mAssistant, never()).modifySource(any(), anyInt(), any());
+ }
+
+ @Test
+ public void modifySelectedChannelIndex_noChangeNeeded_selectWhenAlreadySelected() {
+ Set<Integer> channelIndex = Set.of(2);
+ int sourceId = 1;
+ BluetoothLeBroadcastMetadata mockMetadata = createMetadataWithChannels(
+ createChannel(2, true));
+ when(mAssistant.getSourceMetadata(mBluetoothDevice, sourceId)).thenReturn(mockMetadata);
+ modifySelectedChannelIndex(mProfileManager, mBluetoothDevice, sourceId,
+ channelIndex, true);
+ verify(mAssistant, never()).modifySource(any(), anyInt(), any());
+ }
+
+ @Test
+ public void modifySelectedChannelIndex_noChangeNeeded_deselectWhenAlreadyDeselected() {
+ Set<Integer> channelIndex = Set.of(2);
+ int sourceId = 1;
+ BluetoothLeBroadcastMetadata mockMetadata = createMetadataWithChannels(
+ createChannel(2, false));
+ when(mAssistant.getSourceMetadata(mBluetoothDevice, sourceId)).thenReturn(mockMetadata);
+ modifySelectedChannelIndex(mProfileManager, mBluetoothDevice, sourceId,
+ channelIndex, false);
+ verify(mAssistant, never()).modifySource(any(), anyInt(), any());
+ }
+
+ @Test
+ public void modifySelectedChannelIndex_selectChannel() {
+ Set<Integer> channelIndex = Set.of(2);
+ int sourceId = 1;
+ BluetoothLeBroadcastMetadata mockOriginalMetadata = createMetadataWithChannels(
+ createChannel(2, false));
+ when(mAssistant.getSourceMetadata(mBluetoothDevice, sourceId)).thenReturn(
+ mockOriginalMetadata);
+
+ modifySelectedChannelIndex(mProfileManager, mBluetoothDevice, sourceId,
+ channelIndex, true);
+ ArgumentCaptor<BluetoothLeBroadcastMetadata> metadataCaptor = ArgumentCaptor.forClass(
+ BluetoothLeBroadcastMetadata.class);
+ verify(mAssistant).modifySource(eq(mBluetoothDevice), eq(sourceId),
+ metadataCaptor.capture());
+
+ BluetoothLeBroadcastMetadata updatedMetadata = metadataCaptor.getValue();
+ assertEquals(1, updatedMetadata.getSubgroups().size());
+ List<BluetoothLeBroadcastChannel> updatedChannels =
+ updatedMetadata.getSubgroups().getFirst().getChannels();
+ assertEquals(1, updatedChannels.size());
+ assertEquals(2, updatedChannels.getFirst().getChannelIndex());
+ assertTrue(updatedChannels.getFirst().isSelected());
+ }
+
+ @Test
+ public void modifySelectedChannelIndex_deselectChannel() {
+ Set<Integer> channelIndex = Set.of(2);
+ int sourceId = 1;
+ BluetoothLeBroadcastMetadata mockOriginalMetadata = createMetadataWithChannels(
+ createChannel(2, true));
+ when(mAssistant.getSourceMetadata(mBluetoothDevice, sourceId)).thenReturn(
+ mockOriginalMetadata);
+
+ modifySelectedChannelIndex(mProfileManager, mBluetoothDevice, sourceId,
+ channelIndex, false);
+
+ ArgumentCaptor<BluetoothLeBroadcastMetadata> metadataCaptor = ArgumentCaptor.forClass(
+ BluetoothLeBroadcastMetadata.class);
+ verify(mAssistant).modifySource(eq(mBluetoothDevice), eq(sourceId),
+ metadataCaptor.capture());
+
+ BluetoothLeBroadcastMetadata updatedMetadata = metadataCaptor.getValue();
+ assertEquals(1, updatedMetadata.getSubgroups().size());
+ List<BluetoothLeBroadcastChannel> updatedChannels =
+ updatedMetadata.getSubgroups().getFirst().getChannels();
+ assertEquals(1, updatedChannels.size());
+ assertEquals(2, updatedChannels.getFirst().getChannelIndex());
+ assertFalse(updatedChannels.getFirst().isSelected());
+ }
+
+ @Test
+ public void modifySelectedChannelIndex_selectChannel_multipleChannels() {
+ Set<Integer> channelIndex = Set.of(2);
+ int sourceId = 1;
+ BluetoothLeBroadcastChannel channel1 = createChannel(1, false);
+ BluetoothLeBroadcastChannel channelToSelect = createChannel(2, false);
+ BluetoothLeBroadcastChannel channel3 = createChannel(3, true);
+ BluetoothLeBroadcastMetadata mockOriginalMetadata = createMetadataWithChannels(channel1,
+ channelToSelect, channel3);
+ when(mAssistant.getSourceMetadata(mBluetoothDevice, sourceId)).thenReturn(
+ mockOriginalMetadata);
+
+ modifySelectedChannelIndex(mProfileManager, mBluetoothDevice, sourceId,
+ channelIndex, true);
+ ArgumentCaptor<BluetoothLeBroadcastMetadata> metadataCaptor = ArgumentCaptor.forClass(
+ BluetoothLeBroadcastMetadata.class);
+ verify(mAssistant).modifySource(eq(mBluetoothDevice), eq(sourceId),
+ metadataCaptor.capture());
+
+ BluetoothLeBroadcastMetadata updatedMetadata = metadataCaptor.getValue();
+ assertEquals(1, updatedMetadata.getSubgroups().size());
+ List<BluetoothLeBroadcastChannel> updatedChannels =
+ updatedMetadata.getSubgroups().getFirst().getChannels();
+ assertEquals(3, updatedChannels.size());
+ assertEquals(1, updatedChannels.get(0).getChannelIndex());
+ assertFalse(updatedChannels.get(0).isSelected());
+ assertEquals(2, updatedChannels.get(1).getChannelIndex());
+ assertTrue(updatedChannels.get(1).isSelected());
+ assertEquals(3, updatedChannels.get(2).getChannelIndex());
+ assertTrue(updatedChannels.get(2).isSelected());
+ }
+
+ @Test
+ public void modifySelectedChannelIndex_deselectChannel_multipleChannels() {
+ Set<Integer> channelIndex = Set.of(2);
+ int sourceId = 1;
+ BluetoothLeBroadcastChannel channel1 = createChannel(1, false);
+ BluetoothLeBroadcastChannel channelToDeselect = createChannel(2, true);
+ BluetoothLeBroadcastChannel channel3 = createChannel(3, true);
+ BluetoothLeBroadcastMetadata mockOriginalMetadata = createMetadataWithChannels(channel1,
+ channelToDeselect, channel3);
+ when(mAssistant.getSourceMetadata(mBluetoothDevice, 1)).thenReturn(mockOriginalMetadata);
+
+ modifySelectedChannelIndex(mProfileManager, mBluetoothDevice, sourceId,
+ channelIndex, false);
+ ArgumentCaptor<BluetoothLeBroadcastMetadata> metadataCaptor = ArgumentCaptor.forClass(
+ BluetoothLeBroadcastMetadata.class);
+ verify(mAssistant).modifySource(eq(mBluetoothDevice), eq(sourceId),
+ metadataCaptor.capture());
+
+ BluetoothLeBroadcastMetadata updatedMetadata = metadataCaptor.getValue();
+ assertEquals(1, updatedMetadata.getSubgroups().size());
+ List<BluetoothLeBroadcastChannel> updatedChannels =
+ updatedMetadata.getSubgroups().getFirst().getChannels();
+ assertEquals(3, updatedChannels.size());
+ assertEquals(1, updatedChannels.get(0).getChannelIndex());
+ assertFalse(updatedChannels.get(0).isSelected());
+ assertEquals(2, updatedChannels.get(1).getChannelIndex());
+ assertFalse(updatedChannels.get(1).isSelected());
+ assertEquals(3, updatedChannels.get(2).getChannelIndex());
+ assertTrue(updatedChannels.get(2).isSelected());
+ }
+
+ private BluetoothLeBroadcastMetadata createMetadataWithChannels(
+ BluetoothLeBroadcastChannel... channels) {
+ BluetoothLeBroadcastMetadata mockMetadata = mock(BluetoothLeBroadcastMetadata.class);
+ BluetoothLeBroadcastSubgroup mockSubgroup = mock(BluetoothLeBroadcastSubgroup.class);
+ BluetoothLeAudioContentMetadata mockContentMetadata = mock(
+ BluetoothLeAudioContentMetadata.class);
+ BluetoothLeAudioCodecConfigMetadata mockConfigMetadata = mock(
+ BluetoothLeAudioCodecConfigMetadata.class);
+ List<BluetoothLeBroadcastChannel> channelList = new ArrayList<>();
+ Collections.addAll(channelList, channels);
+ when(mockSubgroup.getChannels()).thenReturn(channelList);
+ when(mockMetadata.getSubgroups()).thenReturn(Collections.singletonList(mockSubgroup));
+ when(mockMetadata.getSourceDevice()).thenReturn(mBluetoothDevice);
+ when(mockSubgroup.getContentMetadata()).thenReturn(mockContentMetadata);
+ when(mockSubgroup.getCodecSpecificConfig()).thenReturn(mockConfigMetadata);
+
+ return mockMetadata;
+ }
+
+ private BluetoothLeBroadcastChannel createChannel(int index, boolean selected) {
+ BluetoothLeBroadcastChannel mockChannel = mock(BluetoothLeBroadcastChannel.class);
+ BluetoothLeAudioCodecConfigMetadata mockCodec = mock(
+ BluetoothLeAudioCodecConfigMetadata.class);
+ when(mockChannel.getChannelIndex()).thenReturn(index);
+ when(mockChannel.isSelected()).thenReturn(selected);
+ when(mockChannel.getCodecMetadata()).thenReturn(mockCodec);
+ return mockChannel;
+ }
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/PrivateBroadcastReceiveDataTest.kt b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/PrivateBroadcastReceiveDataTest.kt
index 5fd67a1..cefac5d 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/PrivateBroadcastReceiveDataTest.kt
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/PrivateBroadcastReceiveDataTest.kt
@@ -20,7 +20,9 @@
import android.bluetooth.BluetoothDevice
import android.os.Parcel
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant.LocalBluetoothLeBroadcastSourceState
+import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant.UNKNOWN_CHANNEL
import com.android.settingslib.bluetooth.PrivateBroadcastReceiveData.Companion.isValid
+import java.util.HashSet
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
@@ -38,7 +40,8 @@
sourceId = 1,
broadcastId = 2,
programInfo = "Test Program",
- state = LocalBluetoothLeBroadcastSourceState.STREAMING
+ state = LocalBluetoothLeBroadcastSourceState.STREAMING,
+ selectedChannelIndex = UNKNOWN_CHANNEL
)
val parcel = Parcel.obtain()
@@ -56,7 +59,8 @@
sink = sink,
sourceId = 1,
broadcastId = 2,
- state = LocalBluetoothLeBroadcastSourceState.STREAMING
+ state = LocalBluetoothLeBroadcastSourceState.STREAMING,
+ selectedChannelIndex = HashSet.newHashSet(1)
)
assertTrue(data.isValid())
}
@@ -67,7 +71,8 @@
sink = null,
sourceId = 1,
broadcastId = 2,
- state = LocalBluetoothLeBroadcastSourceState.STREAMING
+ state = LocalBluetoothLeBroadcastSourceState.STREAMING,
+ selectedChannelIndex = HashSet.newHashSet(1)
)
assertFalse(data.isValid())
}
@@ -78,7 +83,8 @@
sink = sink,
sourceId = -1,
broadcastId = 2,
- state = LocalBluetoothLeBroadcastSourceState.STREAMING
+ state = LocalBluetoothLeBroadcastSourceState.STREAMING,
+ selectedChannelIndex = HashSet.newHashSet(1)
)
assertFalse(data.isValid())
}
@@ -89,7 +95,8 @@
sink = sink,
sourceId = 1,
broadcastId = -1,
- state = LocalBluetoothLeBroadcastSourceState.STREAMING
+ state = LocalBluetoothLeBroadcastSourceState.STREAMING,
+ selectedChannelIndex = HashSet.newHashSet(1)
)
assertFalse(data.isValid())
}
@@ -100,7 +107,8 @@
sink = sink,
sourceId = 1,
broadcastId = 2,
- state = null
+ state = null,
+ selectedChannelIndex = HashSet.newHashSet(1)
)
assertFalse(data.isValid())
}
@@ -110,6 +118,7 @@
assertTrue(PrivateBroadcastReceiveData(sink, 1, 1, state = LocalBluetoothLeBroadcastSourceState.STREAMING).isValid())
assertTrue(PrivateBroadcastReceiveData(sink, 1, 1, state = LocalBluetoothLeBroadcastSourceState.PAUSED).isValid())
assertTrue(PrivateBroadcastReceiveData(sink, 1, 1, state = LocalBluetoothLeBroadcastSourceState.DECRYPTION_FAILED).isValid())
+ assertTrue(PrivateBroadcastReceiveData(sink, 1, 1, state = LocalBluetoothLeBroadcastSourceState.PAUSED_BY_RECEIVER).isValid())
}
private companion object {
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingRepositoryTest.kt b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingRepositoryTest.kt
index 3d4e449..4616533 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingRepositoryTest.kt
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingRepositoryTest.kt
@@ -16,7 +16,6 @@
package com.android.settingslib.bluetooth.devicesettings.data.repository
-import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothDevice
import android.content.ComponentName
import android.content.Context
@@ -78,7 +77,6 @@
@Mock private lateinit var cachedDevice: CachedBluetoothDevice
@Mock private lateinit var bluetoothDevice: BluetoothDevice
@Mock private lateinit var context: Context
- @Mock private lateinit var bluetoothAdapter: BluetoothAdapter
@Mock private lateinit var configService: IDeviceSettingsConfigProviderService.Stub
@Mock private lateinit var settingProviderService1: IDeviceSettingsProviderService.Stub
@Mock private lateinit var settingProviderService2: IDeviceSettingsProviderService.Stub
@@ -135,7 +133,6 @@
underTest =
DeviceSettingRepositoryImpl(
context,
- bluetoothAdapter,
testScope.backgroundScope,
testScope.testScheduler,
)
@@ -145,10 +142,8 @@
fun getDeviceSettingsConfig_withMetadata_success() {
testScope.runTest {
setUpConfigService(true, DEVICE_SETTING_CONFIG)
- `when`(settingProviderService1.serviceStatus)
- .thenReturn(DeviceSettingsProviderServiceStatus(true))
- `when`(settingProviderService2.serviceStatus)
- .thenReturn(DeviceSettingsProviderServiceStatus(true))
+ setUpProviderService(settingProviderService1, true, listOf())
+ setUpProviderService(settingProviderService2, true, listOf())
val config = underTest.getDeviceSettingsConfig(cachedDevice)
@@ -170,10 +165,8 @@
fun getDeviceSettingsConfig_expandable_success() {
testScope.runTest {
setUpConfigService(true, DEVICE_SETTING_CONFIG_EXPANDABLE)
- `when`(settingProviderService1.serviceStatus)
- .thenReturn(DeviceSettingsProviderServiceStatus(true))
- `when`(settingProviderService2.serviceStatus)
- .thenReturn(DeviceSettingsProviderServiceStatus(true))
+ setUpProviderService(settingProviderService1, true, listOf())
+ setUpProviderService(settingProviderService2, true, listOf())
val config = underTest.getDeviceSettingsConfig(cachedDevice)!!
@@ -196,10 +189,8 @@
)
.thenReturn("".toByteArray())
setUpConfigService(true, DEVICE_SETTING_CONFIG)
- `when`(settingProviderService1.serviceStatus)
- .thenReturn(DeviceSettingsProviderServiceStatus(true))
- `when`(settingProviderService2.serviceStatus)
- .thenReturn(DeviceSettingsProviderServiceStatus(true))
+ setUpProviderService(settingProviderService1, true, listOf())
+ setUpProviderService(settingProviderService2, true, listOf())
val config = underTest.getDeviceSettingsConfig(cachedDevice)
@@ -211,10 +202,8 @@
fun getDeviceSettingsConfig_providerServiceNotEnabled_returnNull() {
testScope.runTest {
setUpConfigService(true, DEVICE_SETTING_CONFIG)
- `when`(settingProviderService1.serviceStatus)
- .thenReturn(DeviceSettingsProviderServiceStatus(false))
- `when`(settingProviderService2.serviceStatus)
- .thenReturn(DeviceSettingsProviderServiceStatus(true))
+ setUpProviderService(settingProviderService1, true, listOf())
+ setUpProviderService(settingProviderService2, false, listOf())
val config = underTest.getDeviceSettingsConfig(cachedDevice)
@@ -238,16 +227,8 @@
fun getDeviceSetting_actionSwitchPreference_success() {
testScope.runTest {
setUpConfigService(true, DEVICE_SETTING_CONFIG)
- `when`(settingProviderService1.registerDeviceSettingsListener(any(), any())).then {
- input ->
- input
- .getArgument<IDeviceSettingsListener>(1)
- .onDeviceSettingsChanged(listOf(DEVICE_SETTING_1))
- }
- `when`(settingProviderService1.serviceStatus)
- .thenReturn(DeviceSettingsProviderServiceStatus(true))
- `when`(settingProviderService2.serviceStatus)
- .thenReturn(DeviceSettingsProviderServiceStatus(true))
+ setUpProviderService(settingProviderService1, true, listOf(DEVICE_SETTING_1))
+ setUpProviderService(settingProviderService2, true, listOf())
var setting: DeviceSettingModel? = null
underTest
@@ -264,16 +245,8 @@
fun getDeviceSetting_multiTogglePreference_success() {
testScope.runTest {
setUpConfigService(true, DEVICE_SETTING_CONFIG)
- `when`(settingProviderService2.registerDeviceSettingsListener(any(), any())).then {
- input ->
- input
- .getArgument<IDeviceSettingsListener>(1)
- .onDeviceSettingsChanged(listOf(DEVICE_SETTING_2))
- }
- `when`(settingProviderService1.serviceStatus)
- .thenReturn(DeviceSettingsProviderServiceStatus(true))
- `when`(settingProviderService2.serviceStatus)
- .thenReturn(DeviceSettingsProviderServiceStatus(true))
+ setUpProviderService(settingProviderService1, true, listOf())
+ setUpProviderService(settingProviderService2, true, listOf(DEVICE_SETTING_2))
var setting: DeviceSettingModel? = null
underTest
@@ -290,16 +263,8 @@
fun getDeviceSetting_helpPreference_success() {
testScope.runTest {
setUpConfigService(true, DEVICE_SETTING_CONFIG)
- `when`(settingProviderService2.registerDeviceSettingsListener(any(), any())).then {
- input ->
- input
- .getArgument<IDeviceSettingsListener>(1)
- .onDeviceSettingsChanged(listOf(DEVICE_SETTING_HELP))
- }
- `when`(settingProviderService1.serviceStatus)
- .thenReturn(DeviceSettingsProviderServiceStatus(true))
- `when`(settingProviderService2.serviceStatus)
- .thenReturn(DeviceSettingsProviderServiceStatus(true))
+ setUpProviderService(settingProviderService1, true, listOf())
+ setUpProviderService(settingProviderService2, true, listOf(DEVICE_SETTING_HELP))
var setting: DeviceSettingModel? = null
underTest
@@ -338,16 +303,8 @@
fun updateDeviceSettingState_switchState_success() {
testScope.runTest {
setUpConfigService(true, DEVICE_SETTING_CONFIG)
- `when`(settingProviderService1.registerDeviceSettingsListener(any(), any())).then {
- input ->
- input
- .getArgument<IDeviceSettingsListener>(1)
- .onDeviceSettingsChanged(listOf(DEVICE_SETTING_1))
- }
- `when`(settingProviderService1.serviceStatus)
- .thenReturn(DeviceSettingsProviderServiceStatus(true))
- `when`(settingProviderService2.serviceStatus)
- .thenReturn(DeviceSettingsProviderServiceStatus(true))
+ setUpProviderService(settingProviderService1, true, listOf(DEVICE_SETTING_1))
+ setUpProviderService(settingProviderService2, true, listOf())
var setting: DeviceSettingModel? = null
underTest
@@ -376,16 +333,8 @@
fun updateDeviceSettingState_multiToggleState_success() {
testScope.runTest {
setUpConfigService(true, DEVICE_SETTING_CONFIG)
- `when`(settingProviderService2.registerDeviceSettingsListener(any(), any())).then {
- input ->
- input
- .getArgument<IDeviceSettingsListener>(1)
- .onDeviceSettingsChanged(listOf(DEVICE_SETTING_2))
- }
- `when`(settingProviderService1.serviceStatus)
- .thenReturn(DeviceSettingsProviderServiceStatus(true))
- `when`(settingProviderService2.serviceStatus)
- .thenReturn(DeviceSettingsProviderServiceStatus(true))
+ setUpProviderService(settingProviderService1, true, listOf())
+ setUpProviderService(settingProviderService2, true, listOf(DEVICE_SETTING_2))
var setting: DeviceSettingModel? = null
underTest
@@ -487,6 +436,17 @@
}
}
+ private fun setUpProviderService(mockService: IDeviceSettingsProviderService.Stub, enabled: Boolean, settings: List<DeviceSetting>) {
+ `when`(mockService.registerDeviceSettingsListener(any(), any())).then {
+ input ->
+ input
+ .getArgument<IDeviceSettingsListener>(1)
+ .onDeviceSettingsChanged(settings)
+ }
+ `when`(mockService.serviceStatus)
+ .thenReturn(DeviceSettingsProviderServiceStatus(enabled))
+ }
+
private companion object {
const val BLUETOOTH_ADDRESS = "12:34:56:78"
const val CONFIG_SERVICE_PACKAGE_NAME = "com.android.fake.configservice"
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
index fd03d7d..b94fcbe 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
@@ -762,6 +762,19 @@
}
@Test
+ public void getSessionReleaseType_returnCorrectType() {
+ final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();
+ final RoutingSessionInfo info = mock(RoutingSessionInfo.class);
+ routingSessionInfos.add(info);
+
+ mShadowRouter2Manager.setRoutingSessions(routingSessionInfos);
+ when(info.getReleaseType()).thenReturn(RoutingSessionInfo.RELEASE_TYPE_SHARING);
+
+ assertThat(mInfoMediaManager.getSessionReleaseType())
+ .isEqualTo(RoutingSessionInfo.RELEASE_TYPE_SHARING);
+ }
+
+ @Test
public void releaseSession_removeSuccessfully_returnTrue() {
final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();
final RoutingSessionInfo info = mock(RoutingSessionInfo.class);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InputMediaDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InputMediaDeviceTest.java
index 8624c4d..b142daa 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InputMediaDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InputMediaDeviceTest.java
@@ -47,6 +47,11 @@
private static final String PRODUCT_NAME_USB_HEADSET = "My USB Headset";
private static final String PRODUCT_NAME_BT_HEADSET = "My Bluetooth Headset";
private static final String PRODUCT_NAME_BLE_HEADSET = "My BLE Headset";
+ private static final String ADDR_BUILTIN_MIC = "";
+ private static final String ADDR_WIRED_HEADSET = "";
+ private static final String ADDR_USB_HEADSET = "card=1;device=0";
+ private static final String ADDR_BT_HEADSET = "00:11:22:33:44:55";
+ private static final String ADDR_BLE_HEADSET = "11:11:22:33:44:55";
@Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@@ -63,6 +68,7 @@
InputMediaDevice.create(
mContext,
String.valueOf(BUILTIN_MIC_ID),
+ ADDR_BUILTIN_MIC,
AudioDeviceInfo.TYPE_BUILTIN_MIC,
MAX_VOLUME,
CURRENT_VOLUME,
@@ -78,6 +84,7 @@
InputMediaDevice.create(
mContext,
String.valueOf(BUILTIN_MIC_ID),
+ ADDR_BUILTIN_MIC,
AudioDeviceInfo.TYPE_BUILTIN_MIC,
MAX_VOLUME,
CURRENT_VOLUME,
@@ -94,6 +101,7 @@
InputMediaDevice.create(
mContext,
String.valueOf(WIRED_HEADSET_ID),
+ ADDR_WIRED_HEADSET,
AudioDeviceInfo.TYPE_WIRED_HEADSET,
MAX_VOLUME,
CURRENT_VOLUME,
@@ -110,6 +118,7 @@
InputMediaDevice.create(
mContext,
String.valueOf(USB_HEADSET_ID),
+ ADDR_USB_HEADSET,
AudioDeviceInfo.TYPE_USB_HEADSET,
MAX_VOLUME,
CURRENT_VOLUME,
@@ -125,6 +134,7 @@
InputMediaDevice.create(
mContext,
String.valueOf(USB_HEADSET_ID),
+ ADDR_USB_HEADSET,
AudioDeviceInfo.TYPE_USB_HEADSET,
MAX_VOLUME,
CURRENT_VOLUME,
@@ -141,6 +151,7 @@
InputMediaDevice.create(
mContext,
String.valueOf(BT_HEADSET_ID),
+ ADDR_BT_HEADSET,
AudioDeviceInfo.TYPE_BLUETOOTH_SCO,
MAX_VOLUME,
CURRENT_VOLUME,
@@ -156,6 +167,7 @@
InputMediaDevice.create(
mContext,
String.valueOf(BT_HEADSET_ID),
+ ADDR_BT_HEADSET,
AudioDeviceInfo.TYPE_BLUETOOTH_SCO,
MAX_VOLUME,
CURRENT_VOLUME,
@@ -172,6 +184,7 @@
InputMediaDevice.create(
mContext,
String.valueOf(BLE_HEADSET_ID),
+ ADDR_BLE_HEADSET,
AudioDeviceInfo.TYPE_BLE_HEADSET,
MAX_VOLUME,
CURRENT_VOLUME,
@@ -187,6 +200,7 @@
InputMediaDevice.create(
mContext,
String.valueOf(BLE_HEADSET_ID),
+ ADDR_BLE_HEADSET,
AudioDeviceInfo.TYPE_BLE_HEADSET,
MAX_VOLUME,
CURRENT_VOLUME,
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InputRouteManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InputRouteManagerTest.java
index 88736de..c2023bf 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InputRouteManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InputRouteManagerTest.java
@@ -280,6 +280,7 @@
InputMediaDevice.create(
mContext,
String.valueOf(BUILTIN_MIC_ID),
+ "",
AudioDeviceInfo.TYPE_BUILTIN_MIC,
MAX_VOLUME,
CURRENT_VOLUME,
@@ -428,6 +429,7 @@
return InputMediaDevice.create(
mContext,
String.valueOf(info.getId()),
+ info.getAddress(),
info.getType(),
MAX_VOLUME,
CURRENT_VOLUME,
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
index 585b40d..f820844 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
@@ -657,4 +657,12 @@
verify(mInfoMediaManager).connectToDevice(mInfoMediaDevice1);
verify(mInfoMediaManager).startScan();
}
+
+ @Test
+ public void getSessionReleaseType_returnCorrectType() {
+ when(mInfoMediaManager.getSessionReleaseType())
+ .thenReturn(RoutingSessionInfo.RELEASE_TYPE_SHARING);
+ assertThat(mLocalMediaManager.getSessionReleaseType())
+ .isEqualTo(RoutingSessionInfo.RELEASE_TYPE_SHARING);
+ }
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/service/PreferenceServiceRequestTransformerTest.kt b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/service/PreferenceServiceRequestTransformerTest.kt
new file mode 100644
index 0000000..bad66e5
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/service/PreferenceServiceRequestTransformerTest.kt
@@ -0,0 +1,376 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.service
+
+import androidx.fragment.app.FragmentActivity
+import android.content.Context
+import android.content.Intent
+import android.platform.test.annotations.RequiresFlagsEnabled
+import android.platform.test.flag.junit.CheckFlagsRule
+import android.platform.test.flag.junit.DeviceFlagsValueProvider
+import android.service.settings.preferences.GetValueRequest
+import android.service.settings.preferences.GetValueResult
+import android.service.settings.preferences.MetadataResult
+import android.service.settings.preferences.SetValueRequest
+import android.service.settings.preferences.SetValueResult
+import android.service.settings.preferences.SettingsPreferenceMetadata
+import android.service.settings.preferences.SettingsPreferenceValue
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settingslib.datastore.Permissions
+import com.android.settingslib.flags.Flags.FLAG_SETTINGS_CATALYST
+import com.android.settingslib.graph.PreferenceGetterErrorCode
+import com.android.settingslib.graph.PreferenceGetterFlags
+import com.android.settingslib.graph.PreferenceGetterResponse
+import com.android.settingslib.graph.PreferenceSetterResult
+import com.android.settingslib.graph.preferenceGroupProto
+import com.android.settingslib.graph.preferenceOrGroupProto
+import com.android.settingslib.graph.preferenceProto
+import com.android.settingslib.graph.preferenceScreenProto
+import com.android.settingslib.graph.preferenceValueDescriptorProto
+import com.android.settingslib.graph.preferenceValueProto
+import com.android.settingslib.graph.proto.PreferenceGraphProto
+import com.android.settingslib.graph.rangeValueProto
+import com.android.settingslib.graph.textProto
+import com.android.settingslib.graph.toProto
+import com.android.settingslib.metadata.PreferenceCoordinate
+import com.android.settingslib.metadata.SensitivityLevel
+import com.google.common.truth.Truth.assertThat
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+@RunWith(AndroidJUnit4::class)
+@RequiresFlagsEnabled(FLAG_SETTINGS_CATALYST)
+class PreferenceServiceRequestTransformerTest {
+
+ @get:Rule val checkFlagsRule: CheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()
+
+ private val context: Context = ApplicationProvider.getApplicationContext()
+
+ @Test
+ fun transformCatalystGetMetadataResponse_emptyGraph_returnsFrameworkResponseWithError() {
+ val graphProto = PreferenceGraphProto.newBuilder().build()
+ val fResult = transformCatalystGetMetadataResponse(context, graphProto)
+ with(fResult) {
+ assertThat(resultCode).isEqualTo(MetadataResult.RESULT_UNSUPPORTED)
+ assertThat(metadataList).isEmpty()
+ }
+ }
+
+ @Test
+ fun transformCatalystGetMetadataResponse_populatedGraph_returnsFrameworkResponseWithSuccess() {
+ val screen = preferenceScreenProto {
+ root = preferenceGroupProto {
+ addAllPreferences(
+ listOf(
+ preferenceOrGroupProto {
+ group = preferenceGroupProto {
+ addPreferences(
+ preferenceOrGroupProto {
+ preference = preferenceProto {
+ key = "key1"
+ title = textProto { string = "title1" }
+ enabled = true
+ }
+ }
+ )
+ }
+ },
+ preferenceOrGroupProto {
+ preference = preferenceProto {
+ key = "key2"
+ title = textProto { string = "title2" }
+ enabled = false
+ }
+ },
+ )
+ )
+ }
+ }
+ val graphProto = PreferenceGraphProto.newBuilder().putScreens("screen", screen).build()
+
+ val fResult = transformCatalystGetMetadataResponse(context, graphProto)
+ with(fResult) {
+ assertThat(resultCode).isEqualTo(MetadataResult.RESULT_OK)
+ assertThat(metadataList.size).isEqualTo(2)
+ }
+ assertThat(
+ fResult.metadataList.any {
+ it.key == "key1" &&
+ it.screenKey == "screen" &&
+ it.title == "title1" &&
+ it.isEnabled
+ }
+ )
+ .isTrue()
+ assertThat(
+ fResult.metadataList.any {
+ it.key == "key2" &&
+ it.screenKey == "screen" &&
+ it.title == "title2" &&
+ !it.isEnabled
+ }
+ )
+ .isTrue()
+ }
+
+ @Test
+ fun transformFrameworkGetValueRequest_returnsValidCatalystRequest() {
+ val fRequest = GetValueRequest.Builder("screen", "pref").build()
+ val cRequest = transformFrameworkGetValueRequest(fRequest)
+ with(cRequest) {
+ assertThat(preferences).hasLength(1)
+ assertThat(preferences.first().screenKey).isEqualTo(fRequest.screenKey)
+ assertThat(preferences.first().key).isEqualTo(fRequest.preferenceKey)
+ assertThat(flags).isEqualTo(PreferenceGetterFlags.ALL)
+ }
+ }
+
+ @Test
+ fun transformCatalystGetValueResponse_success_returnsValidFrameworkResponse() {
+ val fRequest = GetValueRequest.Builder("screen", "key").build()
+ val cResult =
+ PreferenceGetterResponse(
+ emptyMap(),
+ mapOf(
+ PreferenceCoordinate(fRequest.screenKey, fRequest.preferenceKey) to
+ preferenceProto {
+ key = "key"
+ title = textProto { string = "title" }
+ summary = textProto { string = "summary" }
+ enabled = true
+ available = true
+ restricted = true
+ persistent = true
+ valueDescriptor = preferenceValueDescriptorProto {
+ rangeValue = rangeValueProto {
+ min = 0
+ max = 10
+ step = 2
+ }
+ }
+ sensitivityLevel = SensitivityLevel.LOW_SENSITIVITY
+ readPermissions = Permissions.allOf("read_permission").toProto()
+ writePermissions = Permissions.anyOf("write_permission").toProto()
+ val intent = Intent(context, FragmentActivity::class.java)
+ launchIntent = intent.toProto()
+ value = preferenceValueProto { booleanValue = true }
+ }
+ ),
+ )
+ val fResult = transformCatalystGetValueResponse(context, fRequest, cResult)
+ assertThat(fResult!!.resultCode).isEqualTo(GetValueResult.RESULT_OK)
+ with(fResult.metadata!!) {
+ assertThat(title).isEqualTo("title")
+ assertThat(summary).isEqualTo("summary")
+ assertThat(isEnabled).isTrue()
+ assertThat(isAvailable).isTrue()
+ assertThat(isRestricted).isTrue()
+ assertThat(isWritable).isTrue()
+ assertThat(writeSensitivity)
+ .isEqualTo(SettingsPreferenceMetadata.EXPECT_POST_CONFIRMATION)
+ assertThat(readPermissions).containsExactly("read_permission")
+ assertThat(writePermissions).containsExactly("write_permission")
+ assertThat(launchIntent).isNotNull()
+ assertThat(launchIntent!!.component!!.className)
+ .isEqualTo(FragmentActivity::class.java.name)
+ val intRange = extras.getBundle("key_int_range")
+ assertThat(intRange).isNotNull()
+ assertThat(intRange!!.getInt("key_min", -1)).isEqualTo(0)
+ assertThat(intRange.getInt("key_max", -1)).isEqualTo(10)
+ assertThat(intRange.getInt("key_step", -1)).isEqualTo(2)
+ }
+ with(fResult.value!!) {
+ assertThat(type).isEqualTo(SettingsPreferenceValue.TYPE_BOOLEAN)
+ assertThat(booleanValue).isTrue()
+ }
+ }
+
+ @Test
+ fun transformCatalystGetValueResponse_sensitivityLevel() {
+ verifySensitivityLevelMapping(
+ SensitivityLevel.NO_SENSITIVITY, SettingsPreferenceMetadata.NO_SENSITIVITY
+ )
+ verifySensitivityLevelMapping(
+ SensitivityLevel.LOW_SENSITIVITY, SettingsPreferenceMetadata.EXPECT_POST_CONFIRMATION
+ )
+ verifySensitivityLevelMapping(
+ SensitivityLevel.MEDIUM_SENSITIVITY, SettingsPreferenceMetadata.DEEPLINK_ONLY
+ )
+ verifySensitivityLevelMapping(
+ SensitivityLevel.HIGH_SENSITIVITY, SettingsPreferenceMetadata.DEEPLINK_ONLY
+ )
+ verifySensitivityLevelMapping(
+ SensitivityLevel.UNKNOWN_SENSITIVITY, SettingsPreferenceMetadata.NO_DIRECT_ACCESS
+ )
+ }
+
+ private fun verifySensitivityLevelMapping(level: Int, expected: Int) {
+ val request = GetValueRequest.Builder("screen", "key").build()
+ val response = PreferenceGetterResponse(
+ emptyMap(),
+ mapOf(PreferenceCoordinate(
+ request.screenKey, request.preferenceKey
+ ) to preferenceProto {
+ key = "key"
+ sensitivityLevel = level
+ }),
+ )
+ val metadata = transformCatalystGetValueResponse(context, request, response)?.metadata!!
+ assertThat(metadata.writeSensitivity).isEqualTo(expected)
+ }
+
+ @Test
+ fun transformCatalystGetValueResponse_success_noValue() {
+ val fRequest = GetValueRequest.Builder("screen", "key").build()
+ val cResult = PreferenceGetterResponse(
+ emptyMap(),
+ mapOf(PreferenceCoordinate(
+ fRequest.screenKey,
+ fRequest.preferenceKey
+ ) to preferenceProto { key = "key" }),
+ )
+ val fResult = transformCatalystGetValueResponse(context, fRequest, cResult)!!
+ assertThat(fResult.resultCode).isEqualTo(GetValueResult.RESULT_UNSUPPORTED)
+ assertThat(fResult.metadata).isNotNull()
+ }
+
+ @Test
+ fun transformCatalystGetValueResponse_failure_returnsValidFrameworkResponse() {
+ val fRequest = GetValueRequest.Builder("screen", "key").build()
+ val cResult =
+ PreferenceGetterResponse(
+ mapOf(
+ PreferenceCoordinate(fRequest.screenKey, fRequest.preferenceKey) to
+ PreferenceGetterErrorCode.NOT_FOUND
+ ),
+ emptyMap(),
+ )
+ val fResult = transformCatalystGetValueResponse(context, fRequest, cResult)
+ with(fResult!!) {
+ assertThat(resultCode).isEqualTo(GetValueResult.RESULT_UNSUPPORTED)
+ assertThat(metadata).isNull()
+ assertThat(value).isNull()
+ }
+ }
+
+ @Test
+ fun transformCatalystGetValueResponse_invalidResponse_returnsNull() {
+ val fRequest = GetValueRequest.Builder("screen", "key").build()
+ val cResult = PreferenceGetterResponse(emptyMap(), emptyMap())
+ val fResult = transformCatalystGetValueResponse(context, fRequest, cResult)
+ assertThat(fResult).isNull()
+ }
+
+ @Test
+ fun transformFrameworkSetValueRequest_typeBoolean_returnsValidCatalystRequest() {
+ val fRequest =
+ SetValueRequest.Builder(
+ "screen",
+ "pref",
+ SettingsPreferenceValue.Builder(SettingsPreferenceValue.TYPE_BOOLEAN)
+ .setBooleanValue(true)
+ .build(),
+ )
+ .build()
+ val cRequest = transformFrameworkSetValueRequest(fRequest)
+ with(cRequest!!) {
+ assertThat(screenKey).isEqualTo(fRequest.screenKey)
+ assertThat(key).isEqualTo(fRequest.preferenceKey)
+ assertThat(value.hasBooleanValue()).isTrue()
+ assertThat(value.booleanValue).isTrue()
+ }
+ }
+
+ @Test
+ fun transformFrameworkSetValueRequest_typeInt_returnsValidCatalystRequest() {
+ val fRequest =
+ SetValueRequest.Builder(
+ "screen",
+ "pref",
+ SettingsPreferenceValue.Builder(SettingsPreferenceValue.TYPE_INT)
+ .setIntValue(5)
+ .build(),
+ )
+ .build()
+ val cRequest = transformFrameworkSetValueRequest(fRequest)
+ with(cRequest!!) {
+ assertThat(screenKey).isEqualTo(fRequest.screenKey)
+ assertThat(key).isEqualTo(fRequest.preferenceKey)
+ assertThat(value.hasIntValue()).isTrue()
+ assertThat(value.intValue).isEqualTo(5)
+ }
+ }
+
+ @Test
+ fun transformFrameworkSetValueRequest_typeString_returnsNull() {
+ val fRequest =
+ SetValueRequest.Builder(
+ "screen",
+ "pref",
+ SettingsPreferenceValue.Builder(SettingsPreferenceValue.TYPE_STRING)
+ .setStringValue("value")
+ .build(),
+ )
+ .build()
+ val cRequest = transformFrameworkSetValueRequest(fRequest)
+ assertThat(cRequest).isNull()
+ }
+
+ @Test
+ fun transformCatalystSetValueResponse_returnsValidFrameworkResponse() {
+ assertThat(transformCatalystSetValueResponse(PreferenceSetterResult.OK).resultCode)
+ .isEqualTo(SetValueResult.RESULT_OK)
+
+ assertThat(transformCatalystSetValueResponse(PreferenceSetterResult.UNAVAILABLE).resultCode)
+ .isEqualTo(SetValueResult.RESULT_UNAVAILABLE)
+
+ assertThat(transformCatalystSetValueResponse(PreferenceSetterResult.DISABLED).resultCode)
+ .isEqualTo(SetValueResult.RESULT_DISABLED)
+
+ assertThat(transformCatalystSetValueResponse(PreferenceSetterResult.UNSUPPORTED).resultCode)
+ .isEqualTo(SetValueResult.RESULT_UNSUPPORTED)
+
+ assertThat(transformCatalystSetValueResponse(PreferenceSetterResult.DISALLOW).resultCode)
+ .isEqualTo(SetValueResult.RESULT_DISALLOW)
+
+ assertThat(
+ transformCatalystSetValueResponse(PreferenceSetterResult.REQUIRE_APP_PERMISSION)
+ .resultCode
+ )
+ .isEqualTo(SetValueResult.RESULT_REQUIRE_APP_PERMISSION)
+
+ assertThat(
+ transformCatalystSetValueResponse(PreferenceSetterResult.REQUIRE_USER_AGREEMENT)
+ .resultCode
+ )
+ .isEqualTo(SetValueResult.RESULT_REQUIRE_USER_CONSENT)
+
+ assertThat(transformCatalystSetValueResponse(PreferenceSetterResult.RESTRICTED).resultCode)
+ .isEqualTo(SetValueResult.RESULT_RESTRICTED)
+
+ assertThat(
+ transformCatalystSetValueResponse(PreferenceSetterResult.INVALID_REQUEST).resultCode
+ )
+ .isEqualTo(SetValueResult.RESULT_INVALID_REQUEST)
+
+ assertThat(
+ transformCatalystSetValueResponse(PreferenceSetterResult.INTERNAL_ERROR).resultCode
+ )
+ .isEqualTo(SetValueResult.RESULT_INTERNAL_ERROR)
+ }
+}
\ No newline at end of file
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/MainSwitchPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/MainSwitchPreferenceTest.java
index 093833e..c920c79 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/MainSwitchPreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/MainSwitchPreferenceTest.java
@@ -47,12 +47,14 @@
private final Context mContext = ApplicationProvider.getApplicationContext();
private View mRootView;
private PreferenceViewHolder mHolder;
+ private MainSwitchBar mMainSwitchBar;
private MainSwitchPreference mPreference;
@Before
public void setUp() {
mRootView = View.inflate(mContext, R.layout.settingslib_main_switch_layout,
null /* parent */);
+ mMainSwitchBar = mRootView.requireViewById(R.id.settingslib_main_switch_bar);
mHolder = PreferenceViewHolder.createInstanceForTests(mRootView);
mPreference = new MainSwitchPreference(mContext);
}
@@ -73,8 +75,7 @@
mPreference.setChecked(true);
mPreference.onBindViewHolder(mHolder);
- assertThat(mRootView.<MainSwitchBar>requireViewById(
- R.id.settingslib_main_switch_bar).isChecked()).isTrue();
+ assertThat(mMainSwitchBar.isChecked()).isTrue();
}
@Test
@@ -95,12 +96,12 @@
mPreference.setOnPreferenceChangeListener((preference, newValue) -> false);
mPreference.onBindViewHolder(mHolder);
- mPreference.performClick();
+ mMainSwitchBar.performClick();
verify(preferenceDataStore, never()).putBoolean(any(), anyBoolean());
mPreference.setOnPreferenceChangeListener((preference, newValue) -> true);
- mPreference.performClick();
+ mMainSwitchBar.performClick();
verify(preferenceDataStore).putBoolean(any(), anyBoolean());
}
}
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index 13c8385..a55a0fe 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -236,6 +236,7 @@
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_CURSOR_FOLLOWING_MODE,
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_JOYSTICK_ENABLED,
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_TWO_FINGER_TRIPLE_TAP_ENABLED,
+ Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MAGNIFY_NAV_AND_IME,
Settings.Secure.ACCESSIBILITY_MOUSE_KEYS_ENABLED,
Settings.Secure.ACCESSIBILITY_PINCH_TO_ZOOM_ANYWHERE_ENABLED,
Settings.Secure.ACCESSIBILITY_SINGLE_FINGER_PANNING_ENABLED,
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index 700a4c1..7eb2a6c 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -333,6 +333,7 @@
VALIDATORS.put(
Secure.ACCESSIBILITY_MAGNIFICATION_TWO_FINGER_TRIPLE_TAP_ENABLED,
BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Secure.ACCESSIBILITY_MAGNIFICATION_MAGNIFY_NAV_AND_IME, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.ACCESSIBILITY_PINCH_TO_ZOOM_ANYWHERE_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.ACCESSIBILITY_SINGLE_FINGER_PANNING_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(
@@ -478,5 +479,6 @@
VALIDATORS.put(Secure.SPELL_CHECKER_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.SELECTED_SPELL_CHECKER, NULLABLE_COMPONENT_NAME_VALIDATOR);
VALIDATORS.put(Secure.SELECTED_SPELL_CHECKER_SUBTYPE, ANY_INTEGER_VALIDATOR);
+ VALIDATORS.put(Secure.PACK_THEME_FEATURE_ENABLED, BOOLEAN_VALIDATOR);
}
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
index 5bbfdf7..98c0791 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
@@ -68,6 +68,7 @@
private static final String UNICODE_LOCALE_EXTENSION_FW = "fw";
private static final String UNICODE_LOCALE_EXTENSION_MU = "mu";
private static final String UNICODE_LOCALE_EXTENSION_NU = "nu";
+ private static final String UNICODE_LOCALE_EXTENSION_MS = "ms";
private static final float FLOAT_TOLERANCE = 0.01f;
/** See frameworks/base/core/res/res/values/config.xml#config_longPressOnPowerBehavior **/
@@ -147,6 +148,7 @@
static {
UNICODE_LOCALE_SUPPORTED_EXTENSIONS.add(UNICODE_LOCALE_EXTENSION_FW);
UNICODE_LOCALE_SUPPORTED_EXTENSIONS.add(UNICODE_LOCALE_EXTENSION_MU);
+ UNICODE_LOCALE_SUPPORTED_EXTENSIONS.add(UNICODE_LOCALE_EXTENSION_MS);
}
private interface SettingsLookup {
@@ -222,15 +224,11 @@
if (sendBroadcast) {
// TODO: http://b/22388012
oldValue = table.lookup(cr, name, UserHandle.USER_SYSTEM);
- } else if (sendBroadcastSystemUI) {
+ } else if (sendBroadcastSystemUI || sendBroadcastAccessibility) {
// This is only done for broadcasts sent to system ui as the consumers are known.
// It would probably be correct to do it for the ones sent to the system, but consumers
// may be depending on the current behavior.
oldValue = table.lookup(cr, name, context.getUserId());
- } else if (sendBroadcastAccessibility) {
- int userId = android.view.accessibility.Flags.restoreA11ySecureSettingsOnHsumDevice()
- ? context.getUserId() : UserHandle.USER_SYSTEM;
- oldValue = table.lookup(cr, name, userId);
}
try {
@@ -336,11 +334,8 @@
context.sendBroadcastAsUser(intent, context.getUser(), null);
}
if (sendBroadcastAccessibility) {
- UserHandle userHandle =
- android.view.accessibility.Flags.restoreA11ySecureSettingsOnHsumDevice()
- ? context.getUser() : UserHandle.SYSTEM;
intent.setPackage("android");
- context.sendBroadcastAsUser(intent, userHandle, null);
+ context.sendBroadcastAsUser(intent, context.getUser(), null);
}
}
}
@@ -508,16 +503,10 @@
}
private boolean shouldSkipAndLetBroadcastHandlesRestoreLogic(String settingName) {
- boolean restoreHandledByBroadcast = Settings.Secure.ACCESSIBILITY_QS_TARGETS.equals(
- settingName)
- || Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE.equals(settingName);
- if (android.view.accessibility.Flags.restoreA11ySecureSettingsOnHsumDevice()) {
- restoreHandledByBroadcast |=
- Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS.equals(settingName)
- || Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES.equals(settingName);
- }
-
- return restoreHandledByBroadcast;
+ return Settings.Secure.ACCESSIBILITY_QS_TARGETS.equals(settingName)
+ || Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE.equals(settingName)
+ || Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS.equals(settingName)
+ || Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES.equals(settingName);
}
private void setAutoRestore(boolean enabled) {
@@ -702,11 +691,6 @@
filtered.add(restoredLocaleWithExtension);
}
}
-
- if (filtered.size() == current.size()) {
- return current; // Nothing added to current locale list.
- }
-
return new LocaleList(filtered.toArray(new Locale[filtered.size()]));
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 83ec371..518dc53 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -1889,6 +1889,10 @@
SecureSettingsProto.Accessibility
.ACCESSIBILITY_MAGNIFICATION_TWO_FINGER_TRIPLE_TAP_ENABLED);
dumpSetting(s, p,
+ Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MAGNIFY_NAV_AND_IME,
+ SecureSettingsProto.Accessibility
+ .ACCESSIBILITY_MAGNIFICATION_MAGNIFY_NAV_AND_IME);
+ dumpSetting(s, p,
Settings.Secure.ACCESSIBILITY_PINCH_TO_ZOOM_ANYWHERE_ENABLED,
SecureSettingsProto.Accessibility
.ACCESSIBILITY_PINCH_TO_ZOOM_ANYWHERE_ENABLED);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 2dcaf08..67ef5d4 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -380,9 +380,6 @@
@GuardedBy("mLock")
private HandlerThread mHandlerThread;
- @GuardedBy("mLock")
- private Handler mHandler;
-
private static final Set<String> sDeviceConfigAllowlistedNamespaces = new ArraySet<>();
// TODO(b/388901162): Remove this when the same constant is exposed as an API in DeviceConfig.
@@ -405,11 +402,11 @@
@EnabledSince(targetSdkVersion=android.os.Build.VERSION_CODES.S)
private static final long ENFORCE_READ_PERMISSION_FOR_MULTI_SIM_DATA_CALL = 172670679L;
-
@Override
public boolean onCreate() {
Settings.setInSystemServer();
+ Handler handler;
synchronized (mLock) {
mUserManager = UserManager.get(getContext());
mPackageManager = AppGlobals.getPackageManager();
@@ -417,7 +414,7 @@
mHandlerThread = new HandlerThread(LOG_TAG,
Process.THREAD_PRIORITY_BACKGROUND);
mHandlerThread.start();
- mHandler = new Handler(mHandlerThread.getLooper());
+ handler = new Handler(mHandlerThread.getLooper());
mSettingsRegistry = new SettingsRegistry(mHandlerThread.getLooper());
}
SettingsState.cacheSystemPackageNamesAndSystemSignature(getContext());
@@ -428,7 +425,7 @@
}
mSettingsRegistry.syncSsaidTableOnStartLocked();
}
- mHandler.post(() -> {
+ handler.post(() -> {
registerBroadcastReceivers();
startWatchingUserRestrictionChanges();
});
@@ -3126,10 +3123,9 @@
private static final String SSAID_USER_KEY = "userkey";
- @GuardedBy("mLock")
private final SparseArray<SettingsState> mSettingsStates = new SparseArray<>();
- private GenerationRegistry mGenerationRegistry;
+ private final GenerationRegistry mGenerationRegistry;
private final Handler mHandler;
@@ -3143,7 +3139,6 @@
mBackupManager = new BackupManager(getContext());
}
- @GuardedBy("mLock")
private void generateUserKeyLocked(int userId) {
// Generate a random key for each user used for creating a new ssaid.
final byte[] keyBytes = new byte[32];
@@ -3167,7 +3162,6 @@
return ByteBuffer.allocate(4).putInt(data.length).array();
}
- @GuardedBy("mLock")
public Setting generateSsaidLocked(PackageInfo callingPkg, int userId) {
// Read the user's key from the ssaid table.
Setting userKeySetting = getSettingLocked(SETTINGS_TYPE_SSAID, userId, SSAID_USER_KEY);
@@ -3229,7 +3223,6 @@
return getSettingLocked(SETTINGS_TYPE_SSAID, userId, uid);
}
- @GuardedBy("mLock")
private void syncSsaidTableOnStartLocked() {
// Verify that each user's packages and ssaid's are in sync.
for (UserInfo user : mUserManager.getAliveUsers()) {
@@ -3264,7 +3257,6 @@
}
}
- @GuardedBy("mLock")
public List<String> getSettingsNamesLocked(int type, int userId) {
final int key = makeKey(type, userId);
SettingsState settingsState = mSettingsStates.get(key);
@@ -3274,7 +3266,6 @@
return settingsState.getSettingNamesLocked();
}
- @GuardedBy("mLock")
public SparseBooleanArray getKnownUsersLocked() {
SparseBooleanArray users = new SparseBooleanArray();
for (int i = mSettingsStates.size()-1; i >= 0; i--) {
@@ -3283,14 +3274,12 @@
return users;
}
- @GuardedBy("mLock")
@Nullable
public SettingsState getSettingsLocked(int type, int userId) {
final int key = makeKey(type, userId);
return mSettingsStates.get(key);
}
- @GuardedBy("mLock")
@Nullable
private SettingsState getOrCreateSettingsStateLocked(int key) {
SettingsState settingsState = mSettingsStates.get(key);
@@ -3304,7 +3293,6 @@
return mSettingsStates.get(key);
}
- @GuardedBy("mLock")
public boolean ensureSettingsForUserLocked(int userId) {
// First make sure this user actually exists.
if (mUserManager.getUserInfo(userId) == null) {
@@ -3350,7 +3338,6 @@
return true;
}
- @GuardedBy("mLock")
private void ensureSettingsStateLocked(int key) {
if (mSettingsStates.get(key) == null) {
final int maxBytesPerPackage = getMaxBytesPerPackageForType(getTypeFromKey(key));
@@ -3360,7 +3347,6 @@
}
}
- @GuardedBy("mLock")
public void removeUserStateLocked(int userId, boolean permanently) {
// We always keep the global settings in memory.
@@ -3404,7 +3390,6 @@
mGenerationRegistry.onUserRemoved(userId);
}
- @GuardedBy("mLock")
public boolean insertSettingLocked(int type, int userId, String name, String value,
String tag, boolean makeDefault, String packageName, boolean forceNotify,
Set<String> criticalSettings, boolean overrideableByRestore) {
@@ -3412,7 +3397,6 @@
packageName, forceNotify, criticalSettings, overrideableByRestore);
}
- @GuardedBy("mLock")
public boolean insertSettingLocked(int type, int userId, String name, String value,
String tag, boolean makeDefault, boolean forceNonSystemPackage, String packageName,
boolean forceNotify, Set<String> criticalSettings, boolean overrideableByRestore) {
@@ -3475,7 +3459,6 @@
* Set Config Settings using consumed keyValues, returns true if the keyValues can be set,
* false otherwise.
*/
- @GuardedBy("mLock")
public boolean setConfigSettingsLocked(int key, String prefix,
Map<String, String> keyValues, String packageName) {
SettingsState settingsState = getOrCreateSettingsStateLocked(key);
@@ -3495,7 +3478,6 @@
return true;
}
- @GuardedBy("mLock")
public boolean deleteSettingLocked(int type, int userId, String name, boolean forceNotify,
Set<String> criticalSettings) {
final int key = makeKey(type, userId);
@@ -3519,7 +3501,6 @@
return success;
}
- @GuardedBy("mLock")
public boolean updateSettingLocked(int type, int userId, String name, String value,
String tag, boolean makeDefault, String packageName, boolean forceNotify,
Set<String> criticalSettings) {
@@ -3545,7 +3526,6 @@
return success;
}
- @GuardedBy("mLock")
public Setting getSettingLocked(int type, int userId, String name) {
final int key = makeKey(type, userId);
@@ -3567,14 +3547,12 @@
return Global.SECURE_FRP_MODE.equals(setting.getName());
}
- @GuardedBy("mLock")
public boolean resetSettingsLocked(int type, int userId, String packageName, int mode,
String tag) {
return resetSettingsLocked(type, userId, packageName, mode, tag, /*prefix=*/
null);
}
- @GuardedBy("mLock")
public boolean resetSettingsLocked(int type, int userId, String packageName, int mode,
String tag, @Nullable String prefix) {
final int key = makeKey(type, userId);
@@ -3683,7 +3661,6 @@
return success;
}
- @GuardedBy("mLock")
public void removeSettingsForPackageLocked(String packageName, int userId) {
// Global and secure settings are signature protected. Apps signed
// by the platform certificate are generally not uninstalled and
@@ -3697,7 +3674,6 @@
}
}
- @GuardedBy("mLock")
public void onUidRemovedLocked(int uid) {
final SettingsState ssaidSettings = getSettingsLocked(SETTINGS_TYPE_SSAID,
UserHandle.getUserId(uid));
@@ -3706,7 +3682,6 @@
}
}
- @GuardedBy("mLock")
private void migrateAllLegacySettingsIfNeededLocked() {
final int key = makeKey(SETTINGS_TYPE_GLOBAL, UserHandle.USER_SYSTEM);
File globalFile = getSettingsFile(key);
@@ -3742,7 +3717,6 @@
}
}
- @GuardedBy("mLock")
private void migrateLegacySettingsForUserIfNeededLocked(int userId) {
// Every user has secure settings and if no file we need to migrate.
final int secureKey = makeKey(SETTINGS_TYPE_SECURE, userId);
@@ -3757,7 +3731,6 @@
migrateLegacySettingsForUserLocked(dbHelper, database, userId);
}
- @GuardedBy("mLock")
private void migrateLegacySettingsForUserLocked(DatabaseHelper dbHelper,
SQLiteDatabase database, int userId) {
// Move over the system settings.
@@ -3802,7 +3775,6 @@
}
}
- @GuardedBy("mLock")
private void migrateLegacySettingsLocked(SettingsState settingsState,
SQLiteDatabase database, String table) {
SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
@@ -3837,7 +3809,6 @@
}
}
- @GuardedBy("mLock")
private void ensureSecureSettingAndroidIdSetLocked(SettingsState secureSettings) {
Setting value = secureSettings.getSettingLocked(Settings.Secure.ANDROID_ID);
@@ -3913,7 +3884,6 @@
name, type, changeType);
}
- @GuardedBy("mLock")
private void notifyForConfigSettingsChangeLocked(int key, String prefix,
List<String> changedSettings) {
@@ -4096,7 +4066,6 @@
mUserId = userId;
}
- @GuardedBy("mLock")
public void upgradeIfNeededLocked() {
// The version of all settings for a user is the same (all users have secure).
SettingsState secureSettings = getSettingsLocked(
@@ -4154,22 +4123,18 @@
systemSettings.setVersionLocked(newVersion);
}
- @GuardedBy("mLock")
private SettingsState getGlobalSettingsLocked() {
return getSettingsLocked(SETTINGS_TYPE_GLOBAL, UserHandle.USER_SYSTEM);
}
- @GuardedBy("mLock")
private SettingsState getSecureSettingsLocked(int userId) {
return getSettingsLocked(SETTINGS_TYPE_SECURE, userId);
}
- @GuardedBy("mLock")
private SettingsState getSsaidSettingsLocked(int userId) {
return getSettingsLocked(SETTINGS_TYPE_SSAID, userId);
}
- @GuardedBy("mLock")
private SettingsState getSystemSettingsLocked(int userId) {
return getSettingsLocked(SETTINGS_TYPE_SYSTEM, userId);
}
@@ -4203,7 +4168,6 @@
* currentVersion = 119;
* }
*/
- @GuardedBy("mLock")
private int onUpgradeLocked(int userId, int oldVersion, int newVersion) {
if (DEBUG) {
Slog.w(LOG_TAG, "Upgrading settings for user: " + userId + " from version: "
@@ -4619,16 +4583,16 @@
// run 142.
if (userId == UserHandle.USER_SYSTEM) {
SettingsState globalSettings = getGlobalSettingsLocked();
- ensureLegacyDefaultValueAndSystemSetUpdatedLocked(globalSettings, userId);
+ ensureLegacyDefaultValueAndSystemSetUpdatedLocked(globalSettings);
globalSettings.persistSettingsLocked();
}
SettingsState secureSettings = getSecureSettingsLocked(mUserId);
- ensureLegacyDefaultValueAndSystemSetUpdatedLocked(secureSettings, userId);
+ ensureLegacyDefaultValueAndSystemSetUpdatedLocked(secureSettings);
secureSettings.persistSettingsLocked();
SettingsState systemSettings = getSystemSettingsLocked(mUserId);
- ensureLegacyDefaultValueAndSystemSetUpdatedLocked(systemSettings, userId);
+ ensureLegacyDefaultValueAndSystemSetUpdatedLocked(systemSettings);
systemSettings.persistSettingsLocked();
currentVersion = 146;
@@ -6399,8 +6363,6 @@
return currentVersion;
}
- @SuppressWarnings("GuardedBy")
- @GuardedBy("mLock")
private void handleDefaultFontScale(@NonNull SettingsState systemSettings) {
final float defaultFontScale = getContext().getResources()
.getFloat(R.dimen.def_device_font_scale);
@@ -6425,22 +6387,18 @@
}
}
- @GuardedBy("mLock")
private void initGlobalSettingsDefaultValLocked(String key, boolean val) {
initGlobalSettingsDefaultValLocked(key, val ? "1" : "0");
}
- @GuardedBy("mLock")
private void initGlobalSettingsDefaultValLocked(String key, int val) {
initGlobalSettingsDefaultValLocked(key, String.valueOf(val));
}
- @GuardedBy("mLock")
private void initGlobalSettingsDefaultValLocked(String key, long val) {
initGlobalSettingsDefaultValLocked(key, String.valueOf(val));
}
- @GuardedBy("mLock")
private void initGlobalSettingsDefaultValLocked(String key, String val) {
final SettingsState globalSettings = getGlobalSettingsLocked();
Setting currentSetting = globalSettings.getSettingLocked(key);
@@ -6567,9 +6525,7 @@
}
}
- @GuardedBy("mLock")
- private void ensureLegacyDefaultValueAndSystemSetUpdatedLocked(SettingsState settings,
- int userId) {
+ private void ensureLegacyDefaultValueAndSystemSetUpdatedLocked(SettingsState settings) {
List<String> names = settings.getSettingNamesLocked();
final int nameCount = names.size();
for (int i = 0; i < nameCount; i++) {
@@ -6642,7 +6598,6 @@
return items;
}
- @GuardedBy("mLock")
private void migrateColonDelimitedStringSettingLocked(SettingsState settingsState,
String setting, String toRemove, String toAdd) {
final Set<String> componentNames = transformColonDelimitedStringToSet(
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index e9b6c73..230f2f8 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -809,6 +809,7 @@
Settings.Secure.NAVIGATION_MODE_RESTORE,
Settings.Secure.V_TO_U_RESTORE_ALLOWLIST,
Settings.Secure.V_TO_U_RESTORE_DENYLIST,
+ Settings.Secure.PACK_THEME_FEATURE_ENABLED,
Settings.Secure.REDACT_OTP_NOTIFICATION_WHILE_CONNECTED_TO_WIFI,
Settings.Secure.OTP_NOTIFICATION_REDACTION_LOCK_TIME);
diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperRestoreTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperRestoreTest.java
index cb3f54e..5620b13 100644
--- a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperRestoreTest.java
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperRestoreTest.java
@@ -32,7 +32,6 @@
import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
import android.provider.SettingsStringUtil;
-import android.view.accessibility.Flags;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.platform.app.InstrumentationRegistry;
@@ -318,7 +317,6 @@
.isEqualTo(Build.VERSION.SDK_INT);
}
- @EnableFlags(Flags.FLAG_RESTORE_A11Y_SECURE_SETTINGS_ON_HSUM_DEVICE)
@Test
public void restoreAccessibilityShortcutTargets_broadcastSent()
throws ExecutionException, InterruptedException {
diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperTest.java
index 2160d31..0c3c58d 100644
--- a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperTest.java
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperTest.java
@@ -400,6 +400,34 @@
new String[] {
"en-US" , "zh-Hans-TW" , "en-UK", "en-GB", "zh-Hans-HK", "fr-FR"
}));
+
+ assertEquals(LocaleList.forLanguageTags("en-US-u-ms-uksystem-mu-celsius"),
+ SettingsHelper.resolveLocales(
+ // restore
+ LocaleList.forLanguageTags("en-US-u-ms-uksystem-mu-celsius"),
+
+ // current
+ LocaleList.forLanguageTags("en-US"),
+
+ // supported
+ new String[] {
+ "en-US"
+ }));
+
+ assertEquals(LocaleList.forLanguageTags(
+ "en-US-u-ms-uksystem-mu-celsius,fr-FR-u-ms-uksystem-mu-celsius"),
+ SettingsHelper.resolveLocales(
+ // restore
+ LocaleList.forLanguageTags(
+ "en-US-u-ms-uksystem-mu-celsius,fr-FR-u-ms-uksystem-mu-celsius"),
+
+ // current
+ LocaleList.forLanguageTags("en-US"),
+
+ // supported
+ new String[] {
+ "en-US", "fr-FR"
+ }));
}
@Test
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 9322c9c..0c2e440 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -802,6 +802,9 @@
<!-- Permission required for CTS test - CtsDeviceLockTestCases -->
<uses-permission android:name="android.permission.MANAGE_DEVICE_LOCK_STATE" />
+ <!-- Permission required for CTS test - CtsDeviceLockTestCases -->
+ <uses-permission android:name="android.permission.GET_DEVICE_LOCK_ENROLLMENT_TYPE" />
+
<!-- Permission required for CTS test - CtsAppFgsTestCases -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_CAMERA" />
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 2d9a8c6..3f723d4 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -989,6 +989,7 @@
optimized_shrink_resources: true,
ignore_warnings: false,
proguard_compatibility: false,
+ d8_on_eng: true,
},
conditions_default: {
optimize: {
diff --git a/packages/SystemUI/OWNERS b/packages/SystemUI/OWNERS
index 728d206..42100db 100644
--- a/packages/SystemUI/OWNERS
+++ b/packages/SystemUI/OWNERS
@@ -18,6 +18,7 @@
ayepin@google.com
bbade@google.com
beverlyt@google.com
+bharatkrsingh@google.com
bhinegardner@google.com
bhnm@google.com
brycelee@google.com
@@ -37,6 +38,7 @@
florenceyang@google.com
gallmann@google.com
graciecheng@google.com
+gshnakar@google.com
gwasserman@google.com
helencheuk@google.com
hwwang@google.com
@@ -76,12 +78,14 @@
nickchameyev@google.com
nicomazz@google.com
nijamkin@google.com
+nikhilshrma@google.com
ogunwale@google.com
omarmt@google.com
patmanning@google.com
peanutbutter@google.com
peskal@google.com
petrcermak@google.com
+pingpramit@google.com
pinyaoting@google.com
pixel@google.com
pomini@google.com
@@ -90,6 +94,7 @@
rgl@google.com
roosa@google.com
saff@google.com
+sandeepsuman@google.com
santie@google.com
shanh@google.com
snoeberger@google.com
diff --git a/packages/SystemUI/TEST_MAPPING b/packages/SystemUI/TEST_MAPPING
index ddf7aa3..880929a 100644
--- a/packages/SystemUI/TEST_MAPPING
+++ b/packages/SystemUI/TEST_MAPPING
@@ -134,6 +134,9 @@
},
{
"name": "SystemUIGoogleRobo2RNGTests"
+ },
+ {
+ "name": "SystemUIGoogleRoboRNGKeyguardTests"
}
],
"imports": [
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable-night/a11ymenu_intro.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable-night/a11ymenu_intro.xml
deleted file mode 100644
index c2fe06a4..0000000
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable-night/a11ymenu_intro.xml
+++ /dev/null
@@ -1,73 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="412dp"
- android:height="300dp"
- android:viewportWidth="412"
- android:viewportHeight="300">
- <path
- android:fillColor="#FF000000"
- android:pathData="M383.9,300H28.1c-15.5,0 -28.1,-12.6 -28.1,-28.1V28.1C0,12.6 12.6,0 28.1,0H383.9c15.5,0 28.1,12.6 28.1,28.1v243.8c0,15.5 -12.6,28.1 -28.1,28.1Z"/>
- <path
- android:pathData="M119.3,39.17h0c13.21,0 23.92,10.71 23.92,23.92h0c0,13.21 -10.71,23.92 -23.92,23.92h0c-13.21,0 -23.92,-10.71 -23.92,-23.92h0c0,-13.21 10.71,-23.92 23.92,-23.92Z"
- android:fillColor="#2197f3"/>
- <path
- android:pathData="M292.7,39.17h0c13.21,0 23.92,10.71 23.92,23.92h0c0,13.21 -10.71,23.92 -23.92,23.92h0c-13.21,0 -23.92,-10.71 -23.92,-23.92h0c0,-13.21 10.71,-23.92 23.92,-23.92Z"
- android:fillColor="#dbdce0"/>
- <path
- android:fillColor="#FF000000"
- android:pathData="M291.5,52.4h2.39v11.81h-2.39v-11.81ZM297.93,56.74l1.64,-1.64c2.24,1.94 3.74,4.78 3.74,8.07 0,5.83 -4.78,10.61 -10.61,10.61s-10.61,-4.78 -10.61,-10.61c0,-3.29 1.49,-6.13 3.74,-8.07l1.64,1.64c-1.79,1.49 -2.99,3.89 -2.99,6.43 0,4.63 3.74,8.37 8.37,8.37 4.63,0 8.37,-3.74 8.37,-8.37 -0.15,-2.69 -1.49,-4.93 -3.29,-6.43Z"
- android:fillType="evenOdd"/>
- <path
- android:pathData="M207.39,39.17h0c13.21,0 23.92,10.71 23.92,23.92h0c0,13.21 -10.71,23.92 -23.92,23.92h0c-13.21,0 -23.92,-10.71 -23.92,-23.92h0c0,-13.21 10.71,-23.92 23.92,-23.92Z"
- android:fillColor="#d9affd"/>
- <path
- android:fillColor="#FF000000"
- android:pathData="M207.39,53.2c-0.59,0 -1.1,-0.21 -1.53,-0.64 -0.42,-0.42 -0.64,-0.93 -0.64,-1.53s0.21,-1.1 0.64,-1.53c0.42,-0.42 0.93,-0.64 1.53,-0.64s1.1,0.21 1.53,0.64c0.42,0.42 0.64,0.93 0.64,1.53s-0.21,1.1 -0.64,1.53 -0.93,0.64 -1.53,0.64ZM204.31,71.39v-14.63c-1.24,-0.1 -2.5,-0.24 -3.76,-0.43 -1.26,-0.19 -2.44,-0.42 -3.53,-0.7l0.44,-1.78c1.58,0.39 3.2,0.68 4.87,0.86 1.67,0.18 3.35,0.27 5.05,0.27s3.38,-0.09 5.05,-0.27c1.67,-0.18 3.29,-0.46 4.87,-0.86l0.44,1.78c-1.09,0.28 -2.26,0.51 -3.53,0.7 -1.26,0.19 -2.52,0.33 -3.76,0.43v14.63h-1.78v-7.23h-2.61v7.23h-1.78ZM202.56,77.31c-0.34,0 -0.61,-0.1 -0.81,-0.31 -0.21,-0.21 -0.31,-0.48 -0.31,-0.81 0,-0.34 0.1,-0.61 0.31,-0.81 0.21,-0.21 0.48,-0.31 0.81,-0.31s0.61,0.1 0.81,0.31c0.21,0.21 0.31,0.48 0.31,0.81 0,0.34 -0.1,0.61 -0.31,0.81 -0.21,0.21 -0.48,0.31 -0.81,0.31ZM207.42,77.31c-0.34,0 -0.61,-0.1 -0.81,-0.31 -0.21,-0.21 -0.31,-0.48 -0.31,-0.81 0,-0.34 0.1,-0.61 0.31,-0.81 0.21,-0.21 0.48,-0.31 0.81,-0.31s0.61,0.1 0.81,0.31c0.21,0.21 0.31,0.48 0.31,0.81 0,0.34 -0.1,0.61 -0.31,0.81 -0.21,0.21 -0.48,0.31 -0.81,0.31ZM212.28,77.31c-0.34,0 -0.61,-0.1 -0.81,-0.31 -0.21,-0.21 -0.31,-0.48 -0.31,-0.81 0,-0.34 0.1,-0.61 0.31,-0.81 0.21,-0.21 0.48,-0.31 0.81,-0.31s0.61,0.1 0.81,0.31c0.21,0.21 0.31,0.48 0.31,0.81 0,0.34 -0.1,0.61 -0.31,0.81 -0.21,0.21 -0.48,0.31 -0.81,0.31Z"/>
- <path
- android:pathData="M119.3,212.98h0c13.21,0 23.92,10.71 23.92,23.92h0c0,13.21 -10.71,23.92 -23.92,23.92h0c-13.21,0 -23.92,-10.71 -23.92,-23.92h0c0,-13.21 10.71,-23.92 23.92,-23.92Z"
- android:fillColor="#fdd663"/>
- <path
- android:fillColor="#FF000000"
- android:pathData="M119.34,251.2l-4.27,-4.14h-5.88v-5.88l-4.23,-4.23 4.23,-4.23v-5.88h5.88l4.27,-4.23 4.2,4.23h5.88v5.88l4.23,4.23 -4.23,4.23v5.88h-5.88l-4.2,4.14ZM119.34,243.08c-1.71,0 -3.16,-0.6 -4.36,-1.8 -1.2,-1.2 -1.8,-2.65 -1.8,-4.36s0.6,-3.16 1.8,-4.36c1.2,-1.2 2.65,-1.8 4.36,-1.8 1.71,0 3.16,0.6 4.36,1.8 1.2,1.2 1.8,2.65 1.8,4.36s-0.6,3.16 -1.8,4.36c-1.2,1.2 -2.65,1.8 -4.36,1.8ZM119.34,241.18c1.2,0 2.21,-0.41 3.03,-1.23 0.82,-0.82 1.23,-1.83 1.23,-3.03 -0,-1.2 -0.41,-2.21 -1.23,-3.03 -0.82,-0.82 -1.83,-1.23 -3.03,-1.23 -1.2,0 -2.21,0.41 -3.03,1.23 -0.82,0.82 -1.23,1.83 -1.23,3.03s0.41,2.21 1.23,3.03c0.82,0.82 1.83,1.23 3.03,1.23ZM119.34,248.55l3.41,-3.38h4.77v-4.77l3.44,-3.44 -3.44,-3.44v-4.77h-4.77l-3.41,-3.44 -3.48,3.44h-4.77v4.77l-3.44,3.44 3.44,3.44v4.77h4.74l3.51,3.38Z"/>
- <path
- android:pathData="M207.39,212.99h0c13.21,0 23.92,10.71 23.92,23.92h0c0,13.21 -10.71,23.92 -23.92,23.92h0c-13.21,0 -23.92,-10.71 -23.92,-23.92h0c0,-13.21 10.71,-23.92 23.92,-23.92Z"
- android:fillColor="#fdd663"/>
- <path
- android:fillColor="#FF000000"
- android:pathData="M207.42,251.21l-4.27,-4.14h-5.88v-5.88l-4.23,-4.23 4.23,-4.23v-5.88h5.88l4.27,-4.23 4.2,4.23h5.88v5.88l4.23,4.23 -4.23,4.23v5.88h-5.88l-4.2,4.14ZM207.42,243.09c1.71,0 3.16,-0.6 4.36,-1.8 1.2,-1.2 1.8,-2.65 1.8,-4.36s-0.6,-3.16 -1.8,-4.36c-1.2,-1.2 -2.65,-1.8 -4.36,-1.8 -1.71,0 -3.16,0.6 -4.36,1.8 -1.2,1.2 -1.8,2.65 -1.8,4.36s0.6,3.16 1.8,4.36c1.2,1.2 2.65,1.8 4.36,1.8ZM207.42,241.19c1.2,0 2.21,-0.41 3.03,-1.23 0.82,-0.82 1.23,-1.83 1.23,-3.03 -0,-1.2 -0.41,-2.21 -1.23,-3.03 -0.82,-0.82 -1.83,-1.23 -3.03,-1.23 -1.2,0 -2.21,0.41 -3.03,1.23 -0.82,0.82 -1.23,1.83 -1.23,3.03s0.41,2.21 1.23,3.03c0.82,0.82 1.83,1.23 3.03,1.23ZM207.42,248.55l3.41,-3.38h4.77v-4.77l3.44,-3.44 -3.44,-3.44v-4.77h-4.77l-3.41,-3.44 -3.48,3.44h-4.77v4.77l-3.44,3.44 3.44,3.44v4.77h4.74l3.51,3.38ZM207.42,241.19c1.2,0 2.21,-0.41 3.03,-1.23 0.82,-0.82 1.23,-1.83 1.23,-3.03 -0,-1.2 -0.41,-2.21 -1.23,-3.03 -0.82,-0.82 -1.83,-1.23 -3.03,-1.23 -1.2,0 -2.21,0.41 -3.03,1.23 -0.82,0.82 -1.23,1.83 -1.23,3.03s0.41,2.21 1.23,3.03c0.82,0.82 1.83,1.23 3.03,1.23Z"/>
- <path
- android:pathData="M292.7,212.98h0c13.21,0 23.92,10.71 23.92,23.92h0c0,13.21 -10.71,23.92 -23.92,23.92h0c-13.21,0 -23.92,-10.71 -23.92,-23.92h0c0,-13.21 10.71,-23.92 23.92,-23.92Z"
- android:fillColor="#84e39f"/>
- <path
- android:fillColor="#FF000000"
- android:pathData="M284.29,248.97c-0.54,0 -1,-0.19 -1.37,-0.57s-0.57,-0.83 -0.57,-1.37v-14.02c0,-0.54 0.19,-1 0.57,-1.37s0.83,-0.57 1.37,-0.57h2.26v-3.1c0,-1.7 0.6,-3.15 1.79,-4.35 1.2,-1.2 2.64,-1.79 4.35,-1.79s3.15,0.6 4.35,1.79c1.2,1.2 1.79,2.64 1.79,4.35v3.1h2.26c0.54,0 1,0.19 1.37,0.57s0.57,0.83 0.57,1.37v14.02c0,0.54 -0.19,1 -0.57,1.37s-0.83,0.57 -1.37,0.57h-16.8ZM284.29,247.03h16.8v-14.02h-16.8v14.02ZM292.7,242.51c0.69,0 1.28,-0.24 1.76,-0.71s0.73,-1.04 0.73,-1.71c0,-0.65 -0.24,-1.23 -0.73,-1.76s-1.07,-0.79 -1.76,-0.79 -1.28,0.26 -1.76,0.79c-0.48,0.53 -0.73,1.11 -0.73,1.76 0,0.67 0.24,1.24 0.73,1.71s1.07,0.71 1.76,0.71ZM288.5,231.07h8.4v-3.1c0,-1.16 -0.41,-2.15 -1.23,-2.97 -0.82,-0.82 -1.81,-1.23 -2.97,-1.23s-2.15,0.41 -2.97,1.23 -1.23,1.81 -1.23,2.97v3.1ZM284.29,247.03v0Z"/>
- <path
- android:pathData="M207.39,126.06h0c13.21,0 23.92,10.71 23.92,23.92h0c0,13.21 -10.71,23.92 -23.92,23.92h0c-13.21,0 -23.92,-10.71 -23.92,-23.92h0c0,-13.21 10.71,-23.92 23.92,-23.92Z"
- android:fillColor="#7ae2d4"/>
- <path
- android:fillColor="#FF000000"
- android:pathData="M210.29,162.68v-2.25c2.34,-0.68 4.26,-1.97 5.74,-3.89 1.49,-1.92 2.23,-4.1 2.23,-6.54 0,-2.44 -0.74,-4.62 -2.21,-6.56 -1.47,-1.93 -3.39,-3.22 -5.76,-3.88v-2.25c2.99,0.68 5.43,2.19 7.32,4.55 1.88,2.35 2.83,5.06 2.83,8.13s-0.94,5.78 -2.83,8.13c-1.88,2.35 -4.32,3.87 -7.32,4.55ZM194.35,154.39v-8.69h5.8l7.24,-7.24v23.18l-7.24,-7.24h-5.8ZM209.56,156.13v-12.21c1.33,0.41 2.38,1.18 3.17,2.32 0.78,1.13 1.18,2.4 1.18,3.8 0,1.38 -0.4,2.63 -1.2,3.77s-1.85,1.91 -3.15,2.32ZM205.21,143.96l-4.09,3.91h-4.6v4.35h4.6l4.09,3.95v-12.21Z"/>
- <path
- android:pathData="M292.7,126.1h0c13.21,0 23.92,10.71 23.92,23.92h0c0,13.21 -10.71,23.92 -23.92,23.92h0c-13.21,0 -23.92,-10.71 -23.92,-23.92h0c0,-13.21 10.71,-23.92 23.92,-23.92Z"
- android:fillColor="#efa5de"/>
- <path
- android:fillColor="#FF000000"
- android:pathData="M299.38,140.4h-13.36v19.23h13.36v-19.23Z"/>
- <path
- android:fillColor="#FF000000"
- android:pathData="M284.23,144.18h-4.81v11.84h4.81v-11.84Z"/>
- <path
- android:fillColor="#FF000000"
- android:pathData="M305.97,144.18h-4.81v11.84h4.81v-11.84Z"/>
- <path
- android:pathData="M288.5,142.7h8.39v14.63h-8.39z"
- android:fillColor="#efa5de"/>
- <path
- android:pathData="M119.3,126.06h0c13.21,0 23.92,10.71 23.92,23.92h0c0,13.21 -10.71,23.92 -23.92,23.92h0c-13.21,0 -23.92,-10.71 -23.92,-23.92h0c0,-13.21 10.71,-23.92 23.92,-23.92Z"
- android:fillColor="#7ae2d4"/>
- <path
- android:fillColor="#FF000000"
- android:pathData="M109.52,154.35v-8.7h5.8l7.25,-7.25v23.19l-7.25,-7.25h-5.8ZM124.74,156.09v-12.21c1.3,0.41 2.36,1.18 3.15,2.32 0.8,1.14 1.2,2.4 1.2,3.8 0,1.43 -0.4,2.69 -1.2,3.8s-1.85,1.87 -3.15,2.28ZM120.39,143.92l-4.09,3.91h-4.6v4.35h4.6l4.09,3.95v-12.21Z"/>
- <path
- android:fillColor="#FF000000"
- android:pathData="M119.09,78.27l-3.9,-4.01 -5.93,-0.08c-0.53,-0.01 -0.99,-0.21 -1.38,-0.61 -0.39,-0.4 -0.58,-0.86 -0.57,-1.39l0.26,-19.77c0.01,-0.53 0.21,-0.99 0.61,-1.38 0.4,-0.39 0.86,-0.58 1.39,-0.57l19.77,0.26c0.53,0.01 0.99,0.21 1.38,0.61 0.39,0.4 0.58,0.86 0.57,1.39l-0.26,19.77c-0.01,0.53 -0.21,0.99 -0.61,1.38 -0.4,0.39 -0.86,0.58 -1.39,0.57l-5.93,-0.08 -4.01,3.9ZM119.25,68.61l1.9,-4.19 4.24,-1.79 -4.19,-1.9 -1.79,-4.24 -1.93,4.19 -4.21,1.79 4.16,1.9 1.82,4.24Z"/>
-</vector>
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/a11ymenu_intro.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/a11ymenu_intro.xml
index 7cc5d53..2bac10a 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/a11ymenu_intro.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/a11ymenu_intro.xml
@@ -4,70 +4,57 @@
android:viewportWidth="412"
android:viewportHeight="300">
<path
- android:pathData="M383.9,300H28.1c-15.5,0 -28.1,-12.6 -28.1,-28.1V28.1C0,12.6 12.6,0 28.1,0H383.9c15.5,0 28.1,12.6 28.1,28.1v243.8c0,15.5 -12.6,28.1 -28.1,28.1Z"
- android:fillColor="#fff"/>
+ android:pathData="M104.36,242.37m-30.35,0a30.35,30.35 0,1 1,60.71 0a30.35,30.35 0,1 1,-60.71 0"
+ android:fillColor="#FFB683"/>
<path
- android:pathData="M119.3,39.17h0c13.21,0 23.92,10.71 23.92,23.92h0c0,13.21 -10.71,23.92 -23.92,23.92h0c-13.21,0 -23.92,-10.71 -23.92,-23.92h0c0,-13.21 10.71,-23.92 23.92,-23.92Z"
- android:fillColor="#2197f3"/>
+ android:pathData="M206.5,242.37m-30.35,0a30.35,30.35 0,1 1,60.71 0a30.35,30.35 0,1 1,-60.71 0"
+ android:fillColor="#FFB683"/>
<path
- android:pathData="M292.7,39.17h0c13.21,0 23.92,10.71 23.92,23.92h0c0,13.21 -10.71,23.92 -23.92,23.92h0c-13.21,0 -23.92,-10.71 -23.92,-23.92h0c0,-13.21 10.71,-23.92 23.92,-23.92Z"
- android:fillColor="#80868b"/>
+ android:pathData="M308.65,242.37m-30.35,0a30.35,30.35 0,1 1,60.71 0a30.35,30.35 0,1 1,-60.71 0"
+ android:fillColor="#60D5F3"/>
<path
- android:pathData="M291.5,52.4h2.39v11.81h-2.39v-11.81ZM297.93,56.74l1.64,-1.64c2.24,1.94 3.74,4.78 3.74,8.07 0,5.83 -4.78,10.61 -10.61,10.61s-10.61,-4.78 -10.61,-10.61c0,-3.29 1.49,-6.13 3.74,-8.07l1.64,1.64c-1.79,1.49 -2.99,3.89 -2.99,6.43 0,4.63 3.74,8.37 8.37,8.37 4.63,0 8.37,-3.74 8.37,-8.37 -0.15,-2.69 -1.49,-4.93 -3.29,-6.43Z"
- android:fillColor="#fff"
- android:fillType="evenOdd"/>
+ android:pathData="M104.36,149.86m-30.35,0a30.35,30.35 0,1 1,60.71 0a30.35,30.35 0,1 1,-60.71 0"
+ android:fillColor="#FFAEE4"/>
<path
- android:pathData="M207.39,39.17h0c13.21,0 23.92,10.71 23.92,23.92h0c0,13.21 -10.71,23.92 -23.92,23.92h0c-13.21,0 -23.92,-10.71 -23.92,-23.92h0c0,-13.21 10.71,-23.92 23.92,-23.92Z"
- android:fillColor="#521bbf"/>
+ android:pathData="M206.5,149.86m-30.35,0a30.35,30.35 0,1 1,60.71 0a30.35,30.35 0,1 1,-60.71 0"
+ android:fillColor="#FFAEE4"/>
<path
- android:pathData="M207.39,53.2c-0.59,0 -1.1,-0.21 -1.53,-0.64 -0.42,-0.42 -0.64,-0.93 -0.64,-1.53s0.21,-1.1 0.64,-1.53c0.42,-0.42 0.93,-0.64 1.53,-0.64s1.1,0.21 1.53,0.64c0.42,0.42 0.64,0.93 0.64,1.53s-0.21,1.1 -0.64,1.53 -0.93,0.64 -1.53,0.64ZM204.31,71.39v-14.63c-1.24,-0.1 -2.5,-0.24 -3.76,-0.43 -1.26,-0.19 -2.44,-0.42 -3.53,-0.7l0.44,-1.78c1.58,0.39 3.2,0.68 4.87,0.86 1.67,0.18 3.35,0.27 5.05,0.27s3.38,-0.09 5.05,-0.27c1.67,-0.18 3.29,-0.46 4.87,-0.86l0.44,1.78c-1.09,0.28 -2.26,0.51 -3.53,0.7 -1.26,0.19 -2.52,0.33 -3.76,0.43v14.63h-1.78v-7.23h-2.61v7.23h-1.78ZM202.56,77.31c-0.34,0 -0.61,-0.1 -0.81,-0.31 -0.21,-0.21 -0.31,-0.48 -0.31,-0.81 0,-0.34 0.1,-0.61 0.31,-0.81 0.21,-0.21 0.48,-0.31 0.81,-0.31s0.61,0.1 0.81,0.31c0.21,0.21 0.31,0.48 0.31,0.81 0,0.34 -0.1,0.61 -0.31,0.81 -0.21,0.21 -0.48,0.31 -0.81,0.31ZM207.42,77.31c-0.34,0 -0.61,-0.1 -0.81,-0.31 -0.21,-0.21 -0.31,-0.48 -0.31,-0.81 0,-0.34 0.1,-0.61 0.31,-0.81 0.21,-0.21 0.48,-0.31 0.81,-0.31s0.61,0.1 0.81,0.31c0.21,0.21 0.31,0.48 0.31,0.81 0,0.34 -0.1,0.61 -0.31,0.81 -0.21,0.21 -0.48,0.31 -0.81,0.31ZM212.28,77.31c-0.34,0 -0.61,-0.1 -0.81,-0.31 -0.21,-0.21 -0.31,-0.48 -0.31,-0.81 0,-0.34 0.1,-0.61 0.31,-0.81 0.21,-0.21 0.48,-0.31 0.81,-0.31s0.61,0.1 0.81,0.31c0.21,0.21 0.31,0.48 0.31,0.81 0,0.34 -0.1,0.61 -0.31,0.81 -0.21,0.21 -0.48,0.31 -0.81,0.31Z"
- android:fillColor="#fff"/>
+ android:pathData="M308.65,149.86m-30.35,0a30.35,30.35 0,1 1,60.71 0a30.35,30.35 0,1 1,-60.71 0"
+ android:fillColor="#A1C9FF"/>
<path
- android:pathData="M119.3,212.98h0c13.21,0 23.92,10.71 23.92,23.92h0c0,13.21 -10.71,23.92 -23.92,23.92h0c-13.21,0 -23.92,-10.71 -23.92,-23.92h0c0,-13.21 10.71,-23.92 23.92,-23.92Z"
- android:fillColor="#de9834"/>
+ android:pathData="M104.36,58.35m-30.35,0a30.35,30.35 0,1 1,60.71 0a30.35,30.35 0,1 1,-60.71 0"
+ android:fillColor="#653661"/>
<path
- android:pathData="M119.34,251.2l-4.27,-4.14h-5.88v-5.88l-4.23,-4.23 4.23,-4.23v-5.88h5.88l4.27,-4.23 4.2,4.23h5.88v5.88l4.23,4.23 -4.23,4.23v5.88h-5.88l-4.2,4.14ZM119.34,243.08c-1.71,0 -3.16,-0.6 -4.36,-1.8 -1.2,-1.2 -1.8,-2.65 -1.8,-4.36s0.6,-3.16 1.8,-4.36c1.2,-1.2 2.65,-1.8 4.36,-1.8 1.71,0 3.16,0.6 4.36,1.8 1.2,1.2 1.8,2.65 1.8,4.36s-0.6,3.16 -1.8,4.36c-1.2,1.2 -2.65,1.8 -4.36,1.8ZM119.34,241.18c1.2,0 2.21,-0.41 3.03,-1.23 0.82,-0.82 1.23,-1.83 1.23,-3.03 -0,-1.2 -0.41,-2.21 -1.23,-3.03 -0.82,-0.82 -1.83,-1.23 -3.03,-1.23 -1.2,0 -2.21,0.41 -3.03,1.23 -0.82,0.82 -1.23,1.83 -1.23,3.03s0.41,2.21 1.23,3.03c0.82,0.82 1.83,1.23 3.03,1.23ZM119.34,248.55l3.41,-3.38h4.77v-4.77l3.44,-3.44 -3.44,-3.44v-4.77h-4.77l-3.41,-3.44 -3.48,3.44h-4.77v4.77l-3.44,3.44 3.44,3.44v4.77h4.74l3.51,3.38Z"
- android:fillColor="#fff"/>
+ android:pathData="M206.5,57.35m-30.35,0a30.35,30.35 0,1 1,60.71 0a30.35,30.35 0,1 1,-60.71 0"
+ android:fillColor="#D9BAFD"/>
<path
- android:pathData="M207.39,212.99h0c13.21,0 23.92,10.71 23.92,23.92h0c0,13.21 -10.71,23.92 -23.92,23.92h0c-13.21,0 -23.92,-10.71 -23.92,-23.92h0c0,-13.21 10.71,-23.92 23.92,-23.92Z"
- android:fillColor="#de9834"/>
+ android:pathData="M308.65,57.35m-30.35,0a30.35,30.35 0,1 1,60.71 0a30.35,30.35 0,1 1,-60.71 0"
+ android:fillColor="#FFB3AE"/>
<path
- android:pathData="M207.42,251.21l-4.27,-4.14h-5.88v-5.88l-4.23,-4.23 4.23,-4.23v-5.88h5.88l4.27,-4.23 4.2,4.23h5.88v5.88l4.23,4.23 -4.23,4.23v5.88h-5.88l-4.2,4.14ZM207.42,243.09c1.71,0 3.16,-0.6 4.36,-1.8 1.2,-1.2 1.8,-2.65 1.8,-4.36s-0.6,-3.16 -1.8,-4.36c-1.2,-1.2 -2.65,-1.8 -4.36,-1.8 -1.71,0 -3.16,0.6 -4.36,1.8 -1.2,1.2 -1.8,2.65 -1.8,4.36s0.6,3.16 1.8,4.36c1.2,1.2 2.65,1.8 4.36,1.8ZM207.42,241.19c1.2,0 2.21,-0.41 3.03,-1.23 0.82,-0.82 1.23,-1.83 1.23,-3.03 -0,-1.2 -0.41,-2.21 -1.23,-3.03 -0.82,-0.82 -1.83,-1.23 -3.03,-1.23 -1.2,0 -2.21,0.41 -3.03,1.23 -0.82,0.82 -1.23,1.83 -1.23,3.03s0.41,2.21 1.23,3.03c0.82,0.82 1.83,1.23 3.03,1.23ZM207.42,248.55l3.41,-3.38h4.77v-4.77l3.44,-3.44 -3.44,-3.44v-4.77h-4.77l-3.41,-3.44 -3.48,3.44h-4.77v4.77l-3.44,3.44 3.44,3.44v4.77h4.74l3.51,3.38ZM207.42,241.19c1.2,0 2.21,-0.41 3.03,-1.23 0.82,-0.82 1.23,-1.83 1.23,-3.03 -0,-1.2 -0.41,-2.21 -1.23,-3.03 -0.82,-0.82 -1.83,-1.23 -3.03,-1.23 -1.2,0 -2.21,0.41 -3.03,1.23 -0.82,0.82 -1.23,1.83 -1.23,3.03s0.41,2.21 1.23,3.03c0.82,0.82 1.83,1.23 3.03,1.23Z"
- android:fillColor="#fff"/>
+ android:pathData="M293.76,155.44V145.78C293.76,144.99 294.05,144.32 294.62,143.74C295.2,143.16 295.88,142.88 296.67,142.88C297.48,142.88 298.17,143.16 298.75,143.74C299.32,144.32 299.61,144.99 299.61,145.78V155.44C299.61,156.23 299.32,156.91 298.75,157.48C298.17,158.06 297.48,158.35 296.67,158.35C295.88,158.35 295.2,158.06 294.62,157.48C294.05,156.91 293.76,156.23 293.76,155.44ZM304.32,161.61C303.59,161.61 302.95,161.35 302.4,160.82C301.88,160.27 301.61,159.63 301.61,158.9V142.32C301.61,141.59 301.88,140.96 302.4,140.44C302.95,139.89 303.59,139.62 304.32,139.62H314.61C315.35,139.62 315.97,139.89 316.5,140.44C317.05,140.96 317.32,141.59 317.32,142.32V158.9C317.32,159.63 317.05,160.27 316.5,160.82C315.97,161.35 315.35,161.61 314.61,161.61H304.32ZM319.32,155.44V145.78C319.32,144.99 319.61,144.32 320.19,143.74C320.76,143.16 321.46,142.88 322.27,142.88C323.05,142.88 323.74,143.16 324.31,143.74C324.89,144.32 325.17,144.99 325.17,145.78V155.44C325.17,156.23 324.89,156.91 324.31,157.48C323.74,158.06 323.05,158.35 322.27,158.35C321.46,158.35 320.76,158.06 320.19,157.48C319.61,156.91 319.32,156.23 319.32,155.44ZM304.32,158.9H314.61V142.32H304.32V158.9Z"
+ android:fillColor="#04409F"/>
<path
- android:pathData="M292.7,212.98h0c13.21,0 23.92,10.71 23.92,23.92h0c0,13.21 -10.71,23.92 -23.92,23.92h0c-13.21,0 -23.92,-10.71 -23.92,-23.92h0c0,-13.21 10.71,-23.92 23.92,-23.92Z"
- android:fillColor="#438947"/>
+ android:pathData="M298.51,258.68C297.75,258.68 297.11,258.42 296.59,257.9C296.06,257.37 295.8,256.73 295.8,255.97V239.52C295.8,238.79 296.06,238.16 296.59,237.63C297.11,237.08 297.75,236.81 298.51,236.81H300.75V233.31C300.75,231.22 301.48,229.43 302.95,227.93C304.44,226.44 306.25,225.7 308.37,225.7C310.49,225.7 312.28,226.44 313.75,227.93C315.24,229.43 315.99,231.22 315.99,233.31V236.81H318.22C318.98,236.81 319.63,237.08 320.15,237.63C320.67,238.16 320.93,238.79 320.93,239.52V255.97C320.93,256.73 320.67,257.37 320.15,257.9C319.63,258.42 318.98,258.68 318.22,258.68H298.51ZM308.37,250.83C309.2,250.83 309.92,250.54 310.53,249.96C311.13,249.36 311.43,248.64 311.43,247.8C311.43,246.99 311.13,246.27 310.53,245.65C309.92,244.99 309.2,244.66 308.37,244.66C307.53,244.66 306.81,244.99 306.21,245.65C305.61,246.27 305.3,246.99 305.3,247.8C305.3,248.64 305.61,249.36 306.21,249.96C306.81,250.54 307.53,250.83 308.37,250.83ZM303.42,236.81H313.32V233.31C313.32,231.95 312.83,230.8 311.86,229.86C310.89,228.89 309.73,228.4 308.37,228.4C307.01,228.4 305.84,228.89 304.87,229.86C303.9,230.8 303.42,231.95 303.42,233.31V236.81Z"
+ android:fillColor="#004E5D"/>
<path
- android:pathData="M284.29,248.97c-0.54,0 -1,-0.19 -1.37,-0.57s-0.57,-0.83 -0.57,-1.37v-14.02c0,-0.54 0.19,-1 0.57,-1.37s0.83,-0.57 1.37,-0.57h2.26v-3.1c0,-1.7 0.6,-3.15 1.79,-4.35 1.2,-1.2 2.64,-1.79 4.35,-1.79s3.15,0.6 4.35,1.79c1.2,1.2 1.79,2.64 1.79,4.35v3.1h2.26c0.54,0 1,0.19 1.37,0.57s0.57,0.83 0.57,1.37v14.02c0,0.54 -0.19,1 -0.57,1.37s-0.83,0.57 -1.37,0.57h-16.8ZM284.29,247.03h16.8v-14.02h-16.8v14.02ZM292.7,242.51c0.69,0 1.28,-0.24 1.76,-0.71s0.73,-1.04 0.73,-1.71c0,-0.65 -0.24,-1.23 -0.73,-1.76s-1.07,-0.79 -1.76,-0.79 -1.28,0.26 -1.76,0.79c-0.48,0.53 -0.73,1.11 -0.73,1.76 0,0.67 0.24,1.24 0.73,1.71s1.07,0.71 1.76,0.71ZM288.5,231.07h8.4v-3.1c0,-1.16 -0.41,-2.15 -1.23,-2.97 -0.82,-0.82 -1.81,-1.23 -2.97,-1.23s-2.15,0.41 -2.97,1.23 -1.23,1.81 -1.23,2.97v3.1ZM284.29,247.03v0Z"
- android:fillColor="#fff"/>
+ android:pathData="M207.21,48.5C206.4,48.5 205.69,48.22 205.09,47.64C204.51,47.04 204.22,46.33 204.22,45.52C204.22,44.71 204.51,44.01 205.09,43.44C205.69,42.84 206.4,42.54 207.21,42.54C208.02,42.54 208.71,42.84 209.29,43.44C209.89,44.01 210.19,44.71 210.19,45.52C210.19,46.33 209.89,47.04 209.29,47.64C208.71,48.22 208.02,48.5 207.21,48.5ZM202.77,72.62V53.26C201.36,53.13 199.96,52.98 198.57,52.82C197.18,52.64 195.83,52.41 194.52,52.12C194.13,52.01 193.82,51.8 193.58,51.49C193.37,51.15 193.32,50.78 193.42,50.39C193.53,50 193.75,49.72 194.09,49.56C194.46,49.38 194.85,49.33 195.27,49.41C197.18,49.83 199.13,50.13 201.12,50.31C203.11,50.47 205.14,50.55 207.21,50.55C209.28,50.55 211.3,50.47 213.29,50.31C215.28,50.13 217.24,49.83 219.15,49.41C219.57,49.33 219.94,49.38 220.29,49.56C220.65,49.72 220.89,50 220.99,50.39C221.1,50.78 221.03,51.15 220.79,51.49C220.59,51.8 220.29,52.01 219.89,52.12C218.58,52.41 217.24,52.64 215.85,52.82C214.46,52.98 213.06,53.13 211.65,53.26V72.62C211.65,72.98 211.51,73.3 211.25,73.56C210.99,73.82 210.66,73.95 210.27,73.95C209.9,73.95 209.59,73.82 209.33,73.56C209.07,73.3 208.93,72.98 208.93,72.62V64.09H205.48V72.62C205.48,72.98 205.35,73.3 205.09,73.56C204.82,73.82 204.51,73.95 204.14,73.95C203.75,73.95 203.42,73.82 203.16,73.56C202.9,73.3 202.77,72.98 202.77,72.62Z"
+ android:fillColor="#5629A4"/>
<path
- android:pathData="M207.39,126.06h0c13.21,0 23.92,10.71 23.92,23.92h0c0,13.21 -10.71,23.92 -23.92,23.92h0c-13.21,0 -23.92,-10.71 -23.92,-23.92h0c0,-13.21 10.71,-23.92 23.92,-23.92Z"
- android:fillColor="#327969"/>
+ android:pathData="M308.85,74.49C306.6,74.49 304.48,74.07 302.49,73.23C300.5,72.37 298.76,71.2 297.27,69.74C295.8,68.24 294.63,66.5 293.77,64.51C292.93,62.52 292.51,60.4 292.51,58.15C292.51,56.21 292.83,54.38 293.46,52.65C294.08,50.9 294.96,49.33 296.09,47.94C296.4,47.52 296.79,47.31 297.27,47.31C297.74,47.31 298.14,47.44 298.48,47.7C298.82,47.99 299.05,48.38 299.15,48.88C299.26,49.35 299.11,49.84 298.72,50.34C297.88,51.44 297.23,52.64 296.75,53.95C296.28,55.26 296.05,56.66 296.05,58.15C296.05,61.71 297.29,64.73 299.78,67.22C302.27,69.71 305.29,70.95 308.85,70.95C312.41,70.95 315.43,69.71 317.92,67.22C320.41,64.73 321.65,61.71 321.65,58.15C321.65,56.66 321.42,55.26 320.95,53.95C320.47,52.64 319.83,51.44 319.02,50.34C318.63,49.84 318.47,49.35 318.55,48.88C318.65,48.41 318.88,48.03 319.22,47.74C319.58,47.46 319.99,47.31 320.43,47.31C320.91,47.31 321.3,47.52 321.61,47.94C322.77,49.35 323.65,50.92 324.28,52.65C324.91,54.38 325.23,56.21 325.23,58.15C325.23,60.4 324.79,62.52 323.93,64.51C323.07,66.5 321.9,68.24 320.43,69.74C318.97,71.2 317.24,72.37 315.25,73.23C313.26,74.07 311.13,74.49 308.85,74.49ZM308.85,60.15C308.35,60.15 307.93,59.98 307.59,59.64C307.25,59.3 307.08,58.88 307.08,58.39V43.58C307.08,43.08 307.25,42.66 307.59,42.32C307.93,41.96 308.35,41.77 308.85,41.77C309.35,41.77 309.77,41.96 310.11,42.32C310.45,42.66 310.62,43.08 310.62,43.58V58.39C310.62,58.88 310.45,59.3 310.11,59.64C309.77,59.98 309.35,60.15 308.85,60.15Z"
+ android:fillColor="#8A1A16"/>
<path
- android:pathData="M210.29,162.68v-2.25c2.34,-0.68 4.26,-1.97 5.74,-3.89 1.49,-1.92 2.23,-4.1 2.23,-6.54 0,-2.44 -0.74,-4.62 -2.21,-6.56 -1.47,-1.93 -3.39,-3.22 -5.76,-3.88v-2.25c2.99,0.68 5.43,2.19 7.32,4.55 1.88,2.35 2.83,5.06 2.83,8.13s-0.94,5.78 -2.83,8.13c-1.88,2.35 -4.32,3.87 -7.32,4.55ZM194.35,154.39v-8.69h5.8l7.24,-7.24v23.18l-7.24,-7.24h-5.8ZM209.56,156.13v-12.21c1.33,0.41 2.38,1.18 3.17,2.32 0.78,1.13 1.18,2.4 1.18,3.8 0,1.38 -0.4,2.63 -1.2,3.77s-1.85,1.91 -3.15,2.32ZM205.21,143.96l-4.09,3.91h-4.6v4.35h4.6l4.09,3.95v-12.21Z"
- android:fillColor="#fff"/>
+ android:pathData="M218.63,150.57C218.63,148.24 217.99,146.13 216.71,144.25C215.45,142.34 213.77,140.94 211.65,140.05C211.3,139.89 211.06,139.65 210.9,139.34C210.74,139 210.73,138.67 210.86,138.36C211.02,137.99 211.26,137.74 211.61,137.61C211.97,137.46 212.35,137.46 212.74,137.61C215.34,138.71 217.42,140.43 218.99,142.76C220.56,145.09 221.35,147.69 221.35,150.57C221.35,153.45 220.56,156.06 218.99,158.39C217.42,160.72 215.34,162.43 212.74,163.53C212.35,163.69 211.97,163.7 211.61,163.57C211.26,163.41 211.02,163.15 210.86,162.79C210.73,162.47 210.74,162.16 210.9,161.84C211.06,161.5 211.3,161.25 211.65,161.1C213.77,160.21 215.45,158.82 216.71,156.93C217.99,155.02 218.63,152.9 218.63,150.57ZM199.35,155.32H194.4C194.04,155.32 193.72,155.19 193.46,154.93C193.2,154.67 193.07,154.35 193.07,153.99V147.23C193.07,146.87 193.2,146.55 193.46,146.29C193.72,146.03 194.04,145.9 194.4,145.9H199.35L204.93,140.32C205.35,139.9 205.83,139.81 206.38,140.05C206.93,140.28 207.21,140.7 207.21,141.3V159.92C207.21,160.52 206.93,160.94 206.38,161.17C205.83,161.41 205.35,161.32 204.93,160.9L199.35,155.32ZM214.28,150.61C214.28,151.87 213.98,153.02 213.37,154.07C212.77,155.11 211.95,155.94 210.9,156.54C210.66,156.67 210.44,156.67 210.23,156.54C210.02,156.38 209.92,156.17 209.92,155.91V145.23C209.92,144.97 210.02,144.77 210.23,144.64C210.44,144.51 210.66,144.51 210.9,144.64C211.95,145.24 212.77,146.08 213.37,147.15C213.98,148.23 214.28,149.38 214.28,150.61Z"
+ android:fillColor="#8D0053"/>
<path
- android:pathData="M292.7,126.1h0c13.21,0 23.92,10.71 23.92,23.92h0c0,13.21 -10.71,23.92 -23.92,23.92h0c-13.21,0 -23.92,-10.71 -23.92,-23.92h0c0,-13.21 10.71,-23.92 23.92,-23.92Z"
- android:fillColor="#9f3ebf"/>
+ android:pathData="M100.23,155.32H95.29C94.92,155.32 94.6,155.19 94.34,154.93C94.08,154.67 93.95,154.35 93.95,153.99V147.23C93.95,146.87 94.08,146.55 94.34,146.29C94.6,146.03 94.92,145.9 95.29,145.9H100.23L105.81,140.32C106.23,139.9 106.71,139.81 107.26,140.05C107.81,140.28 108.09,140.7 108.09,141.3V159.92C108.09,160.52 107.81,160.94 107.26,161.18C106.71,161.41 106.23,161.32 105.81,160.9L100.23,155.32ZM115.16,150.61C115.16,151.87 114.86,153.02 114.25,154.07C113.65,155.11 112.83,155.94 111.78,156.54C111.54,156.67 111.32,156.67 111.11,156.54C110.9,156.38 110.8,156.17 110.8,155.91V145.23C110.8,144.97 110.9,144.77 111.11,144.64C111.32,144.51 111.54,144.51 111.78,144.64C112.83,145.24 113.65,146.08 114.25,147.15C114.86,148.23 115.16,149.38 115.16,150.61Z"
+ android:fillColor="#8D0053"/>
<path
- android:pathData="M299.38,140.4h-13.36v19.23h13.36v-19.23Z"
- android:fillColor="#fff"/>
+ android:pathData="M201.95,255.54H197.35C196.59,255.54 195.95,255.28 195.43,254.76C194.9,254.23 194.64,253.59 194.64,252.83V248.24L191.26,244.86C191.03,244.6 190.85,244.31 190.71,243.99C190.58,243.65 190.52,243.32 190.52,242.97C190.52,242.63 190.58,242.31 190.71,241.99C190.85,241.65 191.03,241.35 191.26,241.09L194.64,237.71V233.12C194.64,232.36 194.9,231.72 195.43,231.19C195.95,230.67 196.59,230.41 197.35,230.41H201.95L205.32,227.03C205.58,226.79 205.88,226.61 206.23,226.48C206.57,226.35 206.91,226.28 207.25,226.28C207.59,226.28 207.91,226.35 208.23,226.48C208.54,226.61 208.84,226.81 209.13,227.07L212.47,230.41H217.06C217.82,230.41 218.46,230.67 218.99,231.19C219.51,231.72 219.77,232.36 219.77,233.12V237.71L223.15,241.09C223.39,241.35 223.57,241.65 223.7,241.99C223.83,242.31 223.9,242.63 223.9,242.97C223.9,243.32 223.83,243.65 223.7,243.99C223.57,244.31 223.39,244.6 223.15,244.86L219.77,248.24V252.83C219.77,253.59 219.51,254.23 218.99,254.76C218.46,255.28 217.82,255.54 217.06,255.54H212.47L209.13,258.84C208.84,259.1 208.54,259.3 208.23,259.43C207.91,259.56 207.59,259.63 207.25,259.63C206.91,259.63 206.57,259.56 206.23,259.43C205.91,259.3 205.62,259.1 205.36,258.84L201.95,255.54ZM207.25,250.71C209.37,250.71 211.19,249.95 212.71,248.43C214.22,246.91 214.98,245.08 214.98,242.93C214.98,240.81 214.22,238.99 212.71,237.48C211.19,235.96 209.37,235.2 207.25,235.2C205.1,235.2 203.27,235.96 201.75,237.48C200.23,238.99 199.47,240.81 199.47,242.93C199.47,245.08 200.23,246.91 201.75,248.43C203.27,249.95 205.1,250.71 207.25,250.71ZM203.04,252.83L207.25,256.92L211.33,252.83H217.06V247.1L221.23,242.97L217.06,238.85V233.12H211.33L207.25,228.96L203.08,233.12H197.35V238.85L193.19,242.97L197.35,247.1V252.83H203.04Z"
+ android:fillColor="#753403"/>
<path
- android:pathData="M284.23,144.18h-4.81v11.84h4.81v-11.84Z"
- android:fillColor="#fff"/>
+ android:pathData="M99.68,255.54H95.09C94.33,255.54 93.69,255.28 93.16,254.76C92.64,254.23 92.38,253.59 92.38,252.83V248.24L89,244.86C88.77,244.6 88.58,244.31 88.45,243.99C88.32,243.65 88.26,243.32 88.26,242.97C88.26,242.63 88.32,242.31 88.45,241.99C88.58,241.65 88.77,241.35 89,241.09L92.38,237.71V233.12C92.38,232.36 92.64,231.72 93.16,231.19C93.69,230.67 94.33,230.41 95.09,230.41H99.68L103.06,227.03C103.32,226.79 103.62,226.61 103.96,226.48C104.3,226.35 104.64,226.28 104.99,226.28C105.32,226.28 105.65,226.35 105.97,226.48C106.28,226.61 106.58,226.81 106.87,227.07L110.21,230.41H114.8C115.56,230.41 116.2,230.67 116.73,231.19C117.25,231.72 117.51,232.36 117.51,233.12V237.71L120.89,241.09C121.13,241.35 121.31,241.65 121.44,241.99C121.57,242.31 121.64,242.63 121.64,242.97C121.64,243.32 121.57,243.65 121.44,243.99C121.31,244.31 121.13,244.6 120.89,244.86L117.51,248.24V252.83C117.51,253.59 117.25,254.23 116.73,254.76C116.2,255.28 115.56,255.54 114.8,255.54H110.21L106.87,258.84C106.58,259.1 106.28,259.3 105.97,259.43C105.65,259.56 105.32,259.63 104.99,259.63C104.64,259.63 104.3,259.56 103.96,259.43C103.65,259.3 103.36,259.1 103.1,258.84L99.68,255.54ZM104.99,250.71C107.11,250.71 108.93,249.95 110.44,248.43C111.96,246.91 112.72,245.08 112.72,242.93C112.72,240.81 111.96,238.99 110.44,237.48C108.93,235.96 107.11,235.2 104.99,235.2C102.84,235.2 101.01,235.96 99.49,237.48C97.97,238.99 97.21,240.81 97.21,242.93C97.21,245.08 97.97,246.91 99.49,248.43C101.01,249.95 102.84,250.71 104.99,250.71ZM104.99,248C103.57,248 102.37,247.52 101.37,246.55C100.4,245.55 99.92,244.35 99.92,242.93C99.92,241.52 100.4,240.33 101.37,239.36C102.37,238.39 103.57,237.91 104.99,237.91C106.4,237.91 107.59,238.39 108.56,239.36C109.53,240.33 110.01,241.52 110.01,242.93C110.01,244.35 109.53,245.55 108.56,246.55C107.59,247.52 106.4,248 104.99,248ZM100.78,252.83L104.99,256.92L109.07,252.83H114.8V247.1L118.96,242.97L114.8,238.85V233.12H109.07L104.99,228.96L100.82,233.12H95.09V238.85L90.93,242.97L95.09,247.1V252.83H100.78Z"
+ android:fillColor="#753403"/>
<path
- android:pathData="M305.97,144.18h-4.81v11.84h4.81v-11.84Z"
- android:fillColor="#fff"/>
- <path
- android:pathData="M288.5,142.7h8.39v14.63h-8.39z"
- android:fillColor="#9f3ebf"/>
- <path
- android:pathData="M119.3,126.06h0c13.21,0 23.92,10.71 23.92,23.92h0c0,13.21 -10.71,23.92 -23.92,23.92h0c-13.21,0 -23.92,-10.71 -23.92,-23.92h0c0,-13.21 10.71,-23.92 23.92,-23.92Z"
- android:fillColor="#327969"/>
- <path
- android:pathData="M109.52,154.35v-8.7h5.8l7.25,-7.25v23.19l-7.25,-7.25h-5.8ZM124.74,156.09v-12.21c1.3,0.41 2.36,1.18 3.15,2.32 0.8,1.14 1.2,2.4 1.2,3.8 0,1.43 -0.4,2.69 -1.2,3.8s-1.85,1.87 -3.15,2.28ZM120.39,143.92l-4.09,3.91h-4.6v4.35h4.6l4.09,3.95v-12.21Z"
- android:fillColor="#fff"/>
- <path
- android:pathData="M119.09,78.27l-3.9,-4.01 -5.93,-0.08c-0.53,-0.01 -0.99,-0.21 -1.38,-0.61 -0.39,-0.4 -0.58,-0.86 -0.57,-1.39l0.26,-19.77c0.01,-0.53 0.21,-0.99 0.61,-1.38 0.4,-0.39 0.86,-0.58 1.39,-0.57l19.77,0.26c0.53,0.01 0.99,0.21 1.38,0.61 0.39,0.4 0.58,0.86 0.57,1.39l-0.26,19.77c-0.01,0.53 -0.21,0.99 -0.61,1.38 -0.4,0.39 -0.86,0.58 -1.39,0.57l-5.93,-0.08 -4.01,3.9ZM119.25,68.61l1.9,-4.19 4.24,-1.79 -4.19,-1.9 -1.79,-4.24 -1.93,4.19 -4.21,1.79 4.16,1.9 1.82,4.24Z"
- android:fillColor="#fff"/>
+ android:pathData="M104.26,74.3L99.87,69.79L93.2,69.7C92.6,69.69 92.09,69.47 91.65,69.01C91.21,68.56 90.99,68.04 91,67.45L91.3,45.2C91.31,44.6 91.53,44.09 91.99,43.65C92.44,43.21 92.96,42.99 93.55,43L115.8,43.3C116.4,43.31 116.91,43.53 117.35,43.99C117.79,44.44 118.01,44.96 118,45.55L117.7,67.8C117.69,68.4 117.46,68.91 117.01,69.35C116.56,69.79 116.04,70.01 115.45,70L108.77,69.91L104.26,74.3ZM104.44,63.44L106.58,58.72L111.36,56.7L106.64,54.56L104.63,49.79L102.45,54.51L97.71,56.52L102.39,58.66L104.44,63.44Z"
+ android:fillColor="#FFD2F6"/>
</vector>
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/values-af/strings.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/values-af/strings.xml
index 56fd06c..d2dcbf7 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/values-af/strings.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/values-af/strings.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="accessibility_menu_service_name" msgid="730136711554740131">"Toeganklikheid-kieslys"</string>
+ <string name="accessibility_menu_service_name" msgid="730136711554740131">"Toeganklikheidkieslys"</string>
<string name="accessibility_menu_intro" msgid="3164193281544042394">"Die Toeganklikheidkieslys bied ’n groot kieslys op die skerm om jou toestel te beheer. Jy kan jou toestel sluit, volume en helderheid beheer, skermskote neem, en meer."</string>
<string name="assistant_label" msgid="6796392082252272356">"Assistent"</string>
<string name="assistant_utterance" msgid="65509599221141377">"Assistent"</string>
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/values-fr-rCA/strings.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/values-fr-rCA/strings.xml
index 851c2c9..87a9503 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/values-fr-rCA/strings.xml
@@ -21,7 +21,7 @@
<string name="previous_button_content_description" msgid="840869171117765966">"Aller à l\'écran précédent"</string>
<string name="next_button_content_description" msgid="6810058269847364406">"Aller à l\'écran suivant"</string>
<string name="accessibility_menu_description" msgid="4458354794093858297">"Le menu Accessibilité propose un grand espace à l\'écran à l\'aide duquel vous pouvez contrôler votre appareil. Utilisez-le pour verrouiller votre appareil, régler le volume et la luminosité, prendre des captures d\'écran et plus."</string>
- <string name="accessibility_menu_summary" msgid="340071398148208130">"Contrôler l\'appareil à l\'aide d\'un menu de grande taille"</string>
+ <string name="accessibility_menu_summary" msgid="340071398148208130">"contrôler l\'appareil à l\'aide d\'un menu de grande taille"</string>
<string name="accessibility_menu_settings_name" msgid="1716888058785672611">"Paramètres du menu Accessibilité"</string>
<string name="accessibility_menu_large_buttons_title" msgid="8978499601044961736">"Boutons de grande taille"</string>
<string name="accessibility_menu_large_buttons_summary" msgid="236873938502785311">"Augmenter la taille des boutons du menu Accessibilité"</string>
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/values-hu/strings.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/values-hu/strings.xml
index 13f8c09..7b3d750 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/values-hu/strings.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/values-hu/strings.xml
@@ -21,7 +21,7 @@
<string name="previous_button_content_description" msgid="840869171117765966">"Ugrás az előző képernyőre"</string>
<string name="next_button_content_description" msgid="6810058269847364406">"Ugrás a következő képernyőre"</string>
<string name="accessibility_menu_description" msgid="4458354794093858297">"A Kisegítő lehetőségek menüje az eszköz vezérlésére szolgáló nagyméretű, képernyőn megjelenő menü. Lezárhatja az eszközt, szabályozhatja a hang- és a fényerőt, képernyőképeket készíthet, és egyebekre is használhatja a funkciót."</string>
- <string name="accessibility_menu_summary" msgid="340071398148208130">"Nagyméretű menün keresztül vezérelheti eszközét"</string>
+ <string name="accessibility_menu_summary" msgid="340071398148208130">"Nagy méretű menün keresztül vezérelheti eszközét"</string>
<string name="accessibility_menu_settings_name" msgid="1716888058785672611">"A kisegítő lehetőségek menü beállításai"</string>
<string name="accessibility_menu_large_buttons_title" msgid="8978499601044961736">"Nagy gombok"</string>
<string name="accessibility_menu_large_buttons_summary" msgid="236873938502785311">"A Kisegítő lehetőségek menüben található gombok méretének növelése"</string>
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/values-iw/strings.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/values-iw/strings.xml
index afb337d..a7156c4 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/values-iw/strings.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/values-iw/strings.xml
@@ -21,7 +21,7 @@
<string name="previous_button_content_description" msgid="840869171117765966">"מעבר למסך הקודם"</string>
<string name="next_button_content_description" msgid="6810058269847364406">"מעבר למסך הבא"</string>
<string name="accessibility_menu_description" msgid="4458354794093858297">"תפריט הנגישות הוא תפריט גדול שמופיע במסך ומאפשר לשלוט במכשיר. אפשר לנעול את המכשיר, לשלוט בעוצמת הקול ובבהירות, לצלם צילומי מסך ועוד."</string>
- <string name="accessibility_menu_summary" msgid="340071398148208130">"שליטה במכשיר באמצעות התפריט הגדול"</string>
+ <string name="accessibility_menu_summary" msgid="340071398148208130">"שליטה במכשיר דרך התפריט הגדול"</string>
<string name="accessibility_menu_settings_name" msgid="1716888058785672611">"הגדרות של תפריט נגישות"</string>
<string name="accessibility_menu_large_buttons_title" msgid="8978499601044961736">"כפתורים גדולים"</string>
<string name="accessibility_menu_large_buttons_summary" msgid="236873938502785311">"הגדלת הכפתורים של תפריט הנגישות"</string>
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/values-ta/strings.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/values-ta/strings.xml
index 57bbc52..380cf44 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/values-ta/strings.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/values-ta/strings.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="accessibility_menu_service_name" msgid="730136711554740131">"அணுகல்தன்மை மெனு"</string>
+ <string name="accessibility_menu_service_name" msgid="730136711554740131">"மாற்றுத்திறன் மெனு"</string>
<string name="accessibility_menu_intro" msgid="3164193281544042394">"அணுகல்தன்மை மெனுவானது உங்கள் சாதனத்தைக் கட்டுப்படுத்துவதற்கு, திரையில் தோன்றும் பெரிய மெனுவை வழங்குகிறது. சாதனத்தைப் பூட்டுதல், ஒலியளவையும் ஒளிர்வையும் மாற்றுதல், ஸ்கிரீன்ஷாட்களை எடுத்தல் போன்ற பலவற்றைச் செய்யலாம்."</string>
<string name="assistant_label" msgid="6796392082252272356">"Assistant"</string>
<string name="assistant_utterance" msgid="65509599221141377">"அசிஸ்டண்ட்"</string>
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/values-zh-rCN/strings.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/values-zh-rCN/strings.xml
index c896c77..8ac18e5 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/values-zh-rCN/strings.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="accessibility_menu_service_name" msgid="730136711554740131">"无障碍功能菜单"</string>
+ <string name="accessibility_menu_service_name" msgid="730136711554740131">"无障碍菜单"</string>
<string name="accessibility_menu_intro" msgid="3164193281544042394">"无障碍功能菜单是一个放大的菜单,方便您通过屏幕控制设备,完成锁屏、控制音量和亮度、截图等操作。"</string>
<string name="assistant_label" msgid="6796392082252272356">"Google 助理"</string>
<string name="assistant_utterance" msgid="65509599221141377">"Google 助理"</string>
diff --git a/packages/SystemUI/aconfig/accessibility.aconfig b/packages/SystemUI/aconfig/accessibility.aconfig
index 3cb3025..853337e 100644
--- a/packages/SystemUI/aconfig/accessibility.aconfig
+++ b/packages/SystemUI/aconfig/accessibility.aconfig
@@ -4,33 +4,6 @@
# NOTE: Keep alphabetized to help limit merge conflicts from multiple simultaneous editors.
flag {
- name: "add_black_background_for_window_magnifier"
- namespace: "accessibility"
- description: "Set the background for SurfaceView in window magnification black."
- bug: "299981434"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
- name: "create_windowless_window_magnifier"
- namespace: "accessibility"
- description: "Uses SurfaceControlViewHost to create the magnifier for window magnification."
- bug: "280992417"
-}
-
-flag {
- name: "delay_show_magnification_button"
- namespace: "accessibility"
- description: "Delays the showing of magnification mode switch button."
- bug: "338259519"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
name: "floating_menu_animated_tuck"
namespace: "accessibility"
description: "Sets up animations for tucking/untucking and adjusts clipbounds."
@@ -155,3 +128,13 @@
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "privacy_dot_live_region"
+ namespace: "accessibility"
+ description: "Exposes the status bar privacy dot as a live region so it is announced when it appears."
+ bug: "197201744"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/packages/SystemUI/aconfig/desktop_users_and_accounts.aconfig b/packages/SystemUI/aconfig/desktop_users_and_accounts.aconfig
index c7e9c9f..f13ba76 100644
--- a/packages/SystemUI/aconfig/desktop_users_and_accounts.aconfig
+++ b/packages/SystemUI/aconfig/desktop_users_and_accounts.aconfig
@@ -1,6 +1,15 @@
package: "com.android.systemui"
container: "system"
+# NOTE: Keep alphabetized to help limit merge conflicts from multiple simultaneous editors.
+
+flag {
+ name: "back_button_on_bouncer"
+ namespace: "desktop_users_and_accounts"
+ description: "Add a 'back' button to bouncer"
+ bug: "392597328"
+}
+
flag {
name: "user_switcher_add_sign_out_option"
namespace: "desktop_users_and_accounts"
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index c9187b0..dc4d969 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -26,10 +26,10 @@
}
flag {
- name: "udfps_view_performance"
+ name: "fingerprint_cancel_race_mitigation"
namespace: "systemui"
- description: "Decrease screen off blocking calls by waiting until the device is finished going to sleep before adding the udfps view."
- bug: "225183106"
+ description: "Mitigate the race condition between KeyguardUpdateMonitor cancelling a fingerprint listening state and FingerprintManager sending the cancellation."
+ bug: "398973663"
metadata {
purpose: PURPOSE_BUGFIX
}
@@ -300,14 +300,6 @@
}
flag {
- name: "device_entry_udfps_refactor"
- namespace: "systemui"
- description: "Refactoring device entry UDFPS icon to use modern architecture and "
- "consolidating it with the lock/unlock icon to create a combined DeviceEntryIconView"
- bug: "279440316"
-}
-
-flag {
name: "visual_interruptions_refactor"
namespace: "systemui"
description: "Enables the refactored version of the code to decide when notifications "
@@ -423,39 +415,6 @@
}
flag {
- name: "status_bar_monochrome_icons_fix"
- namespace: "systemui"
- description: "Fixes the status bar icon size when drawing InsetDrawables (ie. monochrome icons)"
- bug: "329091967"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
- name: "status_bar_show_audio_only_projection_chip"
- namespace: "systemui"
- description: "Show chip on the left side of the status bar when a user is only sharing *audio* "
- "during a media projection"
- bug: "373308507"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
- name: "status_bar_auto_start_screen_record_chip"
- namespace: "systemui"
- description: "When screen recording, use the specified start time to update the screen record "
- "chip state instead of waiting for an official 'recording started' signal"
- bug: "366448907"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-
-flag {
name: "status_bar_chips_modernization"
namespace: "systemui"
description: "Deprecate OngoingCallController and implement OngoingActivityChips"
@@ -543,6 +502,16 @@
}
flag {
+ name: "status_bar_chip_to_hun_animation"
+ namespace: "systemui"
+ description: "Implement a bespoke tap-chip-to-show-HUN animation for SB notification chips"
+ bug: "393369891"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "status_bar_window_no_custom_touch"
namespace: "systemui"
description: "Don't have any custom touch handling in StatusBarWindowView"
@@ -742,6 +711,13 @@
}
flag {
+ name: "screen_reactions"
+ namespace: "systemui"
+ description: "Enables the new screen recording UX"
+ bug: "410901215"
+}
+
+flag {
name: "clipboard_overlay_multiuser"
namespace: "systemui"
description: "Fix clipboard overlay for secondary users"
@@ -1019,16 +995,6 @@
}
flag {
- name: "bind_keyguard_media_visibility"
- namespace: "systemui"
- description: "Binds Keyguard Media Controller Visibility to MediaContainerView"
- bug: "298213983"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
name: "use_notif_inflation_thread_for_footer"
namespace: "systemui"
description: "use the @NotifInflation thread for FooterView and EmptyShadeView inflation"
@@ -1230,16 +1196,6 @@
}
flag {
- name: "communal_scene_ktf_refactor"
- namespace: "systemui"
- description: "refactors the syncing mechanism between communal STL and KTF state."
- bug: "327225415"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
name: "communal_timer_flicker_fix"
namespace: "systemui"
description: "fixes timers on the hub flickering when pausing"
@@ -1340,16 +1296,6 @@
}
flag {
- name: "dream_overlay_updated_font"
- namespace: "systemui"
- description: "Flag to enable updated font settings for dream overlay"
- bug: "349656117"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
name: "dream_overlay_updated_ui"
namespace: "systemui"
description: "Flag to enable updated UI for dream overlay"
@@ -1375,26 +1321,6 @@
}
flag {
- name: "media_controls_user_initiated_deleteintent"
- namespace: "systemui"
- description: "Only dismiss media notifications when the control was removed by the user."
- bug: "335875159"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
- name: "media_controls_lockscreen_shade_bug_fix"
- namespace: "systemui"
- description: "Use ShadeInteractor for media location changes"
- bug: "319244625"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
name: "media_controls_button_media3"
namespace: "systemui"
description: "Enable media action buttons updates using media3"
@@ -1409,26 +1335,6 @@
}
flag {
- name: "media_controls_drawables_reuse_bugfix"
- namespace: "systemui"
- description: "Re-use created media drawables for media controls"
- bug: "358402034"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
- name: "media_controls_umo_inflation_in_background"
- namespace: "systemui"
- description: "Inflate UMO in background thread"
- bug: "368514198"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
name: "media_controls_ui_update"
namespace: "systemui"
description: "Enables media visuals update"
@@ -1436,16 +1342,6 @@
}
flag {
- name: "media_controls_a11y_colors"
- namespace: "systemui"
- description: "Color scheme updates for improved a11y"
- bug: "378848399"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
name: "media_controls_device_manager_background_execution"
namespace: "systemui"
description: "Sends some instances creation to background thread"
@@ -1621,26 +1517,6 @@
}
flag {
- name: "use_transitions_for_keyguard_occluded"
- namespace: "systemui"
- description: "Use Keyguard Transitions to set Notification Shade occlusion state"
- bug: "344716537"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
- name: "relock_with_power_button_immediately"
- namespace: "systemui"
- description: "UDFPS unlock followed by immediate power button push should relock"
- bug: "343327511"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
name: "classic_flags_multi_user"
namespace: "systemui"
description: "Make the classic feature flag loading multi user aware."
@@ -1691,16 +1567,6 @@
}
flag {
- name: "face_message_defer_update"
- namespace: "systemui"
- description: "Only analyze the last n frames when determining whether to defer a face auth help message like low light"
- bug: "351863611"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
name: "non_touchscreen_devices_bypass_falsing"
namespace: "systemui"
description: "Allow non-touchscreen devices to bypass falsing"
@@ -1749,16 +1615,6 @@
}
flag {
- name: "ignore_touches_next_to_notification_shelf"
- namespace: "systemui"
- description: "The shelf can vertically overlap the unlock icon. Ignore touches if so."
- bug: "358424256"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
name: "shade_window_goes_around"
namespace: "systemui"
description: "Enables the shade window to move between displays"
@@ -1816,16 +1672,6 @@
}
flag {
- name: "only_show_media_stream_slider_in_single_volume_mode"
- namespace: "systemui"
- description: "When the device is in single volume mode, only show media stream slider and hide all other stream (e.g. call, notification, alarm, etc) sliders in volume panel"
- bug: "373729625"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
name: "hide_ringer_button_in_single_volume_mode"
namespace: "systemui"
description: "When the device is in single volume mode, hide the ringer button because it doesn't work"
@@ -1903,26 +1749,6 @@
}
flag {
- name: "notification_reentrant_dismiss"
- namespace: "systemui"
- description: "Posts to avoid a crashing reentrant pipeline run"
- bug: "328328054"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
- name: "stoppable_fgs_system_app"
- namespace: "systemui"
- description: "System app with foreground service can opt in to be stoppable."
- bug: "376564917"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
name: "keyguard_transition_force_finish_on_screen_off"
namespace: "systemui"
description: "Forces KTF transitions to finish if the screen turns all the way off."
@@ -1947,6 +1773,16 @@
}
flag {
+ name: "spatial_model_pushback_in_shader"
+ namespace: "systemui"
+ description: "Change implementation of the depth push scaling effect on apps to use a fullscreen shader"
+ bug: "391463550"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "expanded_privacy_indicators_on_large_screen"
namespace: "systemui"
description: "Larger privacy indicators on large screen"
@@ -2246,4 +2082,21 @@
namespace: "desktop_connectivity"
description: "Enables the multi-user WiFi configuration UI in Interent detailed tiles. This flag depends on qs_tile_detailed_view flag"
bug: "406548883"
-}
\ No newline at end of file
+}
+
+flag {
+ name: "spatial_model_bouncer_pushback"
+ namespace: "systemui"
+ description: "Implement the depth push scaling effect in and out of the bouncer."
+ bug: "404243461"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
+ name: "hsu_behavior_changes"
+ namespace: "systemui"
+ description: "Overall changes on Sytem UI when current user is the Headless System User"
+ bug: "408066623"
+}
diff --git a/packages/SystemUI/animation/lib/Android.bp b/packages/SystemUI/animation/lib/Android.bp
index 7d73023..c6e3de61 100644
--- a/packages/SystemUI/animation/lib/Android.bp
+++ b/packages/SystemUI/animation/lib/Android.bp
@@ -25,8 +25,8 @@
java_library {
name: "PlatformAnimationLib-server",
srcs: [
- "src/com/android/systemui/animation/server/*.java",
":PlatformAnimationLib-aidl",
+ "src/com/android/systemui/animation/server/*.java",
],
static_libs: [
"WindowManager-Shell-shared",
@@ -39,8 +39,8 @@
java_library {
name: "PlatformAnimationLib-core",
srcs: [
- "src/com/android/systemui/animation/*.java",
":PlatformAnimationLib-aidl",
+ "src/com/android/systemui/animation/*.java",
],
static_libs: [
"WindowManager-Shell-shared",
@@ -50,13 +50,14 @@
filegroup {
name: "PlatformAnimationLib-client-srcs",
srcs: [
+ ":PlatformAnimationLib-aidl",
"src/com/android/systemui/animation/OriginRemoteTransition.java",
"src/com/android/systemui/animation/OriginTransitionSession.java",
"src/com/android/systemui/animation/SurfaceUIComponent.java",
"src/com/android/systemui/animation/Transactions.java",
+ "src/com/android/systemui/animation/TransitionAnimationController.java",
"src/com/android/systemui/animation/UIComponent.java",
"src/com/android/systemui/animation/ViewUIComponent.java",
- ":PlatformAnimationLib-aidl",
],
}
diff --git a/packages/SystemUI/animation/lib/src/com/android/systemui/animation/OriginRemoteTransition.java b/packages/SystemUI/animation/lib/src/com/android/systemui/animation/OriginRemoteTransition.java
index 56d85ab..76685a4 100644
--- a/packages/SystemUI/animation/lib/src/com/android/systemui/animation/OriginRemoteTransition.java
+++ b/packages/SystemUI/animation/lib/src/com/android/systemui/animation/OriginRemoteTransition.java
@@ -18,9 +18,6 @@
import static android.view.WindowManager.TRANSIT_CHANGE;
-import android.animation.Animator;
-import android.animation.Animator.AnimatorListener;
-import android.animation.ValueAnimator;
import android.annotation.Nullable;
import android.content.Context;
import android.graphics.Rect;
@@ -51,7 +48,8 @@
*
* @hide
*/
-public class OriginRemoteTransition extends IRemoteTransition.Stub {
+public class OriginRemoteTransition extends IRemoteTransition.Stub implements
+ TransitionAnimationController.AnimationRunnerListener {
private static final String TAG = "OriginRemoteTransition";
private static final long FINISH_ANIMATION_TIMEOUT_MS = 100;
@@ -65,9 +63,11 @@
@Nullable private SurfaceControl.Transaction mStartTransaction;
@Nullable private IRemoteTransitionFinishedCallback mFinishCallback;
@Nullable private UIComponent.Transaction mOriginTransaction;
- @Nullable private ValueAnimator mAnimator;
+ @Nullable private TransitionAnimationController mAnimationController;
@Nullable private SurfaceControl mOriginLeash;
- private boolean mCancelled;
+ @Nullable private IBinder mLocalTransactionToken;
+ @Nullable private IBinder mShellTransactionToken;
+
OriginRemoteTransition(
Context context,
@@ -134,6 +134,10 @@
private void startAnimationInternal(
TransitionInfo info, @Nullable WindowAnimationState[] states) {
+
+ // setup shared transaction queue
+ shareTransactionQueue();
+
if (!prepareUIs(info)) {
logE("Unable to prepare UI!");
finishAnimation(/* finished= */ false);
@@ -146,32 +150,37 @@
mOriginTransaction.commit();
mStartTransaction.apply();
- // Start the animator.
- mAnimator = ValueAnimator.ofFloat(0.0f, 1.0f);
- mAnimator.setDuration(mDuration);
- mAnimator.addListener(
- new AnimatorListener() {
- @Override
- public void onAnimationStart(Animator a) {}
+ // configure/start animation controller
+ mAnimationController = new TransitionAnimationController(mHandler, this);
+ mAnimationController.addValueAnimation(
+ TAG + (mIsEntry ? "-entryAnimator" : "-exitAnimator"),
+ TransitionAnimationController.LINEAR_INTERPOLATOR,
+ mDuration,
+ 0,
+ 0f,
+ 1f);
+ mAnimationController.startAnimations();
+ }
- @Override
- public void onAnimationEnd(Animator a) {
- finishAnimation(/* finished= */ !mCancelled);
- }
+ /**
+ * @param animatorId specific ID associated with a given animator, used to disambiguate.
+ * @param canceled whether or not the animation was canceled (terminated) or ran to
+ * completion.
+ */
+ @Override
+ public void onAnimationFinished(String animatorId, boolean canceled) {
+ finishAnimation(/* finished= */ !canceled);
+ }
- @Override
- public void onAnimationCancel(Animator a) {
- mCancelled = true;
- }
-
- @Override
- public void onAnimationRepeat(Animator a) {}
- });
- mAnimator.addUpdateListener(
- a -> {
- mPlayer.onProgress((float) a.getAnimatedValue());
- });
- mAnimator.start();
+ /**
+ * @param animatorId specific ID associated with a given animator, used to disambiguate.
+ * @param progress representative value of the current state of progress, start to finish.
+ * @param isFirstFrame whether or not the current update represents the drawing of the
+ * *first* frame of the animation.
+ */
+ @Override
+ public void onAnimationProgressUpdate(String animatorId, float progress, boolean isFirstFrame) {
+ mPlayer.onProgress(progress);
}
private boolean prepareUIs(TransitionInfo info) {
@@ -304,7 +313,7 @@
mHandler.removeCallbacks(timeoutRunnable);
finishInternalRunnable.run();
};
- if (mAnimator == null) {
+ if (mAnimationController == null) {
// The transition didn't start. Ensure we apply the start transaction and report
// finish afterwards.
mStartTransaction
@@ -314,7 +323,8 @@
mHandler.postDelayed(timeoutRunnable, FINISH_ANIMATION_TIMEOUT_MS);
return;
}
- mAnimator = null;
+ mAnimationController = null;
+
// Notify client that we have ended.
mPlayer.onEnd(finished);
// Detach the origin from the transition leash and report finish after it's done.
@@ -340,18 +350,63 @@
mStartTransaction = null;
mOriginTransaction = null;
mFinishCallback = null;
+ unshareTransactionQueue();
}
public void cancel() {
logD("cancel()");
mHandler.post(
() -> {
- if (mAnimator != null) {
- mAnimator.cancel();
+ if (mAnimationController != null) {
+ mAnimationController.cancelAnimations();
}
});
}
+ /**
+ * Provide server side (shell) token for use in applying transactions.
+ * @hide
+ */
+ public void setShellTransactionToken(IBinder shellApplyToken) {
+ mShellTransactionToken = shellApplyToken;
+ }
+
+ /**
+ * Use server side (shell) transaction-queue instead of local/independent one. This is necessary
+ * if client/server need to coordinate transactions (eg. for shell transitions).
+ */
+ private void shareTransactionQueue() {
+ if (mLocalTransactionToken == null) {
+ mLocalTransactionToken = SurfaceControl.Transaction.getDefaultApplyToken();
+ }
+ setupTransactionQueue();
+ }
+
+ /**
+ * Switch back to using local processes independent transaction queue.
+ */
+ private void unshareTransactionQueue() {
+ if (mLocalTransactionToken == null) {
+ return;
+ }
+ SurfaceControl.Transaction.setDefaultApplyToken(mLocalTransactionToken);
+ mLocalTransactionToken = null;
+ mShellTransactionToken = null;
+ }
+
+ private void setupTransactionQueue() {
+ if (mLocalTransactionToken == null) {
+ return;
+ }
+
+ if (mShellTransactionToken == null) {
+ Log.e(TAG, "Didn't receive apply token from server side (shell)");
+ return;
+ }
+
+ SurfaceControl.Transaction.setDefaultApplyToken(mShellTransactionToken);
+ }
+
private static void logD(String msg) {
if (OriginTransitionSession.DEBUG) {
Log.d(TAG, msg);
diff --git a/packages/SystemUI/animation/lib/src/com/android/systemui/animation/OriginTransitionSession.java b/packages/SystemUI/animation/lib/src/com/android/systemui/animation/OriginTransitionSession.java
index cb3dfb9..e79e352 100644
--- a/packages/SystemUI/animation/lib/src/com/android/systemui/animation/OriginTransitionSession.java
+++ b/packages/SystemUI/animation/lib/src/com/android/systemui/animation/OriginTransitionSession.java
@@ -25,6 +25,7 @@
import android.content.Intent;
import android.os.Build;
import android.os.Handler;
+import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;
import android.util.Log;
@@ -64,7 +65,6 @@
@Nullable private final IRemoteTransition mEntryTransition;
@Nullable private final IRemoteTransition mExitTransition;
private final AtomicInteger mState = new AtomicInteger(NOT_STARTED);
-
@Nullable private RemoteTransition mOriginTransition;
private OriginTransitionSession(
@@ -82,6 +82,7 @@
throw new IllegalArgumentException(
"Entry transition must be supplied if you want to play an exit transition!");
}
+
}
/**
@@ -96,6 +97,8 @@
return false;
}
+ setupTransactionQueues();
+
RemoteTransition remoteTransition = null;
if (hasEntryTransition() && hasExitTransition()) {
logD("start: starting with entry and exit transition.");
@@ -160,6 +163,22 @@
return mOriginTransitions != null && mExitTransition != null;
}
+ private void setupTransactionQueues() {
+ final IBinder shellApplyToken;
+ try {
+ shellApplyToken = mOriginTransitions.getDefaultTransactionApplyToken();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error getting server side (shell) apply token", e);
+ return;
+ }
+ if (mEntryTransition != null && mEntryTransition instanceof OriginRemoteTransition) {
+ ((OriginRemoteTransition) mEntryTransition).setShellTransactionToken(shellApplyToken);
+ }
+ if (mExitTransition != null && mExitTransition instanceof OriginRemoteTransition) {
+ ((OriginRemoteTransition) mExitTransition).setShellTransactionToken(shellApplyToken);
+ }
+ }
+
private void logD(String msg) {
if (DEBUG) {
Log.d(TAG, "Session[" + mName + "] - " + msg);
diff --git a/packages/SystemUI/animation/lib/src/com/android/systemui/animation/TransitionAnimationController.java b/packages/SystemUI/animation/lib/src/com/android/systemui/animation/TransitionAnimationController.java
new file mode 100644
index 0000000..6797ad1
--- /dev/null
+++ b/packages/SystemUI/animation/lib/src/com/android/systemui/animation/TransitionAnimationController.java
@@ -0,0 +1,492 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.ValueAnimator;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Handler;
+import android.util.Log;
+import android.view.animation.BaseInterpolator;
+import android.view.animation.LinearInterpolator;
+import android.view.animation.PathInterpolator;
+
+import com.android.internal.dynamicanimation.animation.FloatValueHolder;
+import com.android.internal.dynamicanimation.animation.SpringAnimation;
+import com.android.internal.dynamicanimation.animation.SpringForce;
+
+import java.util.HashMap;
+
+/**
+ * Controller for transition animations. The controller encapsulates a number of
+ * available animators such as springs and values based on differing models (e.g. physics based
+ * springs with damping and stiffness vs path interpolation defined by specific control points).
+ * @hide
+ */
+public class TransitionAnimationController {
+ private static final String TAG = TransitionAnimationController.class.getSimpleName();
+
+ /**
+ * Listener interface to provide callbacks to clients of the {@link
+ * TransitionAnimationController} and the associated animators.
+ */
+ public interface AnimationRunnerListener {
+
+ /**
+ * Callback for animation finish.
+ *
+ * @param animatorId specific ID associated with a given animator, used to disambiguate.
+ * @param canceled whether or not the animation was canceled (terminated) or ran to
+ * completion.
+ */
+ void onAnimationFinished(String animatorId, boolean canceled);
+
+ /**
+ * Callback for animation progress while running.
+ *
+ * @param animatorId specific ID associated with a given animator, used to disambiguate.
+ * @param progress representative value of the current state of progress, start to finish.
+ * @param isFirstFrame whether or not the current update represents the drawing of the
+ * *first* frame of the animation.
+ */
+ void onAnimationProgressUpdate(String animatorId, float progress, boolean isFirstFrame);
+ }
+
+ /** Predefined interpolator based on material definition for standard.accelerate easing. */
+ public static final BaseInterpolator STANDARD_ACCELERATE_INTERPOLATOR =
+ new PathInterpolator(0.3f, 0.0f, 1.0f, 1.0f);
+
+ /** Predefined interpolator based on material definition for standard.decelerate easing. */
+ public static final BaseInterpolator STANDARD_DECELERATE_INTERPOLATOR =
+ new PathInterpolator(0.0f, 0.0f, 0.0f, 1.0f);
+
+ /** Predefined interpolator based on material definition for emphasized.accelerate easing. */
+ public static final BaseInterpolator EMPHASIZED_ACCELERATE_INTERPOLATOR =
+ new PathInterpolator(0.3f, 0.0f, 0.8f, 0.15f);
+
+ /** Predefined interpolator based on material definition for emphasized.decelarate easing. */
+ public static final BaseInterpolator EMPHASIZED_DECELERATE_INTERPOLATOR =
+ new PathInterpolator(0.05f, 0.7f, 0.1f, 1.0f);
+
+ /** Predefined interpolator based on material definition for constant rate. */
+ public static final BaseInterpolator LINEAR_INTERPOLATOR = new LinearInterpolator();
+
+ /** Predefined durations based on material definition of 'medium' */
+ public static final long MEDIUM1_DURATION = 250L;
+
+ public static final long MEDIUM2_DURATION = 300L;
+ public static final long MEDIUM3_DURATION = 350L;
+ public static final long MEDIUM4_DURATION = 400L;
+
+ /** Predefined durations based on material definition of 'short' */
+ public static final long SHORT1_DURATION = 50L;
+
+ public static final long SHORT2_DURATION = 100L;
+ public static final long SHORT3_DURATION = 150L;
+ public static final long SHORT4_DURATION = 200L;
+
+ /**
+ * A general interface representing an animation that can be run as part of the transition. In
+ * the most basic sense, an animation is expected to start and finish (possibly canceled).
+ */
+ private abstract static class AnimationRunner {
+
+ @NonNull final String mAnimatorId;
+ @Nullable final AnimationRunnerListener mAnimationRunnerListener;
+ boolean mIsFirstFrame = true;
+
+ /**
+ * Create a new AnimationRunner given an ID and listener.
+ *
+ * @param animatorId the String id used for disambiguation when using multiple animators.
+ * @param listener the AnimationRunnerListener that will report the progress/state of the
+ * animator (with reference to the provided ID).
+ */
+ AnimationRunner(String animatorId, AnimationRunnerListener listener) {
+ mAnimatorId = animatorId;
+ mAnimationRunnerListener = listener;
+ }
+
+ abstract void startAnimation();
+
+ abstract boolean isRunning();
+
+ abstract void cancel();
+
+ void start(Handler handler) {
+ handler.post(
+ () -> {
+ startAnimation();
+ });
+ }
+
+ void finish(boolean canceled) {
+ if (mAnimationRunnerListener != null) {
+ mAnimationRunnerListener.onAnimationFinished(mAnimatorId, canceled);
+ }
+ }
+ }
+
+ /**
+ * Animation runner for physics based spring animations. Spring animations require the
+ * specification of coefficients for both damping and stiffness.
+ *
+ * <p>Note that spring animations currently do not support delayed start. Also, they do not
+ * support a specified duration as the extent of the animation is determined exclusively by the
+ * physical characteristics of the spring definition.
+ */
+ private class SpringAnimationRunner extends AnimationRunner {
+
+ SpringAnimation mSpringAnimation;
+ static final float DEFAULT_SPRING_START_POSITION = 0f;
+ static final float DEFAULT_SPRING_END_POSITION = 100f;
+ static final float DEFAULT_SPRING_ANIMATION_PROGRESS_FINISH_THRESHOLD = 0.98f;
+ private static final float SPRING_MIN_VISIBLE_CHANGE = 0.39f;
+
+ protected SpringAnimationRunner(
+ @NonNull String animatorId,
+ float damping,
+ float stiffness,
+ @Nullable AnimationRunnerListener listener) {
+ this(animatorId, damping, stiffness, listener, true, 0);
+ }
+
+ protected SpringAnimationRunner(
+ @NonNull String animatorId,
+ float damping,
+ float stiffness,
+ @Nullable AnimationRunnerListener listener,
+ boolean useFinishThreshold,
+ float finishThreshold) {
+ super(animatorId, listener);
+ mSpringAnimation =
+ new SpringAnimation(new FloatValueHolder())
+ .setStartValue(DEFAULT_SPRING_START_POSITION);
+
+ SpringForce springForce =
+ new SpringForce()
+ .setStiffness(stiffness)
+ .setDampingRatio(damping)
+ .setFinalPosition(DEFAULT_SPRING_END_POSITION);
+
+ mSpringAnimation.setSpring(springForce);
+ mSpringAnimation.setMinimumVisibleChange(SPRING_MIN_VISIBLE_CHANGE);
+ mSpringAnimation.addUpdateListener(
+ (animation, value, velocity) -> {
+ try {
+ final float progress =
+ value
+ / (DEFAULT_SPRING_END_POSITION
+ - DEFAULT_SPRING_START_POSITION);
+ if (useFinishThreshold) {
+ if (progress > ((finishThreshold > 0)
+ ? finishThreshold
+ : DEFAULT_SPRING_ANIMATION_PROGRESS_FINISH_THRESHOLD)
+ && mSpringAnimation != null) {
+ mSpringAnimation.skipToEnd();
+ }
+ }
+ if (mAnimationRunnerListener != null) {
+ mAnimationRunnerListener.onAnimationProgressUpdate(
+ mAnimatorId, progress, mIsFirstFrame);
+ }
+ if (mIsFirstFrame) {
+ mIsFirstFrame = false;
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "SpringAnimation update error:" + e);
+ }
+ });
+
+ mSpringAnimation.addEndListener(
+ (animation, canceled, value, velocity) -> {
+ finish(canceled);
+ });
+ }
+
+ @Override
+ public void startAnimation() {
+ if (mSpringAnimation != null) {
+ mSpringAnimation.start();
+ }
+ }
+
+ @Override
+ public boolean isRunning() {
+ return mSpringAnimation != null && mSpringAnimation.isRunning();
+ }
+
+ @Override
+ void cancel() {
+ if (isRunning()) {
+ mSpringAnimation.cancel();
+ }
+ }
+ }
+
+ /**
+ * Animation runner for interpolator based value animators. Value animators require an easing
+ * specification which provides interpolation, along with a duration as they are time based.
+ *
+ * <p>Note that value animators support delayed start.
+ */
+ private class ValueAnimationRunner extends AnimationRunner {
+
+ ValueAnimator mValueAnimator;
+ static final float DEFAULT_EASING_START_POSITION = 0f;
+ static final float DEFAULT_EASING_END_POSITION = 100f;
+
+ protected ValueAnimationRunner(
+ @NonNull String animatorId,
+ @NonNull BaseInterpolator interpolator,
+ long duration,
+ long delay,
+ @Nullable AnimationRunnerListener listener) {
+ this(animatorId, interpolator, duration, delay,
+ DEFAULT_EASING_START_POSITION, DEFAULT_EASING_END_POSITION, listener);
+ }
+
+
+ protected ValueAnimationRunner(
+ @NonNull String animatorId,
+ @NonNull BaseInterpolator interpolator,
+ long duration,
+ long delay,
+ float startValue,
+ float endValue,
+ @Nullable AnimationRunnerListener listener) {
+ super(animatorId, listener);
+ mValueAnimator =
+ ValueAnimator.ofFloat(startValue, endValue)
+ .setDuration(duration);
+ mValueAnimator.setStartDelay(delay);
+ mValueAnimator.setInterpolator(interpolator);
+
+ mValueAnimator.addUpdateListener(
+ new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(@NonNull ValueAnimator animation) {
+ final float progress = animation.getAnimatedFraction();
+ if (mAnimationRunnerListener != null) {
+ mAnimationRunnerListener.onAnimationProgressUpdate(
+ mAnimatorId, progress, mIsFirstFrame);
+ }
+ if (mIsFirstFrame) {
+ mIsFirstFrame = false;
+ }
+ }
+ });
+
+ mValueAnimator.addListener(
+ new Animator.AnimatorListener() {
+ @Override
+ public void onAnimationStart(@NonNull Animator animation) {}
+
+ @Override
+ public void onAnimationEnd(@NonNull Animator animation) {
+ finish(false);
+ }
+
+ @Override
+ public void onAnimationCancel(@NonNull Animator animation) {
+ finish(true);
+ }
+
+ @Override
+ public void onAnimationRepeat(@NonNull Animator animation) {}
+ });
+ }
+
+ @Override
+ void startAnimation() {
+ if (mValueAnimator != null) {
+ mValueAnimator.start();
+ }
+ }
+
+ @Override
+ boolean isRunning() {
+ return mValueAnimator != null
+ && (mValueAnimator.isStarted() || mValueAnimator.isRunning());
+ }
+
+ @Override
+ void cancel() {
+ if (isRunning()) {
+ mValueAnimator.cancel();
+ }
+ }
+ }
+
+ private final HashMap<String, AnimationRunner> mAnimationRunners = new HashMap<>();
+ private final Handler mHandler;
+ private final AnimationRunnerListener mAnimationRunnerListener;
+
+ /**
+ * Create a new WearTransitionAnimationController which can be used to manage and control the
+ * specific animators required for any given transition.
+ *
+ * @param handler the handler on which the animator will be started. Note that some animators
+ * specifically require that this be the *main* handler.
+ * @param listener an optional listener that clients can provide in order to receive callbacks
+ * regarding the progress/completion of the associated animators.
+ */
+ public TransitionAnimationController(
+ @NonNull Handler handler, @Nullable AnimationRunnerListener listener) {
+ mHandler = handler;
+ mAnimationRunnerListener = listener;
+ }
+
+ /**
+ * Add a value animator to the controller for use with a transition animation.
+ *
+ * @param animatorId the specific ID to associate with this animator. It will be used to
+ * disambiguate between this and other animators that migth exist within this controller for
+ * the purpose of callbacks etc.
+ * @param interpolator the easing specification for the value animator.
+ * @param duration the time based duration (in ms) for the animation.
+ * @param delay the time based delay (in ms) for the animation. If the animation should be run
+ * immediately, this should be 0.
+ * @param startValue the float value at which the value animator should start.
+ * @param endValue the float value at which the value animator should finish.
+ */
+ public void addValueAnimation(
+ @NonNull String animatorId,
+ @NonNull BaseInterpolator interpolator,
+ long duration,
+ long delay,
+ float startValue,
+ float endValue) {
+ if (checkAnimationsRunning()) {
+ throw new IllegalStateException(
+ "Cannot add more animators when animators are already started/running");
+ }
+ mAnimationRunners.put(
+ animatorId,
+ new ValueAnimationRunner(
+ animatorId,
+ interpolator,
+ duration,
+ delay,
+ startValue,
+ endValue,
+ mAnimationRunnerListener));
+ }
+
+ /**
+ * Add a value animator to the controller for use with a transition animation.
+ *
+ * @param animatorId the specific ID to associate with this animator. It will be used to
+ * disambiguate between this and other animators that migth exist within this controller for
+ * the purpose of callbacks etc.
+ * @param interpolator the easing specification for the value animator.
+ * @param duration the time based duration (in ms) for the animation.
+ * @param delay the time based delay (in ms) for the animation. If the animation should be run
+ * immediately, this should be 0.
+ */
+ public void addValueAnimation(
+ @NonNull String animatorId,
+ @NonNull BaseInterpolator interpolator,
+ long duration,
+ long delay) {
+ if (checkAnimationsRunning()) {
+ throw new IllegalStateException(
+ "Cannot add more animators when animators are already started/running");
+ }
+ mAnimationRunners.put(
+ animatorId,
+ new ValueAnimationRunner(
+ animatorId, interpolator, duration, delay, mAnimationRunnerListener));
+ }
+
+ /**
+ * Add a spring animator to the controller for use with the transition animation.
+ *
+ * @param animatorId the specific ID to associate with this animator. It will be used to
+ * disambiguate between this and other animators that migth exist within this controller for
+ * the purpose of callbacks etc.
+ * @param damping the damping coefficient for the spring used in the animation.
+ * @param stiffness the stiffness coefficient for the spring used in the animation.
+ */
+ public void addSpringAnimation(@NonNull String animatorId, float damping, float stiffness) {
+ addSpringAnimation(animatorId, damping, stiffness, true, 0);
+ }
+
+ /**
+ * Add a spring animator with finish threshold control to the controller for use with the
+ * transition animation.
+ *
+ * @param animatorId the specific ID to associate with this animator. It will be used to
+ * disambiguate between this and other animators that migth exist within this controller for
+ * the purpose of callbacks etc.
+ * @param damping the damping coefficient for the spring used in the animation.
+ * @param stiffness the stiffness coefficient for the spring used in the animation.
+ * @param useFinishThreshold true to set a finish threshold at which the animation will skip to
+ * the end, otherwise set to false to let the animation complete.
+ * @param finishThreshold the threshold (e.g. 0.95f for 98% or 0 for the default). This value is
+ * ignored if useFinishThreshold is false.
+ */
+ public void addSpringAnimation(
+ @NonNull String animatorId,
+ float damping,
+ float stiffness,
+ boolean useFinishThreshold,
+ float finishThreshold) {
+ if (checkAnimationsRunning()) {
+ throw new IllegalStateException(
+ "Cannot add more animators when animators are already started/running");
+ }
+ mAnimationRunners.put(
+ animatorId,
+ new SpringAnimationRunner(
+ animatorId,
+ damping,
+ stiffness,
+ mAnimationRunnerListener,
+ useFinishThreshold,
+ finishThreshold));
+ }
+
+ /** Start all animations associated with this controller. */
+ public void startAnimations() {
+ for (AnimationRunner runner : mAnimationRunners.values()) {
+ runner.start(mHandler);
+ }
+ }
+
+ /**
+ * Check if any animations associated with this controller are still running.
+ *
+ * @return true if any of the associated animations are still running, otherwise false.
+ */
+ public boolean checkAnimationsRunning() {
+ for (AnimationRunner runner : mAnimationRunners.values()) {
+ if (runner.isRunning()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /** Stop all animations associated with this controller. */
+ public void cancelAnimations() {
+ for (AnimationRunner runner : mAnimationRunners.values()) {
+ runner.cancel();
+ }
+ }
+}
diff --git a/packages/SystemUI/animation/lib/src/com/android/systemui/animation/server/IOriginTransitionsImpl.java b/packages/SystemUI/animation/lib/src/com/android/systemui/animation/server/IOriginTransitionsImpl.java
index 3b66460..62e602e4 100644
--- a/packages/SystemUI/animation/lib/src/com/android/systemui/animation/server/IOriginTransitionsImpl.java
+++ b/packages/SystemUI/animation/lib/src/com/android/systemui/animation/server/IOriginTransitionsImpl.java
@@ -101,6 +101,12 @@
}
}
+ @Override
+ public IBinder getDefaultTransactionApplyToken() {
+ enforceRemoteTransitionPermission();
+ return SurfaceControl.Transaction.getDefaultApplyToken();
+ }
+
private void enforceRemoteTransitionPermission() {
mContext.enforceCallingPermission(
Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS,
diff --git a/packages/SystemUI/animation/lib/src/com/android/systemui/animation/shared/IOriginTransitions.aidl b/packages/SystemUI/animation/lib/src/com/android/systemui/animation/shared/IOriginTransitions.aidl
index 31cca70..6b1d5d4 100644
--- a/packages/SystemUI/animation/lib/src/com/android/systemui/animation/shared/IOriginTransitions.aidl
+++ b/packages/SystemUI/animation/lib/src/com/android/systemui/animation/shared/IOriginTransitions.aidl
@@ -41,4 +41,9 @@
* origin transition object will reset to a single frame animation.
*/
void cancelOriginTransition(in RemoteTransition originTransition) = 2;
+
+ /**
+ * Returns the token to be used as the default `apply` token with SurfaceControl.Transaction.
+ */
+ IBinder getDefaultTransactionApplyToken() = 3;
}
diff --git a/packages/SystemUI/animation/lib/tests/src/com/android/systemui/animation/OriginTransitionSessionTest.java b/packages/SystemUI/animation/lib/tests/src/com/android/systemui/animation/OriginTransitionSessionTest.java
index 287e53b..c71442a 100644
--- a/packages/SystemUI/animation/lib/tests/src/com/android/systemui/animation/OriginTransitionSessionTest.java
+++ b/packages/SystemUI/animation/lib/tests/src/com/android/systemui/animation/OriginTransitionSessionTest.java
@@ -336,6 +336,11 @@
mRecords.remove(originTransition);
}
+ @Override
+ public IBinder getDefaultTransactionApplyToken() throws RemoteException {
+ return null;
+ }
+
public void runReturnTransition(RemoteTransition originTransition, TransitionInfo info) {
RemoteTransition transition = mRecords.remove(originTransition);
try {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/common/ui/compose/Icon.kt b/packages/SystemUI/compose/features/src/com/android/systemui/common/ui/compose/Icon.kt
index 09db2d6..9e7a73d 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/common/ui/compose/Icon.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/common/ui/compose/Icon.kt
@@ -19,30 +19,25 @@
import androidx.compose.material3.Icon
import androidx.compose.material3.LocalContentColor
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.res.painterResource
-import androidx.core.graphics.drawable.toBitmap
+import com.android.compose.ui.graphics.painter.rememberDrawablePainter
import com.android.systemui.common.shared.model.Icon
/**
* Icon composable that draws [icon] using [tint].
*
* Note: You can use [Color.Unspecified] to disable the tint and keep the original icon colors.
+ * Note: Some drawables aren't compatible with [rememberDrawablePainter], used here for
+ * [Icon.Loaded] icons, and won't be resized from their intrinsic size (b/394738023).
*/
@Composable
fun Icon(icon: Icon, modifier: Modifier = Modifier, tint: Color = LocalContentColor.current) {
val contentDescription = icon.contentDescription?.load()
when (icon) {
is Icon.Loaded -> {
- Icon(
- remember(icon.drawable) { icon.drawable.toBitmap().asImageBitmap() },
- contentDescription,
- modifier,
- tint,
- )
+ Icon(rememberDrawablePainter(icon.drawable), contentDescription, modifier, tint)
}
is Icon.Resource -> Icon(painterResource(icon.res), contentDescription, modifier, tint)
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/common/ui/compose/PagerDots.kt b/packages/SystemUI/compose/features/src/com/android/systemui/common/ui/compose/PagerDots.kt
index ced3c89..f8d3b11 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/common/ui/compose/PagerDots.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/common/ui/compose/PagerDots.kt
@@ -96,7 +96,7 @@
.pagerDotsSemantics(pagerState, coroutineScope)
) {
val rtl = layoutDirection == LayoutDirection.Rtl
- scale(if (rtl) -1f else 1f, 1f, Offset(0f, center.y)) {
+ scale(if (rtl) -1f else 1f, 1f) {
// The impacted index is the neighbor of the active index, in the direction dictated
// from the page offset. The impacted dot will have its width modified
val impactedIndex =
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
index b3cb6f4..e1724a3 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
@@ -58,6 +58,7 @@
import com.android.systemui.communal.ui.compose.extensions.allowGestures
import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
import com.android.systemui.communal.util.CommunalColors
+import com.android.systemui.keyguard.domain.interactor.FromAodTransitionInteractor
import com.android.systemui.keyguard.domain.interactor.FromGlanceableHubTransitionInteractor.Companion.TO_LOCKSCREEN_DURATION
import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor.Companion.TO_GONE_DURATION
import com.android.systemui.scene.shared.model.SceneDataSourceDelegator
@@ -97,6 +98,16 @@
spec = tween(durationMillis = TransitionDuration.TO_GLANCEABLE_HUB_DURATION_MS)
timestampRange(startMillis = 250) { fade(AllElements) }
}
+ to(CommunalScenes.Communal, key = CommunalTransitionKeys.FromAod) {
+ spec =
+ tween(
+ durationMillis =
+ FromAodTransitionInteractor.TO_GLANCEABLE_HUB_DURATION.toInt(
+ DurationUnit.MILLISECONDS
+ )
+ )
+ timestampRange(startMillis = 167) { fade(AllElements) }
+ }
to(CommunalScenes.Communal, key = CommunalTransitionKeys.Swipe) {
spec = tween(durationMillis = TransitionDuration.TO_GLANCEABLE_HUB_DURATION_MS)
translate(Communal.Elements.Grid, Edge.End)
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
index 1954b92..a338e5b 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
@@ -21,6 +21,7 @@
import android.content.res.Configuration
import android.graphics.drawable.Icon
import android.os.SystemClock
+import android.service.dreams.Flags.dreamsV2
import android.util.SizeF
import android.view.MotionEvent
import android.widget.FrameLayout
@@ -94,6 +95,7 @@
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.FilledIconButton
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButtonColors
@@ -1120,6 +1122,7 @@
* 2) remove a widget from the grid and
* 3) exit the edit mode.
*/
+@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Composable
private fun Toolbar(
removeEnabled: Boolean,
@@ -1158,7 +1161,7 @@
onClick = onOpenWidgetPicker,
) {
Icon(Icons.Default.Add, null)
- Text(text = addWidgetText)
+ ToolbarButtonText(text = addWidgetText)
}
}
@@ -1202,7 +1205,7 @@
verticalAlignment = Alignment.CenterVertically,
) {
Icon(Icons.Default.Close, contentDescription = null)
- Text(text = stringResource(R.string.button_to_remove_widget))
+ ToolbarButtonText(stringResource(R.string.button_to_remove_widget))
}
}
}
@@ -1213,11 +1216,20 @@
onClick = onEditDone,
) {
Icon(Icons.Default.Check, contentDescription = null)
- Text(text = stringResource(R.string.hub_mode_editing_exit_button_text))
+ ToolbarButtonText(stringResource(R.string.hub_mode_editing_exit_button_text))
}
}
}
+@Composable
+private fun ToolbarButtonText(text: String) {
+ if (dreamsV2()) {
+ Text(text = text, style = MaterialTheme.typography.titleMedium)
+ } else {
+ Text(text = text)
+ }
+}
+
/**
* Toolbar button that displays as a filled button if primary, and an outline button if secondary.
*/
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/section/CommunalLockSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/section/CommunalLockSection.kt
index 8c681e2..4ec3d59 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/section/CommunalLockSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/section/CommunalLockSection.kt
@@ -127,7 +127,7 @@
val scaleFactor = authController.scaleFactor
val bottomPaddingPx =
context.resources.getDimensionPixelSize(
- com.android.systemui.customization.R.dimen.lock_icon_margin_bottom
+ com.android.systemui.customization.clocks.R.dimen.lock_icon_margin_bottom
)
val heightPx = windowViewBounds.bottom.toFloat()
val (center, radius) =
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/compose/modifiers/SysuiTestTag.kt b/packages/SystemUI/compose/features/src/com/android/systemui/compose/modifiers/SysuiTestTag.kt
index b1afb161..4506408 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/compose/modifiers/SysuiTestTag.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/compose/modifiers/SysuiTestTag.kt
@@ -25,12 +25,20 @@
/**
* Set a test tag on this node so that it is associated with [resId]. This node will then be
* accessible by integration tests using `sysuiResSelector(resId)`.
+ *
+ * Important: This modifier will work only when contained under a [sysUiResTagContainer].
+ *
+ * @see sysUiResTagContainer
*/
@Stable
fun Modifier.sysuiResTag(resId: String): Modifier {
- // TODO(b/372412931): Only compose the semantics modifier once, at the root of the SystemUI
- // window.
- return this.then(TestTagAsResourceIdModifier).testTag("com.android.systemui:id/$resId")
+ return this.testTag("com.android.systemui:id/$resId")
+}
+
+/** Mark this node as a container that contains one or more [sysuiResTag] descendants. */
+@Stable
+fun Modifier.sysUiResTagContainer(): Modifier {
+ return this.then(TestTagAsResourceIdModifier)
}
private val TestTagAsResourceIdModifier = Modifier.semantics { testTagsAsResourceId = true }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt
index 39ec428..e3115cd 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt
@@ -186,8 +186,7 @@
with(lockSection) { LockIcon() }
// Aligned to bottom and constrained to below the lock icon.
- // TODO("b/383588832") change this away from "keyguard_bottom_area"
- Column(modifier = Modifier.fillMaxWidth().sysuiResTag("keyguard_bottom_area")) {
+ Column(modifier = Modifier.fillMaxWidth().sysuiResTag("keyguard_root_view")) {
if (isUdfpsVisible && ambientIndicationSectionOptional.isPresent) {
with(ambientIndicationSectionOptional.get()) {
AmbientIndication(modifier = Modifier.fillMaxWidth())
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/DefaultClockSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/DefaultClockSection.kt
index 0583e8a..5134916 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/DefaultClockSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/DefaultClockSection.kt
@@ -32,7 +32,7 @@
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.compose.animation.scene.ContentScope
import com.android.compose.modifiers.padding
-import com.android.systemui.customization.R
+import com.android.systemui.customization.clocks.R as clocksR
import com.android.systemui.keyguard.ui.composable.blueprint.ClockElementKeys.largeClockElementKey
import com.android.systemui.keyguard.ui.composable.blueprint.ClockElementKeys.smallClockElementKey
import com.android.systemui.keyguard.ui.composable.blueprint.ClockScenes.largeClockScene
@@ -92,8 +92,8 @@
checkNotNull(currentClock).smallClock.view,
modifier =
modifier
- .height(dimensionResource(R.dimen.small_clock_height))
- .padding(horizontal = dimensionResource(R.dimen.clock_padding_start))
+ .height(dimensionResource(clocksR.dimen.small_clock_height))
+ .padding(horizontal = dimensionResource(clocksR.dimen.clock_padding_start))
.padding(top = { smallTopMargin })
.onTopPlacementChanged(onTopChanged)
.burnInAware(viewModel = aodBurnInViewModel, params = burnInParams)
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/LockSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/LockSection.kt
index 3e1252b..f98ed1c 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/LockSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/LockSection.kt
@@ -31,7 +31,7 @@
import com.android.compose.animation.scene.ContentScope
import com.android.compose.animation.scene.ElementKey
import com.android.systemui.biometrics.AuthController
-import com.android.systemui.customization.R as customR
+import com.android.systemui.customization.clocks.R as clocksR
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.flags.FeatureFlagsClassic
@@ -155,7 +155,7 @@
} else {
val scaleFactor = authController.scaleFactor
val bottomPaddingPx =
- context.resources.getDimensionPixelSize(customR.dimen.lock_icon_margin_bottom)
+ context.resources.getDimensionPixelSize(clocksR.dimen.lock_icon_margin_bottom)
val heightPx = windowViewBounds.bottom.toFloat()
Pair(
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/TopAreaSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/TopAreaSection.kt
index 0134240..f87471f 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/TopAreaSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/TopAreaSection.kt
@@ -246,6 +246,7 @@
* @param name Name of resources
* @param density Density required to convert dimen from Int To Dp
*/
+ @Deprecated("Prefer reading dimensions directly")
private fun getDimen(context: Context, name: String, density: Density): Dp {
val res = context.packageManager.getResourcesForApplication(context.packageName)
val id = res.getIdentifier(name, "dimen", context.packageName)
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/WeatherClockSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/WeatherClockSection.kt
index a8b2b13..f764926 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/WeatherClockSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/WeatherClockSection.kt
@@ -29,13 +29,14 @@
import androidx.compose.ui.res.dimensionResource
import com.android.compose.animation.scene.ContentScope
import com.android.compose.animation.scene.ElementKey
-import com.android.systemui.customization.R as customR
+import com.android.systemui.customization.clocks.R as clocksR
import com.android.systemui.keyguard.ui.composable.blueprint.WeatherClockElementKeys
import com.android.systemui.keyguard.ui.composable.modifier.burnInAware
import com.android.systemui.keyguard.ui.viewmodel.AodBurnInViewModel
import com.android.systemui.keyguard.ui.viewmodel.BurnInParameters
import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
import com.android.systemui.plugins.clocks.ClockController
+import com.android.systemui.plugins.clocks.ClockViewIds
import javax.inject.Inject
/** Provides small clock and large clock composables for the weather clock layout. */
@@ -49,11 +50,11 @@
fun ContentScope.Time(clock: ClockController, burnInParams: BurnInParameters) {
Row(
modifier =
- Modifier.padding(horizontal = dimensionResource(customR.dimen.clock_padding_start))
+ Modifier.padding(horizontal = dimensionResource(clocksR.dimen.clock_padding_start))
.burnInAware(aodBurnInViewModel, burnInParams, isClock = true)
) {
WeatherElement(
- weatherClockElementViewId = customR.id.weather_clock_time,
+ weatherClockElementViewId = ClockViewIds.WEATHER_CLOCK_TIME,
clock = clock,
elementKey = WeatherClockElementKeys.timeElementKey,
)
@@ -63,7 +64,7 @@
@Composable
private fun ContentScope.Date(clock: ClockController, modifier: Modifier = Modifier) {
WeatherElement(
- weatherClockElementViewId = customR.id.weather_clock_date,
+ weatherClockElementViewId = ClockViewIds.WEATHER_CLOCK_DATE,
clock = clock,
elementKey = WeatherClockElementKeys.dateElementKey,
modifier = modifier,
@@ -73,7 +74,7 @@
@Composable
private fun ContentScope.Weather(clock: ClockController, modifier: Modifier = Modifier) {
WeatherElement(
- weatherClockElementViewId = customR.id.weather_clock_weather_icon,
+ weatherClockElementViewId = ClockViewIds.WEATHER_CLOCK_ICON,
clock = clock,
elementKey = WeatherClockElementKeys.weatherIconElementKey,
modifier = modifier.wrapContentSize(),
@@ -83,7 +84,7 @@
@Composable
private fun ContentScope.DndAlarmStatus(clock: ClockController, modifier: Modifier = Modifier) {
WeatherElement(
- weatherClockElementViewId = customR.id.weather_clock_alarm_dnd,
+ weatherClockElementViewId = ClockViewIds.WEATHER_CLOCK_ALARM_DND,
clock = clock,
elementKey = WeatherClockElementKeys.dndAlarmElementKey,
modifier = modifier.wrapContentSize(),
@@ -93,7 +94,7 @@
@Composable
private fun ContentScope.Temperature(clock: ClockController, modifier: Modifier = Modifier) {
WeatherElement(
- weatherClockElementViewId = customR.id.weather_clock_temperature,
+ weatherClockElementViewId = ClockViewIds.WEATHER_CLOCK_TEMP,
clock = clock,
elementKey = WeatherClockElementKeys.temperatureElementKey,
modifier = modifier.wrapContentSize(),
@@ -123,14 +124,14 @@
Row(
modifier =
Modifier.height(IntrinsicSize.Max)
- .padding(horizontal = dimensionResource(customR.dimen.clock_padding_start))
+ .padding(horizontal = dimensionResource(clocksR.dimen.clock_padding_start))
.burnInAware(aodBurnInViewModel, burnInParams, isClock = true)
) {
Date(clock = clock, modifier = Modifier.wrapContentSize())
Box(
modifier =
Modifier.fillMaxSize()
- .padding(start = dimensionResource(customR.dimen.clock_padding_start))
+ .padding(start = dimensionResource(clocksR.dimen.clock_padding_start))
) {
Weather(clock = clock, modifier = Modifier.align(Alignment.TopStart))
Temperature(clock = clock, modifier = Modifier.align(Alignment.BottomEnd))
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt
index 7a500ad..aaa155e 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt
@@ -99,6 +99,7 @@
import com.android.internal.jank.InteractionJankMonitor
import com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_SCROLL_FLING
import com.android.systemui.common.ui.compose.windowinsets.LocalScreenCornerRadius
+import com.android.systemui.compose.modifiers.sysuiResTag
import com.android.systemui.res.R
import com.android.systemui.scene.session.ui.composable.SaveableSession
import com.android.systemui.scene.session.ui.composable.sessionCoroutineScope
@@ -503,6 +504,7 @@
Box(
modifier =
modifier
+ .sysuiResTag("notification_stack_scroller")
.element(Notifications.Elements.NotificationScrim)
.overscroll(verticalOverscrollEffect)
.offset {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt
index c1eafcd..b69d46e 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt
@@ -54,6 +54,7 @@
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.layout.layout
import androidx.compose.ui.platform.LocalContext
@@ -68,6 +69,7 @@
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.em
import androidx.compose.ui.unit.sp
+import androidx.core.graphics.drawable.toBitmap
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.compose.collectAsStateWithLifecycle
@@ -81,6 +83,7 @@
import com.android.systemui.animation.Expandable
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.common.ui.compose.Icon
+import com.android.systemui.common.ui.compose.load
import com.android.systemui.compose.modifiers.sysuiResTag
import com.android.systemui.qs.flags.QSComposeFragment
import com.android.systemui.qs.flags.QsInCompose
@@ -350,7 +353,19 @@
),
useModifierBasedImplementation = useModifierBasedExpandable,
) {
- Icon(model.icon, tint = colors.icon, modifier = Modifier.size(20.dp))
+ FooterIcon(model.icon, Modifier.size(20.dp), colors.icon)
+ }
+}
+
+// TODO(b/394738023): Use com.android.systemui.common.ui.compose.Icon instead
+@Composable
+private fun FooterIcon(icon: Icon, modifier: Modifier = Modifier, tint: Color) {
+ val contentDescription = icon.contentDescription?.load()
+ when (icon) {
+ is Icon.Loaded -> {
+ Icon(icon.drawable.toBitmap().asImageBitmap(), contentDescription, modifier, tint)
+ }
+ is Icon.Resource -> Icon(painterResource(icon.res), contentDescription, modifier, tint)
}
}
@@ -550,7 +565,7 @@
fun inactiveButtonColors(): ButtonColors =
ButtonColors(
icon = MaterialTheme.colorScheme.onSurface,
- background = LocalAndroidColorScheme.current.surfaceEffect2,
+ background = LocalAndroidColorScheme.current.surfaceEffect1,
)
@Composable
@@ -558,7 +573,7 @@
fun userSwitcherButtonColors(): ButtonColors =
ButtonColors(
icon = Color.Unspecified,
- background = LocalAndroidColorScheme.current.surfaceEffect2,
+ background = LocalAndroidColorScheme.current.surfaceEffect1,
)
@Composable
@@ -566,7 +581,7 @@
fun blurTextButtonColors(): TextButtonColors =
TextButtonColors(
content = MaterialTheme.colorScheme.onSurface,
- background = LocalAndroidColorScheme.current.surfaceEffect2,
+ background = LocalAndroidColorScheme.current.surfaceEffect1,
border = null,
)
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeOverlay.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeOverlay.kt
index cb03119..a5df09a 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeOverlay.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeOverlay.kt
@@ -187,6 +187,7 @@
if (QsDetailedView.isEnabled) viewModel.detailsViewModel.activeTileDetails else null
AnimatedContent(
+ modifier = Modifier.sysuiResTag("quick_settings_container"),
targetState =
when {
isEditing -> ShadeBodyState.Editing
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/DualShadeEducationalTooltips.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/DualShadeEducationalTooltips.kt
index 6b422e5..04a86a1 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/DualShadeEducationalTooltips.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/DualShadeEducationalTooltips.kt
@@ -18,12 +18,9 @@
package com.android.systemui.scene.ui.composable
-import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Spacer
-import androidx.compose.foundation.layout.fillMaxHeight
-import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.layout.width
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.RichTooltip
import androidx.compose.material3.Text
@@ -32,56 +29,74 @@
import androidx.compose.material3.rememberTooltipState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
+import androidx.compose.ui.layout.Layout
import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.unit.Constraints
import androidx.compose.ui.unit.dp
-import com.android.compose.modifiers.height
import com.android.compose.theme.LocalAndroidColorScheme
import com.android.systemui.lifecycle.rememberViewModel
import com.android.systemui.scene.ui.viewmodel.DualShadeEducationalTooltipsViewModel
@Composable
-fun DualShadeEducationalTooltips(viewModelFactory: DualShadeEducationalTooltipsViewModel.Factory) {
+fun DualShadeEducationalTooltips(
+ viewModelFactory: DualShadeEducationalTooltipsViewModel.Factory,
+ modifier: Modifier = Modifier,
+) {
val context = LocalContext.current
val viewModel =
rememberViewModel(traceName = "DualShadeEducationalTooltips") {
viewModelFactory.create(context)
}
- val visibleTooltip = viewModel.visibleTooltip ?: return
+ val visibleTooltip = viewModel.visibleTooltip
- val anchorBottomY = visibleTooltip.anchorBottomY
- // This Box represents the bounds of the top edge that the user can swipe down on to reveal
- // either of the dual shade overlays. It's used as a convenient way to position the anchor for
- // each of the tooltips that can be shown. As such, this Box is the same size as the status bar.
- Box(
- contentAlignment =
- if (visibleTooltip.isAlignedToStart) {
- Alignment.CenterStart
- } else {
- Alignment.CenterEnd
- },
- modifier = Modifier.fillMaxWidth().height { anchorBottomY }.padding(horizontal = 24.dp),
- ) {
- AnchoredTooltip(
- text = visibleTooltip.text,
- onShown = visibleTooltip.onShown,
- onDismissed = visibleTooltip.onDismissed,
- )
+ Layout(
+ content = {
+ AnchoredTooltip(
+ isVisible = visibleTooltip != null,
+ text = visibleTooltip?.text,
+ onShown = visibleTooltip?.onShown,
+ onDismissed = visibleTooltip?.onDismissed,
+ )
+ },
+ modifier = modifier.fillMaxSize(),
+ ) { measurables, constraints ->
+ check(measurables.size == 1)
+ val placeable =
+ measurables[0].measure(
+ Constraints.fixed(
+ width = visibleTooltip?.anchorBounds?.width ?: 0,
+ height = visibleTooltip?.anchorBounds?.height ?: 0,
+ )
+ )
+ layout(constraints.maxWidth, constraints.maxHeight) {
+ placeable.place(
+ x = visibleTooltip?.anchorBounds?.left ?: 0,
+ y = visibleTooltip?.anchorBounds?.top ?: 0,
+ )
+ }
}
}
@Composable
private fun AnchoredTooltip(
- text: String,
- onShown: () -> Unit,
- onDismissed: () -> Unit,
+ isVisible: Boolean,
+ text: String?,
+ onShown: (() -> Unit)?,
+ onDismissed: (() -> Unit)?,
modifier: Modifier = Modifier,
) {
- val tooltipState = rememberTooltipState(initialIsVisible = true, isPersistent = true)
+ val tooltipState = rememberTooltipState(initialIsVisible = false, isPersistent = true)
- LaunchedEffect(Unit) { onShown() }
+ LaunchedEffect(isVisible) {
+ if (isVisible) {
+ onShown?.invoke()
+ tooltipState.show()
+ } else {
+ tooltipState.dismiss()
+ }
+ }
TooltipBox(
positionProvider = TooltipDefaults.rememberTooltipPositionProvider(),
@@ -95,13 +110,13 @@
caretSize = TooltipDefaults.caretSize,
shadowElevation = 2.dp,
) {
- Text(text = text, modifier = Modifier.padding(8.dp))
+ Text(text = text ?: "", modifier = Modifier.padding(8.dp))
}
},
state = tooltipState,
onDismissRequest = onDismissed,
modifier = modifier,
) {
- Spacer(modifier = Modifier.width(48.dp).fillMaxHeight())
+ Spacer(modifier = Modifier.fillMaxSize())
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
index b0df3c58..155b0e4 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
@@ -204,7 +204,7 @@
Box(
modifier =
- Modifier.fillMaxSize().pointerInput(Unit) {
+ modifier.fillMaxSize().pointerInput(Unit) {
awaitEachGesture {
awaitFirstDown(false)
viewModel.onSceneContainerUserInputStarted()
@@ -219,7 +219,7 @@
SceneTransitionLayout(
state = state,
- modifier = modifier.fillMaxSize(),
+ modifier = Modifier.fillMaxSize(),
swipeSourceDetector = viewModel.swipeSourceDetector,
) {
sceneByKey.forEach { (sceneKey, scene) ->
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt
index b0c3dd3..b549c90 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt
@@ -195,7 +195,12 @@
val ScrimBackground: Color
@Composable
@ReadOnlyComposable
- get() = Color(notificationScrim(LocalContext.current, Flags.notificationShadeBlur()))
+ get() =
+ if (Flags.notificationShadeBlur()) {
+ Color(notificationScrim(LocalContext.current, /* blurSupported= */ true))
+ } else {
+ Color.Transparent
+ }
val PanelBackground: Color
@Composable
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt
index 600b2e3..7aee1d4 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt
@@ -19,6 +19,9 @@
import android.view.ContextThemeWrapper
import android.view.ViewGroup
+import androidx.compose.animation.core.Animatable
+import androidx.compose.animation.core.CubicBezierEasing
+import androidx.compose.animation.core.tween
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
@@ -33,6 +36,7 @@
import androidx.compose.foundation.layout.defaultMinSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.widthIn
@@ -40,6 +44,7 @@
import androidx.compose.material3.ColorScheme
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
@@ -50,11 +55,15 @@
import androidx.compose.ui.graphics.TransformOrigin
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.layout.Layout
+import androidx.compose.ui.layout.onGloballyPositioned
+import androidx.compose.ui.layout.positionInWindow
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.Constraints
+import androidx.compose.ui.unit.IntOffset
+import androidx.compose.ui.unit.IntRect
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.max
@@ -81,6 +90,7 @@
import com.android.systemui.kairos.buildSpec
import com.android.systemui.privacy.OngoingPrivacyChip
import com.android.systemui.res.R
+import com.android.systemui.scene.shared.model.DualShadeEducationElement
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shade.ui.composable.ShadeHeader.Colors.onScrimDim
import com.android.systemui.shade.ui.composable.ShadeHeader.Dimensions.ChipPaddingHorizontal
@@ -99,6 +109,8 @@
import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.composeWrapper
import com.android.systemui.statusbar.policy.Clock
import com.android.systemui.util.composable.kairos.ActivatedKairosSpec
+import kotlin.math.roundToInt
+import kotlinx.coroutines.delay
object ShadeHeader {
object Elements {
@@ -262,7 +274,7 @@
)
}
}
- Spacer(modifier = Modifier.width(5.dp))
+ Spacer(modifier = Modifier.height(16.dp))
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.element(ShadeHeader.Elements.ExpandedContent),
@@ -343,6 +355,16 @@
NotificationsChip(
onClick = viewModel::onNotificationIconChipClicked,
backgroundColor = chipHighlight.backgroundColor(MaterialTheme.colorScheme),
+ modifier =
+ Modifier.bouncy(
+ isEnabled = viewModel.animateNotificationsChipBounce,
+ onBoundsChange = { bounds ->
+ viewModel.onDualShadeEducationElementBoundsChange(
+ element = DualShadeEducationElement.Notifications,
+ bounds = bounds,
+ )
+ },
+ ),
) {
VariableDayDate(
longerDateText = viewModel.longerDateText,
@@ -362,6 +384,16 @@
SystemIconChip(
backgroundColor = chipHighlight.backgroundColor(MaterialTheme.colorScheme),
onClick = viewModel::onSystemIconChipClicked,
+ modifier =
+ Modifier.bouncy(
+ isEnabled = viewModel.animateSystemIconChipBounce,
+ onBoundsChange = { bounds ->
+ viewModel.onDualShadeEducationElementBoundsChange(
+ element = DualShadeEducationElement.QuickSettings,
+ bounds = bounds,
+ )
+ },
+ ),
) {
val paddingEnd =
with(LocalDensity.current) {
@@ -791,6 +823,75 @@
)
}
+/** Modifies the given [Modifier] such that it shows a looping vertical bounce animation. */
+@Composable
+private fun Modifier.bouncy(
+ isEnabled: Boolean,
+ onBoundsChange: (bounds: IntRect) -> Unit,
+): Modifier {
+ val density = LocalDensity.current
+ val animatable = remember { Animatable(0f) }
+ LaunchedEffect(isEnabled) {
+ if (isEnabled) {
+ while (true) {
+ // Lifts the element up to the first peak.
+ animatable.animateTo(
+ targetValue = with(density) { -(10.dp).toPx() },
+ animationSpec =
+ tween(
+ durationMillis = 200,
+ easing = CubicBezierEasing(0.15f, 0f, 0.23f, 1f),
+ ),
+ )
+ // Drops the element back to the ground from the first peak.
+ animatable.animateTo(
+ targetValue = 0f,
+ animationSpec =
+ tween(
+ durationMillis = 167,
+ easing = CubicBezierEasing(0.74f, 0f, 0.22f, 1f),
+ ),
+ )
+ // Lifts the element up again, this time to the second, smaller peak.
+ animatable.animateTo(
+ targetValue = with(density) { -(5.dp).toPx() },
+ animationSpec =
+ tween(
+ durationMillis = 150,
+ easing = CubicBezierEasing(0.62f, 0f, 0.35f, 1f),
+ ),
+ )
+ // Drops the element back to the ground from the second peak.
+ animatable.animateTo(
+ targetValue = 0f,
+ animationSpec =
+ tween(
+ durationMillis = 117,
+ easing = CubicBezierEasing(0.67f, 0f, 0.51f, 1f),
+ ),
+ )
+ // Wait for a moment before repeating it.
+ delay(1000)
+ }
+ } else {
+ animatable.animateTo(targetValue = 0f, animationSpec = tween(durationMillis = 500))
+ }
+ }
+
+ return this.thenIf(isEnabled) {
+ Modifier.offset { IntOffset(x = 0, y = animatable.value.roundToInt()) }
+ .onGloballyPositioned { coordinates ->
+ val offset = coordinates.positionInWindow()
+ onBoundsChange(
+ IntRect(
+ offset = IntOffset(x = offset.x.roundToInt(), y = offset.y.roundToInt()),
+ size = coordinates.size,
+ )
+ )
+ }
+ }
+}
+
private fun shouldUseExpandedFormat(state: TransitionState): Boolean {
return when (state) {
is TransitionState.Idle -> {
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/model/DualShadeEducationalTooltipModel.kt b/packages/SystemUI/compose/features/src/com/android/systemui/underlay/ui/compose/UnderlayComposableProvider.kt
similarity index 60%
copy from packages/SystemUI/src/com/android/systemui/scene/domain/model/DualShadeEducationalTooltipModel.kt
copy to packages/SystemUI/compose/features/src/com/android/systemui/underlay/ui/compose/UnderlayComposableProvider.kt
index f63e132..7d78cb0 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/model/DualShadeEducationalTooltipModel.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/underlay/ui/compose/UnderlayComposableProvider.kt
@@ -14,11 +14,17 @@
* limitations under the License.
*/
-package com.android.systemui.scene.domain.model
+package com.android.systemui.underlay.ui.compose
-/** Enumerates the type of tooltip to show. */
-enum class DualShadeEducationalTooltipModel {
- None,
- ForNotificationsShade,
- ForQuickSettingsShade,
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import javax.inject.Inject
+
+interface UnderlayComposableProvider {
+ @Composable fun Content(modifier: Modifier)
+}
+
+class UnderlayComposableProviderImpl @Inject constructor() : UnderlayComposableProvider {
+
+ @Composable override fun Content(modifier: Modifier) {}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/underlay/ui/compose/UnderlayComposableProviderModule.kt b/packages/SystemUI/compose/features/src/com/android/systemui/underlay/ui/compose/UnderlayComposableProviderModule.kt
new file mode 100644
index 0000000..2189147
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/underlay/ui/compose/UnderlayComposableProviderModule.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.underlay
+
+import com.android.systemui.underlay.ui.compose.UnderlayComposableProvider
+import com.android.systemui.underlay.ui.compose.UnderlayComposableProviderImpl
+import dagger.Binds
+import dagger.Module
+
+@Module
+interface UnderlayComposableProviderModule {
+
+ @Binds
+ fun bindUnderlayComposableProvider(
+ provider: UnderlayComposableProviderImpl
+ ): UnderlayComposableProvider
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/underlay/ui/compose/UnderlayContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/underlay/ui/compose/UnderlayContainer.kt
index 2353c29..2ba26d6 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/underlay/ui/compose/UnderlayContainer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/underlay/ui/compose/UnderlayContainer.kt
@@ -16,25 +16,76 @@
package com.android.systemui.underlay.ui.compose
+import android.graphics.Rect
import android.util.Log
-import android.view.SurfaceView
+import android.view.ViewTreeObserver
+import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.WindowInsets
+import androidx.compose.foundation.layout.asPaddingValues
+import androidx.compose.foundation.layout.mandatorySystemGestures
import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.DisposableEffect
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.layout.onGloballyPositioned
+import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
-import androidx.compose.ui.viewinterop.AndroidView
import com.android.compose.PlatformIconButton
import com.android.systemui.res.R
+import com.android.systemui.underlay.ui.shape.TopConcaveArcShape
@Composable
-fun UnderlayContainer(modifier: Modifier = Modifier) {
- Box(modifier = modifier) {
- // TODO: b/407634988 - Add rounded horns
+fun UnderlayContainer(modifier: Modifier = Modifier, content: UnderlayComposableProvider) {
+ val density = LocalDensity.current
+ val hornRadiusPx = with(density) { HornRadius.toPx() }
- DelegatedSurfaceView()
+ val view = LocalView.current
+ var touchableRect: Rect by remember { mutableStateOf((Rect())) }
+
+ DisposableEffect(view, touchableRect) {
+ val listener =
+ ViewTreeObserver.OnComputeInternalInsetsListener { inoutInfo ->
+ inoutInfo.setTouchableInsets(
+ ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION
+ )
+ inoutInfo.touchableRegion.set(touchableRect)
+ }
+
+ view.viewTreeObserver.addOnComputeInternalInsetsListener(listener)
+
+ onDispose { view.viewTreeObserver.removeOnComputeInternalInsetsListener(listener) }
+ }
+
+ val underlayShape = remember(HornRadius) { TopConcaveArcShape(HornRadius) }
+
+ val mandatorySystemGesturesBottomPadding =
+ WindowInsets.mandatorySystemGestures.asPaddingValues().calculateBottomPadding()
+
+ Box(
+ modifier =
+ modifier
+ .onGloballyPositioned { layoutCoordinates ->
+ val heightPx = layoutCoordinates.size.height
+ val widthPx = layoutCoordinates.size.width
+ val hornHeightPx = hornRadiusPx.toInt()
+ touchableRect = Rect(0, hornHeightPx, widthPx, heightPx)
+ }
+ .background(color = MaterialTheme.colorScheme.inverseSurface, shape = underlayShape)
+ .clip(underlayShape)
+ // Offset above gesture region and below horns
+ .padding(top = HornRadius, bottom = mandatorySystemGesturesBottomPadding)
+ ) {
+ content.Content(modifier = Modifier)
// Close Button.
PlatformIconButton(
@@ -50,13 +101,5 @@
}
}
-/**
- * The SurfaceView content, which is currently blank, will be populated by another app using the
- * DelegatedUI framework.
- */
-@Composable
-private fun DelegatedSurfaceView(modifier: Modifier = Modifier) {
- AndroidView(factory = { context -> SurfaceView(context) }, modifier = modifier)
-}
-
private const val TAG = "UnderlayContainer"
+private val HornRadius = 28.dp
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt
index 5d0d2d2..e83f9fc 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt
@@ -192,7 +192,11 @@
hapticsViewModelFactory?.let {
Haptics.Enabled(
hapticsViewModelFactory = it,
- hapticFilter = state.hapticFilter,
+ hapticConfigs =
+ VolumeHapticsConfigsProvider.discreteConfigs(
+ state.valueRange.stepSize(),
+ state.hapticFilter,
+ ),
orientation = Orientation.Horizontal,
)
} ?: Haptics.Disabled,
@@ -372,16 +376,15 @@
hapticsViewModelFactory: SliderHapticsViewModel.Factory?,
): SliderHapticsViewModel? {
return hapticsViewModelFactory?.let {
+ val configs =
+ VolumeHapticsConfigsProvider.discreteConfigs(valueRange.stepSize(), hapticFilter)
rememberViewModel(traceName = "SliderHapticsViewModel") {
it.create(
interactionSource,
valueRange,
Orientation.Horizontal,
- VolumeHapticsConfigsProvider.sliderHapticFeedbackConfig(
- valueRange,
- hapticFilter,
- ),
- VolumeHapticsConfigsProvider.seekableSliderTrackerConfig,
+ configs.hapticFeedbackConfig,
+ configs.sliderTrackerConfig,
)
}
.also { hapticsViewModel ->
@@ -399,3 +402,5 @@
}
}
}
+
+private fun ClosedFloatingPointRange<Float>.stepSize(): Float = 1f / (endInclusive - start)
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
index ab3d942..c4b6273 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
@@ -52,7 +52,6 @@
import androidx.compose.ui.util.fastForEachIndexed
import androidx.compose.ui.util.fastForEachReversed
import androidx.compose.ui.util.lerp
-import com.android.compose.animation.scene.Element.Companion.SizeUnspecified
import com.android.compose.animation.scene.content.Content
import com.android.compose.animation.scene.content.state.TransitionState
import com.android.compose.animation.scene.transformation.CustomPropertyTransformation
@@ -106,16 +105,9 @@
var targetSize by mutableStateOf(SizeUnspecified)
var targetOffset by mutableStateOf(Offset.Unspecified)
- /**
- * The *approach* state of this element in this content, i.e. the intermediate layout state
- * during transitions, used for smooth animation. Note: These values are computed before
- * measuring the children.
- */
- var approachSize by mutableStateOf(SizeUnspecified)
-
/** The last state this element had in this content. */
var lastOffset = Offset.Unspecified
- var lastSize = SizeUnspecified
+ var lastSize by mutableStateOf(SizeUnspecified)
var lastScale = Scale.Unspecified
var lastAlpha = AlphaUnspecified
@@ -236,7 +228,10 @@
content.key,
layoutImpl.elements.getValue(key),
state,
- )
+ ) &&
+ // Always draw in the original content when overscrolling.
+ state.progress > 0f &&
+ state.progress < 1f
},
)
}
@@ -348,11 +343,7 @@
override fun isMeasurementApproachInProgress(lookaheadSize: IntSize): Boolean {
// TODO(b/324191441): Investigate whether making this check more complex (checking if this
// element is shared or transformed) would lead to better performance.
- val isTransitioning = isAnyStateTransitioning()
- if (!isTransitioning) {
- stateInContent.approachSize = SizeUnspecified
- }
- return isTransitioning
+ return isAnyStateTransitioning()
}
override fun Placeable.PlacementScope.isPlacementApproachInProgress(
@@ -404,7 +395,6 @@
// sharedElement isn't part of either but the element is still rendered as part of
// the underlying scene that is currently not being transitioned.
val currentState = currentTransitionStates.last().last()
- stateInContent.approachSize = Element.SizeUnspecified
val shouldPlaceInThisContent =
elementContentWhenIdle(
layoutImpl,
@@ -422,15 +412,7 @@
val transition = elementState as? TransitionState.Transition
val placeable =
- approachMeasure(
- layoutImpl = layoutImpl,
- element = element,
- transition = transition,
- stateInContent = stateInContent,
- measurable = measurable,
- constraints = constraints,
- )
- stateInContent.lastSize = placeable.size()
+ measure(layoutImpl, element, transition, stateInContent, measurable, constraints)
return layout(placeable.width, placeable.height) { place(elementState, placeable) }
}
@@ -1203,7 +1185,7 @@
)
}
-private fun approachMeasure(
+private fun measure(
layoutImpl: SceneTransitionLayoutImpl,
element: Element,
transition: TransitionState.Transition?,
@@ -1234,7 +1216,7 @@
maybePlaceable?.let { placeable ->
stateInContent.sizeBeforeInterruption = Element.SizeUnspecified
stateInContent.sizeInterruptionDelta = IntSize.Zero
- stateInContent.approachSize = Element.SizeUnspecified
+ stateInContent.lastSize = placeable.size()
return placeable
}
@@ -1258,8 +1240,7 @@
},
)
- // Important: Set approachSize before child measurement. Could be used for their calculations.
- stateInContent.approachSize = interruptedSize
+ stateInContent.lastSize = interruptedSize
return measurable.measure(
Constraints.fixed(
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
index cfd59fd..35ff3bd 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
@@ -160,11 +160,15 @@
fun ElementKey.targetSize(content: ContentKey): IntSize?
/**
- * Return the *approaching* size of [this] element in the given [content], i.e. thethe size the
- * element when is transitioning, or `null` if the element is not composed and measured in that
- * content (yet).
+ * Return the *last known size* of [this] element in the given [content], i.e. the size of the
+ * element, or `null` if the element is not composed and measured in that content (yet).
+ *
+ * Note: Usually updated **after** the measurement pass and after processing children. However,
+ * if the target size is known **in advance** (like during transitions involving transformations
+ * or shared elements), the update happens **before** measurement pass. This earlier update
+ * allows children to potentially use this predetermined size during their own measurement.
*/
- fun ElementKey.approachSize(content: ContentKey): IntSize?
+ fun ElementKey.lastSize(content: ContentKey): IntSize?
/**
* Return the *target* offset of [this] element in the given [content], i.e. the size of the
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/UserActionDistanceScopeImpl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/UserActionDistanceScopeImpl.kt
index 0d5ae81..b92d5c2 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/UserActionDistanceScopeImpl.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/UserActionDistanceScopeImpl.kt
@@ -31,8 +31,8 @@
}
}
- override fun ElementKey.approachSize(content: ContentKey): IntSize? {
- return layoutImpl.elements[this]?.stateByContent?.get(content)?.approachSize.takeIf {
+ override fun ElementKey.lastSize(content: ContentKey): IntSize? {
+ return layoutImpl.elements[this]?.stateByContent?.get(content)?.lastSize.takeIf {
it != Element.SizeUnspecified
}
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/mechanics/GestureContextUtils.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/mechanics/GestureContextUtils.kt
new file mode 100644
index 0000000..b19fed5
--- /dev/null
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/mechanics/GestureContextUtils.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.compose.animation.scene.mechanics
+
+import com.android.compose.animation.scene.ContentScope
+import com.android.compose.animation.scene.SceneTransitionLayout
+import com.android.mechanics.GestureContext
+import com.android.mechanics.spec.InputDirection
+
+/**
+ * Provides a [GestureContext] retrieving gesture details from the current [SceneTransitionLayout]
+ * transition, using default values as a fallback if the transition's own gesture context isn't
+ * available.
+ */
+fun ContentScope.gestureContextOrDefault(
+ defaultDragOffset: Float = 0f,
+ defaultDirection: InputDirection = InputDirection.Max,
+): GestureContext {
+ return object : GestureContext {
+ override val direction: InputDirection
+ get() = gestureContext?.direction ?: defaultDirection
+
+ override val dragOffset: Float
+ get() = gestureContext?.dragOffset ?: defaultDragOffset
+
+ private val gestureContext
+ get() = layoutState.currentTransition?.gestureContext
+ }
+}
diff --git a/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_dragFullyClose.json b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_dragFullyClose.json
index 770f859..0a3bfaa 100644
--- a/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_dragFullyClose.json
+++ b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_dragFullyClose.json
@@ -606,13 +606,13 @@
0.48078007,
0.33033127,
0.22004372,
- 0.1432175,
- 0.09153092,
- 0.057634592,
- 0.035840213,
- 0.022048414,
- 0.013435662,
- 0.008117795,
+ 0.14321749,
+ 0.09153093,
+ 0.057634596,
+ 0.035840217,
+ 0.022048427,
+ 0.013435689,
+ 0.008117776,
0,
0,
0,
diff --git a/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_dragHalfClose.json b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_dragHalfClose.json
index 6b7de56..18b575e 100644
--- a/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_dragHalfClose.json
+++ b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_dragHalfClose.json
@@ -625,12 +625,12 @@
0.4955439,
0.34154767,
0.22803628,
- 0.14868057,
- 0.09515619,
- 0.059987247,
- 0.037340224,
- 0.02299112,
- 0.01402092,
+ 0.14868054,
+ 0.095156215,
+ 0.05998723,
+ 0.037340246,
+ 0.022991102,
+ 0.014020911,
0.008477271,
0,
0,
diff --git a/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_dragOpen.json b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_dragOpen.json
index 13f75d2..a6a6243 100644
--- a/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_dragOpen.json
+++ b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_dragOpen.json
@@ -340,7 +340,7 @@
},
{
"width": 150,
- "height": 36.4
+ "height": 38
},
{
"width": 150,
@@ -494,7 +494,7 @@
0,
0,
0.0067873597,
- 0.06125766,
+ 0.07340753,
0.19080031,
0.39327443,
0.5711931,
diff --git a/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_flingClose.json b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_flingClose.json
index 015df8f..3b171788 100644
--- a/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_flingClose.json
+++ b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_flingClose.json
@@ -412,16 +412,16 @@
0.99532944,
0.8594856,
0.65783304,
- 0.47089338,
- 0.32286334,
- 0.21474117,
- 0.139602,
+ 0.4708934,
+ 0.3228633,
+ 0.21474116,
+ 0.13960199,
0.089136004,
- 0.056082606,
- 0.03485179,
- 0.02142787,
- 0.013050735,
- 0.007881463,
+ 0.05608259,
+ 0.034851786,
+ 0.021427846,
+ 0.013050736,
+ 0.007881473,
0,
0,
0,
diff --git a/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_flingOpen.json b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_flingOpen.json
index f202fcd..48f552c 100644
--- a/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_flingOpen.json
+++ b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_flingOpen.json
@@ -326,7 +326,7 @@
0,
0,
0.008216977,
- 0.06259775,
+ 0.0747174,
0.19032806,
0.39281356,
0.57081985,
diff --git a/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_magneticDetachAndReattach.json b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_magneticDetachAndReattach.json
index 4c57bda..1cb293c 100644
--- a/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_magneticDetachAndReattach.json
+++ b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_magneticDetachAndReattach.json
@@ -456,7 +456,7 @@
},
{
"width": 150,
- "height": 24.8
+ "height": 25.6
},
{
"width": 150,
@@ -572,7 +572,7 @@
},
{
"width": 132.8,
- "height": 38
+ "height": 34.8
},
{
"width": 131.2,
@@ -657,7 +657,7 @@
0,
0,
0.012518823,
- 0.0741024,
+ 0.09768742,
0.22542936,
0.42628878,
0.5976642,
@@ -705,7 +705,7 @@
1,
1,
0.9944124,
- 0.9417388,
+ 0.93202823,
0.81841844,
0.61578125,
0.43616113,
diff --git a/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_triggeredRevealCloseTransition.json b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_triggeredRevealCloseTransition.json
index 26c80e3..74b2965 100644
--- a/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_triggeredRevealCloseTransition.json
+++ b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_triggeredRevealCloseTransition.json
@@ -293,14 +293,14 @@
0.8118515,
0.60931784,
0.43090785,
- 0.29299664,
+ 0.29299662,
0.19368339,
- 0.12531388,
- 0.079705715,
- 0.049988627,
- 0.030979574,
- 0.019001365,
- 0.011548042,
+ 0.12531386,
+ 0.07970572,
+ 0.049988635,
+ 0.030979585,
+ 0.019001357,
+ 0.011548039,
0,
0,
0,
diff --git a/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_dragFullyClose.json b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_dragFullyClose.json
index 5ac0621..0aa0c2a 100644
--- a/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_dragFullyClose.json
+++ b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_dragFullyClose.json
@@ -570,13 +570,13 @@
0.48078007,
0.33033127,
0.22004372,
- 0.1432175,
- 0.09153092,
- 0.057634592,
- 0.035840213,
- 0.022048414,
- 0.013435662,
- 0.008117795,
+ 0.14321749,
+ 0.09153093,
+ 0.057634596,
+ 0.035840217,
+ 0.022048427,
+ 0.013435689,
+ 0.008117776,
0
]
}
diff --git a/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_dragHalfClose.json b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_dragHalfClose.json
index 1cae67b..1e90168 100644
--- a/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_dragHalfClose.json
+++ b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_dragHalfClose.json
@@ -607,12 +607,12 @@
0.4955439,
0.34154767,
0.22803628,
- 0.14868057,
- 0.09515619,
- 0.059987247,
- 0.037340224,
- 0.02299112,
- 0.01402092,
+ 0.14868054,
+ 0.095156215,
+ 0.05998723,
+ 0.037340246,
+ 0.022991102,
+ 0.014020911,
0.008477271,
0,
0,
diff --git a/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_dragOpen.json b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_dragOpen.json
index 1bf6a62..2939a837 100644
--- a/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_dragOpen.json
+++ b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_dragOpen.json
@@ -380,7 +380,7 @@
},
{
"width": 150,
- "height": 52.4
+ "height": 54.8
},
{
"width": 150,
@@ -494,7 +494,7 @@
0,
0,
0.0067873597,
- 0.06125766,
+ 0.07340753,
0.19080031,
0.39327443,
0.5711931,
diff --git a/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_flingClose.json b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_flingClose.json
index ca87bc2..3c8d92e 100644
--- a/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_flingClose.json
+++ b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_flingClose.json
@@ -403,16 +403,16 @@
0.99532944,
0.8594856,
0.65783304,
- 0.47089338,
- 0.32286334,
- 0.21474117,
- 0.139602,
+ 0.4708934,
+ 0.3228633,
+ 0.21474116,
+ 0.13960199,
0.089136004,
- 0.056082606,
- 0.03485179,
- 0.02142787,
- 0.013050735,
- 0.007881463,
+ 0.05608259,
+ 0.034851786,
+ 0.021427846,
+ 0.013050736,
+ 0.007881473,
0,
0,
0,
diff --git a/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_flingOpen.json b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_flingOpen.json
index 98519db..156767b 100644
--- a/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_flingOpen.json
+++ b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_flingOpen.json
@@ -344,7 +344,7 @@
0,
0,
0.008216977,
- 0.06259775,
+ 0.0747174,
0.19032806,
0.39281356,
0.57081985,
diff --git a/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_magneticDetachAndReattach.json b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_magneticDetachAndReattach.json
index 850cee9..c400b66 100644
--- a/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_magneticDetachAndReattach.json
+++ b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_magneticDetachAndReattach.json
@@ -472,7 +472,7 @@
},
{
"width": 150,
- "height": 42.4
+ "height": 43.6
},
{
"width": 150,
@@ -568,7 +568,7 @@
},
{
"width": 150,
- "height": 88
+ "height": 87.2
},
{
"width": 150,
@@ -672,7 +672,7 @@
0,
0,
0.0066464543,
- 0.059778452,
+ 0.07101154,
0.1875459,
0.39009166,
0.5686131,
@@ -726,7 +726,7 @@
1,
1,
0.98828065,
- 0.9288363,
+ 0.9075689,
0.7806658,
0.57941735,
0.40687433,
diff --git a/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_triggeredRevealCloseTransition.json b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_triggeredRevealCloseTransition.json
index afa005a..e3f1888 100644
--- a/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_triggeredRevealCloseTransition.json
+++ b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_triggeredRevealCloseTransition.json
@@ -284,14 +284,14 @@
0.8118515,
0.60931784,
0.43090785,
- 0.29299664,
+ 0.29299662,
0.19368339,
- 0.12531388,
- 0.079705715,
- 0.049988627,
- 0.030979574,
- 0.019001365,
- 0.011548042,
+ 0.12531386,
+ 0.07970572,
+ 0.049988635,
+ 0.030979585,
+ 0.019001357,
+ 0.011548039,
0,
0,
0,
diff --git a/packages/SystemUI/compose/scene/tests/goldens/motionValue_withAnimation_prolongsTransition.json b/packages/SystemUI/compose/scene/tests/goldens/motionValue_withAnimation_prolongsTransition.json
index bda53bbf..0676fb9 100644
--- a/packages/SystemUI/compose/scene/tests/goldens/motionValue_withAnimation_prolongsTransition.json
+++ b/packages/SystemUI/compose/scene/tests/goldens/motionValue_withAnimation_prolongsTransition.json
@@ -32,15 +32,15 @@
86.9892,
63.112034,
43.8049,
- 29.501678,
- 19.439606,
- 12.599068,
- 8.06028,
+ 29.50168,
+ 19.439608,
+ 12.599069,
+ 8.060281,
5.102936,
- 3.2029724,
- 1.9959946,
- 1.2362518,
- 0.761673,
+ 3.202975,
+ 1.9959927,
+ 1.2362528,
+ 0.7616764,
0
]
}
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt
index aff5aa0..e1e2918 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt
@@ -51,6 +51,7 @@
import androidx.compose.ui.platform.LocalViewConfiguration
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.assertIsEqualTo
import androidx.compose.ui.test.assertIsNotDisplayed
import androidx.compose.ui.test.assertPositionInRootIsEqualTo
import androidx.compose.ui.test.assertTopPositionInRootIsEqualTo
@@ -2320,23 +2321,28 @@
MutableSceneTransitionLayoutStateForTests(SceneA, SceneTransitions.Empty)
}
- lateinit var fooHeight: () -> Dp?
- val fooHeightPreChildMeasure = mutableListOf<Dp?>()
+ lateinit var lastFooHeight: () -> Dp?
+ var firstFooHeightBeforeMeasuringChild: Dp? = null
val scope =
rule.setContentAndCreateMainScope {
val density = LocalDensity.current
SceneTransitionLayoutForTesting(state) {
scene(SceneA) {
- fooHeight = {
- with(density) { TestElements.Foo.approachSize(SceneA)?.height?.toDp() }
+ SideEffect {
+ lastFooHeight = {
+ with(density) { TestElements.Foo.lastSize(SceneA)?.height?.toDp() }
+ }
}
Box(Modifier.element(TestElements.Foo).size(200.dp)) {
Box(
Modifier.approachLayout(
isMeasurementApproachInProgress = { false },
approachMeasure = { measurable, constraints ->
- fooHeightPreChildMeasure += fooHeight()
+ if (firstFooHeightBeforeMeasuringChild == null) {
+ firstFooHeightBeforeMeasuringChild = lastFooHeight()
+ }
+
measurable.measure(constraints).run {
layout(width, height) {}
}
@@ -2351,37 +2357,55 @@
var progress by mutableFloatStateOf(0f)
val transition = transition(from = SceneA, to = SceneB, progress = { progress })
- var countApproachPass = fooHeightPreChildMeasure.size
+
+ fun assertDp(actual: Dp?, expected: Dp, subject: String) {
+ assertThat(actual).isNotNull()
+ actual!!.assertIsEqualTo(expected, subject, tolerance = 0.5.dp)
+ }
// Idle state: Scene A.
assertThat(state.isTransitioning()).isFalse()
- assertThat(fooHeight()).isNull()
+ assertDp(actual = lastFooHeight(), expected = 200.dp, subject = "lastFooHeight")
// Start transition: Scene A -> Scene B (progress 0%).
+ firstFooHeightBeforeMeasuringChild = null
scope.launch { state.startTransition(transition) }
rule.waitForIdle()
assertThat(state.isTransitioning()).isTrue()
- assertThat(fooHeightPreChildMeasure[countApproachPass]?.value).isWithin(.5f).of(200f)
- assertThat(fooHeight()).isNotNull()
- countApproachPass = fooHeightPreChildMeasure.size
+ assertDp(
+ actual = firstFooHeightBeforeMeasuringChild,
+ expected = 200.dp,
+ subject = "firstFooHeightBeforeMeasuringChild",
+ )
+ assertDp(actual = lastFooHeight(), expected = 200.dp, subject = "lastFooHeight")
// progress 50%: height is going from 200dp to 100dp, so 150dp is expected now.
+ firstFooHeightBeforeMeasuringChild = null
progress = 0.5f
rule.waitForIdle()
- assertThat(fooHeightPreChildMeasure[countApproachPass]?.value).isWithin(.5f).of(150f)
- assertThat(fooHeight()).isNotNull()
- countApproachPass = fooHeightPreChildMeasure.size
+ assertDp(
+ actual = firstFooHeightBeforeMeasuringChild,
+ expected = 150.dp,
+ subject = "firstFooHeightBeforeMeasuringChild",
+ )
+ assertDp(actual = lastFooHeight(), expected = 150.dp, subject = "lastFooHeight")
+ firstFooHeightBeforeMeasuringChild = null
progress = 1f
rule.waitForIdle()
- assertThat(fooHeightPreChildMeasure[countApproachPass]?.value).isWithin(.5f).of(100f)
- assertThat(fooHeight()).isNotNull()
- countApproachPass = fooHeightPreChildMeasure.size
+ assertDp(
+ actual = firstFooHeightBeforeMeasuringChild,
+ expected = 100.dp,
+ subject = "firstFooHeightBeforeMeasuringChild",
+ )
+ assertDp(actual = lastFooHeight(), expected = 100.dp, subject = "lastFooHeight")
+ firstFooHeightBeforeMeasuringChild = null
transition.finish()
rule.waitForIdle()
assertThat(state.isTransitioning()).isFalse()
- assertThat(fooHeight()).isNull()
- assertThat(fooHeightPreChildMeasure.size).isEqualTo(countApproachPass)
+ assertThat(firstFooHeightBeforeMeasuringChild).isNull()
+ // null because SceneA does not exist anymore.
+ assertThat(lastFooHeight()).isNull()
}
}
diff --git a/packages/SystemUI/customization/Android.bp b/packages/SystemUI/customization/Android.bp
index 81d92fa..3622cb7 100644
--- a/packages/SystemUI/customization/Android.bp
+++ b/packages/SystemUI/customization/Android.bp
@@ -26,19 +26,20 @@
name: "SystemUICustomizationLib",
use_resource_processor: true,
srcs: [
+ "src/**/*.aidl",
"src/**/*.java",
"src/**/*.kt",
- "src/**/*.aidl",
],
static_libs: [
"PlatformAnimationLib",
"PluginCoreLib",
+ "SystemUIClocks-CommonLib",
"SystemUIPluginLib",
"SystemUIUnfoldLib",
- "kotlinx_coroutines",
- "monet",
"dagger2",
"jsr330",
+ "kotlinx_coroutines",
+ "monet",
],
libs: [
// Keep android-specific libraries as libs instead of static_libs, so that they don't break
@@ -50,10 +51,13 @@
"androidx.recyclerview_recyclerview",
"kotlinx_coroutines_android",
],
- resource_dirs: [
- "res",
- ],
+ resource_dirs: ["res"],
min_sdk_version: "current",
plugins: ["dagger2-compiler"],
kotlincflags: ["-Xjvm-default=all"],
+
+ optimize: {
+ proguard_flags_files: ["proguard_customization.flags"],
+ export_proguard_flags_files: true,
+ },
}
diff --git a/packages/SystemUI/customization/clocks/common/Android.bp b/packages/SystemUI/customization/clocks/common/Android.bp
new file mode 100644
index 0000000..103dca7
--- /dev/null
+++ b/packages/SystemUI/customization/clocks/common/Android.bp
@@ -0,0 +1,60 @@
+// Copyright (C) 2025 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ default_team: "trendy_team_system_ui_please_use_a_more_specific_subteam_if_possible_",
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_packages_SystemUI_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_packages_SystemUI_license"],
+}
+
+android_library {
+ name: "SystemUIClocks-CommonLib",
+ use_resource_processor: true,
+ manifest: "AndroidManifest.xml",
+ srcs: [
+ "src/**/*.java",
+ "src/**/*.kt",
+ ],
+ static_libs: [
+ "PlatformAnimationLib",
+ "PluginCoreLib",
+ "SystemUIPluginLib",
+ "dagger2",
+ "jsr330",
+ "kotlinx_coroutines",
+ "monet",
+ ],
+ libs: [
+ // Keep android-specific libraries as libs instead of static_libs, so that they don't break
+ // things when included as transitive dependencies in robolectric targets.
+ "androidx.concurrent_concurrent-futures",
+ "androidx.dynamicanimation_dynamicanimation",
+ "androidx.lifecycle_lifecycle-runtime-ktx",
+ "androidx.lifecycle_lifecycle-viewmodel-ktx",
+ "androidx.recyclerview_recyclerview",
+ "kotlinx_coroutines_android",
+ ],
+ resource_dirs: ["res"],
+ min_sdk_version: "current",
+ plugins: ["dagger2-compiler"],
+ kotlincflags: ["-Xjvm-default=all"],
+ optimize: {
+ proguard_flags_files: ["proguard_clocks.flags"],
+ export_proguard_flags_files: true,
+ },
+}
diff --git a/packages/SystemUI/customization/clocks/common/AndroidManifest.xml b/packages/SystemUI/customization/clocks/common/AndroidManifest.xml
new file mode 100644
index 0000000..b27e7c1
--- /dev/null
+++ b/packages/SystemUI/customization/clocks/common/AndroidManifest.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2025 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.systemui.customization.clocks">
+
+</manifest>
diff --git a/packages/SystemUI/customization/clocks/common/proguard_clocks.flags b/packages/SystemUI/customization/clocks/common/proguard_clocks.flags
new file mode 100644
index 0000000..ab3901c
--- /dev/null
+++ b/packages/SystemUI/customization/clocks/common/proguard_clocks.flags
@@ -0,0 +1,5 @@
+-include ../../../proguard_kotlin.flags
+
+-keepclassmembers class androidx.constraintlayout.widget.ConstraintSet { public *; }
+-keepclassmembers class androidx.constraintlayout.widget.ConstraintSet$* { public *; }
+-keepclassmembers class com.android.systemui.customization.clocks.R$* { public static <fields>; }
diff --git a/packages/SystemUI/customization/res/values-h700dp/dimens.xml b/packages/SystemUI/customization/clocks/common/res/values-h700dp/dimens.xml
similarity index 100%
rename from packages/SystemUI/customization/res/values-h700dp/dimens.xml
rename to packages/SystemUI/customization/clocks/common/res/values-h700dp/dimens.xml
diff --git a/packages/SystemUI/customization/res/values-h800dp/dimens.xml b/packages/SystemUI/customization/clocks/common/res/values-h800dp/dimens.xml
similarity index 100%
rename from packages/SystemUI/customization/res/values-h800dp/dimens.xml
rename to packages/SystemUI/customization/clocks/common/res/values-h800dp/dimens.xml
diff --git a/packages/SystemUI/customization/res/values-land/dimens.xml b/packages/SystemUI/customization/clocks/common/res/values-land/dimens.xml
similarity index 100%
rename from packages/SystemUI/customization/res/values-land/dimens.xml
rename to packages/SystemUI/customization/clocks/common/res/values-land/dimens.xml
diff --git a/packages/SystemUI/customization/res/values-sw400dp/dimens.xml b/packages/SystemUI/customization/clocks/common/res/values-sw400dp/dimens.xml
similarity index 100%
rename from packages/SystemUI/customization/res/values-sw400dp/dimens.xml
rename to packages/SystemUI/customization/clocks/common/res/values-sw400dp/dimens.xml
diff --git a/packages/SystemUI/customization/res/values-sw600dp-land/dimens.xml b/packages/SystemUI/customization/clocks/common/res/values-sw600dp-land/dimens.xml
similarity index 100%
rename from packages/SystemUI/customization/res/values-sw600dp-land/dimens.xml
rename to packages/SystemUI/customization/clocks/common/res/values-sw600dp-land/dimens.xml
diff --git a/packages/SystemUI/customization/res/values-sw600dp/dimens.xml b/packages/SystemUI/customization/clocks/common/res/values-sw600dp/dimens.xml
similarity index 100%
rename from packages/SystemUI/customization/res/values-sw600dp/dimens.xml
rename to packages/SystemUI/customization/clocks/common/res/values-sw600dp/dimens.xml
diff --git a/packages/SystemUI/customization/res/values-sw720dp-land/dimens.xml b/packages/SystemUI/customization/clocks/common/res/values-sw720dp-land/dimens.xml
similarity index 100%
rename from packages/SystemUI/customization/res/values-sw720dp-land/dimens.xml
rename to packages/SystemUI/customization/clocks/common/res/values-sw720dp-land/dimens.xml
diff --git a/packages/SystemUI/customization/res/values-sw720dp-port/dimens.xml b/packages/SystemUI/customization/clocks/common/res/values-sw720dp-port/dimens.xml
similarity index 100%
rename from packages/SystemUI/customization/res/values-sw720dp-port/dimens.xml
rename to packages/SystemUI/customization/clocks/common/res/values-sw720dp-port/dimens.xml
diff --git a/packages/SystemUI/customization/res/values/dimens.xml b/packages/SystemUI/customization/clocks/common/res/values/dimens.xml
similarity index 93%
rename from packages/SystemUI/customization/res/values/dimens.xml
rename to packages/SystemUI/customization/clocks/common/res/values/dimens.xml
index e2cf300..ea954cc 100644
--- a/packages/SystemUI/customization/res/values/dimens.xml
+++ b/packages/SystemUI/customization/clocks/common/res/values/dimens.xml
@@ -36,9 +36,11 @@
<dimen name="weather_date_icon_padding">28dp</dimen>
<dimen name="clock_vertical_digit_buffer">8dp</dimen>
+ <dimen name="weather_clock_smartspace_bottom_margin">10dp</dimen>
+
<!-- When large clock is showing, offset the smartspace by this amount -->
<dimen name="keyguard_smartspace_top_offset">6dp</dimen>
- <!--Dimens used in both lockscreen preview and smartspace -->
+ <!--Dimens used in both lockscreen preview and smartspace -->
<dimen name="date_weather_view_height">24dp</dimen>
<dimen name="enhanced_smartspace_height">104dp</dimen>
<dimen name="status_view_margin_horizontal">0dp</dimen>
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/CanvasUtil.kt b/packages/SystemUI/customization/clocks/common/src/com/android/systemui/customization/clocks/CanvasUtil.kt
similarity index 95%
rename from packages/SystemUI/customization/src/com/android/systemui/shared/clocks/CanvasUtil.kt
rename to packages/SystemUI/customization/clocks/common/src/com/android/systemui/customization/clocks/CanvasUtil.kt
index 9857d7f..b774c28 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/CanvasUtil.kt
+++ b/packages/SystemUI/customization/clocks/common/src/com/android/systemui/customization/clocks/CanvasUtil.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.shared.clocks
+package com.android.systemui.customization.clocks
import android.graphics.Canvas
import com.android.systemui.plugins.clocks.VPoint
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockLogger.kt b/packages/SystemUI/customization/clocks/common/src/com/android/systemui/customization/clocks/ClockLogger.kt
similarity index 96%
rename from packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockLogger.kt
rename to packages/SystemUI/customization/clocks/common/src/com/android/systemui/customization/clocks/ClockLogger.kt
index 4f04aaa..702f7b1 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockLogger.kt
+++ b/packages/SystemUI/customization/clocks/common/src/com/android/systemui/customization/clocks/ClockLogger.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.plugins.clocks
+package com.android.systemui.customization.clocks
import android.view.View
import android.view.View.MeasureSpec
@@ -22,6 +22,8 @@
import com.android.systemui.log.core.LogcatOnlyMessageBuffer
import com.android.systemui.log.core.Logger
import com.android.systemui.log.core.MessageBuffer
+import com.android.systemui.plugins.clocks.VPointF
+import com.android.systemui.plugins.clocks.VRect
import kotlin.math.abs
class ClockLogger(private val view: View?, buffer: MessageBuffer, tag: String) :
diff --git a/packages/SystemUI/customization/clocks/common/src/com/android/systemui/customization/clocks/ContextUtil.kt b/packages/SystemUI/customization/clocks/common/src/com/android/systemui/customization/clocks/ContextUtil.kt
new file mode 100644
index 0000000..48c5ad7
--- /dev/null
+++ b/packages/SystemUI/customization/clocks/common/src/com/android/systemui/customization/clocks/ContextUtil.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.customization.clocks
+
+import android.content.Context
+import com.android.internal.R as internalR
+import com.android.internal.policy.SystemBarUtils
+
+object ContextUtil {
+ fun Context.getSafeStatusBarHeight(): Int {
+ return this.displayNoVerify?.let { display ->
+ SystemBarUtils.getStatusBarHeight(this.resources, display.cutout)
+ } ?: this.resources.getDimensionPixelSize(internalR.dimen.status_bar_height)
+ }
+}
diff --git a/packages/SystemUI/customization/clocks/common/src/com/android/systemui/customization/clocks/DefaultClockFaceLayout.kt b/packages/SystemUI/customization/clocks/common/src/com/android/systemui/customization/clocks/DefaultClockFaceLayout.kt
new file mode 100644
index 0000000..c73cb3a
--- /dev/null
+++ b/packages/SystemUI/customization/clocks/common/src/com/android/systemui/customization/clocks/DefaultClockFaceLayout.kt
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.customization.clocks
+
+import android.util.DisplayMetrics
+import android.view.View
+import androidx.constraintlayout.widget.ConstraintSet
+import androidx.constraintlayout.widget.ConstraintSet.BOTTOM
+import androidx.constraintlayout.widget.ConstraintSet.END
+import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
+import androidx.constraintlayout.widget.ConstraintSet.START
+import androidx.constraintlayout.widget.ConstraintSet.TOP
+import androidx.constraintlayout.widget.ConstraintSet.WRAP_CONTENT
+import com.android.systemui.customization.clocks.ContextUtil.getSafeStatusBarHeight
+import com.android.systemui.customization.clocks.R as clocksR
+import com.android.systemui.plugins.clocks.AodClockBurnInModel
+import com.android.systemui.plugins.clocks.ClockFaceLayout
+import com.android.systemui.plugins.clocks.ClockPreviewConfig
+import com.android.systemui.plugins.clocks.ClockViewIds
+
+/** A ClockFaceLayout that applies the default lockscreen layout to a single view */
+open class DefaultClockFaceLayout(val view: View) : ClockFaceLayout {
+ override val views = listOf(view)
+
+ override fun applyConstraints(constraints: ConstraintSet): ConstraintSet {
+ if (views.size != 1) {
+ throw IllegalArgumentException(
+ "Should have only one container view when using DefaultClockFaceLayout"
+ )
+ }
+ return constraints
+ }
+
+ override fun applyPreviewConstraints(
+ clockPreviewConfig: ClockPreviewConfig,
+ constraints: ConstraintSet,
+ ): ConstraintSet {
+ val res = view.context.resources
+ return constraints.apply {
+ constrainWidth(ClockViewIds.LOCKSCREEN_CLOCK_VIEW_LARGE, WRAP_CONTENT)
+ constrainHeight(ClockViewIds.LOCKSCREEN_CLOCK_VIEW_LARGE, WRAP_CONTENT)
+ constrainMaxHeight(ClockViewIds.LOCKSCREEN_CLOCK_VIEW_LARGE, 0)
+
+ val largeClockTopMargin =
+ view.context.getSafeStatusBarHeight() +
+ res.getDimensionPixelSize(clocksR.dimen.small_clock_padding_top) +
+ res.getDimensionPixelSize(clocksR.dimen.keyguard_smartspace_top_offset) +
+ res.getDimensionPixelSize(clocksR.dimen.date_weather_view_height) +
+ res.getDimensionPixelSize(clocksR.dimen.enhanced_smartspace_height)
+ connect(
+ ClockViewIds.LOCKSCREEN_CLOCK_VIEW_LARGE,
+ TOP,
+ PARENT_ID,
+ TOP,
+ largeClockTopMargin,
+ )
+ connect(ClockViewIds.LOCKSCREEN_CLOCK_VIEW_LARGE, START, PARENT_ID, START)
+ connect(ClockViewIds.LOCKSCREEN_CLOCK_VIEW_LARGE, END, PARENT_ID, END)
+
+ clockPreviewConfig.udfpsTop?.let { udfpsTop ->
+ connect(
+ ClockViewIds.LOCKSCREEN_CLOCK_VIEW_LARGE,
+ BOTTOM,
+ PARENT_ID,
+ BOTTOM,
+ (res.displayMetrics.heightPixels - udfpsTop).toInt(),
+ )
+ }
+ ?: run {
+ // Copied calculation codes from applyConstraints in DefaultDeviceEntrySection
+ clockPreviewConfig.lockViewId?.let { lockViewId ->
+ connect(ClockViewIds.LOCKSCREEN_CLOCK_VIEW_LARGE, BOTTOM, lockViewId, TOP)
+ }
+ ?: run {
+ val bottomPaddingPx =
+ res.getDimensionPixelSize(clocksR.dimen.lock_icon_margin_bottom)
+ val defaultDensity =
+ DisplayMetrics.DENSITY_DEVICE_STABLE.toFloat() /
+ DisplayMetrics.DENSITY_DEFAULT.toFloat()
+ val lockIconRadiusPx = (defaultDensity * 36).toInt()
+ val clockBottomMargin = bottomPaddingPx + 2 * lockIconRadiusPx
+ connect(
+ ClockViewIds.LOCKSCREEN_CLOCK_VIEW_LARGE,
+ BOTTOM,
+ PARENT_ID,
+ BOTTOM,
+ clockBottomMargin,
+ )
+ }
+ }
+
+ constrainWidth(ClockViewIds.LOCKSCREEN_CLOCK_VIEW_SMALL, WRAP_CONTENT)
+ constrainHeight(
+ ClockViewIds.LOCKSCREEN_CLOCK_VIEW_SMALL,
+ res.getDimensionPixelSize(clocksR.dimen.small_clock_height),
+ )
+ connect(
+ ClockViewIds.LOCKSCREEN_CLOCK_VIEW_SMALL,
+ START,
+ PARENT_ID,
+ START,
+ res.getDimensionPixelSize(clocksR.dimen.clock_padding_start) +
+ res.getDimensionPixelSize(clocksR.dimen.status_view_margin_horizontal),
+ )
+
+ val smallClockTopMargin = clockPreviewConfig.getSmallClockTopPadding()
+ connect(
+ ClockViewIds.LOCKSCREEN_CLOCK_VIEW_SMALL,
+ TOP,
+ PARENT_ID,
+ TOP,
+ smallClockTopMargin,
+ )
+ }
+ }
+
+ override fun applyAodBurnIn(aodBurnInModel: AodClockBurnInModel) {
+ // Default clock doesn't need detailed control of view
+ }
+}
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FontUtils.kt b/packages/SystemUI/customization/clocks/common/src/com/android/systemui/customization/clocks/FontUtils.kt
similarity index 96%
rename from packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FontUtils.kt
rename to packages/SystemUI/customization/clocks/common/src/com/android/systemui/customization/clocks/FontUtils.kt
index 722d76b..7a54b37 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FontUtils.kt
+++ b/packages/SystemUI/customization/clocks/common/src/com/android/systemui/customization/clocks/FontUtils.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.shared.clocks
+package com.android.systemui.customization.clocks
import com.android.systemui.animation.AxisDefinition
import com.android.systemui.plugins.clocks.AxisType
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ViewUtils.kt b/packages/SystemUI/customization/clocks/common/src/com/android/systemui/customization/clocks/ViewUtils.kt
similarity index 96%
rename from packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ViewUtils.kt
rename to packages/SystemUI/customization/clocks/common/src/com/android/systemui/customization/clocks/ViewUtils.kt
index 0740b0e..4f5b22a 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ViewUtils.kt
+++ b/packages/SystemUI/customization/clocks/common/src/com/android/systemui/customization/clocks/ViewUtils.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.shared.clocks
+package com.android.systemui.customization.clocks
import android.graphics.Rect
import android.view.View
diff --git a/packages/SystemUI/customization/proguard_customization.flags b/packages/SystemUI/customization/proguard_customization.flags
new file mode 100644
index 0000000..1f4f28e
--- /dev/null
+++ b/packages/SystemUI/customization/proguard_customization.flags
@@ -0,0 +1 @@
+-include ../proguard_kotlin.flags
diff --git a/packages/SystemUI/customization/res/values/ids.xml b/packages/SystemUI/customization/res/values/ids.xml
deleted file mode 100644
index 3a3e06b..0000000
--- a/packages/SystemUI/customization/res/values/ids.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<resources>
- <item type="id" name="lockscreen_clock_view_large" />
- <item type="id" name="lockscreen_clock_view" />
-
- <!-- View ids for elements in large weather clock -->
- <item type="id" name="weather_clock_time" />
- <item type="id" name="weather_clock_date" />
- <item type="id" name="weather_clock_weather_icon" />
- <item type="id" name="weather_clock_temperature" />
- <item type="id" name="weather_clock_alarm_dnd" />
-
- <item type="id" name="HOUR_DIGIT_PAIR"/>
- <item type="id" name="MINUTE_DIGIT_PAIR"/>
- <item type="id" name="HOUR_FIRST_DIGIT"/>
- <item type="id" name="HOUR_SECOND_DIGIT"/>
- <item type="id" name="MINUTE_FIRST_DIGIT"/>
- <item type="id" name="MINUTE_SECOND_DIGIT"/>
- <item type="id" name="TIME_FULL_FORMAT"/>
- <item type="id" name="DATE_FORMAT"/>
-</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt
index 2f819d1..cd708e6 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt
@@ -37,11 +37,11 @@
import com.android.systemui.animation.TextAnimatorListener
import com.android.systemui.animation.TypefaceVariantCacheImpl
import com.android.systemui.customization.R
+import com.android.systemui.customization.clocks.ClockLogger
+import com.android.systemui.customization.clocks.ClockLogger.Companion.escapeTime
import com.android.systemui.log.core.LogLevel
import com.android.systemui.log.core.LogcatOnlyMessageBuffer
import com.android.systemui.log.core.MessageBuffer
-import com.android.systemui.plugins.clocks.ClockLogger
-import com.android.systemui.plugins.clocks.ClockLogger.Companion.escapeTime
import java.io.PrintWriter
import java.util.Calendar
import java.util.Locale
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
index 0648412..4301413 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
@@ -580,7 +580,8 @@
return availableClocks[clockId]?.provider?.getClockPickerConfig(clockSettings)
}
- fun createExampleClock(clockId: ClockId): ClockController? = createClock(clockId)
+ fun createExampleClock(ctx: Context, clockId: ClockId): ClockController? =
+ createClock(ctx, clockId)
/**
* Adds [listener] to receive future clock changes.
@@ -602,10 +603,10 @@
clockChangeListeners.remove(listener)
}
- fun createCurrentClock(): ClockController {
+ fun createCurrentClock(ctx: Context): ClockController {
val clockId = currentClockId
if (isEnabled && clockId.isNotEmpty()) {
- val clock = createClock(clockId)
+ val clock = createClock(ctx, clockId)
if (clock != null) {
logger.i({ "Rendering clock $str1" }) { str1 = clockId }
return clock
@@ -617,15 +618,15 @@
}
}
- return createClock(DEFAULT_CLOCK_ID)!!
+ return createClock(ctx, DEFAULT_CLOCK_ID)!!
}
- private fun createClock(targetClockId: ClockId): ClockController? {
+ private fun createClock(ctx: Context, targetClockId: ClockId): ClockController? {
var settings = this.settings ?: ClockSettings()
if (targetClockId != settings.clockId) {
settings = settings.copy(clockId = targetClockId)
}
- return availableClocks[targetClockId]?.provider?.createClock(settings)
+ return availableClocks[targetClockId]?.provider?.createClock(ctx, settings)
}
fun dump(pw: PrintWriter, args: Array<out String>) {
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ComposedDigitalLayerController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ComposedDigitalLayerController.kt
index 5fbbf0b..35e2f95 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ComposedDigitalLayerController.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ComposedDigitalLayerController.kt
@@ -164,7 +164,10 @@
}
override fun onThemeChanged(theme: ThemeConfig) {
- view.updateColor(theme.getDefaultColor(clockCtx.context))
+ view.updateColor(
+ lockscreenColor = theme.getDefaultColor(clockCtx.context),
+ aodColor = clockCtx.resources.getColor(android.R.color.system_accent1_100),
+ )
}
override fun onFontSettingChanged(fontSizePx: Float) {
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
index 450cece..dcbe557 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
@@ -22,7 +22,9 @@
import android.view.LayoutInflater
import android.widget.FrameLayout
import androidx.annotation.VisibleForTesting
-import com.android.systemui.customization.R
+import com.android.systemui.customization.R as customR
+import com.android.systemui.customization.clocks.DefaultClockFaceLayout
+import com.android.systemui.customization.clocks.R as clocksR
import com.android.systemui.log.core.MessageBuffer
import com.android.systemui.plugins.clocks.AlarmData
import com.android.systemui.plugins.clocks.ClockAnimations
@@ -36,10 +38,11 @@
import com.android.systemui.plugins.clocks.ClockFaceEvents
import com.android.systemui.plugins.clocks.ClockMessageBuffers
import com.android.systemui.plugins.clocks.ClockSettings
-import com.android.systemui.plugins.clocks.DefaultClockFaceLayout
+import com.android.systemui.plugins.clocks.ClockViewIds
import com.android.systemui.plugins.clocks.ThemeConfig
import com.android.systemui.plugins.clocks.WeatherData
import com.android.systemui.plugins.clocks.ZenData
+import com.android.systemui.shared.Flags.ambientAod
import java.io.PrintWriter
import java.util.Locale
import java.util.TimeZone
@@ -64,15 +67,16 @@
private val burmeseNf = NumberFormat.getInstance(Locale.forLanguageTag("my"))
private val burmeseNumerals = burmeseNf.format(FORMAT_NUMBER.toLong())
private val burmeseLineSpacing =
- resources.getFloat(R.dimen.keyguard_clock_line_spacing_scale_burmese)
- private val defaultLineSpacing = resources.getFloat(R.dimen.keyguard_clock_line_spacing_scale)
+ resources.getFloat(clocksR.dimen.keyguard_clock_line_spacing_scale_burmese)
+ private val defaultLineSpacing =
+ resources.getFloat(clocksR.dimen.keyguard_clock_line_spacing_scale)
override val events: DefaultClockEvents
override val config: ClockConfig by lazy {
ClockConfig(
DEFAULT_CLOCK_ID,
- resources.getString(R.string.clock_default_name),
- resources.getString(R.string.clock_default_description),
+ resources.getString(customR.string.clock_default_name),
+ resources.getString(customR.string.clock_default_description),
)
}
@@ -80,14 +84,14 @@
val parent = FrameLayout(ctx)
smallClock =
DefaultClockFaceController(
- layoutInflater.inflate(R.layout.clock_default_small, parent, false)
+ layoutInflater.inflate(customR.layout.clock_default_small, parent, false)
as AnimatableClockView,
settings?.seedColor,
messageBuffers?.smallClockMessageBuffer,
)
largeClock =
LargeClockFaceController(
- layoutInflater.inflate(R.layout.clock_default_large, parent, false)
+ layoutInflater.inflate(customR.layout.clock_default_large, parent, false)
as AnimatableClockView,
settings?.seedColor,
messageBuffers?.largeClockMessageBuffer,
@@ -128,17 +132,14 @@
override val config = ClockFaceConfig()
override var theme = ThemeConfig(true, seedColor)
- override val layout =
- DefaultClockFaceLayout(view).apply {
- views[0].id =
- resources.getIdentifier("lockscreen_clock_view", "id", ctx.packageName)
- }
+ override val layout = DefaultClockFaceLayout(view)
override var animations: DefaultClockAnimations = DefaultClockAnimations(view, 0f, 0f)
internal set
init {
- view.setColors(DOZE_COLOR, currentColor)
+ view.id = ClockViewIds.LOCKSCREEN_CLOCK_VIEW_SMALL
+ view.setColors(getAodColor(), currentColor)
messageBuffer?.let { view.messageBuffer = it }
}
@@ -155,7 +156,8 @@
}
currentColor = color
- view.setColors(DOZE_COLOR, color)
+
+ view.setColors(getAodColor(), color)
if (!animations.dozeState.isActive) {
view.animateColorChange()
}
@@ -175,6 +177,14 @@
}
open fun recomputePadding(targetRegion: Rect?) {}
+
+ private fun getAodColor(): Int {
+ return if (ambientAod()) {
+ ctx.resources.getColor(android.R.color.system_accent1_100)
+ } else {
+ DOZE_COLOR
+ }
+ }
}
inner class LargeClockFaceController(
@@ -182,14 +192,11 @@
seedColor: Int?,
messageBuffer: MessageBuffer?,
) : DefaultClockFaceController(view, seedColor, messageBuffer) {
- override val layout =
- DefaultClockFaceLayout(view).apply {
- views[0].id =
- resources.getIdentifier("lockscreen_clock_view_large", "id", ctx.packageName)
- }
+ override val layout = DefaultClockFaceLayout(view)
override val config = ClockFaceConfig(hasCustomPositionUpdatedAnimation = true)
init {
+ view.id = ClockViewIds.LOCKSCREEN_CLOCK_VIEW_LARGE
view.hasCustomPositionUpdatedAnimation = true
animations = LargeClockAnimations(view, 0f, 0f)
}
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt
index d778bc0..9996016 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt
@@ -19,12 +19,12 @@
import android.os.Vibrator
import android.view.LayoutInflater
import com.android.systemui.customization.R
+import com.android.systemui.customization.clocks.ClockLogger
import com.android.systemui.log.core.MessageBuffer
import com.android.systemui.plugins.clocks.AxisPresetConfig
import com.android.systemui.plugins.clocks.ClockAxisStyle
import com.android.systemui.plugins.clocks.ClockController
import com.android.systemui.plugins.clocks.ClockFontAxis.Companion.merge
-import com.android.systemui.plugins.clocks.ClockLogger
import com.android.systemui.plugins.clocks.ClockMessageBuffers
import com.android.systemui.plugins.clocks.ClockMetadata
import com.android.systemui.plugins.clocks.ClockPickerConfig
@@ -49,7 +49,6 @@
/** Provides the default system clock */
class DefaultClockProvider(
- val ctx: Context,
val layoutInflater: LayoutInflater,
val resources: Resources,
private val isClockReactiveVariantsEnabled: Boolean = false,
@@ -74,7 +73,7 @@
return clocks
}
- override fun createClock(settings: ClockSettings): ClockController {
+ override fun createClock(ctx: Context, settings: ClockSettings): ClockController {
if (getClocks().all { it.clockId != settings.clockId }) {
throw IllegalArgumentException("${settings.clockId} is unsupported by $TAG")
}
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockController.kt
index 84f45fc..2f48462 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockController.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockController.kt
@@ -19,6 +19,8 @@
import android.content.res.Resources
import com.android.systemui.animation.GSFAxes
import com.android.systemui.customization.R
+import com.android.systemui.customization.clocks.FontUtils.put
+import com.android.systemui.customization.clocks.FontUtils.toClockAxis
import com.android.systemui.plugins.clocks.AlarmData
import com.android.systemui.plugins.clocks.AxisPresetConfig
import com.android.systemui.plugins.clocks.AxisType
@@ -32,8 +34,6 @@
import com.android.systemui.plugins.clocks.ClockSettings
import com.android.systemui.plugins.clocks.WeatherData
import com.android.systemui.plugins.clocks.ZenData
-import com.android.systemui.shared.clocks.FontUtils.put
-import com.android.systemui.shared.clocks.FontUtils.toClockAxis
import com.android.systemui.shared.clocks.view.FlexClockView
import java.io.PrintWriter
import java.util.Locale
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockFaceController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockFaceController.kt
index 18aa728..5371e12 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockFaceController.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockFaceController.kt
@@ -23,7 +23,11 @@
import android.widget.FrameLayout
import com.android.app.animation.Interpolators
import com.android.systemui.animation.GSFAxes
-import com.android.systemui.customization.R
+import com.android.systemui.customization.clocks.DefaultClockFaceLayout
+import com.android.systemui.customization.clocks.FontUtils.get
+import com.android.systemui.customization.clocks.FontUtils.set
+import com.android.systemui.customization.clocks.R
+import com.android.systemui.customization.clocks.ViewUtils.computeLayoutDiff
import com.android.systemui.plugins.clocks.AlarmData
import com.android.systemui.plugins.clocks.ClockAnimations
import com.android.systemui.plugins.clocks.ClockAxisStyle
@@ -33,14 +37,11 @@
import com.android.systemui.plugins.clocks.ClockFaceEvents
import com.android.systemui.plugins.clocks.ClockFaceLayout
import com.android.systemui.plugins.clocks.ClockFontAxis.Companion.merge
-import com.android.systemui.plugins.clocks.DefaultClockFaceLayout
+import com.android.systemui.plugins.clocks.ClockViewIds
import com.android.systemui.plugins.clocks.ThemeConfig
import com.android.systemui.plugins.clocks.WeatherData
import com.android.systemui.plugins.clocks.ZenData
import com.android.systemui.shared.clocks.FlexClockController.Companion.getDefaultAxes
-import com.android.systemui.shared.clocks.FontUtils.get
-import com.android.systemui.shared.clocks.FontUtils.set
-import com.android.systemui.shared.clocks.ViewUtils.computeLayoutDiff
import com.android.systemui.shared.clocks.view.FlexClockView
import com.android.systemui.shared.clocks.view.HorizontalAlignment
import com.android.systemui.shared.clocks.view.VerticalAlignment
@@ -89,7 +90,8 @@
override val layout: ClockFaceLayout =
DefaultClockFaceLayout(view).apply {
views[0].id =
- if (isLargeClock) R.id.lockscreen_clock_view_large else R.id.lockscreen_clock_view
+ if (isLargeClock) ClockViewIds.LOCKSCREEN_CLOCK_VIEW_LARGE
+ else ClockViewIds.LOCKSCREEN_CLOCK_VIEW_SMALL
}
override val events = FlexClockFaceEvents()
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/SimpleDigitalHandLayerController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/SimpleDigitalHandLayerController.kt
index eef910c..d85680b 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/SimpleDigitalHandLayerController.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/SimpleDigitalHandLayerController.kt
@@ -22,17 +22,19 @@
import android.widget.RelativeLayout
import androidx.annotation.VisibleForTesting
import com.android.systemui.animation.TextAnimator
-import com.android.systemui.customization.R
import com.android.systemui.log.core.Logger
+import com.android.systemui.monet.ColorScheme
import com.android.systemui.plugins.clocks.AlarmData
import com.android.systemui.plugins.clocks.ClockAnimations
import com.android.systemui.plugins.clocks.ClockAxisStyle
import com.android.systemui.plugins.clocks.ClockEvents
import com.android.systemui.plugins.clocks.ClockFaceConfig
import com.android.systemui.plugins.clocks.ClockFaceEvents
+import com.android.systemui.plugins.clocks.ClockViewIds
import com.android.systemui.plugins.clocks.ThemeConfig
import com.android.systemui.plugins.clocks.WeatherData
import com.android.systemui.plugins.clocks.ZenData
+import com.android.systemui.shared.Flags.ambientAod
import com.android.systemui.shared.clocks.view.HorizontalAlignment
import com.android.systemui.shared.clocks.view.SimpleDigitalClockTextView
import com.android.systemui.shared.clocks.view.VerticalAlignment
@@ -49,13 +51,7 @@
val timespec: DigitalTimespec,
val dateTimeFormat: String,
) {
- fun generateDigitalLayerIdString(): String {
- return when {
- timespec == DigitalTimespec.TIME_FULL_FORMAT -> "$timespec"
- "h" in dateTimeFormat -> "HOUR_$timespec"
- else -> "MINUTE_$timespec"
- }
- }
+ fun getViewId(): Int = timespec.getViewId("h" in dateTimeFormat)
}
data class DigitalAlignment(
@@ -70,11 +66,13 @@
val transitionInterpolator: Interpolator? = null,
)
-enum class DigitalTimespec {
- TIME_FULL_FORMAT,
- DIGIT_PAIR,
- FIRST_DIGIT,
- SECOND_DIGIT,
+enum class DigitalTimespec(private val hourViewId: Int, private val minuteViewId: Int) {
+ TIME_FULL_FORMAT(ClockViewIds.TIME_FULL_FORMAT, ClockViewIds.TIME_FULL_FORMAT),
+ DIGIT_PAIR(ClockViewIds.HOUR_DIGIT_PAIR, ClockViewIds.MINUTE_DIGIT_PAIR),
+ FIRST_DIGIT(ClockViewIds.HOUR_FIRST_DIGIT, ClockViewIds.MINUTE_FIRST_DIGIT),
+ SECOND_DIGIT(ClockViewIds.HOUR_SECOND_DIGIT, ClockViewIds.MINUTE_SECOND_DIGIT);
+
+ fun getViewId(isHour: Boolean): Int = if (isHour) hourViewId else minuteViewId
}
open class SimpleDigitalHandLayerController(
@@ -106,12 +104,8 @@
layerCfg.alignment.verticalAlignment?.let { view.verticalAlignment = it }
layerCfg.alignment.horizontalAlignment?.let { view.horizontalAlignment = it }
view.applyStyles(layerCfg.style, layerCfg.aodStyle)
- view.id =
- clockCtx.resources.getIdentifier(
- layerCfg.generateDigitalLayerIdString(),
- "id",
- clockCtx.context.getPackageName(),
- )
+
+ view.id = layerCfg.getViewId()
}
fun refreshTime() {
@@ -130,13 +124,13 @@
val lp = view.layoutParams as RelativeLayout.LayoutParams
lp.addRule(RelativeLayout.TEXT_ALIGNMENT_CENTER)
when (view.id) {
- R.id.HOUR_DIGIT_PAIR -> {
+ ClockViewIds.HOUR_DIGIT_PAIR -> {
lp.addRule(RelativeLayout.CENTER_VERTICAL)
lp.addRule(RelativeLayout.ALIGN_PARENT_START)
}
- R.id.MINUTE_DIGIT_PAIR -> {
+ ClockViewIds.MINUTE_DIGIT_PAIR -> {
lp.addRule(RelativeLayout.CENTER_VERTICAL)
- lp.addRule(RelativeLayout.END_OF, R.id.HOUR_DIGIT_PAIR)
+ lp.addRule(RelativeLayout.END_OF, ClockViewIds.HOUR_DIGIT_PAIR)
}
else -> {
throw Exception("cannot apply two pairs layout to view ${view.id}")
@@ -232,7 +226,23 @@
}
override fun onThemeChanged(theme: ThemeConfig) {
- view.updateColor(theme.getDefaultColor(clockCtx.context))
+ if (ambientAod()) {
+ val aodColor =
+ theme.seedColor?.let {
+ val colorScheme =
+ ColorScheme(
+ it,
+ false, // darkTheme is not used for palette generation
+ )
+ colorScheme.accent1.s100
+ } ?: clockCtx.resources.getColor(android.R.color.system_accent1_100)
+ view.updateColor(
+ lockscreenColor = theme.getDefaultColor(clockCtx.context),
+ aodColor = aodColor,
+ )
+ } else {
+ view.updateColor(lockscreenColor = theme.getDefaultColor(clockCtx.context))
+ }
refreshTime()
}
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/FlexClockView.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/FlexClockView.kt
index 5756116..b60943b 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/FlexClockView.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/FlexClockView.kt
@@ -25,18 +25,19 @@
import androidx.annotation.VisibleForTesting
import androidx.core.view.children
import com.android.app.animation.Interpolators
-import com.android.systemui.customization.R
+import com.android.systemui.customization.clocks.CanvasUtil.translate
+import com.android.systemui.customization.clocks.CanvasUtil.use
+import com.android.systemui.customization.clocks.ClockLogger
+import com.android.systemui.customization.clocks.R
+import com.android.systemui.customization.clocks.ViewUtils.measuredSize
import com.android.systemui.plugins.clocks.ClockAxisStyle
-import com.android.systemui.plugins.clocks.ClockLogger
+import com.android.systemui.plugins.clocks.ClockViewIds
import com.android.systemui.plugins.clocks.VPointF
import com.android.systemui.plugins.clocks.VPointF.Companion.max
import com.android.systemui.plugins.clocks.VPointF.Companion.times
import com.android.systemui.plugins.clocks.VRectF
-import com.android.systemui.shared.clocks.CanvasUtil.translate
-import com.android.systemui.shared.clocks.CanvasUtil.use
import com.android.systemui.shared.clocks.ClockContext
import com.android.systemui.shared.clocks.DigitTranslateAnimator
-import com.android.systemui.shared.clocks.ViewUtils.measuredSize
import java.util.Locale
import kotlin.collections.filterNotNull
import kotlin.collections.map
@@ -222,13 +223,13 @@
var offset =
maxChildSize.run {
when (child.id) {
- R.id.HOUR_FIRST_DIGIT -> VPointF.ZERO
- R.id.HOUR_SECOND_DIGIT -> VPointF(x, 0f)
- R.id.HOUR_DIGIT_PAIR -> VPointF.ZERO
+ ClockViewIds.HOUR_FIRST_DIGIT -> VPointF.ZERO
+ ClockViewIds.HOUR_SECOND_DIGIT -> VPointF(x, 0f)
+ ClockViewIds.HOUR_DIGIT_PAIR -> VPointF.ZERO
// Add a small vertical buffer for second line views
- R.id.MINUTE_DIGIT_PAIR -> VPointF(0f, y + yBuffer)
- R.id.MINUTE_FIRST_DIGIT -> VPointF(0f, y + yBuffer)
- R.id.MINUTE_SECOND_DIGIT -> VPointF(x, y + yBuffer)
+ ClockViewIds.MINUTE_DIGIT_PAIR -> VPointF(0f, y + yBuffer)
+ ClockViewIds.MINUTE_FIRST_DIGIT -> VPointF(0f, y + yBuffer)
+ ClockViewIds.MINUTE_SECOND_DIGIT -> VPointF(x, y + yBuffer)
else -> VPointF.ZERO
}
}
@@ -266,8 +267,8 @@
requestLayout()
}
- fun updateColor(color: Int) {
- childViews.forEach { view -> view.updateColor(color) }
+ fun updateColor(lockscreenColor: Int, aodColor: Int) {
+ childViews.forEach { view -> view.updateColor(lockscreenColor, aodColor) }
invalidate()
}
@@ -465,12 +466,12 @@
fun updateDirectionalTargetTranslate(id: Int, targetTranslation: VPointF): VPointF {
return targetTranslation *
when (id) {
- R.id.HOUR_FIRST_DIGIT -> VPointF(-1, -1)
- R.id.HOUR_SECOND_DIGIT -> VPointF(1, -1)
- R.id.MINUTE_FIRST_DIGIT -> VPointF(-1, 1)
- R.id.MINUTE_SECOND_DIGIT -> VPointF(1, 1)
- R.id.HOUR_DIGIT_PAIR -> VPointF(-1, -1)
- R.id.MINUTE_DIGIT_PAIR -> VPointF(-1, 1)
+ ClockViewIds.HOUR_FIRST_DIGIT -> VPointF(-1, -1)
+ ClockViewIds.HOUR_SECOND_DIGIT -> VPointF(1, -1)
+ ClockViewIds.MINUTE_FIRST_DIGIT -> VPointF(-1, 1)
+ ClockViewIds.MINUTE_SECOND_DIGIT -> VPointF(1, 1)
+ ClockViewIds.HOUR_DIGIT_PAIR -> VPointF(-1, -1)
+ ClockViewIds.MINUTE_DIGIT_PAIR -> VPointF(-1, 1)
else -> VPointF(1, 1)
}
}
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextView.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextView.kt
index 2962d46..8697ec3 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextView.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextView.kt
@@ -40,24 +40,24 @@
import com.android.systemui.animation.GSFAxes
import com.android.systemui.animation.TextAnimator
import com.android.systemui.animation.TextAnimatorListener
-import com.android.systemui.customization.R
+import com.android.systemui.customization.clocks.CanvasUtil.translate
+import com.android.systemui.customization.clocks.CanvasUtil.use
+import com.android.systemui.customization.clocks.ClockLogger
+import com.android.systemui.customization.clocks.FontUtils.set
+import com.android.systemui.customization.clocks.ViewUtils.measuredSize
+import com.android.systemui.customization.clocks.ViewUtils.size
import com.android.systemui.plugins.clocks.ClockAxisStyle
-import com.android.systemui.plugins.clocks.ClockLogger
+import com.android.systemui.plugins.clocks.ClockViewIds
import com.android.systemui.plugins.clocks.VPoint
import com.android.systemui.plugins.clocks.VPointF
import com.android.systemui.plugins.clocks.VPointF.Companion.size
import com.android.systemui.plugins.clocks.VRectF
import com.android.systemui.shared.Flags.ambientAod
-import com.android.systemui.shared.clocks.CanvasUtil.translate
-import com.android.systemui.shared.clocks.CanvasUtil.use
import com.android.systemui.shared.clocks.ClockContext
import com.android.systemui.shared.clocks.DigitTranslateAnimator
import com.android.systemui.shared.clocks.DimensionParser
import com.android.systemui.shared.clocks.FLEX_CLOCK_ID
import com.android.systemui.shared.clocks.FontTextStyle
-import com.android.systemui.shared.clocks.FontUtils.set
-import com.android.systemui.shared.clocks.ViewUtils.measuredSize
-import com.android.systemui.shared.clocks.ViewUtils.size
import java.lang.Thread
import kotlin.math.max
import kotlin.math.min
@@ -197,10 +197,15 @@
var textBorderWidth = 0f
var measuredBaseline = 0
var lockscreenColor = Color.WHITE
+ var aodColor = Color.WHITE
- fun updateColor(color: Int) {
- lockscreenColor = color
+ fun updateColor(lockscreenColor: Int, aodColor: Int = Color.WHITE) {
+ this.lockscreenColor = lockscreenColor
+ if (ambientAod()) {
+ this.aodColor = aodColor
+ }
lockScreenPaint.color = lockscreenColor
+
if (dozeFraction < 1f) {
textAnimator.setTextStyle(TextAnimator.Style(color = lockscreenColor))
}
@@ -355,7 +360,7 @@
textAnimator.setTextStyle(
TextAnimator.Style(
fVar = if (isDozing) aodFontVariation else lsFontVariation,
- color = if (isDozing && !ambientAod()) AOD_COLOR else lockscreenColor,
+ color = if (isDozing) aodColor else lockscreenColor,
textSize = if (isDozing) aodFontSizePx else lockScreenPaint.textSize,
),
TextAnimator.Animation(
@@ -442,10 +447,10 @@
}
private fun isSingleDigit(): Boolean {
- return id == R.id.HOUR_FIRST_DIGIT ||
- id == R.id.HOUR_SECOND_DIGIT ||
- id == R.id.MINUTE_FIRST_DIGIT ||
- id == R.id.MINUTE_SECOND_DIGIT
+ return id == ClockViewIds.HOUR_FIRST_DIGIT ||
+ id == ClockViewIds.HOUR_SECOND_DIGIT ||
+ id == ClockViewIds.MINUTE_FIRST_DIGIT ||
+ id == ClockViewIds.MINUTE_SECOND_DIGIT
}
/** Returns the interpolated text bounding rect based on interpolation progress */
@@ -706,7 +711,6 @@
AxisAnimation(GSFAxes.SLANT, 0f),
)
- val AOD_COLOR = Color.WHITE
private val LS_WEIGHT_AXIS = GSFAxes.WEIGHT to 400f
private val AOD_WEIGHT_AXIS = GSFAxes.WEIGHT to 200f
private val WIDTH_AXIS = GSFAxes.WIDTH to 85f
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
index 003a67c..7f5e1f9 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
@@ -49,6 +49,7 @@
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
+import com.android.systemui.flags.DisableSceneContainer
import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
@@ -291,6 +292,7 @@
}
@Test
+ @DisableSceneContainer
fun onInitConfiguresViewMode() {
underTest.onInit()
verify(view)
@@ -305,11 +307,13 @@
}
@Test
+ @DisableSceneContainer
fun setAccessibilityDelegate() {
verify(view).accessibilityDelegate = eq(faceAuthAccessibilityDelegate)
}
@Test
+ @DisableSceneContainer
fun showSecurityScreen_canInflateAllModes() {
val modes = SecurityMode.values()
for (mode in modes) {
@@ -324,6 +328,7 @@
}
@Test
+ @DisableSceneContainer
fun onResourcesUpdate_callsThroughOnRotationChange() {
clearInvocations(view)
@@ -367,6 +372,7 @@
}
@Test
+ @DisableSceneContainer
fun onInterceptTap_inhibitsFalsingInSidedSecurityMode() {
whenever(view.isTouchOnTheOtherSideOfSecurity(any())).thenReturn(false)
touchDown()
@@ -377,6 +383,7 @@
}
@Test
+ @DisableSceneContainer
fun showSecurityScreen_oneHandedMode_flagDisabled_noOneHandedMode() {
testableResources.addOverride(R.bool.can_use_one_handed_bouncer, false)
setupGetSecurityView(SecurityMode.Pattern)
@@ -393,6 +400,7 @@
}
@Test
+ @DisableSceneContainer
fun showSecurityScreen_oneHandedMode_flagEnabled_oneHandedMode() {
testableResources.addOverride(R.bool.can_use_one_handed_bouncer, true)
setupGetSecurityView(SecurityMode.Pattern)
@@ -408,6 +416,7 @@
}
@Test
+ @DisableSceneContainer
fun showSecurityScreen_oneHandedMode_flagEnabled_oneHandedMode_simpin() {
testableResources.addOverride(R.bool.can_use_one_handed_bouncer, true)
setupGetSecurityView(SecurityMode.SimPin)
@@ -423,6 +432,7 @@
}
@Test
+ @DisableSceneContainer
fun showSecurityScreen_oneHandedMode_flagEnabled_oneHandedMode_simpuk() {
testableResources.addOverride(R.bool.can_use_one_handed_bouncer, true)
setupGetSecurityView(SecurityMode.SimPuk)
@@ -438,6 +448,7 @@
}
@Test
+ @DisableSceneContainer
fun showSecurityScreen_twoHandedMode_flagEnabled_noOneHandedMode() {
testableResources.addOverride(R.bool.can_use_one_handed_bouncer, true)
setupGetSecurityView(SecurityMode.Password)
@@ -453,6 +464,7 @@
}
@Test
+ @DisableSceneContainer
fun addUserSwitcherCallback() {
val captor = ArgumentCaptor.forClass(UserSwitcherCallback::class.java)
setupGetSecurityView(SecurityMode.Password)
@@ -469,6 +481,7 @@
}
@Test
+ @DisableSceneContainer
fun addUserSwitchCallback() {
underTest.onViewAttached()
verify(userSwitcherController).addUserSwitchCallback(any())
@@ -477,12 +490,14 @@
}
@Test
+ @DisableSceneContainer
fun onBouncerVisibilityChanged_resetsScale() {
underTest.onBouncerVisibilityChanged(false)
verify(view).resetScale()
}
@Test
+ @DisableSceneContainer
fun showNextSecurityScreenOrFinish_DeviceNotSecure() {
// GIVEN the current security method is SimPin
whenever(keyguardUpdateMonitor.getUserHasTrust(anyInt())).thenReturn(false)
@@ -506,6 +521,7 @@
}
@Test
+ @DisableSceneContainer
fun showNextSecurityScreenOrFinish_ignoresCallWhenSecurityMethodHasChanged() {
// GIVEN current security mode has been set to PIN
underTest.showSecurityScreen(SecurityMode.PIN)
@@ -525,6 +541,7 @@
}
@Test
+ @DisableSceneContainer
fun showNextSecurityScreenOrFinish_SimPin_Swipe() {
// GIVEN the current security method is SimPin
whenever(keyguardUpdateMonitor.getUserHasTrust(anyInt())).thenReturn(false)
@@ -549,6 +566,7 @@
}
@Test
+ @DisableSceneContainer
fun showNextSecurityScreenOrFinish_SimPin_Swipe_userNotSetup() {
// GIVEN the current security method is SimPin
whenever(keyguardUpdateMonitor.getUserHasTrust(anyInt())).thenReturn(false)
@@ -574,6 +592,7 @@
}
@Test
+ @DisableSceneContainer
fun showNextSecurityScreenOrFinish_SimPin_Password() {
// GIVEN the current security method is SimPin
whenever(keyguardUpdateMonitor.getUserHasTrust(anyInt())).thenReturn(false)
@@ -600,6 +619,7 @@
}
@Test
+ @DisableSceneContainer
fun showNextSecurityScreenOrFinish_SimPin_SimPin() {
// GIVEN the current security method is SimPin
whenever(keyguardUpdateMonitor.getUserHasTrust(anyInt())).thenReturn(false)
@@ -625,6 +645,7 @@
}
@Test
+ @DisableSceneContainer
fun showNextSecurityScreenOrFinish_calledWithNoAuthentication_butRequiresSimPin() {
// GIVEN trust is true (extended unlock)
whenever(keyguardUpdateMonitor.getUserHasTrust(anyInt())).thenReturn(true)
@@ -641,6 +662,7 @@
}
@Test
+ @DisableSceneContainer
fun onSwipeUp_forwardsItToFaceAuthInteractor() {
val registeredSwipeListener = registeredSwipeListener
setupGetSecurityView(SecurityMode.Password)
@@ -650,6 +672,7 @@
}
@Test
+ @DisableSceneContainer
fun onDensityOrFontScaleChanged() {
val configurationListenerArgumentCaptor =
ArgumentCaptor.forClass(ConfigurationController.ConfigurationListener::class.java)
@@ -661,6 +684,7 @@
}
@Test
+ @DisableSceneContainer
fun onThemeChanged() {
val configurationListenerArgumentCaptor =
ArgumentCaptor.forClass(ConfigurationController.ConfigurationListener::class.java)
@@ -671,6 +695,7 @@
}
@Test
+ @DisableSceneContainer
fun onUiModeChanged() {
val configurationListenerArgumentCaptor =
ArgumentCaptor.forClass(ConfigurationController.ConfigurationListener::class.java)
@@ -681,6 +706,7 @@
}
@Test
+ @DisableSceneContainer
fun hasDismissActions() {
Assert.assertFalse("Action not set yet", underTest.hasDismissActions())
underTest.setOnDismissAction(mock(), null /* cancelAction */)
@@ -688,6 +714,7 @@
}
@Test
+ @DisableSceneContainer
fun willRunDismissFromKeyguardIsTrue() {
val action: OnDismissAction = mock()
whenever(action.willRunAnimationOnKeyguard()).thenReturn(true)
@@ -697,6 +724,7 @@
}
@Test
+ @DisableSceneContainer
fun willRunDismissFromKeyguardIsFalse() {
val action: OnDismissAction = mock()
whenever(action.willRunAnimationOnKeyguard()).thenReturn(false)
@@ -706,6 +734,7 @@
}
@Test
+ @DisableSceneContainer
fun willRunDismissFromKeyguardIsFalseWhenNoDismissActionSet() {
underTest.setOnDismissAction(null /* action */, null /* cancelAction */)
underTest.finish(0 /* currentUser */)
@@ -713,6 +742,7 @@
}
@Test
+ @DisableSceneContainer
fun onStartingToHide() {
underTest.onStartingToHide()
verify(viewFlipperController)
@@ -722,6 +752,7 @@
}
@Test
+ @DisableSceneContainer
fun startAppearAnimation_ifDelayed() {
val argumentCaptor = ArgumentCaptor.forClass(OnPreDrawListener::class.java)
whenever(view.isAppearAnimationDelayed).thenReturn(true)
@@ -739,6 +770,7 @@
}
@Test
+ @DisableSceneContainer
fun appearAnimation_willNotStart_ifNotDelayed() {
whenever(view.isAppearAnimationDelayed).thenReturn(false)
val viewTreeObserver: ViewTreeObserver = mock()
@@ -753,6 +785,7 @@
}
@Test
+ @DisableSceneContainer
fun gravityReappliedOnConfigurationChange() {
// Set initial gravity
testableResources.addOverride(R.integer.keyguard_host_view_gravity, Gravity.CENTER)
@@ -773,6 +806,7 @@
}
@Test
+ @DisableSceneContainer
fun gravityUsesOneHandGravityWhenApplicable() {
testableResources.addOverride(R.integer.keyguard_host_view_gravity, Gravity.CENTER)
testableResources.addOverride(
@@ -805,12 +839,14 @@
}
@Test
+ @DisableSceneContainer
fun updateKeyguardPositionDelegatesToSecurityContainer() {
underTest.updateKeyguardPosition(1.0f)
verify(view).updatePositionByTouchX(1.0f)
}
@Test
+ @DisableSceneContainer
fun reinflateViewFlipper() {
val onViewInflatedCallback = KeyguardSecurityViewFlipperController.OnViewInflatedCallback {}
underTest.reinflateViewFlipper(onViewInflatedCallback)
@@ -822,6 +858,7 @@
}
@Test
+ @DisableSceneContainer
fun setExpansion_setsAlpha() {
underTest.setExpansion(KeyguardBouncerConstants.EXPANSION_VISIBLE)
verify(view).alpha = 1f
@@ -986,6 +1023,7 @@
}
@Test
+ @DisableSceneContainer
fun testResetUserSwitcher() {
val userSwitcher = mock(View::class.java)
whenever(view.findViewById<View>(R.id.keyguard_bouncer_user_switcher))
@@ -996,6 +1034,7 @@
}
@Test
+ @DisableSceneContainer
fun testOnUserSwitched() {
val userSwitchCallbackArgumentCaptor =
argumentCaptor<UserSwitcherController.UserSwitchCallback>()
@@ -1009,6 +1048,7 @@
}
@Test
+ @DisableSceneContainer
fun showAlmostAtWipeDialog_calledOnMainUser_setsCorrectUserType() {
val mainUserId = 10
@@ -1025,6 +1065,7 @@
}
@Test
+ @DisableSceneContainer
fun showAlmostAtWipeDialog_calledOnNonMainUser_setsCorrectUserType() {
val secondaryUserId = 10
val mainUserId = 0
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardUnfoldTransitionTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardUnfoldTransitionTest.kt
index b0db8b7..3f9d253 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardUnfoldTransitionTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardUnfoldTransitionTest.kt
@@ -20,8 +20,8 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.customization.R as customR
import com.android.systemui.keyguard.ui.view.KeyguardRootView
+import com.android.systemui.plugins.clocks.ClockViewIds
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.res.R
import com.android.systemui.shade.NotificationShadeWindowView
@@ -90,7 +90,7 @@
underTest.statusViewCentered = true
val view = View(context)
- whenever(keyguardRootView.findViewById<View>(customR.id.lockscreen_clock_view_large))
+ whenever(keyguardRootView.findViewById<View>(ClockViewIds.LOCKSCREEN_CLOCK_VIEW_LARGE))
.thenReturn(view)
progressListener.onTransitionStarted()
@@ -111,7 +111,7 @@
whenever(statusBarStateController.getState()).thenReturn(SHADE)
val view = View(context)
- whenever(keyguardRootView.findViewById<View>(customR.id.lockscreen_clock_view_large))
+ whenever(keyguardRootView.findViewById<View>(ClockViewIds.LOCKSCREEN_CLOCK_VIEW_LARGE))
.thenReturn(view)
progressListener.onTransitionStarted()
@@ -134,7 +134,7 @@
val view = View(context)
whenever(
notificationShadeWindowView.findViewById<View>(
- customR.id.lockscreen_clock_view_large
+ ClockViewIds.LOCKSCREEN_CLOCK_VIEW_LARGE
)
)
.thenReturn(view)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/AccessibilityRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/AccessibilityRepositoryTest.kt
index 84cbef8..39323e2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/AccessibilityRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/AccessibilityRepositoryTest.kt
@@ -16,19 +16,27 @@
package com.android.systemui.accessibility.data.repository
+import android.accessibilityservice.AccessibilityServiceInfo
+import android.os.Handler
import android.view.accessibility.AccessibilityManager
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.backgroundScope
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.mockito.withArgCaptor
import com.google.common.truth.Truth.assertThat
+import java.util.concurrent.Executor
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers
+import org.mockito.ArgumentMatchers.eq
import org.mockito.Mock
import org.mockito.Mockito.verify
import org.mockito.junit.MockitoJUnit
@@ -42,9 +50,23 @@
// mocks
@Mock private lateinit var a11yManager: AccessibilityManager
+ private val testKosmos = testKosmos()
+ private val testScope = testKosmos.testScope
+ private val backgroundScope = testKosmos.backgroundScope
+
+ @Mock private lateinit var bgExecutor: Executor
+
+ @Mock private lateinit var bgHandler: Handler
// real impls
- private val underTest by lazy { AccessibilityRepository(a11yManager) }
+ private val underTest by lazy {
+ AccessibilityRepository(
+ a11yManager = a11yManager,
+ backgroundExecutor = bgExecutor,
+ backgroundHandler = bgHandler,
+ backgroundScope = backgroundScope,
+ )
+ }
@Test
fun isTouchExplorationEnabled_reflectsA11yManager_initFalse() = runTest {
@@ -65,8 +87,11 @@
whenever(a11yManager.isTouchExplorationEnabled).thenReturn(false)
val isTouchExplorationEnabled by collectLastValue(underTest.isTouchExplorationEnabled)
runCurrent()
- withArgCaptor { verify(a11yManager).addTouchExplorationStateChangeListener(capture()) }
- .onTouchExplorationStateChanged(/* enabled = */ true)
+ withArgCaptor {
+ verify(a11yManager)
+ .addTouchExplorationStateChangeListener(capture(), ArgumentMatchers.notNull())
+ }
+ .onTouchExplorationStateChanged(/* enabled= */ true)
assertThat(isTouchExplorationEnabled).isTrue()
}
@@ -75,8 +100,98 @@
whenever(a11yManager.isTouchExplorationEnabled).thenReturn(true)
val isTouchExplorationEnabled by collectLastValue(underTest.isTouchExplorationEnabled)
runCurrent()
- withArgCaptor { verify(a11yManager).addTouchExplorationStateChangeListener(capture()) }
- .onTouchExplorationStateChanged(/* enabled = */ false)
+ withArgCaptor {
+ verify(a11yManager)
+ .addTouchExplorationStateChangeListener(capture(), ArgumentMatchers.notNull())
+ }
+ .onTouchExplorationStateChanged(/* enabled= */ false)
assertThat(isTouchExplorationEnabled).isFalse()
}
+
+ @Test
+ fun isEnabledFiltered_reflectsA11yManager_initFalse() =
+ testScope.runTest {
+ whenever(a11yManager.getEnabledAccessibilityServiceList(eq(FILTERED_A11Y_SERVICES)))
+ .thenReturn(emptyList<AccessibilityServiceInfo>())
+ val isEnabledFiltered by collectLastValue(underTest.isEnabledFiltered)
+ assertThat(isEnabledFiltered).isFalse()
+ }
+
+ @Test
+ fun isEnabledFiltered_reflectsA11yManager_changeTrue() =
+ testScope.runTest {
+ whenever(a11yManager.getEnabledAccessibilityServiceList(eq(FILTERED_A11Y_SERVICES)))
+ .thenReturn(emptyList())
+ val isEnabledFiltered by collectLastValue(underTest.isEnabledFiltered)
+ runCurrent()
+ withArgCaptor {
+ verify(a11yManager)
+ .addAccessibilityServicesStateChangeListener(
+ ArgumentMatchers.notNull(),
+ capture(),
+ )
+ }
+ .onAccessibilityServicesStateChanged(a11yManager)
+ assertThat(isEnabledFiltered).isFalse()
+
+ // Change the services list to a non-empty list
+ val wantedList = listOf(AccessibilityServiceInfo())
+ whenever(a11yManager.getEnabledAccessibilityServiceList(eq(FILTERED_A11Y_SERVICES)))
+ .thenReturn(wantedList)
+ val isEnabledFiltered2 by collectLastValue(underTest.isEnabledFiltered)
+ runCurrent()
+ withArgCaptor {
+ verify(a11yManager)
+ .addAccessibilityServicesStateChangeListener(
+ ArgumentMatchers.notNull(),
+ capture(),
+ )
+ }
+ .onAccessibilityServicesStateChanged(a11yManager)
+ assertThat(isEnabledFiltered2).isTrue()
+ }
+
+ @Test
+ fun isEnabledFiltered_reflectsA11yManager_changeFalse() =
+ testScope.runTest {
+ val wantedList = listOf(AccessibilityServiceInfo())
+ whenever(a11yManager.getEnabledAccessibilityServiceList(eq(FILTERED_A11Y_SERVICES)))
+ .thenReturn(wantedList)
+ val isEnabledFiltered by collectLastValue(underTest.isEnabledFiltered)
+ runCurrent()
+ withArgCaptor {
+ verify(a11yManager)
+ .addAccessibilityServicesStateChangeListener(
+ ArgumentMatchers.notNull(),
+ capture(),
+ )
+ }
+ .onAccessibilityServicesStateChanged(a11yManager)
+ assertThat(isEnabledFiltered).isTrue()
+
+ // Change the services list to an emptylist
+ whenever(a11yManager.getEnabledAccessibilityServiceList(eq(FILTERED_A11Y_SERVICES)))
+ .thenReturn(emptyList())
+
+ val isEnabledFiltered2 by collectLastValue(underTest.isEnabledFiltered)
+ runCurrent()
+ withArgCaptor {
+ verify(a11yManager)
+ .addAccessibilityServicesStateChangeListener(
+ ArgumentMatchers.notNull(),
+ capture(),
+ )
+ }
+ .onAccessibilityServicesStateChanged(a11yManager)
+ assertThat(isEnabledFiltered2).isFalse()
+ }
+
+ companion object {
+ private const val FILTERED_A11Y_SERVICES =
+ AccessibilityServiceInfo.FEEDBACK_AUDIBLE or
+ AccessibilityServiceInfo.FEEDBACK_SPOKEN or
+ AccessibilityServiceInfo.FEEDBACK_VISUAL or
+ AccessibilityServiceInfo.FEEDBACK_HAPTIC or
+ AccessibilityServiceInfo.FEEDBACK_BRAILLE
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/AmbientVolumeLayoutTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/AmbientVolumeLayoutTest.java
index 455329f..d4b79a7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/AmbientVolumeLayoutTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/AmbientVolumeLayoutTest.java
@@ -78,7 +78,7 @@
public void setUp() {
mLayout = new AmbientVolumeLayout(mContext);
mLayout.setListener(mListener);
- mLayout.setExpandable(true);
+ mLayout.setControlExpandable(true);
mLayout.setMutable(true);
prepareDevices();
@@ -100,29 +100,29 @@
}
@Test
- public void setExpandable_expandable_expandIconVisible() {
- mLayout.setExpandable(true);
+ public void setControlExpandable_expandable_expandIconVisible() {
+ mLayout.setControlExpandable(true);
assertThat(mExpandIcon.getVisibility()).isEqualTo(VISIBLE);
}
@Test
- public void setExpandable_notExpandable_expandIconGone() {
- mLayout.setExpandable(false);
+ public void setControlExpandable_notExpandable_expandIconGone() {
+ mLayout.setControlExpandable(false);
assertThat(mExpandIcon.getVisibility()).isEqualTo(View.GONE);
}
@Test
- public void setExpanded_expanded_assertControlUiCorrect() {
- mLayout.setExpanded(true);
+ public void setControlExpanded_expanded_assertControlUiCorrect() {
+ mLayout.setControlExpanded(true);
assertControlUiCorrect();
}
@Test
- public void setExpanded_notExpanded_assertControlUiCorrect() {
- mLayout.setExpanded(false);
+ public void setControlExpanded_notExpanded_assertControlUiCorrect() {
+ mLayout.setControlExpanded(false);
assertControlUiCorrect();
}
@@ -158,7 +158,7 @@
@Test
public void updateLayout_unmuteAndExpanded_volumeIconIsCorrect() {
mLayout.setMuted(false);
- mLayout.setExpanded(true);
+ mLayout.setControlExpanded(true);
mLayout.updateLayout();
int expectedLevel = calculateVolumeLevel(TEST_LEFT_VOLUME_LEVEL, TEST_RIGHT_VOLUME_LEVEL);
@@ -168,7 +168,7 @@
@Test
public void updateLayout_unmuteAndNotExpanded_volumeIconIsCorrect() {
mLayout.setMuted(false);
- mLayout.setExpanded(false);
+ mLayout.setControlExpanded(false);
mLayout.updateLayout();
int expectedLevel = calculateVolumeLevel(TEST_UNIFIED_VOLUME_LEVEL,
@@ -178,7 +178,7 @@
@Test
public void setSliderEnabled_expandedAndLeftIsDisabled_volumeIconIsCorrect() {
- mLayout.setExpanded(true);
+ mLayout.setControlExpanded(true);
mLayout.setSliderEnabled(SIDE_LEFT, false);
int expectedLevel = calculateVolumeLevel(0, TEST_RIGHT_VOLUME_LEVEL);
@@ -187,7 +187,7 @@
@Test
public void setSliderValue_expandedAndLeftValueChanged_volumeIconIsCorrect() {
- mLayout.setExpanded(true);
+ mLayout.setControlExpanded(true);
mLayout.setSliderValue(SIDE_LEFT, 4);
int expectedLevel = calculateVolumeLevel(4, TEST_RIGHT_VOLUME_LEVEL);
@@ -199,7 +199,7 @@
}
private void assertControlUiCorrect() {
- final boolean expanded = mLayout.isExpanded();
+ final boolean expanded = mLayout.isControlExpanded();
final Map<Integer, AmbientVolumeSlider> sliders = mLayout.getSliders();
if (expanded) {
assertThat(sliders.get(SIDE_UNIFIED).getVisibility()).isEqualTo(GONE);
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/back/domain/interactor/BackActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/back/domain/interactor/BackActionInteractorTest.kt
index 8573312..107fd62 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/back/domain/interactor/BackActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/back/domain/interactor/BackActionInteractorTest.kt
@@ -31,6 +31,7 @@
import com.android.internal.statusbar.IStatusBarService
import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
+import com.android.systemui.flags.DisableSceneContainer
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.kosmos.testScope
import com.android.systemui.plugins.statusbar.StatusBarStateController
@@ -73,6 +74,7 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
+@DisableSceneContainer
class BackActionInteractorTest : SysuiTestCase() {
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
index c4fafc1..8232252 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
@@ -15,9 +15,9 @@
*/
package com.android.systemui.biometrics
-import android.app.ActivityTaskManager
-import android.content.pm.PackageManager
+import android.content.packageManager
import android.content.res.Configuration
+import android.content.testableContext
import android.hardware.biometrics.BiometricAuthenticator
import android.hardware.biometrics.BiometricConstants
import android.hardware.biometrics.BiometricManager
@@ -26,54 +26,36 @@
import android.hardware.biometrics.PromptInfo
import android.hardware.biometrics.PromptVerticalListContentView
import android.hardware.face.FaceSensorPropertiesInternal
-import android.hardware.fingerprint.FingerprintManager
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal
import android.os.IBinder
import android.os.UserManager
+import android.os.userManager
import android.testing.TestableLooper
import android.testing.TestableLooper.RunWithLooper
import android.testing.ViewUtils
import android.view.View
import android.view.WindowInsets
import android.view.WindowManager
-import android.view.accessibility.AccessibilityManager
import android.widget.ScrollView
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.internal.jank.InteractionJankMonitor
-import com.android.internal.widget.LockPatternUtils
import com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTERN
import com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PIN
-import com.android.launcher3.icons.IconProvider
+import com.android.internal.widget.lockPatternUtils
import com.android.systemui.SysuiTestCase
-import com.android.systemui.biometrics.data.repository.FakeBiometricStatusRepository
-import com.android.systemui.biometrics.data.repository.FakeDisplayStateRepository
-import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository
-import com.android.systemui.biometrics.data.repository.FakePromptRepository
-import com.android.systemui.biometrics.domain.interactor.BiometricStatusInteractor
-import com.android.systemui.biometrics.domain.interactor.BiometricStatusInteractorImpl
-import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor
-import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractorImpl
-import com.android.systemui.biometrics.domain.interactor.FakeCredentialInteractor
-import com.android.systemui.biometrics.domain.interactor.PromptCredentialInteractor
-import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractorImpl
-import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor
-import com.android.systemui.biometrics.ui.viewmodel.CredentialViewModel
-import com.android.systemui.biometrics.ui.viewmodel.PromptViewModel
-import com.android.systemui.display.data.repository.FakeDisplayRepository
+import com.android.systemui.biometrics.domain.interactor.promptSelectorInteractor
+import com.android.systemui.biometrics.ui.viewmodel.credentialViewModel
+import com.android.systemui.biometrics.ui.viewmodel.promptViewModel
+import com.android.systemui.concurrency.fakeExecutor
import com.android.systemui.haptics.msdl.msdlPlayer
-import com.android.systemui.keyguard.WakefulnessLifecycle
+import com.android.systemui.haptics.vibratorHelper
+import com.android.systemui.jank.interactionJankMonitor
+import com.android.systemui.keyguard.wakefulnessLifecycle
+import com.android.systemui.kosmos.testScope
import com.android.systemui.res.R
-import com.android.systemui.statusbar.VibratorHelper
import com.android.systemui.testKosmos
-import com.android.systemui.user.domain.interactor.SelectedUserInteractor
-import com.android.systemui.util.concurrency.FakeExecutor
-import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.test.StandardTestDispatcher
-import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import org.junit.After
import org.junit.Before
@@ -98,88 +80,26 @@
@RunWithLooper(setAsMainLooper = true)
@SmallTest
open class AuthContainerViewTest : SysuiTestCase() {
-
@JvmField @Rule var mockitoRule = MockitoJUnit.rule()
@Mock lateinit var callback: AuthDialogCallback
- @Mock lateinit var userManager: UserManager
- @Mock lateinit var fingerprintManager: FingerprintManager
- @Mock lateinit var lockPatternUtils: LockPatternUtils
- @Mock lateinit var wakefulnessLifecycle: WakefulnessLifecycle
@Mock lateinit var windowToken: IBinder
- @Mock lateinit var interactionJankMonitor: InteractionJankMonitor
- @Mock lateinit var vibrator: VibratorHelper
- @Mock lateinit var udfpsUtils: UdfpsUtils
- @Mock lateinit var authController: AuthController
- @Mock lateinit var selectedUserInteractor: SelectedUserInteractor
- @Mock private lateinit var packageManager: PackageManager
- @Mock private lateinit var activityTaskManager: ActivityTaskManager
- @Mock private lateinit var accessibilityManager: AccessibilityManager
-
- private lateinit var displayRepository: FakeDisplayRepository
- private lateinit var displayStateInteractor: DisplayStateInteractor
- private lateinit var udfpsOverlayInteractor: UdfpsOverlayInteractor
- private lateinit var biometricStatusInteractor: BiometricStatusInteractor
- private lateinit var iconProvider: IconProvider
-
- private val testScope = TestScope(StandardTestDispatcher())
- private val fakeExecutor = FakeExecutor(FakeSystemClock())
- private val biometricPromptRepository = FakePromptRepository()
- private val biometricStatusRepository = FakeBiometricStatusRepository()
- private val fingerprintRepository = FakeFingerprintPropertyRepository()
- private val displayStateRepository = FakeDisplayStateRepository()
- private val credentialInteractor = FakeCredentialInteractor()
- private val bpCredentialInteractor =
- PromptCredentialInteractor(
- Dispatchers.Main.immediate,
- biometricPromptRepository,
- credentialInteractor,
- )
- private val promptSelectorInteractor by lazy {
- PromptSelectorInteractorImpl(
- fingerprintRepository,
- displayStateInteractor,
- credentialInteractor,
- biometricPromptRepository,
- lockPatternUtils,
- )
- }
-
- private val credentialViewModel = CredentialViewModel(mContext, bpCredentialInteractor)
- private val defaultLogoIcon = context.getDrawable(R.drawable.ic_android)
private val kosmos = testKosmos()
- private val msdlPlayer = kosmos.msdlPlayer
+ private val context = kosmos.testableContext
+ private val lockPatternUtils = kosmos.lockPatternUtils
+ private val packageManager = kosmos.packageManager
+ private val userManager: UserManager = kosmos.userManager
+
+ private val testScope = kosmos.testScope
+ private val fakeExecutor = kosmos.fakeExecutor
+
+ private val defaultLogoIcon = context.getDrawable(R.drawable.ic_android)
private var authContainer: TestAuthContainerView? = null
@Before
fun setup() {
- displayRepository = FakeDisplayRepository()
-
- displayStateInteractor =
- DisplayStateInteractorImpl(
- testScope.backgroundScope,
- mContext,
- fakeExecutor,
- displayStateRepository,
- displayRepository,
- )
- udfpsOverlayInteractor =
- UdfpsOverlayInteractor(
- context,
- authController,
- selectedUserInteractor,
- fingerprintManager,
- testScope.backgroundScope,
- )
- biometricStatusInteractor =
- BiometricStatusInteractorImpl(
- activityTaskManager,
- biometricStatusRepository,
- fingerprintRepository,
- )
- iconProvider = IconProvider(context)
// Set up default logo icon
whenever(packageManager.getApplicationIcon(OP_PACKAGE_NAME)).thenReturn(defaultLogoIcon)
context.setMockPackageManager(packageManager)
@@ -428,12 +348,6 @@
@Test
fun testShowBiometricUI_ContentViewWithMoreOptionsButton() {
- var isButtonClicked = false
- val contentView =
- PromptContentViewWithMoreOptionsButton.Builder()
- .setMoreOptionsButtonListener(fakeExecutor) { _, _ -> isButtonClicked = true }
- .build()
-
val container = initializeFingerprintContainer()
waitForIdleSync()
@@ -483,10 +397,9 @@
@Test
fun testShowCredentialUI_withContentViewWithMoreOptionsButton() {
- val contentView =
- PromptContentViewWithMoreOptionsButton.Builder()
- .setMoreOptionsButtonListener(fakeExecutor) { _, _ -> }
- .build()
+ PromptContentViewWithMoreOptionsButton.Builder()
+ .setMoreOptionsButtonListener(fakeExecutor) { _, _ -> }
+ .build()
val container =
initializeFingerprintContainer(
authenticators = BiometricManager.Authenticators.DEVICE_CREDENTIAL
@@ -499,6 +412,7 @@
@Test
fun testCredentialViewUsesEffectiveUserId() {
+ kosmos.userManager
whenever(userManager.getCredentialOwnerProfile(anyInt())).thenReturn(200)
whenever(lockPatternUtils.getCredentialTypeForUser(eq(200)))
.thenReturn(CREDENTIAL_TYPE_PATTERN)
@@ -667,27 +581,17 @@
testScope.backgroundScope,
fingerprintProps,
faceProps,
- wakefulnessLifecycle,
- userManager,
+ kosmos.wakefulnessLifecycle,
+ kosmos.userManager,
null /* authContextPlugins */,
- lockPatternUtils,
- interactionJankMonitor,
- { promptSelectorInteractor },
- PromptViewModel(
- displayStateInteractor,
- promptSelectorInteractor,
- context,
- udfpsOverlayInteractor,
- biometricStatusInteractor,
- udfpsUtils,
- iconProvider,
- activityTaskManager,
- accessibilityManager,
- ),
- { credentialViewModel },
- fakeExecutor,
- vibrator,
- msdlPlayer,
+ kosmos.lockPatternUtils,
+ kosmos.interactionJankMonitor,
+ { kosmos.promptSelectorInteractor },
+ kosmos.promptViewModel,
+ { kosmos.credentialViewModel },
+ kosmos.fakeExecutor,
+ kosmos.vibratorHelper,
+ kosmos.msdlPlayer,
) {
override fun postOnAnimation(runnable: Runnable) {
runnable.run()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthControllerTest.java
index f4185ee..cdeb5a8 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthControllerTest.java
@@ -23,6 +23,7 @@
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertNotSame;
import static junit.framework.Assert.assertNull;
import static junit.framework.Assert.assertTrue;
@@ -995,8 +996,8 @@
}
@Test
- public void testShowDialog_whenOwnerNotInForeground() {
- PromptInfo promptInfo = createTestPromptInfo();
+ public void testShowDialog_whenOwnerNotInForegroundAndNotVisible() {
+ final PromptInfo promptInfo = createTestPromptInfo();
promptInfo.setAllowBackgroundAuthentication(false);
switchTask("other_package");
mAuthController.showAuthenticationDialog(promptInfo,
@@ -1014,6 +1015,27 @@
}
@Test
+ public void testShowDialog_whenOwnerNotInForegroundAndVisible() {
+ final PromptInfo promptInfo = createTestPromptInfo();
+ final AuthController.Callback callback = mock(AuthController.Callback.class);
+ promptInfo.setAllowBackgroundAuthentication(false);
+ switchTaskWithVisibility("other_package", true /* isVisible */);
+ mAuthController.addCallback(callback);
+ mAuthController.showAuthenticationDialog(promptInfo,
+ mReceiver /* receiver */,
+ new int[]{1} /* sensorIds */,
+ false /* credentialAllowed */,
+ true /* requireConfirmation */,
+ 0 /* userId */,
+ 0 /* operationId */,
+ "testPackage",
+ REQUEST_ID);
+
+ assertNotNull(mAuthController.mCurrentDialog);
+ verify(mDialog1).show(mWindowManager);
+ }
+
+ @Test
public void testShowDialog_visibleBackgroundUser() {
int backgroundUserId = 1001;
int backgroundDisplayId = 1001;
@@ -1109,16 +1131,31 @@
REQUEST_ID);
}
- private void switchTask(String packageName) {
+ private void switchTaskWithVisibility(String packageName, boolean isVisible) {
final List<ActivityManager.RunningTaskInfo> tasks = new ArrayList<>();
final ActivityManager.RunningTaskInfo taskInfo =
mock(ActivityManager.RunningTaskInfo.class);
taskInfo.topActivity = mock(ComponentName.class);
when(taskInfo.topActivity.getPackageName()).thenReturn(packageName);
+ when(taskInfo.topActivity.getClassName()).thenReturn(AuthControllerTest.class.getName());
tasks.add(taskInfo);
+
+ final ActivityManager.RunningTaskInfo callingTaskInfo =
+ mock(ActivityManager.RunningTaskInfo.class);
+ callingTaskInfo.topActivity = mock(ComponentName.class);
+ when(callingTaskInfo.topActivity.getPackageName()).thenReturn("Dialog1");
+ when(callingTaskInfo.topActivity.getClassName()).thenReturn(
+ AuthControllerTest.class.getName());
+ callingTaskInfo.isVisible = isVisible;
+ tasks.add(callingTaskInfo);
+
when(mActivityTaskManager.getTasks(anyInt())).thenReturn(tasks);
}
+ private void switchTask(String packageName) {
+ switchTaskWithVisibility(packageName, false /* isVisible */);
+ }
+
private PromptInfo createTestPromptInfo() {
PromptInfo promptInfo = new PromptInfo();
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/BiometricTestExtensions.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/BiometricTestExtensions.kt
index 002ed17..15f309c1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/BiometricTestExtensions.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/BiometricTestExtensions.kt
@@ -34,6 +34,9 @@
import com.android.systemui.bouncer.data.repository.keyguardBouncerRepository
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.res.R
+import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.scene.shared.model.Overlays
import com.android.systemui.util.mockito.whenever
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
@@ -205,8 +208,16 @@
fpsDetectionRunning: Boolean,
isUnlockingWithFpAllowed: Boolean,
) {
- kosmos.keyguardBouncerRepository.setPrimaryShow(isShowing)
- kosmos.keyguardBouncerRepository.setPrimaryStartingToHide(false)
+ if (SceneContainerFlag.isEnabled) {
+ if (isShowing) {
+ kosmos.sceneInteractor.showOverlay(Overlays.Bouncer, "")
+ } else {
+ kosmos.sceneInteractor.hideOverlay(Overlays.Bouncer, "")
+ }
+ } else {
+ kosmos.keyguardBouncerRepository.setPrimaryShow(isShowing)
+ kosmos.keyguardBouncerRepository.setPrimaryStartingToHide(false)
+ }
val primaryStartDisappearAnimation = if (isAnimatingAway) Runnable {} else null
kosmos.keyguardBouncerRepository.setPrimaryStartDisappearAnimation(
primaryStartDisappearAnimation
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/FaceHelpMessageDeferralTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/FaceHelpMessageDeferralTest.kt
index add7a7f..76db8a2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/FaceHelpMessageDeferralTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/FaceHelpMessageDeferralTest.kt
@@ -16,12 +16,9 @@
package com.android.systemui.biometrics
-import android.platform.test.annotations.DisableFlags
-import android.platform.test.annotations.EnableFlags
-import android.platform.test.flag.junit.FlagsParameterization
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.keyguard.logging.BiometricMessageDeferralLogger
-import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
import com.android.systemui.util.time.FakeSystemClock
@@ -35,30 +32,16 @@
import org.mockito.Mock
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
-import platform.test.runner.parameterized.ParameterizedAndroidJunit4
-import platform.test.runner.parameterized.Parameters
@SmallTest
-@RunWith(ParameterizedAndroidJunit4::class)
+@RunWith(AndroidJUnit4::class)
@android.platform.test.annotations.EnabledOnRavenwood
-class FaceHelpMessageDeferralTest(flags: FlagsParameterization) : SysuiTestCase() {
+class FaceHelpMessageDeferralTest : SysuiTestCase() {
val threshold = .75f
@Mock lateinit var logger: BiometricMessageDeferralLogger
@Mock lateinit var dumpManager: DumpManager
val systemClock = FakeSystemClock()
- companion object {
- @JvmStatic
- @Parameters(name = "{0}")
- fun getParams(): List<FlagsParameterization> {
- return FlagsParameterization.allCombinationsOf(Flags.FLAG_FACE_MESSAGE_DEFER_UPDATE)
- }
- }
-
- init {
- mSetFlagsRule.setFlagsParameterization(flags)
- }
-
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
@@ -130,26 +113,6 @@
}
@Test
- @DisableFlags(Flags.FLAG_FACE_MESSAGE_DEFER_UPDATE)
- fun testReturnsMostFrequentDeferredMessage() {
- val biometricMessageDeferral = createMsgDeferral(setOf(1, 2))
-
- // WHEN there's 80% of the messages are msgId=1 and 20% is msgId=2
- biometricMessageDeferral.processFrame(1)
- biometricMessageDeferral.processFrame(1)
- biometricMessageDeferral.processFrame(1)
- biometricMessageDeferral.processFrame(1)
- biometricMessageDeferral.updateMessage(1, "msgId-1")
-
- biometricMessageDeferral.processFrame(2)
- biometricMessageDeferral.updateMessage(2, "msgId-2")
-
- // THEN the most frequent deferred message that meets the threshold is returned
- assertEquals("msgId-1", biometricMessageDeferral.getDeferredMessage())
- }
-
- @Test
- @EnableFlags(Flags.FLAG_FACE_MESSAGE_DEFER_UPDATE)
fun testReturnsMostFrequentDeferredMessage_onlyAnalyzesLastNWindow() {
val biometricMessageDeferral = createMsgDeferral(setOf(1, 2))
@@ -166,23 +129,6 @@
}
@Test
- @DisableFlags(Flags.FLAG_FACE_MESSAGE_DEFER_UPDATE)
- fun testReturnsMostFrequentDeferredMessage_analyzesAllFrames() {
- val biometricMessageDeferral = createMsgDeferral(setOf(1, 2))
-
- // WHEN there's 80% of the messages are msgId=1 and 20% is msgId=2, but the last
- // N window only contains messages with msgId=2
- repeat(80) { biometricMessageDeferral.processFrame(1) }
- biometricMessageDeferral.updateMessage(1, "msgId-1")
- systemClock.setElapsedRealtime(systemClock.elapsedRealtime() + 501L)
- repeat(20) { biometricMessageDeferral.processFrame(2) }
- biometricMessageDeferral.updateMessage(2, "msgId-2")
-
- // THEN the most frequent deferred message is returned
- assertEquals("msgId-1", biometricMessageDeferral.getDeferredMessage())
- }
-
- @Test
fun testDeferredMessage_mustMeetThreshold() {
val biometricMessageDeferral = createMsgDeferral(setOf(1))
@@ -207,9 +153,7 @@
val biometricMessageDeferral = createMsgDeferral(setOf(1, 2))
// GIVEN two msgId=1 events processed
- biometricMessageDeferral.processFrame(
- 1,
- )
+ biometricMessageDeferral.processFrame(1)
biometricMessageDeferral.updateMessage(1, "msgId-1")
biometricMessageDeferral.processFrame(1)
biometricMessageDeferral.updateMessage(1, "msgId-1")
@@ -240,10 +184,7 @@
@Test
fun testDeferredMessage_meetThresholdWithIgnoredFrames() {
val biometricMessageDeferral =
- createMsgDeferral(
- messagesToDefer = setOf(1),
- acquiredInfoToIgnore = setOf(4),
- )
+ createMsgDeferral(messagesToDefer = setOf(1), acquiredInfoToIgnore = setOf(4))
// WHEN more nonDeferredMessages are shown than the deferred message; HOWEVER the
// nonDeferredMessages are in acquiredInfoToIgnore
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index 56c0182..0637603 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -45,8 +45,6 @@
import android.testing.TestableLooper.RunWithLooper;
import android.view.LayoutInflater;
import android.view.Surface;
-import android.view.View;
-import android.view.ViewRootImpl;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityManager;
@@ -63,13 +61,13 @@
import com.android.systemui.biometrics.udfps.SinglePointerTouchProcessor;
import com.android.systemui.biometrics.ui.viewmodel.DefaultUdfpsTouchOverlayViewModel;
import com.android.systemui.biometrics.ui.viewmodel.DeviceEntryUdfpsTouchOverlayViewModel;
+import com.android.systemui.biometrics.ui.viewmodel.PromptUdfpsTouchOverlayViewModel;
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor;
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor;
import com.android.systemui.camera.CameraGestureHelper;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.keyguard.UserActivityNotifierKosmosKt;
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
@@ -82,7 +80,6 @@
import com.android.systemui.power.shared.model.WakeSleepReason;
import com.android.systemui.power.shared.model.WakefulnessState;
import com.android.systemui.shade.domain.interactor.ShadeInteractor;
-import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
@@ -98,8 +95,6 @@
import dagger.Lazy;
-import javax.inject.Provider;
-
import kotlinx.coroutines.CoroutineScope;
import org.junit.Before;
@@ -115,6 +110,8 @@
import java.util.ArrayList;
import java.util.List;
+import javax.inject.Provider;
+
@SmallTest
@RunWith(AndroidJUnit4.class)
@RunWithLooper(setAsMainLooper = true)
@@ -151,8 +148,6 @@
@Mock
private AccessibilityManager mAccessibilityManager;
@Mock
- private LockscreenShadeTransitionController mLockscreenShadeTransitionController;
- @Mock
private ScreenLifecycle mScreenLifecycle;
@Mock
private VibratorHelper mVibrator;
@@ -178,9 +173,6 @@
@Mock
private UdfpsDisplayMode mUdfpsDisplayMode;
@Mock
- private FeatureFlags mFeatureFlags;
- // Stuff for configuring mocks
- @Mock
private SystemUIDialogManager mSystemUIDialogManager;
@Mock
private ActivityTransitionAnimator mActivityTransitionAnimator;
@@ -204,31 +196,21 @@
private ArgumentCaptor<IUdfpsOverlayController> mOverlayCaptor;
private IUdfpsOverlayController mOverlayController;
@Captor
- private ArgumentCaptor<View> mViewCaptor;
- @Captor
- private ArgumentCaptor<View.OnHoverListener> mHoverListenerCaptor;
- @Captor
- private ArgumentCaptor<Runnable> mOnDisplayConfiguredCaptor;
- @Captor
private ArgumentCaptor<ScreenLifecycle.Observer> mScreenObserverCaptor;
- @Captor
- private ArgumentCaptor<UdfpsController.UdfpsOverlayController> mUdfpsOverlayControllerCaptor;
- private ScreenLifecycle.Observer mScreenObserver;
private FingerprintSensorPropertiesInternal mOpticalProps;
- private FingerprintSensorPropertiesInternal mUltrasonicProps;
private PowerInteractor mPowerInteractor;
private FakePowerRepository mPowerRepository;
@Mock
private InputManager mInputManager;
@Mock
- private ViewRootImpl mViewRootImpl;
- @Mock
private KeyguardTransitionInteractor mKeyguardTransitionInteractor;
@Mock
private Lazy<DeviceEntryUdfpsTouchOverlayViewModel> mDeviceEntryUdfpsTouchOverlayViewModel;
@Mock
private Lazy<DefaultUdfpsTouchOverlayViewModel> mDefaultUdfpsTouchOverlayViewModel;
@Mock
+ private Lazy<PromptUdfpsTouchOverlayViewModel> mPromptUdfpsTouchOverlayViewModel;
+ @Mock
private Provider<CameraGestureHelper> mCameraGestureHelper;
@Before
@@ -269,13 +251,6 @@
FingerprintSensorProperties.TYPE_UDFPS_OPTICAL,
true /* resetLockoutRequiresHardwareAuthToken */);
- mUltrasonicProps = new FingerprintSensorPropertiesInternal(2 /* sensorId */,
- SensorProperties.STRENGTH_STRONG,
- 5 /* maxEnrollmentsPerUser */,
- componentInfo,
- FingerprintSensorProperties.TYPE_UDFPS_ULTRASONIC,
- true /* resetLockoutRequiresHardwareAuthToken */);
-
mFgExecutor = new FakeExecutor(new FakeSystemClock());
// Create a fake background executor.
@@ -327,6 +302,7 @@
mKeyguardTransitionInteractor,
mDeviceEntryUdfpsTouchOverlayViewModel,
mDefaultUdfpsTouchOverlayViewModel,
+ mPromptUdfpsTouchOverlayViewModel,
mUdfpsOverlayInteractor,
mPowerInteractor,
mock(CoroutineScope.class),
@@ -335,7 +311,6 @@
verify(mFingerprintManager).setUdfpsOverlayController(mOverlayCaptor.capture());
mOverlayController = mOverlayCaptor.getValue();
verify(mScreenLifecycle).addObserver(mScreenObserverCaptor.capture());
- mScreenObserver = mScreenObserverCaptor.getValue();
mUdfpsController.updateOverlayParams(sensorProps, new UdfpsOverlayParams());
mUdfpsController.setUdfpsDisplayMode(mUdfpsDisplayMode);
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorTest.kt
index 91ec1cb..5e1086b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorTest.kt
@@ -33,6 +33,7 @@
import com.android.systemui.bouncer.ui.BouncerViewDelegate
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor
+import com.android.systemui.flags.DisableSceneContainer
import com.android.systemui.keyguard.DismissCallbackRegistry
import com.android.systemui.keyguard.data.repository.FakeTrustRepository
import com.android.systemui.plugins.ActivityStarter
@@ -59,6 +60,7 @@
@SmallTest
@RunWithLooper(setAsMainLooper = true)
@RunWith(AndroidJUnit4::class)
+@DisableSceneContainer
class PrimaryBouncerInteractorTest : SysuiTestCase() {
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private lateinit var repository: KeyguardBouncerRepository
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt
index 7cfbf5e..4b71a63 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt
@@ -29,6 +29,7 @@
import com.android.systemui.bouncer.domain.interactor.bouncerInteractor
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
+import com.android.systemui.flags.DisableSceneContainer
import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.inputmethod.data.model.InputMethodModel
import com.android.systemui.inputmethod.data.repository.fakeInputMethodRepository
@@ -348,6 +349,7 @@
}
@EnableFlags(com.android.systemui.Flags.FLAG_COMPOSE_BOUNCER)
+ @DisableSceneContainer
@Test
fun consumeConfirmKeyEvents_toPreventItFromPropagating() =
testScope.runTest { verifyConfirmKeyEventsBehavior(keyUpEventConsumed = true) }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalDreamStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalDreamStartableTest.kt
index ad7df9e..b371c20 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalDreamStartableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalDreamStartableTest.kt
@@ -27,6 +27,7 @@
import com.android.systemui.communal.domain.interactor.communalSceneInteractor
import com.android.systemui.communal.domain.interactor.communalSettingsInteractor
import com.android.systemui.communal.domain.interactor.setCommunalV2Enabled
+import com.android.systemui.flags.DisableSceneContainer
import com.android.systemui.flags.Flags.COMMUNAL_SERVICE_ENABLED
import com.android.systemui.flags.fakeFeatureFlagsClassic
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
@@ -55,6 +56,7 @@
@SmallTest
@EnableFlags(Flags.FLAG_COMMUNAL_HUB)
+@DisableSceneContainer
@RunWith(ParameterizedAndroidJunit4::class)
class CommunalDreamStartableTest(flags: FlagsParameterization) : SysuiTestCase() {
private val kosmos = testKosmos()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/DeviceInactiveConditionTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/DeviceInactiveConditionTest.kt
deleted file mode 100644
index e0feb75..0000000
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/DeviceInactiveConditionTest.kt
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright (C) 2025 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.communal
-
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.android.keyguard.keyguardUpdateMonitor
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.condition.testStart
-import com.android.systemui.keyguard.WakefulnessLifecycle
-import com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_ASLEEP
-import com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_AWAKE
-import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
-import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
-import com.android.systemui.keyguard.shared.model.DozeStateModel
-import com.android.systemui.keyguard.shared.model.DozeTransitionModel
-import com.android.systemui.keyguard.wakefulnessLifecycle
-import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.kosmos.applicationCoroutineScope
-import com.android.systemui.kosmos.runTest
-import com.android.systemui.kosmos.useUnconfinedTestDispatcher
-import com.android.systemui.statusbar.policy.keyguardStateController
-import com.android.systemui.testKosmos
-import com.android.systemui.util.kotlin.javaAdapter
-import com.google.common.truth.Truth.assertThat
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.kotlin.argumentCaptor
-import org.mockito.kotlin.doReturn
-import org.mockito.kotlin.verify
-import org.mockito.kotlin.whenever
-
-@SmallTest
-@RunWith(AndroidJUnit4::class)
-class DeviceInactiveConditionTest : SysuiTestCase() {
- private val kosmos =
- testKosmos().useUnconfinedTestDispatcher().also {
- whenever(it.wakefulnessLifecycle.wakefulness) doReturn WAKEFULNESS_AWAKE
- }
-
- private val Kosmos.underTest by
- Kosmos.Fixture {
- DeviceInactiveCondition(
- applicationCoroutineScope,
- applicationCoroutineScope,
- keyguardStateController,
- wakefulnessLifecycle,
- keyguardUpdateMonitor,
- keyguardInteractor,
- javaAdapter,
- )
- }
-
- @Test
- fun asleep_conditionTrue() =
- kosmos.runTest {
- // Condition is false to start.
- testStart(underTest)
- assertThat(underTest.isConditionMet).isFalse()
-
- // Condition is true when device goes to sleep.
- sleep()
- assertThat(underTest.isConditionMet).isTrue()
- }
-
- @Test
- fun dozingAndAsleep_conditionFalse() =
- kosmos.runTest {
- // Condition is true when device is asleep.
- testStart(underTest)
- sleep()
- assertThat(underTest.isConditionMet).isTrue()
-
- // Condition turns false after doze starts.
- fakeKeyguardRepository.setDozeTransitionModel(
- DozeTransitionModel(from = DozeStateModel.UNINITIALIZED, to = DozeStateModel.DOZE)
- )
- assertThat(underTest.isConditionMet).isFalse()
- }
-
- fun Kosmos.sleep() {
- whenever(wakefulnessLifecycle.wakefulness) doReturn WAKEFULNESS_ASLEEP
- argumentCaptor<WakefulnessLifecycle.Observer>().apply {
- verify(wakefulnessLifecycle).addObserver(capture())
- firstValue.onStartedGoingToSleep()
- }
- }
-}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalAutoOpenInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalAutoOpenInteractorTest.kt
index a6be3ce..c9c577b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalAutoOpenInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalAutoOpenInteractorTest.kt
@@ -34,6 +34,7 @@
import com.android.systemui.dock.DockManager
import com.android.systemui.dock.fakeDockManager
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.advanceTimeBy
import com.android.systemui.kosmos.collectLastValue
import com.android.systemui.kosmos.runTest
import com.android.systemui.kosmos.useUnconfinedTestDispatcher
@@ -42,6 +43,7 @@
import com.android.systemui.user.data.repository.fakeUserRepository
import com.android.systemui.util.settings.fakeSettings
import com.google.common.truth.Truth.assertThat
+import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.runBlocking
import org.junit.Before
import org.junit.Test
@@ -145,6 +147,7 @@
SuppressionReason.ReasonWhenToAutoShow(FEATURE_AUTO_OPEN or FEATURE_MANUAL_OPEN)
)
+ advanceTimeBy(1.milliseconds)
posturingRepository.fake.emitPositionState(
PositionState(
stationary = PositionState.StationaryState.Stationary(confidence = 1f),
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
index b65ecf46..6947415 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
@@ -42,7 +42,6 @@
import com.android.systemui.communal.data.model.SuppressionReason
import com.android.systemui.communal.data.repository.fakeCommunalMediaRepository
import com.android.systemui.communal.data.repository.fakeCommunalPrefsRepository
-import com.android.systemui.communal.data.repository.fakeCommunalSceneRepository
import com.android.systemui.communal.data.repository.fakeCommunalSmartspaceRepository
import com.android.systemui.communal.data.repository.fakeCommunalTutorialRepository
import com.android.systemui.communal.data.repository.fakeCommunalWidgetRepository
@@ -51,6 +50,7 @@
import com.android.systemui.communal.shared.model.CommunalContentSize
import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.communal.shared.model.EditModeState
+import com.android.systemui.flags.DisableSceneContainer
import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.flags.Flags
import com.android.systemui.flags.fakeFeatureFlagsClassic
@@ -379,29 +379,7 @@
assertThat(ctaTileContent).isEmpty()
}
- @Test
- fun listensToSceneChange() =
- kosmos.runTest {
- kosmos.setCommunalAvailable(true)
-
- val desiredScene by collectLastValue(underTest.desiredScene)
- assertThat(desiredScene).isEqualTo(CommunalScenes.Blank)
-
- val targetScene = CommunalScenes.Communal
- fakeCommunalSceneRepository.changeScene(targetScene)
- assertThat(desiredScene).isEqualTo(targetScene)
- }
-
- @Test
- fun updatesScene() =
- kosmos.runTest {
- val targetScene = CommunalScenes.Communal
- underTest.changeScene(targetScene, "test")
-
- val desiredScene by collectLastValue(fakeCommunalSceneRepository.currentScene)
- assertThat(desiredScene).isEqualTo(targetScene)
- }
-
+ @DisableSceneContainer
@Test
fun transitionProgress_onTargetScene_fullProgress() =
kosmos.runTest {
@@ -420,6 +398,7 @@
.isEqualTo(CommunalTransitionProgressModel.Idle(targetScene))
}
+ @DisableSceneContainer
@Test
fun transitionProgress_notOnTargetScene_noProgress() =
kosmos.runTest {
@@ -439,6 +418,7 @@
.isEqualTo(CommunalTransitionProgressModel.Idle(currentScene))
}
+ @DisableSceneContainer
@Test
fun transitionProgress_transitioningToTrackedScene() =
kosmos.runTest {
@@ -487,6 +467,7 @@
.isEqualTo(CommunalTransitionProgressModel.Idle(targetScene))
}
+ @DisableSceneContainer
@Test
fun transitionProgress_transitioningAwayFromTrackedScene() =
kosmos.runTest {
@@ -550,6 +531,7 @@
assertThat(isCommunalShowing).isEqualTo(true)
}
+ @DisableSceneContainer
@Test
fun isCommunalShowing_whenSceneContainerDisabled() =
kosmos.runTest {
@@ -572,8 +554,8 @@
assertThat(isCommunalShowing).isFalse()
}
- @Test
@EnableSceneContainer
+ @Test
fun isCommunalShowing_whenSceneContainerEnabled() =
kosmos.runTest {
// Verify default is false
@@ -589,8 +571,8 @@
assertThat(isCommunalShowing).isFalse()
}
- @Test
@EnableSceneContainer
+ @Test
fun isCommunalShowing_whenSceneContainerEnabledAndChangeToLegacyScene() =
kosmos.runTest {
// Verify default is false
@@ -607,88 +589,6 @@
}
@Test
- fun isIdleOnCommunal() =
- kosmos.runTest {
- val transitionState =
- MutableStateFlow<ObservableTransitionState>(
- ObservableTransitionState.Idle(CommunalScenes.Blank)
- )
- fakeCommunalSceneRepository.setTransitionState(transitionState)
-
- // isIdleOnCommunal is false when not on communal.
- val isIdleOnCommunal by collectLastValue(underTest.isIdleOnCommunal)
- assertThat(isIdleOnCommunal).isEqualTo(false)
-
- // Transition to communal.
- transitionState.value = ObservableTransitionState.Idle(CommunalScenes.Communal)
-
- // isIdleOnCommunal is now true since we're on communal.
- assertThat(isIdleOnCommunal).isEqualTo(true)
-
- // Start transition away from communal.
- transitionState.value =
- ObservableTransitionState.Transition(
- fromScene = CommunalScenes.Communal,
- toScene = CommunalScenes.Blank,
- currentScene = flowOf(CommunalScenes.Blank),
- progress = flowOf(0f),
- isInitiatedByUserInput = false,
- isUserInputOngoing = flowOf(false),
- )
-
- // isIdleOnCommunal turns false as soon as transition away starts.
- assertThat(isIdleOnCommunal).isEqualTo(false)
- }
-
- @Test
- fun isCommunalVisible() =
- kosmos.runTest {
- val transitionState =
- MutableStateFlow<ObservableTransitionState>(
- ObservableTransitionState.Idle(CommunalScenes.Blank)
- )
- fakeCommunalSceneRepository.setTransitionState(transitionState)
-
- // isCommunalVisible is false when not on communal.
- val isCommunalVisible by collectLastValue(underTest.isCommunalVisible)
- assertThat(isCommunalVisible).isEqualTo(false)
-
- // Start transition to communal.
- transitionState.value =
- ObservableTransitionState.Transition(
- fromScene = CommunalScenes.Blank,
- toScene = CommunalScenes.Communal,
- currentScene = flowOf(CommunalScenes.Communal),
- progress = flowOf(0f),
- isInitiatedByUserInput = false,
- isUserInputOngoing = flowOf(false),
- )
-
- // isCommunalVisible is true once transition starts.
- assertThat(isCommunalVisible).isEqualTo(true)
-
- // Finish transition to communal
- transitionState.value = ObservableTransitionState.Idle(CommunalScenes.Communal)
-
- // isCommunalVisible is true since we're on communal.
- assertThat(isCommunalVisible).isEqualTo(true)
-
- // Start transition away from communal.
- transitionState.value =
- ObservableTransitionState.Transition(
- fromScene = CommunalScenes.Communal,
- toScene = CommunalScenes.Blank,
- currentScene = flowOf(CommunalScenes.Blank),
- progress = flowOf(1.0f),
- isInitiatedByUserInput = false,
- isUserInputOngoing = flowOf(false),
- )
-
- // isCommunalVisible is still true as the false as soon as transition away runs.
- assertThat(isCommunalVisible).isEqualTo(true)
- }
-
- @Test
fun testShowWidgetEditorStartsActivity() =
kosmos.runTest {
val editModeState by collectLastValue(communalSceneInteractor.editModeState)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractorTest.kt
index 7bdac47..1beca31 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractorTest.kt
@@ -18,12 +18,9 @@
import android.content.res.Configuration.ORIENTATION_LANDSCAPE
import android.content.res.Configuration.ORIENTATION_PORTRAIT
-import android.platform.test.annotations.DisableFlags
-import android.platform.test.annotations.EnableFlags
import android.platform.test.flag.junit.FlagsParameterization
import androidx.test.filters.SmallTest
import com.android.compose.animation.scene.ObservableTransitionState
-import com.android.systemui.Flags.FLAG_SCENE_CONTAINER
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.ActivityTransitionAnimator
import com.android.systemui.communal.data.repository.communalSceneRepository
@@ -31,7 +28,10 @@
import com.android.systemui.communal.domain.model.CommunalTransitionProgressModel
import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.flags.DisableSceneContainer
+import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.flags.andSceneContainer
+import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.kosmos.testScope
import com.android.systemui.scene.initialSceneKey
import com.android.systemui.scene.shared.model.Scenes
@@ -80,7 +80,7 @@
private val underTest by lazy { kosmos.communalSceneInteractor }
private val keyguardStateController: KeyguardStateController = kosmos.keyguardStateController
- @DisableFlags(FLAG_SCENE_CONTAINER)
+ @DisableSceneContainer
@Test
fun changeScene() =
testScope.runTest {
@@ -91,7 +91,7 @@
assertThat(currentScene).isEqualTo(CommunalScenes.Communal)
}
- @DisableFlags(FLAG_SCENE_CONTAINER)
+ @DisableSceneContainer
@Test
fun changeScene_callsSceneStateProcessor() =
testScope.runTest {
@@ -107,9 +107,9 @@
verify(callback).onSceneAboutToChange(CommunalScenes.Communal, null)
}
- @DisableFlags(FLAG_SCENE_CONTAINER)
+ @DisableSceneContainer
@Test
- fun changeScene_doesNotCallSceneStateProcessorForDuplicateState() =
+ fun changeScene_doesNotCallSceneStateProcessorForDuplicateState_ifKeyguardStateIsNull() =
testScope.runTest {
val callback: OnSceneAboutToChangeListener = mock()
underTest.registerSceneStateProcessor(callback)
@@ -123,7 +123,27 @@
verify(callback, never()).onSceneAboutToChange(any(), anyOrNull())
}
- @DisableFlags(FLAG_SCENE_CONTAINER)
+ @DisableSceneContainer
+ @Test
+ fun changeScene_callSceneStateProcessorForDuplicateScene_ifBlankSceneWithKeyguardState() =
+ testScope.runTest {
+ val callback: OnSceneAboutToChangeListener = mock()
+ underTest.registerSceneStateProcessor(callback)
+
+ val currentScene by collectLastValue(underTest.currentScene)
+ underTest.changeScene(CommunalScenes.Communal, "test")
+ assertThat(currentScene).isEqualTo(CommunalScenes.Communal)
+
+ underTest.changeScene(CommunalScenes.Blank, "test", keyguardState = KeyguardState.AOD)
+ assertThat(currentScene).isEqualTo(CommunalScenes.Blank)
+ verify(callback).onSceneAboutToChange(CommunalScenes.Blank, KeyguardState.AOD)
+
+ underTest.changeScene(CommunalScenes.Blank, "test", keyguardState = KeyguardState.GONE)
+ assertThat(currentScene).isEqualTo(CommunalScenes.Blank)
+ verify(callback).onSceneAboutToChange(CommunalScenes.Blank, KeyguardState.GONE)
+ }
+
+ @DisableSceneContainer
@Test
fun snapToScene() =
testScope.runTest {
@@ -134,7 +154,7 @@
assertThat(currentScene).isEqualTo(CommunalScenes.Communal)
}
- @DisableFlags(FLAG_SCENE_CONTAINER)
+ @DisableSceneContainer
@Test
fun snapToSceneWithDelay() =
testScope.runTest {
@@ -150,7 +170,7 @@
assertThat(currentScene).isEqualTo(CommunalScenes.Communal)
}
- @DisableFlags(FLAG_SCENE_CONTAINER)
+ @DisableSceneContainer
@Test
fun transitionProgress_fullProgress() =
testScope.runTest {
@@ -169,7 +189,7 @@
.isEqualTo(CommunalTransitionProgressModel.Idle(CommunalScenes.Communal))
}
- @DisableFlags(FLAG_SCENE_CONTAINER)
+ @DisableSceneContainer
@Test
fun transitionProgress_transitioningAwayFromTrackedScene() =
testScope.runTest {
@@ -210,7 +230,7 @@
.isEqualTo(CommunalTransitionProgressModel.Idle(CommunalScenes.Communal))
}
- @DisableFlags(FLAG_SCENE_CONTAINER)
+ @DisableSceneContainer
@Test
fun transitionProgress_transitioningToTrackedScene() =
testScope.runTest {
@@ -248,7 +268,7 @@
.isEqualTo(CommunalTransitionProgressModel.Idle(CommunalScenes.Communal))
}
- @DisableFlags(FLAG_SCENE_CONTAINER)
+ @DisableSceneContainer
@Test
fun isIdleOnCommunal() =
testScope.runTest {
@@ -276,7 +296,7 @@
assertThat(isIdleOnCommunal).isEqualTo(false)
}
- @DisableFlags(FLAG_SCENE_CONTAINER)
+ @DisableSceneContainer
@Test
fun isTransitioningToOrIdleOnCommunal() =
testScope.runTest {
@@ -318,7 +338,7 @@
assertThat(isTransitioningToOrIdleOnCommunal).isEqualTo(false)
}
- @DisableFlags(FLAG_SCENE_CONTAINER)
+ @DisableSceneContainer
@Test
fun isCommunalVisible() =
testScope.runTest {
@@ -359,7 +379,7 @@
assertThat(isCommunalVisible).isEqualTo(true)
}
- @EnableFlags(FLAG_SCENE_CONTAINER)
+ @EnableSceneContainer
@Test
fun changeScene_legacyCommunalScene_mapToStfScene() =
testScope.runTest {
@@ -381,7 +401,7 @@
assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
}
- @EnableFlags(FLAG_SCENE_CONTAINER)
+ @EnableSceneContainer
@Test
fun changeScene_stfScenes() =
testScope.runTest {
@@ -403,7 +423,7 @@
assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
}
- @EnableFlags(FLAG_SCENE_CONTAINER)
+ @EnableSceneContainer
@Test
fun snapToScene_legacyCommunalScene_mapToStfScene() =
testScope.runTest {
@@ -425,7 +445,7 @@
assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
}
- @EnableFlags(FLAG_SCENE_CONTAINER)
+ @EnableSceneContainer
@Test
fun snapToScene_stfScenes() =
testScope.runTest {
@@ -447,7 +467,7 @@
assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
}
- @EnableFlags(FLAG_SCENE_CONTAINER)
+ @EnableSceneContainer
@Test
fun isIdleOnCommunal_sceneContainerEnabled() =
testScope.runTest {
@@ -492,7 +512,7 @@
assertThat(isIdleOnCommunal).isEqualTo(false)
}
- @EnableFlags(FLAG_SCENE_CONTAINER)
+ @EnableSceneContainer
@Test
fun isCommunalVisible_sceneContainerEnabled() =
testScope.runTest {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalSceneTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalSceneTransitionInteractorTest.kt
index aa96073..dd8005c1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalSceneTransitionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalSceneTransitionInteractorTest.kt
@@ -24,16 +24,19 @@
import com.android.systemui.Flags.FLAG_COMMUNAL_HUB
import com.android.systemui.SysuiTestCase
import com.android.systemui.communal.data.repository.communalSceneRepository
+import com.android.systemui.communal.data.repository.fakeCommunalSceneRepository
import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.coroutines.collectValues
import com.android.systemui.flags.DisableSceneContainer
import com.android.systemui.flags.Flags
import com.android.systemui.flags.fakeFeatureFlagsClassic
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
+import com.android.systemui.keyguard.data.repository.keyguardRepository
import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository
import com.android.systemui.keyguard.data.repository.realKeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCER
+import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
import com.android.systemui.keyguard.shared.model.KeyguardState.GLANCEABLE_HUB
import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
@@ -46,16 +49,22 @@
import com.android.systemui.keyguard.shared.model.TransitionState.RUNNING
import com.android.systemui.keyguard.shared.model.TransitionState.STARTED
import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.collectLastValue
+import com.android.systemui.kosmos.collectValues
+import com.android.systemui.kosmos.runTest
import com.android.systemui.kosmos.testScope
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
+import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest
+import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest
+import com.android.systemui.power.domain.interactor.powerInteractor
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
-import kotlin.time.Duration.Companion.seconds
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.launch
-import kotlinx.coroutines.test.advanceTimeBy
-import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.TestCoroutineScheduler
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
@@ -66,14 +75,14 @@
@EnableFlags(FLAG_COMMUNAL_HUB)
@DisableSceneContainer
class CommunalSceneTransitionInteractorTest : SysuiTestCase() {
-
private val kosmos =
- testKosmos().apply { keyguardTransitionRepository = realKeyguardTransitionRepository }
+ testKosmos().useUnconfinedTestDispatcher().apply {
+ keyguardTransitionRepository = realKeyguardTransitionRepository
+ }
private val testScope = kosmos.testScope
private val underTest by lazy { kosmos.communalSceneTransitionInteractor }
private val keyguardTransitionRepository by lazy { kosmos.realKeyguardTransitionRepository }
- private val keyguardRepository by lazy { kosmos.fakeKeyguardRepository }
private val ownerName = CommunalSceneTransitionInteractor::class.java.simpleName
private val progress = MutableSharedFlow<Float>()
@@ -106,6 +115,7 @@
kosmos.fakeFeatureFlagsClassic.set(Flags.COMMUNAL_SERVICE_ENABLED, true)
underTest.start()
kosmos.communalSceneRepository.setTransitionState(sceneTransitions)
+
testScope.launch {
keyguardTransitionRepository.emitInitialStepsFromOff(LOCKSCREEN, testSetup = true)
}
@@ -114,7 +124,7 @@
/** Transition from blank to glanceable hub. This is the default case. */
@Test
fun transition_from_blank_end_in_hub() =
- testScope.runTest {
+ kosmos.runTest {
sceneTransitions.value = blankToHub
val currentStep by collectLastValue(keyguardTransitionRepository.transitions)
@@ -170,7 +180,7 @@
/** Transition from hub to lockscreen. */
@Test
fun transition_from_hub_end_in_lockscreen() =
- testScope.runTest {
+ kosmos.runTest {
sceneTransitions.value = hubToBlank
val currentStep by collectLastValue(keyguardTransitionRepository.transitions)
@@ -214,12 +224,11 @@
/** Transition from hub to dream. */
@Test
fun transition_from_hub_end_in_dream() =
- testScope.runTest {
+ kosmos.runTest {
// Device is dreaming and occluded.
- kosmos.fakeKeyguardRepository.setKeyguardOccluded(true)
- kosmos.fakeKeyguardRepository.setDreaming(true)
- kosmos.fakeKeyguardRepository.setDreamingWithOverlay(true)
- runCurrent()
+ fakeKeyguardRepository.setKeyguardOccluded(true)
+ fakeKeyguardRepository.setDreaming(true)
+ fakeKeyguardRepository.setDreamingWithOverlay(true)
sceneTransitions.value = hubToBlank
@@ -266,7 +275,6 @@
fun transition_from_hub_end_in_occluded() =
testScope.runTest {
kosmos.fakeKeyguardRepository.setKeyguardOccluded(true)
- runCurrent()
sceneTransitions.value = hubToBlank
@@ -311,9 +319,8 @@
/** Transition from hub to gone. */
@Test
fun transition_from_hub_end_in_gone() =
- testScope.runTest {
- kosmos.fakeKeyguardRepository.setKeyguardGoingAway(true)
- runCurrent()
+ kosmos.runTest {
+ fakeKeyguardRepository.setKeyguardGoingAway(true)
sceneTransitions.value = hubToBlank
@@ -355,10 +362,342 @@
)
}
+ @Test
+ fun transitionFromHub_ifAsleep_endInAod() =
+ kosmos.runTest {
+ keyguardRepository.setAodAvailable(true)
+ powerInteractor.setAsleepForTest()
+
+ sceneTransitions.value = hubToBlank
+
+ val currentStep by collectLastValue(keyguardTransitionRepository.transitions)
+
+ assertThat(currentStep)
+ .isEqualTo(
+ TransitionStep(
+ from = GLANCEABLE_HUB,
+ to = AOD,
+ transitionState = STARTED,
+ value = 0f,
+ ownerName = ownerName,
+ )
+ )
+
+ progress.emit(0.4f)
+ assertThat(currentStep)
+ .isEqualTo(
+ TransitionStep(
+ from = GLANCEABLE_HUB,
+ to = AOD,
+ transitionState = RUNNING,
+ value = 0.4f,
+ ownerName = ownerName,
+ )
+ )
+
+ sceneTransitions.value = Idle(CommunalScenes.Blank)
+ assertThat(currentStep)
+ .isEqualTo(
+ TransitionStep(
+ from = GLANCEABLE_HUB,
+ to = AOD,
+ transitionState = FINISHED,
+ value = 1f,
+ ownerName = ownerName,
+ )
+ )
+ }
+
+ @Test
+ fun transitionFromAodToHub_interruptedByAsleepThenAwake_endInHub() =
+ kosmos.runTest {
+ fakeKeyguardRepository.setKeyguardShowing(true)
+ sceneTransitions.value = Idle(CommunalScenes.Blank)
+ powerInteractor.setAsleepForTest()
+ keyguardRepository.setAodAvailable(true)
+
+ transitionTo(LOCKSCREEN, AOD, testScope.testScheduler)
+
+ // 1. Click power button, awake
+ powerInteractor.setAwakeForTest()
+ // target scene is Communal
+ fakeCommunalSceneRepository.currentScene.value = CommunalScenes.Communal
+ sceneTransitions.value = blankToHub
+
+ val currentStep by collectLastValue(keyguardTransitionRepository.transitions)
+ val allSteps by collectValues(keyguardTransitionRepository.transitions)
+
+ assertThat(currentStep)
+ .isEqualTo(
+ TransitionStep(
+ from = AOD,
+ to = GLANCEABLE_HUB,
+ transitionState = STARTED,
+ value = 0f,
+ ownerName = ownerName,
+ )
+ )
+
+ progress.emit(0.4f)
+ assertThat(currentStep)
+ .isEqualTo(
+ TransitionStep(
+ from = AOD,
+ to = GLANCEABLE_HUB,
+ transitionState = RUNNING,
+ value = 0.4f,
+ ownerName = ownerName,
+ )
+ )
+ var numToDrop = allSteps.size
+
+ // 2. Click power button, asleep
+ powerInteractor.setAsleepForTest()
+ fakeCommunalSceneRepository.currentScene.value = CommunalScenes.Blank
+
+ sceneTransitions.value =
+ ObservableTransitionState.Transition(
+ fromScene = CommunalScenes.Blank,
+ toScene = CommunalScenes.Communal,
+ currentScene = fakeCommunalSceneRepository.currentScene,
+ progress = progress,
+ isInitiatedByUserInput = true,
+ isUserInputOngoing = flowOf(false),
+ )
+
+ assertThat(allSteps.drop(numToDrop))
+ .containsExactly(
+ // cancel AOD->hub, reverse back ->AOD
+ TransitionStep(
+ from = AOD,
+ to = GLANCEABLE_HUB,
+ value = 0.4f,
+ transitionState = CANCELED,
+ ownerName = ownerName,
+ ),
+ TransitionStep(
+ from = GLANCEABLE_HUB,
+ to = AOD,
+ value = 0.6f,
+ transitionState = STARTED,
+ ownerName = ownerName,
+ ),
+ )
+ // update progress to 0.8 as reversed ST progress is 0.2
+ progress.emit(0.2f)
+ assertThat(currentStep)
+ .isEqualTo(
+ TransitionStep(
+ from = GLANCEABLE_HUB,
+ to = AOD,
+ transitionState = RUNNING,
+ value = 0.8f,
+ ownerName = ownerName,
+ )
+ )
+ numToDrop = allSteps.size
+
+ // 3. Click power button again to wake
+ powerInteractor.setAwakeForTest()
+ fakeCommunalSceneRepository.currentScene.value = CommunalScenes.Communal
+
+ sceneTransitions.value =
+ ObservableTransitionState.Transition(
+ fromScene = CommunalScenes.Blank,
+ toScene = CommunalScenes.Communal,
+ currentScene = fakeCommunalSceneRepository.currentScene,
+ progress = progress,
+ isInitiatedByUserInput = true,
+ isUserInputOngoing = flowOf(false),
+ )
+ progress.emit(0.4f)
+
+ sceneTransitions.value = Idle(CommunalScenes.Communal)
+
+ assertThat(allSteps.drop(numToDrop))
+ .containsExactly(
+ TransitionStep(
+ from = GLANCEABLE_HUB,
+ to = AOD,
+ value = 0.8f,
+ transitionState = CANCELED,
+ ownerName = ownerName,
+ ),
+ // awake -> Hub
+ TransitionStep(
+ from = AOD,
+ to = GLANCEABLE_HUB,
+ value = 0f,
+ transitionState = STARTED,
+ ownerName = ownerName,
+ ),
+ TransitionStep(
+ from = AOD,
+ to = GLANCEABLE_HUB,
+ value = 0.4f,
+ transitionState = RUNNING,
+ ownerName = ownerName,
+ ),
+ TransitionStep(
+ from = AOD,
+ to = GLANCEABLE_HUB,
+ value = 1f,
+ transitionState = FINISHED,
+ ownerName = ownerName,
+ ),
+ )
+ .inOrder()
+ }
+
+ @Test
+ fun transitionFromHubToAod_interruptedByAwakeThenAsleep_endInAod() =
+ kosmos.runTest {
+ fakeKeyguardRepository.setKeyguardShowing(true)
+ sceneTransitions.value = Idle(CommunalScenes.Communal)
+ keyguardRepository.setAodAvailable(true)
+
+ // 1. Click power button -> Blank (AOD)
+ powerInteractor.setAsleepForTest()
+ fakeCommunalSceneRepository.currentScene.value = CommunalScenes.Blank
+
+ sceneTransitions.value =
+ ObservableTransitionState.Transition(
+ fromScene = CommunalScenes.Communal,
+ toScene = CommunalScenes.Blank,
+ currentScene = fakeCommunalSceneRepository.currentScene,
+ progress = progress,
+ isInitiatedByUserInput = true,
+ isUserInputOngoing = flowOf(false),
+ )
+
+ val currentStep by collectLastValue(keyguardTransitionRepository.transitions)
+ val allSteps by collectValues(keyguardTransitionRepository.transitions)
+
+ assertThat(currentStep)
+ .isEqualTo(
+ TransitionStep(
+ from = GLANCEABLE_HUB,
+ to = AOD,
+ transitionState = STARTED,
+ value = 0f,
+ ownerName = ownerName,
+ )
+ )
+
+ progress.emit(0.4f)
+ assertThat(currentStep)
+ .isEqualTo(
+ TransitionStep(
+ from = GLANCEABLE_HUB,
+ to = AOD,
+ transitionState = RUNNING,
+ value = 0.4f,
+ ownerName = ownerName,
+ )
+ )
+
+ var numToDrop = allSteps.size
+ // 2. Click power button to wake
+ powerInteractor.setAwakeForTest()
+ fakeCommunalSceneRepository.currentScene.value = CommunalScenes.Communal
+
+ // Starts a reversed transition Communal->Blank (currentScene=Communal)
+ sceneTransitions.value =
+ ObservableTransitionState.Transition(
+ fromScene = CommunalScenes.Communal,
+ toScene = CommunalScenes.Blank,
+ currentScene = fakeCommunalSceneRepository.currentScene,
+ progress = progress,
+ isInitiatedByUserInput = true,
+ isUserInputOngoing = flowOf(false),
+ )
+
+ assertThat(allSteps.drop(numToDrop))
+ .containsExactly(
+ TransitionStep(
+ from = GLANCEABLE_HUB,
+ to = AOD,
+ value = 0.4f,
+ transitionState = CANCELED,
+ ownerName = ownerName,
+ ),
+ TransitionStep(
+ from = AOD,
+ to = GLANCEABLE_HUB,
+ value = 0.6f,
+ transitionState = STARTED,
+ ownerName = ownerName,
+ ),
+ )
+
+ // update progress to 0.8 as reversed ST progress is 0.2
+ progress.emit(0.2f)
+ assertThat(currentStep)
+ .isEqualTo(
+ TransitionStep(
+ from = AOD,
+ to = GLANCEABLE_HUB,
+ transitionState = RUNNING,
+ value = 0.8f,
+ ownerName = ownerName,
+ )
+ )
+ numToDrop = allSteps.size
+
+ // 3. Click power button starts Communal->Blank (Hub->AOD)
+ powerInteractor.setAsleepForTest()
+ fakeCommunalSceneRepository.currentScene.value = CommunalScenes.Blank
+
+ sceneTransitions.value =
+ ObservableTransitionState.Transition(
+ fromScene = CommunalScenes.Communal,
+ toScene = CommunalScenes.Blank,
+ currentScene = fakeCommunalSceneRepository.currentScene,
+ progress = progress,
+ isInitiatedByUserInput = true,
+ isUserInputOngoing = flowOf(false),
+ )
+ progress.emit(0.7f)
+ sceneTransitions.value = Idle(CommunalScenes.Blank)
+
+ assertThat(allSteps.drop(numToDrop))
+ .containsExactly(
+ TransitionStep(
+ from = AOD,
+ to = GLANCEABLE_HUB,
+ transitionState = FINISHED,
+ value = 1f,
+ ownerName = ownerName,
+ ),
+ TransitionStep(
+ from = GLANCEABLE_HUB,
+ to = AOD,
+ value = 0f,
+ transitionState = STARTED,
+ ownerName = ownerName,
+ ),
+ TransitionStep(
+ from = GLANCEABLE_HUB,
+ to = AOD,
+ value = 0.7f,
+ transitionState = RUNNING,
+ ownerName = ownerName,
+ ),
+ TransitionStep(
+ from = GLANCEABLE_HUB,
+ to = AOD,
+ value = 1f,
+ transitionState = FINISHED,
+ ownerName = ownerName,
+ ),
+ )
+ .inOrder()
+ }
+
/** Transition from blank to hub, then settle back in blank. */
@Test
fun transition_from_blank_end_in_blank() =
- testScope.runTest {
+ kosmos.runTest {
sceneTransitions.value = blankToHub
val currentStep by collectLastValue(keyguardTransitionRepository.transitions)
@@ -422,9 +761,8 @@
@Test
fun transition_to_occluded_with_changed_scene_respected_just_once() =
- testScope.runTest {
+ kosmos.runTest {
underTest.onSceneAboutToChange(CommunalScenes.Blank, OCCLUDED)
- runCurrent()
sceneTransitions.value = hubToBlank
val currentStep by collectLastValue(keyguardTransitionRepository.transitions)
@@ -467,7 +805,7 @@
@Test
fun transition_from_blank_interrupted() =
- testScope.runTest {
+ kosmos.runTest {
sceneTransitions.value = blankToHub
val currentStep by collectLastValue(keyguardTransitionRepository.transitions)
@@ -539,7 +877,7 @@
*/
@Test
fun transition_to_hub_duplicate_does_not_change_ktf() =
- testScope.runTest {
+ kosmos.runTest {
sceneTransitions.value =
ObservableTransitionState.Transition(
fromScene = CommunalScenes.Blank,
@@ -606,6 +944,93 @@
}
/**
+ * Blank -> Hub (currentScene = Hub) transition is interrupted by Blank -> Hub (currentScene =
+ * Blank). KTF state should be updated in this case.
+ */
+ @Test
+ fun transitionToHub_interruptedByReverseTransitionToBlank_changesKtf() =
+ kosmos.runTest {
+ fakeCommunalSceneRepository.currentScene.value = CommunalScenes.Communal
+ sceneTransitions.value =
+ ObservableTransitionState.Transition(
+ fromScene = CommunalScenes.Blank,
+ toScene = CommunalScenes.Communal,
+ currentScene = fakeCommunalSceneRepository.currentScene,
+ progress = progress,
+ isInitiatedByUserInput = false,
+ isUserInputOngoing = flowOf(false),
+ )
+
+ val currentStep by collectLastValue(keyguardTransitionRepository.transitions)
+ val allSteps by collectValues(keyguardTransitionRepository.transitions)
+
+ assertThat(currentStep)
+ .isEqualTo(
+ TransitionStep(
+ from = LOCKSCREEN,
+ to = GLANCEABLE_HUB,
+ transitionState = STARTED,
+ value = 0f,
+ ownerName = ownerName,
+ )
+ )
+ progress.emit(0.4f)
+
+ assertThat(currentStep)
+ .isEqualTo(
+ TransitionStep(
+ from = LOCKSCREEN,
+ to = GLANCEABLE_HUB,
+ transitionState = RUNNING,
+ value = 0.4f,
+ ownerName = ownerName,
+ )
+ )
+ val numToDrop = allSteps.size
+
+ // Starts a reversed transition Blank->Hub (currentScene=Blank)
+ fakeCommunalSceneRepository.currentScene.value = CommunalScenes.Blank
+ sceneTransitions.value =
+ ObservableTransitionState.Transition(
+ fromScene = CommunalScenes.Blank,
+ toScene = CommunalScenes.Communal,
+ currentScene = fakeCommunalSceneRepository.currentScene,
+ progress = progress,
+ isInitiatedByUserInput = false,
+ isUserInputOngoing = flowOf(false),
+ )
+
+ assertThat(allSteps.drop(numToDrop))
+ .containsExactly(
+ TransitionStep(
+ from = LOCKSCREEN,
+ to = GLANCEABLE_HUB,
+ transitionState = CANCELED,
+ value = 0.4f,
+ ownerName = ownerName,
+ ),
+ TransitionStep(
+ from = GLANCEABLE_HUB,
+ to = LOCKSCREEN,
+ transitionState = STARTED,
+ value = 0.6f,
+ ownerName = ownerName,
+ ),
+ )
+ sceneTransitions.value = Idle(CommunalScenes.Blank)
+ assertThat(currentStep)
+ .isEqualTo(
+ TransitionStep(
+ from = GLANCEABLE_HUB,
+ to = LOCKSCREEN,
+ transitionState = FINISHED,
+ value = 1f,
+ ownerName = ownerName,
+ )
+ )
+ }
+
+ /**
* STL: Hub -> Blank, then interrupt in KTF LS -> OCCLUDED, then STL still finishes in Blank.
* After a KTF transition is started (GLANCEABLE_HUB -> LOCKSCREEN) KTF immediately considers
* the active scene to be LOCKSCREEN. This means that all listeners for LOCKSCREEN are active
@@ -618,7 +1043,7 @@
*/
@Test
fun transition_to_blank_interrupted_by_ktf_transition_then_finish_in_blank() =
- testScope.runTest {
+ kosmos.runTest {
sceneTransitions.value = hubToBlank
val currentStep by collectLastValue(keyguardTransitionRepository.transitions)
@@ -705,7 +1130,7 @@
*/
@Test
fun transition_to_blank_interrupted_by_ktf_transition_then_finish_in_hub() =
- testScope.runTest {
+ kosmos.runTest {
sceneTransitions.value = hubToBlank
val currentStep by collectLastValue(keyguardTransitionRepository.transitions)
@@ -787,8 +1212,8 @@
/** Verifies that we correctly transition to GONE after keyguard goes away */
@Test
fun transition_to_blank_after_unlock_should_go_to_gone() =
- testScope.runTest {
- keyguardRepository.setKeyguardShowing(true)
+ kosmos.runTest {
+ fakeKeyguardRepository.setKeyguardShowing(true)
sceneTransitions.value = Idle(CommunalScenes.Communal)
val currentStep by collectLastValue(keyguardTransitionRepository.transitions)
@@ -805,16 +1230,14 @@
)
// Keyguard starts exiting after a while, then fully exits after some time.
- advanceTimeBy(1.seconds)
- keyguardRepository.setKeyguardGoingAway(true)
- advanceTimeBy(2.seconds)
- keyguardRepository.setKeyguardGoingAway(false)
- keyguardRepository.setKeyguardShowing(false)
- runCurrent()
+ fakeKeyguardRepository.setKeyguardGoingAway(true)
// We snap to the blank scene as a result of keyguard going away.
sceneTransitions.value = Idle(CommunalScenes.Blank)
+ fakeKeyguardRepository.setKeyguardGoingAway(false)
+ fakeKeyguardRepository.setKeyguardShowing(false)
+
assertThat(currentStep)
.isEqualTo(
TransitionStep(
@@ -835,7 +1258,7 @@
*/
@Test
fun transition_to_blank_after_ktf_started_another_transition() =
- testScope.runTest {
+ kosmos.runTest {
// Another transition has already started to the alternate bouncer.
keyguardTransitionRepository.startTransition(
TransitionInfo(
@@ -853,9 +1276,7 @@
val numToDrop = allSteps.size
sceneTransitions.value = hubToBlank
- runCurrent()
progress.emit(0.4f)
- runCurrent()
// We land on blank.
sceneTransitions.value = Idle(CommunalScenes.Blank)
@@ -908,4 +1329,27 @@
)
.inOrder()
}
+
+ private suspend fun Kosmos.transitionTo(
+ fromState: KeyguardState,
+ toState: KeyguardState,
+ testScheduler: TestCoroutineScheduler,
+ ) {
+ val uuid =
+ realKeyguardTransitionRepository.startTransition(
+ TransitionInfo(
+ ownerName = this.javaClass.simpleName,
+ from = fromState,
+ to = toState,
+ animator = null,
+ modeOnCanceled = TransitionModeOnCanceled.RESET,
+ )
+ )
+ testScheduler.runCurrent()
+ checkNotNull(uuid).let {
+ realKeyguardTransitionRepository.updateTransition(it, 0.5f, RUNNING)
+ realKeyguardTransitionRepository.updateTransition(it, 1f, FINISHED)
+ }
+ testScheduler.runCurrent()
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/WidgetTrampolineInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/WidgetTrampolineInteractorTest.kt
index d15ec2f..9d56e9c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/WidgetTrampolineInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/WidgetTrampolineInteractorTest.kt
@@ -19,11 +19,13 @@
import android.app.ActivityManager.RunningTaskInfo
import android.app.usage.UsageEvents
import android.content.pm.UserInfo
+import android.platform.test.flag.junit.FlagsParameterization
import android.service.dream.dreamManager
-import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.usagestats.data.repository.fakeUsageStatsRepository
+import com.android.systemui.flags.DisableSceneContainer
+import com.android.systemui.flags.andSceneContainer
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionState
@@ -54,22 +56,24 @@
import org.mockito.kotlin.never
import org.mockito.kotlin.times
import org.mockito.kotlin.verify
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
@SmallTest
-@RunWith(AndroidJUnit4::class)
-class WidgetTrampolineInteractorTest : SysuiTestCase() {
+@RunWith(ParameterizedAndroidJunit4::class)
+class WidgetTrampolineInteractorTest(flags: FlagsParameterization) : SysuiTestCase() {
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
- private val activityStarter = kosmos.activityStarter
- private val usageStatsRepository = kosmos.fakeUsageStatsRepository
- private val taskStackChangeListeners = kosmos.taskStackChangeListeners
- private val keyguardTransitionRepository = kosmos.fakeKeyguardTransitionRepository
- private val userTracker = kosmos.fakeUserTracker
- private val systemClock = kosmos.fakeSystemClock
+ private val activityStarter by lazy { kosmos.activityStarter }
+ private val usageStatsRepository by lazy { kosmos.fakeUsageStatsRepository }
+ private val taskStackChangeListeners by lazy { kosmos.taskStackChangeListeners }
+ private val keyguardTransitionRepository by lazy { kosmos.fakeKeyguardTransitionRepository }
+ private val userTracker by lazy { kosmos.fakeUserTracker }
+ private val systemClock by lazy { kosmos.fakeSystemClock }
- private val underTest = kosmos.widgetTrampolineInteractor
+ private val underTest by lazy { kosmos.widgetTrampolineInteractor }
@Before
fun setUp() {
@@ -77,6 +81,7 @@
systemClock.setCurrentTimeMillis(testScope.currentTime)
}
+ @DisableSceneContainer
@Test
fun testNewTaskStartsWhileOnHub_triggersUnlock() =
testScope.runTest {
@@ -90,6 +95,7 @@
verify(activityStarter).dismissKeyguardThenExecute(any(), anyOrNull(), any())
}
+ @DisableSceneContainer
@Test
fun testNewTaskStartsWhileOnHub_stopsDream() =
testScope.runTest {
@@ -111,6 +117,7 @@
}
}
+ @DisableSceneContainer
@Test
fun testNewTaskStartsAfterExitingHub_doesNotTriggerUnlock() =
testScope.runTest {
@@ -124,6 +131,7 @@
verify(activityStarter, never()).dismissKeyguardThenExecute(any(), anyOrNull(), any())
}
+ @DisableSceneContainer
@Test
fun testNewTaskStartsAfterTimeout_doesNotTriggerUnlock() =
testScope.runTest {
@@ -137,6 +145,7 @@
verify(activityStarter, never()).dismissKeyguardThenExecute(any(), anyOrNull(), any())
}
+ @DisableSceneContainer
@Test
fun testActivityResumedWhileOnHub_triggersUnlock() =
testScope.runTest {
@@ -150,6 +159,7 @@
verify(activityStarter).dismissKeyguardThenExecute(any(), anyOrNull(), any())
}
+ @DisableSceneContainer
@Test
fun testActivityResumedAfterExitingHub_doesNotTriggerUnlock() =
testScope.runTest {
@@ -164,6 +174,7 @@
verify(activityStarter, never()).dismissKeyguardThenExecute(any(), anyOrNull(), any())
}
+ @DisableSceneContainer
@Test
fun testActivityDestroyed_doesNotTriggerUnlock() =
testScope.runTest {
@@ -177,6 +188,7 @@
verify(activityStarter, never()).dismissKeyguardThenExecute(any(), anyOrNull(), any())
}
+ @DisableSceneContainer
@Test
fun testMultipleActivityEvents_triggersUnlockOnlyOnce() =
testScope.runTest {
@@ -236,7 +248,17 @@
runCurrent()
}
- private companion object {
- val MAIN_USER: UserInfo = UserInfo(0, "primary", UserInfo.FLAG_MAIN)
+ init {
+ mSetFlagsRule.setFlagsParameterization(flags)
+ }
+
+ companion object {
+ private val MAIN_USER: UserInfo = UserInfo(0, "primary", UserInfo.FLAG_MAIN)
+
+ @JvmStatic
+ @Parameters(name = "{0}")
+ fun getParams(): List<FlagsParameterization> {
+ return FlagsParameterization.allCombinationsOf().andSceneContainer()
+ }
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalTransitionAnimatorControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalTransitionAnimatorControllerTest.kt
index a4261b0..73211c3 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalTransitionAnimatorControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalTransitionAnimatorControllerTest.kt
@@ -23,9 +23,12 @@
import com.android.systemui.communal.domain.interactor.communalSceneInteractor
import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.flags.DisableSceneContainer
+import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.kosmos.testScope
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.testKosmos
-import com.google.common.truth.Truth
+import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.advanceTimeBy
import kotlinx.coroutines.test.runTest
import org.junit.Assert.assertFalse
@@ -67,6 +70,7 @@
}
}
+ @DisableSceneContainer
@Test
fun animationCancelled_launchingWidgetStateIsCleared() {
with(kosmos) {
@@ -75,7 +79,7 @@
val scene by collectLastValue(communalSceneInteractor.currentScene)
communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
- Truth.assertThat(scene).isEqualTo(CommunalScenes.Communal)
+ assertThat(scene).isEqualTo(CommunalScenes.Communal)
communalSceneInteractor.setIsLaunchingWidget(true)
assertTrue(launching!!)
@@ -94,6 +98,35 @@
}
}
+ @EnableSceneContainer
+ @Test
+ fun animationCancelled_sceneContainerEnabled_launchingWidgetStateIsCleared() {
+ with(kosmos) {
+ testScope.runTest {
+ val launching by collectLastValue(communalSceneInteractor.isLaunchingWidget)
+ val scene by collectLastValue(communalSceneInteractor.currentScene)
+
+ communalSceneInteractor.changeScene(Scenes.Communal, "test")
+ assertThat(scene).isEqualTo(Scenes.Communal)
+ communalSceneInteractor.setIsLaunchingWidget(true)
+ assertTrue(launching!!)
+
+ underTest.onIntentStarted(willAnimate = true)
+ assertTrue(launching!!)
+ verify(controller).onIntentStarted(willAnimate = true)
+
+ underTest.onTransitionAnimationStart(isExpandingFullyAbove = true)
+ assertTrue(launching!!)
+ verify(controller).onTransitionAnimationStart(isExpandingFullyAbove = true)
+
+ underTest.onTransitionAnimationCancelled(newKeyguardOccludedState = true)
+ assertFalse(launching!!)
+ verify(controller).onTransitionAnimationCancelled(newKeyguardOccludedState = true)
+ }
+ }
+ }
+
+ @DisableSceneContainer
@Test
fun animationComplete_launchingWidgetStateIsClearedAndSceneIsChanged() {
with(kosmos) {
@@ -102,7 +135,7 @@
val scene by collectLastValue(communalSceneInteractor.currentScene)
communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
- Truth.assertThat(scene).isEqualTo(CommunalScenes.Communal)
+ assertThat(scene).isEqualTo(CommunalScenes.Communal)
communalSceneInteractor.setIsLaunchingWidget(true)
assertTrue(launching!!)
@@ -118,7 +151,38 @@
underTest.onTransitionAnimationEnd(isExpandingFullyAbove = true)
assertFalse(launching!!)
- Truth.assertThat(scene).isEqualTo(CommunalScenes.Blank)
+ assertThat(scene).isEqualTo(CommunalScenes.Blank)
+ verify(controller).onTransitionAnimationEnd(isExpandingFullyAbove = true)
+ }
+ }
+ }
+
+ @EnableSceneContainer
+ @Test
+ fun animationComplete_sceneContainerEnabled_launchingWidgetStateIsClearedAndSceneIsChanged() {
+ with(kosmos) {
+ testScope.runTest {
+ val launching by collectLastValue(communalSceneInteractor.isLaunchingWidget)
+ val scene by collectLastValue(communalSceneInteractor.currentScene)
+
+ communalSceneInteractor.changeScene(Scenes.Communal, "test")
+ assertThat(scene).isEqualTo(Scenes.Communal)
+ communalSceneInteractor.setIsLaunchingWidget(true)
+ assertTrue(launching!!)
+
+ underTest.onIntentStarted(willAnimate = true)
+ assertTrue(launching!!)
+ verify(controller).onIntentStarted(willAnimate = true)
+
+ underTest.onTransitionAnimationStart(isExpandingFullyAbove = true)
+ assertTrue(launching!!)
+ verify(controller).onTransitionAnimationStart(isExpandingFullyAbove = true)
+
+ testScope.advanceTimeBy(ActivityTransitionAnimator.TIMINGS.totalDuration)
+
+ underTest.onTransitionAnimationEnd(isExpandingFullyAbove = true)
+ assertFalse(launching!!)
+ assertThat(scene).isEqualTo(Scenes.Lockscreen)
verify(controller).onTransitionAnimationEnd(isExpandingFullyAbove = true)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractorTest.kt
index 89d3060..f849635 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractorTest.kt
@@ -97,9 +97,8 @@
kosmos.keyguardBouncerRepository.setAlternateVisible(false)
kosmos.sceneInteractor.changeScene(Scenes.Lockscreen, "reason")
- } else {
- underTest = kosmos.deviceEntryHapticsInteractor
}
+ underTest = kosmos.deviceEntryHapticsInteractor
}
@DisableSceneContainer
@@ -208,7 +207,7 @@
@EnableSceneContainer
@Test
- fun playSuccessHaptic_onDeviceEntryFromUdfps_sceneContainer() =
+ fun playSuccessHaptic_onDeviceEntryFromUdfps() =
testScope.runTest {
kosmos.configureKeyguardBypass(isBypassAvailable = false)
underTest = kosmos.deviceEntryHapticsInteractor
@@ -222,7 +221,7 @@
@EnableSceneContainer
@Test
- fun playSuccessHaptic_onDeviceEntryFromSfps_sceneContainer() =
+ fun playSuccessHaptic_onDeviceEntryFromSfps() =
testScope.runTest {
kosmos.configureKeyguardBypass(isBypassAvailable = false)
underTest = kosmos.deviceEntryHapticsInteractor
@@ -242,7 +241,7 @@
@EnableSceneContainer
@Test
- fun playSuccessHaptic_onDeviceEntryFromFaceAuth_sceneContainer() =
+ fun playSuccessHaptic_onDeviceEntryFromFaceAuth() =
testScope.runTest {
enrollFace()
kosmos.configureKeyguardBypass(isBypassAvailable = true)
@@ -256,7 +255,7 @@
@OptIn(ExperimentalCoroutinesApi::class)
@EnableSceneContainer
@Test
- fun skipSuccessHaptic_onFaceAuthSuccess_whenBypassDisabled_sceneContainer() =
+ fun skipSuccessHaptic_onFaceAuthSuccess_whenBypassDisabled() =
testScope.runTest {
underTest = kosmos.deviceEntryHapticsInteractor
val playSuccessHaptic by collectLastValue(underTest.playSuccessHapticOnDeviceEntry)
@@ -272,7 +271,7 @@
@EnableSceneContainer
@Test
- fun skipSuccessHaptic_onDeviceEntryFromSfps_whenPowerDown_sceneContainer() =
+ fun skipSuccessHaptic_onDeviceEntryFromSfps_whenPowerDown() =
testScope.runTest {
kosmos.configureKeyguardBypass(isBypassAvailable = false)
underTest = kosmos.deviceEntryHapticsInteractor
@@ -293,7 +292,7 @@
@EnableSceneContainer
@Test
- fun skipSuccessHaptic_onDeviceEntryFromSfps_whenPowerButtonRecentlyPressed_sceneContainer() =
+ fun skipSuccessHaptic_onDeviceEntryFromSfps_whenPowerButtonRecentlyPressed() =
testScope.runTest {
kosmos.configureKeyguardBypass(isBypassAvailable = false)
underTest = kosmos.deviceEntryHapticsInteractor
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/OccludingAppDeviceEntryInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/OccludingAppDeviceEntryInteractorTest.kt
index f14ab53..07fa2b8 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/OccludingAppDeviceEntryInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/OccludingAppDeviceEntryInteractorTest.kt
@@ -43,6 +43,10 @@
import com.android.systemui.plugins.activityStarter
import com.android.systemui.power.data.repository.fakePowerRepository
import com.android.systemui.res.R
+import com.android.systemui.scene.data.repository.Idle
+import com.android.systemui.scene.data.repository.setSceneTransition
+import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.whenever
@@ -329,6 +333,10 @@
bouncerRepository.setPrimaryShow(!isOnOccludingApp)
bouncerRepository.setAlternateVisible(!isOnOccludingApp)
+ if (isOnCommunal) {
+ kosmos.sceneInteractor.changeScene(Scenes.Communal, "")
+ kosmos.setSceneTransition(Idle(Scenes.Communal))
+ }
kosmos.fakeCommunalSceneRepository.setTransitionState(
flowOf(
ObservableTransitionState.Idle(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayRegistrantTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayRegistrantTest.kt
index 1e937b4..3c2a9af 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayRegistrantTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayRegistrantTest.kt
@@ -23,11 +23,11 @@
import android.content.pm.ServiceInfo
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
+import android.service.dreams.Flags
import android.service.dreams.IDreamManager
import android.testing.TestableLooper
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.communal.domain.interactor.communalSettingsInteractor
import com.android.systemui.communal.domain.interactor.setCommunalV2ConfigEnabled
@@ -123,7 +123,7 @@
/** Verify overlay registered when enabled in manifest. */
@Test
- @DisableFlags(Flags.FLAG_GLANCEABLE_HUB_V2)
+ @DisableFlags(Flags.FLAG_DREAMS_V2)
fun testRegisteredWhenEnabledWithManifest() {
serviceInfo.enabled = true
start()
@@ -131,10 +131,10 @@
verify(dreamManager).registerDreamOverlayService(componentName)
}
- /** Verify overlay registered for mobile hub with flag. */
+ /** Verify overlay registered for dreams v2 with flag. */
@Test
- @EnableFlags(Flags.FLAG_GLANCEABLE_HUB_V2)
- fun testRegisteredForMobileHub() {
+ @EnableFlags(Flags.FLAG_DREAMS_V2)
+ fun testRegisteredForDreamsV2() {
kosmos.setCommunalV2ConfigEnabled(true)
start()
@@ -143,12 +143,12 @@
}
/**
- * Make sure dream overlay not registered when not in manifest and not hub mode on mobile is not
+ * Make sure dream overlay not registered when not in manifest and not dreams on mobile is not
* enabled.
*/
@Test
- @DisableFlags(Flags.FLAG_GLANCEABLE_HUB_V2)
- fun testDisabledForMobileWithoutMobileHub() {
+ @DisableFlags(Flags.FLAG_DREAMS_V2)
+ fun testDisabledForMobileWithoutDreamsV2() {
start()
verify(packageManager, never())
@@ -162,7 +162,7 @@
/** Ensure service unregistered when component is disabled at runtime. */
@Test
- @EnableFlags(Flags.FLAG_GLANCEABLE_HUB_V2)
+ @EnableFlags(Flags.FLAG_DREAMS_V2)
fun testUnregisteredWhenComponentDisabled() {
kosmos.setCommunalV2ConfigEnabled(true)
start()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/slider/SliderStateTrackerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/slider/SliderStateTrackerTest.kt
index 8060f7b..7ae436b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/slider/SliderStateTrackerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/slider/SliderStateTrackerTest.kt
@@ -21,6 +21,7 @@
import com.android.systemui.SysuiTestCase
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.advanceTimeBy
import kotlinx.coroutines.test.runTest
@@ -32,7 +33,9 @@
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyNoMoreInteractions
import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.eq
+@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
class SliderStateTrackerTest : SysuiTestCase() {
@@ -98,6 +101,54 @@
assertThat(mSliderStateTracker.isWaiting).isTrue()
}
+ @Test
+ fun onProgressChangeByProgram_onIdle_insideSliderRange_movesToArrowMovedOnce() = runTest {
+ initTracker(CoroutineScope(UnconfinedTestDispatcher(testScheduler)))
+
+ // GIVEN a progress change by the program at the middle of the slider
+ val progress = 0.5f
+ sliderEventProducer.sendEvent(
+ SliderEvent(SliderEventType.PROGRESS_CHANGE_BY_PROGRAM, progress)
+ )
+
+ // THEN the tracker moves to the ARROW_HANDLE_MOVED_ONCE state and the state listener is
+ // called accordingly
+ assertThat(mSliderStateTracker.currentState).isEqualTo(SliderState.ARROW_HANDLE_MOVED_ONCE)
+ verify(sliderStateListener).onSelectAndArrow(eq(progress))
+ }
+
+ @Test
+ fun onProgressChangeByProgram_onIdle_onUpperBookend_executesOnUpperBookendAndResets() =
+ runTest {
+ initTracker(CoroutineScope(UnconfinedTestDispatcher(testScheduler)))
+
+ // GIVEN a progress change by the program at the middle of the slider
+ val progress = 1f
+ sliderEventProducer.sendEvent(
+ SliderEvent(SliderEventType.PROGRESS_CHANGE_BY_PROGRAM, progress)
+ )
+
+ // THEN the tracker executes on the upper bookend and resets the state
+ assertThat(mSliderStateTracker.currentState).isEqualTo(SliderState.IDLE)
+ verify(sliderStateListener).onUpperBookend()
+ }
+
+ @Test
+ fun onProgressChangeByProgram_onIdle_onLowerBookend_executesOnLowerBookendAndResets() =
+ runTest {
+ initTracker(CoroutineScope(UnconfinedTestDispatcher(testScheduler)))
+
+ // GIVEN a progress change by the program at the middle of the slider
+ val progress = 0f
+ sliderEventProducer.sendEvent(
+ SliderEvent(SliderEventType.PROGRESS_CHANGE_BY_PROGRAM, progress)
+ )
+
+ // THEN the tracker executes on the upper bookend and resets the state
+ assertThat(mSliderStateTracker.currentState).isEqualTo(SliderState.IDLE)
+ verify(sliderStateListener).onLowerBookend()
+ }
+
// Tests on the WAIT state
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewControllerTest.java
index 925ace2..dc035ff 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewControllerTest.java
@@ -17,7 +17,6 @@
package com.android.systemui.keyguard;
-import static com.android.systemui.flags.Flags.KEYGUARD_TALKBACK_FIX;
import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_BATTERY;
import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_BIOMETRIC_MESSAGE;
import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_DISCLOSURE;
@@ -43,7 +42,6 @@
import com.android.keyguard.logging.KeyguardLogger;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.flags.FakeFeatureFlags;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.phone.KeyguardIndicationTextView;
import com.android.systemui.util.concurrency.DelayableExecutor;
@@ -83,10 +81,8 @@
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
when(mView.getTextColors()).thenReturn(ColorStateList.valueOf(Color.WHITE));
- FakeFeatureFlags flags = new FakeFeatureFlags();
- flags.set(KEYGUARD_TALKBACK_FIX, true);
mController = new KeyguardIndicationRotateTextViewController(mView, mExecutor,
- mStatusBarStateController, mLogger, flags);
+ mStatusBarStateController, mLogger);
mController.onViewAttached();
verify(mStatusBarStateController).addCallback(mStatusBarStateListenerCaptor.capture());
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt
index 3f1cadc5..4357b7d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt
@@ -22,6 +22,7 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.defaultDeviceState
import com.android.systemui.deviceStateManager
+import com.android.systemui.flags.DisableSceneContainer
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.shared.system.smartspace.ILauncherUnlockAnimationController
import com.android.systemui.statusbar.NotificationShadeWindowController
@@ -54,6 +55,7 @@
@RunWith(AndroidJUnit4::class)
@RunWithLooper
@SmallTest
+@DisableSceneContainer // Class is unused in flexi.
class KeyguardUnlockAnimationControllerTest : SysuiTestCase() {
private lateinit var keyguardUnlockAnimationController: KeyguardUnlockAnimationController
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceHapticViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceHapticViewModelTest.kt
index ac3f4258..044e71589 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceHapticViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceHapticViewModelTest.kt
@@ -136,9 +136,9 @@
}
private fun toggleQuickAffordance(on: Boolean) {
- underTest.onQuickAffordanceLongPress()
+ underTest.onQuickAffordanceLongPress(isActivated = false)
underTest.updateActivatedHistory(!on)
- underTest.onQuickAffordanceLongPress()
+ underTest.onQuickAffordanceLongPress(isActivated = true)
underTest.updateActivatedHistory(on)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
index f15568b..34520bb 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
@@ -45,9 +45,8 @@
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
-import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
@@ -73,9 +72,8 @@
@Mock private lateinit var userTracker: UserTracker
@Mock private lateinit var lockPatternUtils: LockPatternUtils
@Captor private lateinit var updateCallbackCaptor: ArgumentCaptor<KeyguardUpdateMonitorCallback>
- private val mainDispatcher = StandardTestDispatcher()
- private val testDispatcher = StandardTestDispatcher()
- private val testScope = TestScope(testDispatcher)
+ private val dispatcher = UnconfinedTestDispatcher()
+ private val testScope = TestScope(dispatcher)
private lateinit var systemClock: FakeSystemClock
private lateinit var facePropertyRepository: FakeFacePropertyRepository
@@ -94,7 +92,7 @@
dozeTransitionListener,
authController,
dreamOverlayCallbackController,
- mainDispatcher,
+ dispatcher,
testScope.backgroundScope,
systemClock,
facePropertyRepository,
@@ -172,7 +170,6 @@
var latest: Boolean? = null
val job = underTest.isKeyguardShowing.onEach { latest = it }.launchIn(this)
- runCurrent()
assertThat(latest).isFalse()
assertThat(underTest.isKeyguardShowing()).isFalse()
@@ -181,13 +178,11 @@
whenever(keyguardStateController.isShowing).thenReturn(true)
captor.value.onKeyguardShowingChanged()
- runCurrent()
assertThat(latest).isTrue()
assertThat(underTest.isKeyguardShowing()).isTrue()
whenever(keyguardStateController.isShowing).thenReturn(false)
captor.value.onKeyguardShowingChanged()
- runCurrent()
assertThat(latest).isFalse()
assertThat(underTest.isKeyguardShowing()).isFalse()
@@ -201,7 +196,6 @@
var latest: Boolean? = null
val job = underTest.isKeyguardOccluded.onEach { latest = it }.launchIn(this)
- runCurrent()
assertThat(latest).isFalse()
val captor = argumentCaptor<KeyguardStateController.Callback>()
@@ -209,12 +203,10 @@
whenever(keyguardStateController.isOccluded).thenReturn(true)
captor.value.onKeyguardShowingChanged()
- runCurrent()
assertThat(latest).isTrue()
whenever(keyguardStateController.isOccluded).thenReturn(false)
captor.value.onKeyguardShowingChanged()
- runCurrent()
assertThat(latest).isFalse()
job.cancel()
@@ -226,7 +218,6 @@
whenever(keyguardStateController.isUnlocked).thenReturn(false)
val isKeyguardUnlocked by collectLastValue(underTest.isKeyguardDismissible)
- runCurrent()
assertThat(isKeyguardUnlocked).isFalse()
val captor = argumentCaptor<KeyguardStateController.Callback>()
@@ -234,12 +225,10 @@
whenever(keyguardStateController.isUnlocked).thenReturn(true)
captor.value.onUnlockedChanged()
- runCurrent()
assertThat(isKeyguardUnlocked).isTrue()
whenever(keyguardStateController.isUnlocked).thenReturn(false)
captor.value.onKeyguardShowingChanged()
- runCurrent()
assertThat(isKeyguardUnlocked).isFalse()
}
@@ -277,12 +266,11 @@
fun isKeyguardGoingAway() =
testScope.runTest {
val isGoingAway by collectLastValue(underTest.isKeyguardGoingAway)
- assertThat(isGoingAway).isFalse()
- underTest.isKeyguardGoingAway.value = true
+ underTest.isKeyguardGoingAway.tryEmit(true)
assertThat(isGoingAway).isTrue()
- underTest.isKeyguardGoingAway.value = false
+ underTest.isKeyguardGoingAway.tryEmit(false)
assertThat(isGoingAway).isFalse()
}
@@ -306,7 +294,6 @@
var latest: Boolean? = null
val job = underTest.isDreamingWithOverlay.onEach { latest = it }.launchIn(this)
- runCurrent()
assertThat(latest).isFalse()
val listener =
@@ -315,11 +302,9 @@
}
listener.onStartDream()
- runCurrent()
assertThat(latest).isTrue()
listener.onWakeUp()
- runCurrent()
assertThat(latest).isFalse()
job.cancel()
@@ -335,7 +320,6 @@
val values = mutableListOf<DozeTransitionModel>()
val job = underTest.dozeTransitionModel.onEach(values::add).launchIn(this)
- runCurrent()
val listener =
withArgCaptor<DozeTransitionCallback> {
verify(dozeTransitionListener).addCallback(capture())
@@ -344,26 +328,20 @@
// These don't have to reflect real transitions from the DozeMachine. Only that the
// transitions are properly emitted
listener.onDozeTransition(DozeMachine.State.INITIALIZED, DozeMachine.State.DOZE)
- runCurrent()
listener.onDozeTransition(DozeMachine.State.DOZE, DozeMachine.State.DOZE_AOD)
- runCurrent()
listener.onDozeTransition(DozeMachine.State.DOZE_AOD_DOCKED, DozeMachine.State.FINISH)
- runCurrent()
listener.onDozeTransition(
DozeMachine.State.DOZE_REQUEST_PULSE,
DozeMachine.State.DOZE_PULSING,
)
- runCurrent()
listener.onDozeTransition(
DozeMachine.State.DOZE_SUSPEND_TRIGGERS,
DozeMachine.State.DOZE_PULSE_DONE,
)
- runCurrent()
listener.onDozeTransition(
DozeMachine.State.DOZE_AOD_PAUSING,
DozeMachine.State.DOZE_AOD_PAUSED,
)
- runCurrent()
assertThat(values)
.isEqualTo(
@@ -392,7 +370,6 @@
)
job.cancel()
- runCurrent()
verify(dozeTransitionListener).removeCallback(listener)
}
@@ -402,7 +379,6 @@
val values = mutableListOf<Point?>()
val job = underTest.fingerprintSensorLocation.onEach(values::add).launchIn(this)
- runCurrent()
val captor = argumentCaptor<AuthController.Callback>()
verify(authController).addCallback(captor.capture())
@@ -416,7 +392,6 @@
.onEach {
whenever(authController.fingerprintSensorLocation).thenReturn(it)
captor.value.onFingerprintLocationChanged()
- runCurrent()
}
.also { dispatchedSensorLocations ->
assertThat(values).isEqualTo(listOf(null) + dispatchedSensorLocations)
@@ -430,7 +405,6 @@
testScope.runTest {
val values = mutableListOf<Point?>()
val job = underTest.faceSensorLocation.onEach(values::add).launchIn(this)
- runCurrent()
// An initial, null value should be initially emitted so that flows combined with this
// one
@@ -439,10 +413,7 @@
assertThat(values).isEqualTo(listOf(null))
listOf(Point(500, 500), Point(0, 0), null, Point(250, 250))
- .onEach {
- facePropertyRepository.setSensorLocation(it)
- runCurrent()
- }
+ .onEach { facePropertyRepository.setSensorLocation(it) }
.also { dispatchedSensorLocations ->
assertThat(values).isEqualTo(listOf(null) + dispatchedSensorLocations)
}
@@ -456,8 +427,6 @@
val values = mutableListOf<BiometricUnlockSource?>()
val job = underTest.biometricUnlockState.onEach { values.add(it.source) }.launchIn(this)
- runCurrent()
-
// An initial, null value should be initially emitted so that flows combined with this
// one
// emit values immediately. The biometric unlock source is expected to be nullable, so
@@ -476,7 +445,6 @@
BiometricUnlockMode.NONE,
BiometricUnlockSource.Companion.fromBiometricSourceType(biometricSourceType),
)
- runCurrent()
}
assertThat(values)
@@ -496,7 +464,7 @@
@Test
fun isEncryptedOrLockdown() =
- TestScope(mainDispatcher).runTest {
+ testScope.runTest {
whenever(userTracker.userId).thenReturn(0)
whenever(keyguardUpdateMonitor.isEncryptedOrLockdown(0)).thenReturn(true)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt
index 3a8603d..291d8c4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt
@@ -64,7 +64,9 @@
kosmos.fakeKeyguardRepository,
context,
powerRepository,
- mock()
+ mock(),
+ mock(),
+ kosmos.testScope.backgroundScope,
)
}
@@ -89,41 +91,35 @@
// default reveal despite a biometric unlock.
fakeKeyguardRepository.setBiometricUnlockState(
BiometricUnlockMode.WAKE_AND_UNLOCK,
- BiometricUnlockSource.FINGERPRINT_SENSOR
+ BiometricUnlockSource.FINGERPRINT_SENSOR,
)
runCurrent()
- values.assertEffectsMatchPredicates(
- { it == DEFAULT_REVEAL_EFFECT },
- )
+ values.assertEffectsMatchPredicates({ it == DEFAULT_REVEAL_EFFECT })
// We got a source but still have no sensor locations, so should be sticking with
// the default effect.
fakeKeyguardRepository.setBiometricUnlockState(
BiometricUnlockMode.WAKE_AND_UNLOCK,
- BiometricUnlockSource.FINGERPRINT_SENSOR
+ BiometricUnlockSource.FINGERPRINT_SENSOR,
)
runCurrent()
- values.assertEffectsMatchPredicates(
- { it == DEFAULT_REVEAL_EFFECT },
- )
+ values.assertEffectsMatchPredicates({ it == DEFAULT_REVEAL_EFFECT })
// We got a location for the face sensor, but we unlocked with fingerprint.
val faceLocation = Point(250, 0)
fakeKeyguardRepository.setFaceSensorLocation(faceLocation)
runCurrent()
- values.assertEffectsMatchPredicates(
- { it == DEFAULT_REVEAL_EFFECT },
- )
+ values.assertEffectsMatchPredicates({ it == DEFAULT_REVEAL_EFFECT })
// Now we have fingerprint sensor locations, and wake and unlock via fingerprint.
val fingerprintLocation = Point(500, 500)
fakeKeyguardRepository.setFingerprintSensorLocation(fingerprintLocation)
fakeKeyguardRepository.setBiometricUnlockState(
BiometricUnlockMode.WAKE_AND_UNLOCK_PULSING,
- BiometricUnlockSource.FINGERPRINT_SENSOR
+ BiometricUnlockSource.FINGERPRINT_SENSOR,
)
// We should now have switched to the circle reveal, at the fingerprint location.
@@ -141,18 +137,18 @@
val valuesPrevSize = values.size
fakeKeyguardRepository.setBiometricUnlockState(
BiometricUnlockMode.WAKE_AND_UNLOCK_PULSING,
- BiometricUnlockSource.FINGERPRINT_SENSOR
+ BiometricUnlockSource.FINGERPRINT_SENSOR,
)
fakeKeyguardRepository.setBiometricUnlockState(
BiometricUnlockMode.WAKE_AND_UNLOCK_FROM_DREAM,
- BiometricUnlockSource.FINGERPRINT_SENSOR
+ BiometricUnlockSource.FINGERPRINT_SENSOR,
)
assertEquals(valuesPrevSize, values.size)
// Non-biometric unlock, we should return to the default reveal.
fakeKeyguardRepository.setBiometricUnlockState(
BiometricUnlockMode.NONE,
- BiometricUnlockSource.FINGERPRINT_SENSOR
+ BiometricUnlockSource.FINGERPRINT_SENSOR,
)
runCurrent()
@@ -170,7 +166,7 @@
// CircleReveal.
fakeKeyguardRepository.setBiometricUnlockState(
BiometricUnlockMode.WAKE_AND_UNLOCK,
- BiometricUnlockSource.FACE_SENSOR
+ BiometricUnlockSource.FACE_SENSOR,
)
runCurrent()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorTest.kt
index 5bcf73b..a2ea83ca 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorTest.kt
@@ -328,7 +328,7 @@
assertThat(showIndicatorForDeviceEntry).containsExactly(false, true)
}
- private fun updatePrimaryBouncer(
+ private fun TestScope.updatePrimaryBouncer(
isShowing: Boolean,
isAnimatingAway: Boolean,
fpsDetectionRunning: Boolean,
@@ -339,14 +339,7 @@
val primaryStartDisappearAnimation = if (isAnimatingAway) Runnable {} else null
bouncerRepository.setPrimaryStartDisappearAnimation(primaryStartDisappearAnimation)
- whenever(keyguardUpdateMonitor.isFingerprintDetectionRunning)
- .thenReturn(fpsDetectionRunning)
- whenever(keyguardUpdateMonitor.isUnlockingWithFingerprintAllowed)
- .thenReturn(isUnlockingWithFpAllowed)
- mContext.orCreateTestableResources.addOverride(
- R.bool.config_show_sidefps_hint_on_bouncer,
- true,
- )
+ updateBouncer(isShowing && !isAnimatingAway, fpsDetectionRunning, isUnlockingWithFpAllowed)
}
private fun TestScope.updateBouncer(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractorTest.kt
index 8fdbf9b..ac42531 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractorTest.kt
@@ -45,6 +45,7 @@
import com.android.systemui.communal.domain.interactor.communalSceneInteractor
import com.android.systemui.communal.domain.interactor.setCommunalV2ConfigEnabled
import com.android.systemui.communal.shared.model.CommunalScenes
+import com.android.systemui.flags.DisableSceneContainer
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepositorySpy
@@ -129,6 +130,7 @@
@Test
@EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
+ @DisableSceneContainer // KeyguardDismissTransitionInteractor is not used in flexi.
fun transitionToGone_keyguardOccludedThenAltBouncer_authed_wmStateRefactor() =
testScope.runTest {
transitionRepository.sendTransitionSteps(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorTest.kt
index 7a5e2bb..a968933 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorTest.kt
@@ -47,6 +47,7 @@
import com.android.systemui.communal.domain.interactor.setCommunalV2Available
import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.flags.DisableSceneContainer
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepositorySpy
@@ -160,6 +161,7 @@
@Test
@EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
+ @DisableSceneContainer
fun testTransitionToGone_onWakeUp_ifPowerButtonGestureDetected_fromAod_dismissibleKeyguard() =
testScope.runTest {
kosmos.fakeKeyguardRepository.setKeyguardDismissible(true)
@@ -174,6 +176,7 @@
@Test
@EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
+ @DisableSceneContainer
fun testTransitionToGone_onWakeUp_ifPowerButtonGestureDetected_fromGone() =
testScope.runTest {
val isGone by
@@ -216,6 +219,7 @@
@Test
@EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
+ @DisableSceneContainer
fun testTransitionToOccluded_onWakeUp_ifPowerButtonGestureDetectedAfterFinishedInAod_fromGone() =
testScope.runTest {
val isGone by
@@ -296,6 +300,7 @@
@Test
@EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
+ @DisableSceneContainer
fun testWakeAndUnlock_transitionsToGone_onlyAfterDismissCallPostWakeup() =
testScope.runTest {
kosmos.fakeKeyguardRepository.setBiometricUnlockState(
@@ -322,7 +327,12 @@
powerInteractor.setAwakeForTest()
runCurrent()
+ // Waking up from wake and unlock should not start any transitions, we'll wait for the
+ // dismiss call.
+ assertThat(transitionRepository).noTransitionsStarted()
+
underTest.dismissAod()
+ advanceTimeBy(100) // account for debouncing
assertThat(transitionRepository)
.startedTransition(from = KeyguardState.AOD, to = KeyguardState.GONE)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorTest.kt
index 049d964..afffc15 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorTest.kt
@@ -35,6 +35,7 @@
import com.android.systemui.communal.domain.interactor.setCommunalAvailable
import com.android.systemui.communal.domain.interactor.setCommunalV2ConfigEnabled
import com.android.systemui.communal.shared.model.CommunalScenes
+import com.android.systemui.flags.DisableSceneContainer
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepositorySpy
import com.android.systemui.keyguard.data.repository.keyguardOcclusionRepository
@@ -135,19 +136,6 @@
}
@Test
- @DisableFlags(FLAG_KEYGUARD_WM_STATE_REFACTOR)
- fun testTransitionToGone_onWakeup_whenGoingAway() =
- kosmos.runTest {
- keyguardInteractor.setIsKeyguardGoingAway(true)
-
- powerInteractor.setAwakeForTest()
- testScope.advanceTimeBy(60L)
-
- assertThat(transitionRepository)
- .startedTransition(from = KeyguardState.DOZING, to = KeyguardState.GONE)
- }
-
- @Test
fun testTransitionToDreaming() =
kosmos.runTest {
// Ensure dozing is off
@@ -165,6 +153,7 @@
@Test
@EnableFlags(FLAG_KEYGUARD_WM_STATE_REFACTOR)
+ @DisableSceneContainer // Specifically testing keyguard_wm_state_refactor enabled w/o flexi.
fun testTransitionToLockscreen_onWake_canDream_ktfRefactor() =
kosmos.runTest {
setCommunalAvailable(true)
@@ -314,6 +303,7 @@
@Test
@EnableFlags(FLAG_KEYGUARD_WM_STATE_REFACTOR)
+ @DisableSceneContainer
fun testTransitionToGone_onWakeUp_ifPowerButtonGestureDetected_fromAod_dismissableKeyguard() =
kosmos.runTest {
fakeKeyguardRepository.setKeyguardDismissible(true)
@@ -327,6 +317,7 @@
@Test
@EnableFlags(FLAG_KEYGUARD_WM_STATE_REFACTOR)
+ @DisableSceneContainer
fun testTransitionToGone_onWakeUp_ifPowerButtonGestureDetected_fromGone() =
kosmos.runTest {
val isGone by
@@ -365,6 +356,7 @@
@Test
@EnableFlags(FLAG_KEYGUARD_WM_STATE_REFACTOR)
@Suppress("ktlint:standard:max-line-length")
+ @DisableSceneContainer
fun testTransitionToOccluded_onWakeUp_ifPowerButtonGestureDetectedAfterFinishedInAod_fromGone() =
kosmos.runTest {
val isGone by
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractorTest.kt
index 57b1299..bb4eda4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractorTest.kt
@@ -23,6 +23,7 @@
import com.android.internal.widget.LockPatternUtils
import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
+import com.android.systemui.flags.DisableSceneContainer
import com.android.systemui.keyguard.data.repository.fakeBiometricSettingsRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepositorySpy
@@ -50,7 +51,7 @@
this.keyguardTransitionRepository = fakeKeyguardTransitionRepositorySpy
}
private val testScope = kosmos.testScope
- private val underTest = kosmos.fromGoneTransitionInteractor
+ private val underTest by lazy { kosmos.fromGoneTransitionInteractor }
private val keyguardTransitionRepository = kosmos.fakeKeyguardTransitionRepositorySpy
@Before
@@ -105,6 +106,7 @@
@Test
@EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
+ @DisableSceneContainer
fun testTransitionsToLockscreen_ifFinishedInGone_wmRefactor() =
testScope.runTest {
keyguardTransitionRepository.sendTransitionSteps(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorTest.kt
index 63960ab..3c95f77 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorTest.kt
@@ -25,6 +25,7 @@
import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectValues
+import com.android.systemui.flags.DisableSceneContainer
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
@@ -58,7 +59,7 @@
}
private val testScope = kosmos.testScope
- private val underTest = kosmos.fromLockscreenTransitionInteractor
+ private val underTest by lazy { kosmos.fromLockscreenTransitionInteractor }
private lateinit var transitionRepository: FakeKeyguardTransitionRepository
private val shadeRepository = kosmos.fakeShadeRepository
private val keyguardRepository = kosmos.fakeKeyguardRepository
@@ -173,6 +174,7 @@
@Test
@EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
+ @DisableSceneContainer
fun testTransitionsToGone_whenDismissFlingWhileDismissable_flagEnabled() =
testScope.runTest {
underTest.start()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorTest.kt
index d9e7622..87a52c3 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorTest.kt
@@ -30,6 +30,7 @@
import com.android.systemui.communal.domain.interactor.setCommunalV2ConfigEnabled
import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.coroutines.collectValues
+import com.android.systemui.flags.DisableSceneContainer
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepositorySpy
import com.android.systemui.keyguard.data.repository.keyguardOcclusionRepository
import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository
@@ -76,7 +77,7 @@
testKosmos().apply {
this.keyguardTransitionRepository = fakeKeyguardTransitionRepositorySpy
}
- val underTest = kosmos.fromPrimaryBouncerTransitionInteractor
+ val underTest by lazy { kosmos.fromPrimaryBouncerTransitionInteractor }
val testScope = kosmos.testScope
val transitionRepository = kosmos.fakeKeyguardTransitionRepositorySpy
val bouncerRepository = kosmos.fakeKeyguardBouncerRepository
@@ -164,6 +165,7 @@
@Test
@EnableFlags(FLAG_KEYGUARD_WM_STATE_REFACTOR)
+ @DisableSceneContainer // PRIMARY_BOUNCER is not used in flexi.
fun testReturnToLockscreen_whenBouncerHides() =
testScope.runTest {
underTest.start()
@@ -188,6 +190,7 @@
@Test
@EnableFlags(FLAG_KEYGUARD_WM_STATE_REFACTOR)
+ @DisableSceneContainer
fun testReturnToGlanceableHub_whenBouncerHides_ifIdleOnCommunal() =
testScope.runTest {
underTest.start()
@@ -215,6 +218,7 @@
@Test
@EnableFlags(FLAG_KEYGUARD_WM_STATE_REFACTOR)
+ @DisableSceneContainer
fun testTransitionToOccluded_bouncerHide_occludingActivityOnTop() =
testScope.runTest {
underTest.start()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/InWindowLauncherUnlockAnimationInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/InWindowLauncherUnlockAnimationInteractorTest.kt
index 6241865e..cebb224 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/InWindowLauncherUnlockAnimationInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/InWindowLauncherUnlockAnimationInteractorTest.kt
@@ -20,6 +20,7 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectValues
+import com.android.systemui.flags.DisableSceneContainer
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
import com.android.systemui.keyguard.data.repository.inWindowLauncherUnlockAnimationRepository
@@ -46,14 +47,16 @@
@RunWith(AndroidJUnit4::class)
class InWindowLauncherUnlockAnimationInteractorTest : SysuiTestCase() {
private val kosmos = testKosmos()
- private val underTest =
+ private val underTest by lazy {
InWindowLauncherUnlockAnimationInteractor(
kosmos.inWindowLauncherUnlockAnimationRepository,
kosmos.applicationCoroutineScope,
+ kosmos.applicationCoroutineScope,
kosmos.keyguardTransitionInteractor,
{ kosmos.keyguardSurfaceBehindRepository },
kosmos.activityManagerWrapper,
)
+ }
private val testScope = kosmos.testScope
private lateinit var transitionRepository: FakeKeyguardTransitionRepository
@Mock private lateinit var activityManagerWrapper: ActivityManagerWrapper
@@ -71,31 +74,27 @@
}
@Test
+ @DisableSceneContainer
fun testTransitioningToGoneWithInWindowAnimation_trueIfTopActivityIsLauncher_andTransitioningToGone() =
testScope.runTest {
+ transitionToGoneThenLockscreen(withLauncherUnderneath = true)
+
val values by collectValues(underTest.transitioningToGoneWithInWindowAnimation)
runCurrent()
assertEquals(
listOf(
- false, // False by default.
+ false // False by default.
),
- values
+ values,
)
- // Put launcher on top
- kosmos.inWindowLauncherUnlockAnimationRepository.setLauncherActivityClass(
- launcherClassName
- )
- activityManagerWrapper.mockTopActivityClassName(launcherClassName)
- runCurrent()
-
// Should still be false since we're not transitioning to GONE.
assertEquals(
listOf(
- false, // False by default.
+ false // False by default.
),
- values
+ values,
)
transitionRepository.sendTransitionStep(
@@ -112,7 +111,7 @@
false,
true, // -> GONE + launcher is behind
),
- values
+ values,
)
activityManagerWrapper.mockTopActivityClassName("not_launcher")
@@ -131,7 +130,7 @@
true, // Top activity should be sampled, if it changes midway it should not
// matter.
),
- values
+ values,
)
transitionRepository.sendTransitionStep(
@@ -149,35 +148,23 @@
true,
false, // False once we're not transitioning anymore.
),
- values
+ values,
)
}
@Test
fun testTransitioningToGoneWithInWindowAnimation_falseIfTopActivityIsLauncherPartwayThrough() =
testScope.runTest {
+ transitionToGoneThenLockscreen(withLauncherUnderneath = false)
+
val values by collectValues(underTest.transitioningToGoneWithInWindowAnimation)
runCurrent()
assertEquals(
listOf(
- false, // False by default.
+ false // False by default.
),
- values
- )
-
- // Put not launcher on top
- kosmos.inWindowLauncherUnlockAnimationRepository.setLauncherActivityClass(
- launcherClassName
- )
- activityManagerWrapper.mockTopActivityClassName("not_launcher")
- runCurrent()
-
- assertEquals(
- listOf(
- false,
- ),
- values
+ values,
)
transitionRepository.sendTransitionStep(
@@ -189,12 +176,7 @@
)
runCurrent()
- assertEquals(
- listOf(
- false,
- ),
- values
- )
+ assertEquals(listOf(false), values)
activityManagerWrapper.mockTopActivityClassName(launcherClassName)
transitionRepository.sendTransitionStep(
@@ -206,12 +188,7 @@
)
runCurrent()
- assertEquals(
- listOf(
- false,
- ),
- values
- )
+ assertEquals(listOf(false), values)
transitionRepository.sendTransitionStep(
TransitionStep(
@@ -222,39 +199,21 @@
)
runCurrent()
- assertEquals(
- listOf(
- false,
- ),
- values
- )
+ assertEquals(listOf(false), values)
}
@Test
fun testTransitioningToGoneWithInWindowAnimation_falseIfTopActivityIsLauncherWhileNotTransitioningToGone() =
testScope.runTest {
+ transitionToGoneThenLockscreen(withLauncherUnderneath = true)
val values by collectValues(underTest.transitioningToGoneWithInWindowAnimation)
runCurrent()
assertEquals(
listOf(
- false, // False by default.
+ false // False by default.
),
- values
- )
-
- // Put launcher on top
- kosmos.inWindowLauncherUnlockAnimationRepository.setLauncherActivityClass(
- launcherClassName
- )
- activityManagerWrapper.mockTopActivityClassName(launcherClassName)
- runCurrent()
-
- assertEquals(
- listOf(
- false,
- ),
- values
+ values,
)
transitionRepository.sendTransitionStep(
@@ -266,32 +225,25 @@
)
runCurrent()
- assertEquals(
- listOf(
- false,
- ),
- values
- )
+ assertEquals(listOf(false), values)
}
@Test
+ @DisableSceneContainer
fun testShouldStartInWindowAnimation_trueOnceSurfaceAvailable_falseWhenTransitionEnds() =
testScope.runTest {
+ transitionToGoneThenLockscreen(withLauncherUnderneath = true)
+
val values by collectValues(underTest.shouldStartInWindowAnimation)
runCurrent()
assertEquals(
listOf(
- false, // False by default.
+ false // False by default.
),
- values
+ values,
)
- // Put Launcher on top and begin transitioning to GONE.
- kosmos.inWindowLauncherUnlockAnimationRepository.setLauncherActivityClass(
- launcherClassName
- )
- activityManagerWrapper.mockTopActivityClassName(launcherClassName)
transitionRepository.sendTransitionStep(
TransitionStep(
transitionState = TransitionState.STARTED,
@@ -301,12 +253,7 @@
)
runCurrent()
- assertEquals(
- listOf(
- false,
- ),
- values
- )
+ assertEquals(listOf(false), values)
kosmos.keyguardSurfaceBehindRepository.setSurfaceRemoteAnimationTargetAvailable(true)
runCurrent()
@@ -316,7 +263,7 @@
false,
true, // The surface is now available, so we should start the animation.
),
- values
+ values,
)
transitionRepository.sendTransitionStep(
@@ -328,34 +275,24 @@
)
runCurrent()
- assertEquals(
- listOf(
- false,
- true,
- false,
- ),
- values
- )
+ assertEquals(listOf(false, true, false), values)
}
@Test
fun testShouldStartInWindowAnimation_neverTrueIfSurfaceNotAvailable() =
testScope.runTest {
+ transitionToGoneThenLockscreen(withLauncherUnderneath = true)
+
val values by collectValues(underTest.shouldStartInWindowAnimation)
runCurrent()
assertEquals(
listOf(
- false, // False by default.
+ false // False by default.
),
- values
+ values,
)
- // Put Launcher on top and begin transitioning to GONE.
- kosmos.inWindowLauncherUnlockAnimationRepository.setLauncherActivityClass(
- launcherClassName
- )
- activityManagerWrapper.mockTopActivityClassName(launcherClassName)
transitionRepository.sendTransitionStep(
TransitionStep(
transitionState = TransitionState.STARTED,
@@ -372,32 +309,23 @@
)
runCurrent()
- assertEquals(
- listOf(
- false,
- ),
- values
- )
+ assertEquals(listOf(false), values)
}
@Test
fun testShouldStartInWindowAnimation_falseIfSurfaceAvailable_afterTransitionInterrupted() =
testScope.runTest {
+ transitionToGoneThenLockscreen(withLauncherUnderneath = true)
val values by collectValues(underTest.shouldStartInWindowAnimation)
runCurrent()
assertEquals(
listOf(
- false, // False by default.
+ false // False by default.
),
- values
+ values,
)
- // Put Launcher on top and begin transitioning to GONE.
- kosmos.inWindowLauncherUnlockAnimationRepository.setLauncherActivityClass(
- launcherClassName
- )
- activityManagerWrapper.mockTopActivityClassName(launcherClassName)
transitionRepository.sendTransitionStep(
TransitionStep(
transitionState = TransitionState.STARTED,
@@ -422,11 +350,26 @@
kosmos.keyguardSurfaceBehindRepository.setSurfaceRemoteAnimationTargetAvailable(true)
runCurrent()
- assertEquals(
- listOf(
- false,
- ),
- values
- )
+ assertEquals(listOf(false), values)
}
+
+ /** Transitions to GONE from LOCKSCREEN after setting launcher underneath (or not). */
+ private suspend fun transitionToGoneThenLockscreen(withLauncherUnderneath: Boolean) {
+ transitionRepository.sendTransitionSteps(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.GONE,
+ testScope,
+ )
+
+ kosmos.inWindowLauncherUnlockAnimationRepository.setLauncherActivityClass(launcherClassName)
+ activityManagerWrapper.mockTopActivityClassName(
+ if (withLauncherUnderneath) launcherClassName else "not_launcher"
+ )
+
+ transitionRepository.sendTransitionSteps(
+ from = KeyguardState.GONE,
+ to = KeyguardState.LOCKSCREEN,
+ testScope,
+ )
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractorTest.kt
index f38d7c2..1b2f2ea 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractorTest.kt
@@ -27,17 +27,13 @@
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.DisableSceneContainer
import com.android.systemui.flags.EnableSceneContainer
-import com.android.systemui.keyguard.data.repository.fakeKeyguardClockRepository
import com.android.systemui.keyguard.data.repository.keyguardBlueprintRepository
import com.android.systemui.keyguard.ui.view.layout.blueprints.DefaultKeyguardBlueprint
import com.android.systemui.keyguard.ui.view.layout.blueprints.SplitShadeKeyguardBlueprint
import com.android.systemui.kosmos.testScope
-import com.android.systemui.plugins.clocks.ClockConfig
-import com.android.systemui.plugins.clocks.ClockController
import com.android.systemui.shade.data.repository.shadeRepository
import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.advanceUntilIdle
import kotlinx.coroutines.test.runCurrent
@@ -45,7 +41,6 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.Mock
import org.mockito.Mockito.reset
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
@@ -58,16 +53,12 @@
private val testScope = kosmos.testScope
private val underTest by lazy { kosmos.keyguardBlueprintInteractor }
private val keyguardBlueprintRepository by lazy { kosmos.keyguardBlueprintRepository }
- private val clockRepository by lazy { kosmos.fakeKeyguardClockRepository }
private val configurationRepository by lazy { kosmos.fakeConfigurationRepository }
private val fingerprintPropertyRepository by lazy { kosmos.fakeFingerprintPropertyRepository }
- @Mock private lateinit var clockController: ClockController
-
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
- whenever(clockController.config).thenReturn(ClockConfig("TEST", "Test", ""))
fingerprintPropertyRepository.setProperties(
sensorId = 1,
strength = SensorStrength.STRONG,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardClockInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardClockInteractorTest.kt
index a08c0de..19c1611 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardClockInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardClockInteractorTest.kt
@@ -120,7 +120,7 @@
val value by collectLastValue(underTest.clockSize)
kosmos.shadeRepository.setShadeLayoutWide(false)
val userMedia = MediaData().copy(active = true)
- kosmos.mediaFilterRepository.addSelectedUserMediaEntry(userMedia)
+ kosmos.mediaFilterRepository.addCurrentUserMediaEntry(userMedia)
assertThat(value).isEqualTo(ClockSize.SMALL)
}
@@ -132,7 +132,7 @@
val value by collectLastValue(underTest.clockSize)
val userMedia = MediaData().copy(active = true)
kosmos.shadeRepository.setShadeLayoutWide(true)
- kosmos.mediaFilterRepository.addSelectedUserMediaEntry(userMedia)
+ kosmos.mediaFilterRepository.addCurrentUserMediaEntry(userMedia)
kosmos.keyguardRepository.setIsDozing(false)
assertThat(value).isEqualTo(ClockSize.SMALL)
@@ -156,7 +156,7 @@
val value by collectLastValue(underTest.clockSize)
val userMedia = MediaData().copy(active = true)
kosmos.shadeRepository.setShadeLayoutWide(true)
- kosmos.mediaFilterRepository.addSelectedUserMediaEntry(userMedia)
+ kosmos.mediaFilterRepository.addCurrentUserMediaEntry(userMedia)
kosmos.keyguardRepository.setIsDozing(true)
assertThat(value).isEqualTo(ClockSize.LARGE)
@@ -170,7 +170,7 @@
val userMedia = MediaData().copy(active = true)
kosmos.fakeKeyguardClockRepository.setSelectedClockSize(ClockSizeSetting.SMALL)
kosmos.shadeRepository.setShadeLayoutWide(true)
- kosmos.mediaFilterRepository.addSelectedUserMediaEntry(userMedia)
+ kosmos.mediaFilterRepository.addCurrentUserMediaEntry(userMedia)
kosmos.keyguardRepository.setIsDozing(true)
assertThat(value).isEqualTo(ClockSize.SMALL)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
index c8aae5e..d2a0bd8 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
@@ -28,6 +28,7 @@
import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
+import com.android.systemui.flags.DisableSceneContainer
import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
@@ -330,6 +331,7 @@
}
@Test
+ @DisableSceneContainer
fun dismissAlpha_onGlanceableHub_doesNotEmitWhenShadeResets() =
testScope.runTest {
val dismissAlpha by collectValues(underTest.dismissAlpha)
@@ -452,6 +454,7 @@
}
@Test
+ @DisableSceneContainer
fun keyguardTranslationY_whenTransitioningToGoneAndShadeIsExpandingEmitsNonZero() =
testScope.runTest {
val keyguardTranslationY by collectLastValue(underTest.keyguardTranslationY)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractorTest.kt
index 5826665..fb0e262 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractorTest.kt
@@ -23,6 +23,7 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.back.domain.interactor.BackActionInteractor
+import com.android.systemui.flags.DisableSceneContainer
import com.android.systemui.media.controls.util.MediaSessionLegacyHelperWrapper
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.power.domain.interactor.PowerInteractor
@@ -219,6 +220,7 @@
}
@Test
+ @DisableSceneContainer
fun interceptMediaKey_keyguard_SBKVMdoesNotHandle_doesNotHandleMediaKey() {
val keyEvent = KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_VOLUME_UP)
whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
@@ -229,6 +231,7 @@
}
@Test
+ @DisableSceneContainer
fun interceptMediaKey_keyguard_handleMediaKey() {
val keyEvent = KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_VOLUME_UP)
whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
index 3c0d2f0..b4cb675 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
@@ -20,13 +20,14 @@
import android.app.admin.DevicePolicyManager
import android.os.UserHandle
import android.platform.test.annotations.EnableFlags
-import android.view.accessibility.AccessibilityManager
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.widget.LockPatternUtils
import com.android.keyguard.logging.KeyguardQuickAffordancesLogger
import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
+import com.android.systemui.accessibility.domain.interactor.AccessibilityInteractor
+import com.android.systemui.accessibility.domain.interactor.accessibilityInteractor
import com.android.systemui.animation.DialogTransitionAnimator
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
@@ -34,6 +35,7 @@
import com.android.systemui.coroutines.collectValues
import com.android.systemui.dock.DockManager
import com.android.systemui.dock.DockManagerFake
+import com.android.systemui.flags.DisableSceneContainer
import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.haptics.msdl.fakeMSDLPlayer
@@ -99,7 +101,7 @@
@Mock private lateinit var shadeInteractor: ShadeInteractor
@Mock private lateinit var logger: KeyguardQuickAffordancesLogger
@Mock private lateinit var metricsLogger: KeyguardQuickAffordancesMetricsLogger
- @Mock private lateinit var accessibilityManager: AccessibilityManager
+ @Mock private lateinit var accessibilityInteractor: AccessibilityInteractor
private lateinit var underTest: KeyguardQuickAffordanceInteractor
@@ -201,14 +203,14 @@
biometricSettingsRepository = biometricSettingsRepository,
backgroundDispatcher = kosmos.testDispatcher,
appContext = context,
- accessibilityManager = accessibilityManager,
+ accessibilityInteractor = accessibilityInteractor,
sceneInteractor = { kosmos.sceneInteractor },
msdlPlayer = msdlPlayer,
)
kosmos.keyguardQuickAffordanceInteractor = underTest
whenever(shadeInteractor.anyExpansion).thenReturn(MutableStateFlow(0f))
- whenever(accessibilityManager.isEnabled()).thenReturn(false)
+ whenever(accessibilityInteractor.isEnabledFiltered).thenReturn(MutableStateFlow(false))
}
@Test
@@ -328,6 +330,7 @@
}
@Test
+ @DisableSceneContainer
fun quickAffordance_bottomStartAffordanceHiddenWhenLockscreenIsNotShowing() =
testScope.runTest {
repository.setKeyguardShowing(false)
@@ -679,7 +682,7 @@
@Test
fun useLongPress_withA11yEnabled_isFalse() =
testScope.runTest {
- whenever(accessibilityManager.isEnabled()).thenReturn(true)
+ whenever(accessibilityInteractor.isEnabledFiltered).thenReturn(MutableStateFlow(true))
val useLongPress by collectLastValue(underTest.useLongPress())
assertThat(useLongPress).isFalse()
}
@@ -687,7 +690,7 @@
@Test
fun useLongPress_withA11yDisabled_isFalse() =
testScope.runTest {
- whenever(accessibilityManager.isEnabled()).thenReturn(false)
+ whenever(accessibilityInteractor.isEnabledFiltered).thenReturn(MutableStateFlow(false))
val useLongPress by collectLastValue(underTest.useLongPress())
assertThat(useLongPress).isTrue()
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardShowWhileAwakeInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardShowWhileAwakeInteractorTest.kt
index cfe3826..8e54243 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardShowWhileAwakeInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardShowWhileAwakeInteractorTest.kt
@@ -21,6 +21,7 @@
import com.android.internal.widget.lockPatternUtils
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectValues
+import com.android.systemui.flags.DisableSceneContainer
import com.android.systemui.keyguard.data.repository.biometricSettingsRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
@@ -40,6 +41,7 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
@kotlinx.coroutines.ExperimentalCoroutinesApi
+@DisableSceneContainer
class KeyguardShowWhileAwakeInteractorTest : SysuiTestCase() {
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardStateCallbackInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardStateCallbackInteractorTest.kt
index 89a53f5..c21cf20 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardStateCallbackInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardStateCallbackInteractorTest.kt
@@ -24,6 +24,7 @@
import com.android.keyguard.trustManager
import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
+import com.android.systemui.flags.DisableSceneContainer
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
import com.android.systemui.keyguard.dismissCallbackRegistry
import com.android.systemui.keyguard.shared.model.KeyguardState
@@ -50,6 +51,7 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
@EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
+@DisableSceneContainer
class KeyguardStateCallbackInteractorTest : SysuiTestCase() {
private val kosmos = testKosmos()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
index e400755..37c2545 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
@@ -567,9 +567,6 @@
@DisableSceneContainer
fun goneToDreaming() =
testScope.runTest {
- // Setup - Move past initial delay with [KeyguardInteractor#isAbleToDream]
- advanceTimeBy(600L)
-
// GIVEN a prior transition has run to GONE
runTransitionAndSetWakefulness(KeyguardState.LOCKSCREEN, KeyguardState.GONE)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractorTest.kt
index 561eee7..e035243 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractorTest.kt
@@ -32,6 +32,7 @@
import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectValues
+import com.android.systemui.flags.DisableSceneContainer
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.BiometricUnlockMode
@@ -59,6 +60,7 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
+@DisableSceneContainer
class KeyguardWakeDirectlyToGoneInteractorTest : SysuiTestCase() {
private var lastRegisteredBroadcastReceiver: BroadcastReceiver? = null
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt
index 96db014..5ee1059 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt
@@ -150,15 +150,64 @@
}
@Test
- fun supportsAmbientMode() =
+ fun maxAlpha_doesNotSupportAmbientMode() =
+ kosmos.testScope.runTest {
+ val maxAlpha by collectLastValue(underTest.maxAlpha)
+ underTest.setWallpaperSupportsAmbientMode(false)
+
+ assertThat(maxAlpha).isEqualTo(1f)
+ }
+
+ @Test
+ fun maxAlpha_supportsAmbientModeWithDarkScrim() =
kosmos.testScope.runTest {
val maxAlpha by collectLastValue(underTest.maxAlpha)
assertThat(maxAlpha).isEqualTo(1f)
underTest.setWallpaperSupportsAmbientMode(true)
- assertThat(maxAlpha).isLessThan(1f)
+ fakeLightRevealScrimRepository.useDarkWallpaperScrim.value = true
- underTest.setWallpaperSupportsAmbientMode(false)
+ assertThat(maxAlpha).isEqualTo(0.64f)
+ }
+
+ @Test
+ fun maxAlpha_supportsAmbientModeWithLightScrim() =
+ kosmos.testScope.runTest {
+ val maxAlpha by collectLastValue(underTest.maxAlpha)
+ assertThat(maxAlpha).isEqualTo(1f)
+
+ underTest.setWallpaperSupportsAmbientMode(true)
+ fakeLightRevealScrimRepository.useDarkWallpaperScrim.value = false
+
+ assertThat(maxAlpha).isEqualTo(0.4f)
+ }
+
+ @Test
+ fun maxAlpha_supportsAmbientModeDuringTransitionIsOpaque() =
+ kosmos.testScope.runTest {
+ val maxAlpha by collectLastValue(underTest.maxAlpha)
+
+ underTest.setWallpaperSupportsAmbientMode(true)
+ fakeLightRevealScrimRepository.useDarkWallpaperScrim.value = true
+
+ fakeKeyguardTransitionRepository.sendTransitionSteps(
+ listOf(
+ TransitionStep(
+ from = KeyguardState.GONE,
+ to = KeyguardState.AOD,
+ value = 0f,
+ transitionState = TransitionState.STARTED,
+ ),
+ TransitionStep(
+ from = KeyguardState.GONE,
+ to = KeyguardState.AOD,
+ value = 0.4f,
+ transitionState = TransitionState.RUNNING,
+ ),
+ ),
+ kosmos.testScope,
+ )
+
assertThat(maxAlpha).isEqualTo(1f)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt
index 4f35114..7093ddb 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt
@@ -45,6 +45,7 @@
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.shared.model.Overlays
import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.statusbar.policy.data.repository.fakeDeviceProvisioningRepository
import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
@@ -340,14 +341,14 @@
@Test
@EnableSceneContainer
- fun surfaceBehindVisibility_whileSceneContainerNotVisible_alwaysTrue() =
+ fun surfaceBehindVisibility_whileDeviceNotProvisioned_alwaysTrue() =
testScope.runTest {
val isSurfaceBehindVisible by collectLastValue(underTest.value.surfaceBehindVisibility)
val currentScene by collectLastValue(kosmos.sceneInteractor.currentScene)
assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
assertThat(isSurfaceBehindVisible).isFalse()
- kosmos.sceneInteractor.setVisible(false, "test")
+ kosmos.fakeDeviceProvisioningRepository.setDeviceProvisioned(false)
runCurrent()
assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlowTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlowTest.kt
index eec5771..0bbdfbb 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlowTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlowTest.kt
@@ -20,22 +20,22 @@
import androidx.test.filters.SmallTest
import com.android.app.animation.Interpolators.EMPHASIZED_ACCELERATE
import com.android.systemui.SysuiTestCase
-import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.coroutines.collectValues
+import com.android.systemui.flags.DisableSceneContainer
+import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.Edge
import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
-import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
+import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
-import com.android.systemui.kosmos.testScope
-import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.kosmos.collectLastValue
+import com.android.systemui.kosmos.collectValues
+import com.android.systemui.kosmos.runCurrent
+import com.android.systemui.kosmos.runTest
import com.android.systemui.shade.shadeTestUtil
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlin.time.Duration.Companion.milliseconds
-import kotlinx.coroutines.test.runCurrent
-import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -43,34 +43,34 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
class KeyguardTransitionAnimationFlowTest : SysuiTestCase() {
- val kosmos = testKosmos()
- val testScope = kosmos.testScope
- val animationFlow = kosmos.keyguardTransitionAnimationFlow
- val repository = kosmos.fakeKeyguardTransitionRepository
- val shadeTestUtil by lazy { kosmos.shadeTestUtil }
+ private val kosmos = testKosmos()
+ private lateinit var animationFlow: KeyguardTransitionAnimationFlow
+ private lateinit var repository: FakeKeyguardTransitionRepository
private lateinit var underTest: KeyguardTransitionAnimationFlow.FlowBuilder
@Before
fun setUp() {
+ animationFlow = kosmos.keyguardTransitionAnimationFlow
+ repository = kosmos.fakeKeyguardTransitionRepository
underTest =
animationFlow
.setup(
duration = 1000.milliseconds,
- edge = Edge.create(from = Scenes.Gone, to = DREAMING),
+ edge = Edge.create(from = LOCKSCREEN, to = DREAMING),
)
- .setupWithoutSceneContainer(edge = Edge.create(from = GONE, to = DREAMING))
+ .setupWithoutSceneContainer(edge = Edge.create(from = LOCKSCREEN, to = DREAMING))
}
@Test(expected = IllegalArgumentException::class)
fun zeroDurationThrowsException() =
- testScope.runTest {
+ kosmos.runTest {
val flow = underTest.sharedFlow(duration = 0.milliseconds, onStep = { it })
}
@Test(expected = IllegalArgumentException::class)
fun startTimePlusDurationGreaterThanTransitionDurationThrowsException() =
- testScope.runTest {
+ kosmos.runTest {
val flow =
underTest.sharedFlow(
startTime = 300.milliseconds,
@@ -81,7 +81,7 @@
@Test
fun onFinishRunsWhenSpecified() =
- testScope.runTest {
+ kosmos.runTest {
val flow =
underTest.sharedFlow(
duration = 100.milliseconds,
@@ -96,8 +96,9 @@
}
@Test
+ @DisableSceneContainer // CANCELED steps are filtered out when the scene framework is enabled.
fun onCancelRunsWhenSpecified() =
- testScope.runTest {
+ kosmos.runTest {
val flow =
underTest.sharedFlow(
duration = 100.milliseconds,
@@ -113,7 +114,7 @@
@Test
fun onStepReturnsNullEmitsNothing() =
- testScope.runTest {
+ kosmos.runTest {
val flow = underTest.sharedFlow(duration = 100.milliseconds, onStep = { null })
var animationValues = collectLastValue(flow)
runCurrent()
@@ -124,7 +125,7 @@
@Test
fun usesStartTime() =
- testScope.runTest {
+ kosmos.runTest {
val flow =
underTest.sharedFlow(
startTime = 500.milliseconds,
@@ -152,7 +153,7 @@
@Test
fun usesInterpolator() =
- testScope.runTest {
+ kosmos.runTest {
val flow =
underTest.sharedFlow(
duration = 1000.milliseconds,
@@ -176,7 +177,7 @@
@Test
fun usesOnStepToDoubleValue() =
- testScope.runTest {
+ kosmos.runTest {
val flow = underTest.sharedFlow(duration = 1000.milliseconds, onStep = { it * 2 })
val animationValues by collectLastValue(flow)
runCurrent()
@@ -195,7 +196,7 @@
@Test
fun usesOnStepToDoubleValueWithState() =
- testScope.runTest {
+ kosmos.runTest {
val flow =
underTest.sharedFlowWithState(duration = 1000.milliseconds, onStep = { it * 2 })
val animationValues by collectLastValue(flow)
@@ -205,7 +206,7 @@
assertThat(animationValues)
.isEqualTo(
StateToValue(
- from = GONE,
+ from = LOCKSCREEN,
to = DREAMING,
transitionState = TransitionState.STARTED,
value = 0f,
@@ -215,7 +216,7 @@
assertThat(animationValues)
.isEqualTo(
StateToValue(
- from = GONE,
+ from = LOCKSCREEN,
to = DREAMING,
transitionState = TransitionState.RUNNING,
value = 0.6f,
@@ -225,7 +226,7 @@
assertThat(animationValues)
.isEqualTo(
StateToValue(
- from = GONE,
+ from = LOCKSCREEN,
to = DREAMING,
transitionState = TransitionState.RUNNING,
value = 1.2f,
@@ -235,7 +236,7 @@
assertThat(animationValues)
.isEqualTo(
StateToValue(
- from = GONE,
+ from = LOCKSCREEN,
to = DREAMING,
transitionState = TransitionState.RUNNING,
value = 1.6f,
@@ -245,7 +246,7 @@
assertThat(animationValues)
.isEqualTo(
StateToValue(
- from = GONE,
+ from = LOCKSCREEN,
to = DREAMING,
transitionState = TransitionState.RUNNING,
value = 2f,
@@ -255,7 +256,7 @@
assertThat(animationValues)
.isEqualTo(
StateToValue(
- from = GONE,
+ from = LOCKSCREEN,
to = DREAMING,
transitionState = TransitionState.FINISHED,
value = null,
@@ -265,7 +266,7 @@
@Test
fun sameFloatValueWithTheSameTransitionStateDoesNotEmitTwice() =
- testScope.runTest {
+ kosmos.runTest {
val flow = underTest.sharedFlow(duration = 1000.milliseconds, onStep = { it })
val values by collectValues(flow)
runCurrent()
@@ -279,7 +280,7 @@
@Test
fun sameFloatValueWithADifferentTransitionStateDoesEmitTwice() =
- testScope.runTest {
+ kosmos.runTest {
val flow = underTest.sharedFlow(duration = 1000.milliseconds, onStep = { it })
val values by collectValues(flow)
runCurrent()
@@ -294,7 +295,7 @@
@Test
fun sharedFlowWithShadeExpanded() =
- testScope.runTest {
+ kosmos.runTest {
val flow =
underTest.sharedFlowWithShade(
duration = 1000.milliseconds,
@@ -313,7 +314,7 @@
@Test
fun sharedFlowWithShadeNotExpanded() =
- testScope.runTest {
+ kosmos.runTest {
val flow =
underTest.sharedFlowWithShade(
duration = 1000.milliseconds,
@@ -331,8 +332,9 @@
}
@Test
+ @DisableSceneContainer // CANCELED steps are filtered out when the scene framework is enabled.
fun sharedFlowWithShadeExpanded_onCancelRunsWhenSpecified() =
- testScope.runTest {
+ kosmos.runTest {
val flow =
underTest.sharedFlowWithShade(
duration = 100.milliseconds,
@@ -348,8 +350,9 @@
}
@Test
+ @DisableSceneContainer // CANCELED steps are filtered out when the scene framework is enabled.
fun sharedFlowWithShadeNotExpanded_onCancelRunsWhenSpecified() =
- testScope.runTest {
+ kosmos.runTest {
val flow =
underTest.sharedFlowWithShade(
duration = 100.milliseconds,
@@ -373,7 +376,7 @@
state: TransitionState = TransitionState.RUNNING,
): TransitionStep {
return TransitionStep(
- from = GONE,
+ from = LOCKSCREEN,
to = DREAMING,
value = value,
transitionState = state,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinderTest.kt
index 3800608..521c9ff 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinderTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinderTest.kt
@@ -25,6 +25,7 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.bouncer.domain.interactor.givenCanShowAlternateBouncer
+import com.android.systemui.flags.DisableSceneContainer
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.kosmos.testScope
@@ -47,6 +48,7 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
+@DisableSceneContainer
@TestableLooper.RunWithLooper(setAsMainLooper = true)
class AlternateBouncerViewBinderTest : SysuiTestCase() {
private val kosmos = testKosmos()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSectionTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSectionTest.kt
index ddaae1a..c0b2a17 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSectionTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSectionTest.kt
@@ -17,7 +17,6 @@
package com.android.systemui.keyguard.ui.view.layout.sections
-import android.content.pm.PackageManager
import android.content.res.Resources
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
@@ -28,7 +27,8 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.customization.R as customR
+import com.android.systemui.customization.clocks.R as clocksR
+import com.android.systemui.flags.DisableSceneContainer
import com.android.systemui.keyguard.domain.interactor.keyguardBlueprintInteractor
import com.android.systemui.keyguard.domain.interactor.keyguardClockInteractor
import com.android.systemui.keyguard.domain.interactor.keyguardSmartspaceInteractor
@@ -39,6 +39,7 @@
import com.android.systemui.keyguard.ui.viewmodel.keyguardSmartspaceViewModel
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
+import com.android.systemui.plugins.clocks.ClockViewIds
import com.android.systemui.res.R
import com.android.systemui.shade.LargeScreenHeaderHelper
import com.android.systemui.shade.data.repository.shadeRepository
@@ -46,75 +47,62 @@
import com.android.systemui.statusbar.policy.fakeConfigurationController
import com.android.systemui.statusbar.ui.fakeSystemBarUtilsProxy
import com.android.systemui.testKosmos
-import com.android.systemui.util.mockito.eq
-import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.advanceUntilIdle
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.ArgumentMatchers.anyInt
-import org.mockito.ArgumentMatchers.anyString
import org.mockito.MockitoAnnotations
import org.mockito.kotlin.mock
@RunWith(AndroidJUnit4::class)
@SmallTest
+@DisableSceneContainer
class ClockSectionTest : SysuiTestCase() {
private lateinit var underTest: ClockSection
+ private val resources: Resources
+ get() = context.resources
+
+ private val SMART_SPACE_DATE_WEATHER_HEIGHT: Int
+ get() = resources.getDimensionPixelSize(clocksR.dimen.date_weather_view_height)
+
+ private val ENHANCED_SMART_SPACE_HEIGHT: Int
+ get() = resources.getDimensionPixelSize(clocksR.dimen.enhanced_smartspace_height)
+
private val KEYGUARD_SMARTSPACE_TOP_OFFSET: Int
- get() =
- kosmos.fakeSystemBarUtilsProxy.getStatusBarHeight() / 2 +
- context.resources.getDimensionPixelSize(customR.dimen.keyguard_smartspace_top_offset)
+ get() {
+ return kosmos.fakeSystemBarUtilsProxy.getStatusBarHeight() / 2 +
+ context.resources.getDimensionPixelSize(
+ clocksR.dimen.keyguard_smartspace_top_offset
+ )
+ }
private val LARGE_CLOCK_TOP_WITHOUT_SMARTSPACE: Int
- get() =
- kosmos.fakeSystemBarUtilsProxy.getStatusBarHeight() +
- context.resources.getDimensionPixelSize(customR.dimen.small_clock_padding_top) +
- context.resources.getDimensionPixelSize(customR.dimen.keyguard_smartspace_top_offset)
+ get() {
+ return kosmos.fakeSystemBarUtilsProxy.getStatusBarHeight() +
+ context.resources.getDimensionPixelSize(clocksR.dimen.small_clock_padding_top) +
+ context.resources.getDimensionPixelSize(
+ clocksR.dimen.keyguard_smartspace_top_offset
+ )
+ }
- private val LARGE_CLOCK_TOP
- get() =
- LARGE_CLOCK_TOP_WITHOUT_SMARTSPACE +
+ private val LARGE_CLOCK_TOP: Int
+ get() {
+ return LARGE_CLOCK_TOP_WITHOUT_SMARTSPACE +
SMART_SPACE_DATE_WEATHER_HEIGHT +
ENHANCED_SMART_SPACE_HEIGHT
+ }
- private val CLOCK_FADE_TRANSLATION_Y
- get() = context.resources.getDimensionPixelSize(customR.dimen.small_clock_height)
+ private val CLOCK_FADE_TRANSLATION_Y: Int
+ get() = resources.getDimensionPixelSize(clocksR.dimen.small_clock_height)
- private var DIMENSION_BY_IDENTIFIER: List<Pair<String, Int>> = listOf()
private lateinit var kosmos: Kosmos
@Before
fun setup() {
- DIMENSION_BY_IDENTIFIER =
- listOf(
- "date_weather_view_height" to SMART_SPACE_DATE_WEATHER_HEIGHT,
- "enhanced_smartspace_height" to ENHANCED_SMART_SPACE_HEIGHT,
- )
-
MockitoAnnotations.initMocks(this)
- val remoteResources =
- mock<Resources>().apply {
- whenever(getIdentifier(anyString(), eq("dimen"), anyString())).then { invocation ->
- val name = invocation.arguments[0] as String
- val index = DIMENSION_BY_IDENTIFIER.indexOfFirst { (key, _) -> key == name }
- // increment index so that the not-found sentinel value lines up w/ what is
- // returned by getIdentifier when a resource is not found
- index + 1
- }
- whenever(getDimensionPixelSize(anyInt())).then { invocation ->
- val id = invocation.arguments[0] as Int
- DIMENSION_BY_IDENTIFIER[id - 1].second
- }
- }
- mContext.setMockPackageManager(
- mock<PackageManager>().apply {
- whenever(getResourcesForApplication(anyString())).thenReturn(remoteResources)
- }
- )
kosmos = testKosmos()
with(kosmos) {
@@ -145,8 +133,8 @@
val cs = ConstraintSet()
underTest.applyDefaultConstraints(cs)
- assertLargeClockTop(cs, LARGE_CLOCK_TOP)
- assertSmallClockTop(cs)
+ cs.assertLargeClock(topMargin = LARGE_CLOCK_TOP)
+ cs.assertSmallClock()
}
@Test
@@ -162,8 +150,10 @@
val cs = ConstraintSet()
underTest.applyDefaultConstraints(cs)
- assertLargeClockTop(cs, KEYGUARD_SMARTSPACE_TOP_OFFSET + ENHANCED_SMART_SPACE_HEIGHT)
- assertSmallClockTop(cs)
+ cs.assertLargeClock(
+ topMargin = KEYGUARD_SMARTSPACE_TOP_OFFSET + ENHANCED_SMART_SPACE_HEIGHT
+ )
+ cs.assertSmallClock()
}
@Test
@@ -185,8 +175,8 @@
val cs = ConstraintSet()
underTest.applyDefaultConstraints(cs)
- assertLargeClockTop(cs, LARGE_CLOCK_TOP)
- assertSmallClockTop(cs)
+ cs.assertLargeClock(topMargin = LARGE_CLOCK_TOP)
+ cs.assertSmallClock()
}
}
@@ -209,111 +199,10 @@
val cs = ConstraintSet()
underTest.applyDefaultConstraints(cs)
- assertLargeClockTop(
- cs,
- KEYGUARD_SMARTSPACE_TOP_OFFSET + ENHANCED_SMART_SPACE_HEIGHT,
+ cs.assertLargeClock(
+ topMargin = KEYGUARD_SMARTSPACE_TOP_OFFSET + ENHANCED_SMART_SPACE_HEIGHT
)
- assertSmallClockTop(cs)
- }
- }
-
- @Test
- @DisableFlags(com.android.systemui.shared.Flags.FLAG_CLOCK_REACTIVE_SMARTSPACE_LAYOUT)
- fun testApplyDefaultConstraints_LargeClock_MissingSmartspace_SplitShade() =
- kosmos.testScope.runTest {
- with(kosmos) {
- DIMENSION_BY_IDENTIFIER = listOf() // Remove Smartspace from mock
- val isShadeLayoutWide by collectLastValue(shadeRepository.isShadeLayoutWide)
- val isLargeClockVisible by
- collectLastValue(keyguardClockViewModel.isLargeClockVisible)
-
- shadeRepository.setShadeLayoutWide(true)
- keyguardClockInteractor.setClockSize(ClockSize.LARGE)
- notificationsKeyguardInteractor.setNotificationsFullyHidden(true)
- keyguardSmartspaceInteractor.setBcSmartspaceVisibility(VISIBLE)
- fakeConfigurationController.notifyConfigurationChanged()
- advanceUntilIdle()
-
- val cs = ConstraintSet()
- underTest.applyDefaultConstraints(cs)
-
- assertLargeClockTop(cs, LARGE_CLOCK_TOP_WITHOUT_SMARTSPACE)
- assertSmallClockTop(cs)
- }
- }
-
- @Test
- @EnableFlags(com.android.systemui.shared.Flags.FLAG_CLOCK_REACTIVE_SMARTSPACE_LAYOUT)
- fun testApplyDefaultConstraints_LargeClock_MissingSmartspace_SplitShade_ReactiveSmartspace() =
- kosmos.testScope.runTest {
- with(kosmos) {
- DIMENSION_BY_IDENTIFIER = listOf() // Remove Smartspace from mock
- val isShadeLayoutWide by collectLastValue(shadeRepository.isShadeLayoutWide)
- val isLargeClockVisible by
- collectLastValue(keyguardClockViewModel.isLargeClockVisible)
-
- shadeRepository.setShadeLayoutWide(true)
- keyguardClockInteractor.setClockSize(ClockSize.LARGE)
- notificationsKeyguardInteractor.setNotificationsFullyHidden(true)
- keyguardSmartspaceInteractor.setBcSmartspaceVisibility(VISIBLE)
- fakeConfigurationController.notifyConfigurationChanged()
- advanceUntilIdle()
-
- val cs = ConstraintSet()
- underTest.applyDefaultConstraints(cs)
-
- assertLargeClockTop(cs, KEYGUARD_SMARTSPACE_TOP_OFFSET)
- assertSmallClockTop(cs)
- }
- }
-
- @Test
- @DisableFlags(com.android.systemui.shared.Flags.FLAG_CLOCK_REACTIVE_SMARTSPACE_LAYOUT)
- fun testApplyDefaultConstraints_LargeClock_MissingSmartspace_NonSplitShade() =
- kosmos.testScope.runTest {
- with(kosmos) {
- DIMENSION_BY_IDENTIFIER = listOf() // Remove Smartspace from mock
- val isShadeLayoutWide by collectLastValue(shadeRepository.isShadeLayoutWide)
- val isLargeClockVisible by
- collectLastValue(keyguardClockViewModel.isLargeClockVisible)
-
- shadeRepository.setShadeLayoutWide(false)
- keyguardClockInteractor.setClockSize(ClockSize.LARGE)
- notificationsKeyguardInteractor.setNotificationsFullyHidden(true)
- keyguardSmartspaceInteractor.setBcSmartspaceVisibility(VISIBLE)
- fakeConfigurationController.notifyConfigurationChanged()
- advanceUntilIdle()
-
- val cs = ConstraintSet()
- underTest.applyDefaultConstraints(cs)
-
- assertLargeClockTop(cs, LARGE_CLOCK_TOP_WITHOUT_SMARTSPACE)
- assertSmallClockTop(cs)
- }
- }
-
- @Test
- @EnableFlags(com.android.systemui.shared.Flags.FLAG_CLOCK_REACTIVE_SMARTSPACE_LAYOUT)
- fun testApplyDefaultConstraints_LargeClock_MissingSmartspace_NonSplitShade_ReactiveSmartspace() =
- kosmos.testScope.runTest {
- with(kosmos) {
- DIMENSION_BY_IDENTIFIER = listOf() // Remove Smartspace from mock
- val isShadeLayoutWide by collectLastValue(shadeRepository.isShadeLayoutWide)
- val isLargeClockVisible by
- collectLastValue(keyguardClockViewModel.isLargeClockVisible)
-
- shadeRepository.setShadeLayoutWide(false)
- keyguardClockInteractor.setClockSize(ClockSize.LARGE)
- notificationsKeyguardInteractor.setNotificationsFullyHidden(true)
- keyguardSmartspaceInteractor.setBcSmartspaceVisibility(VISIBLE)
- fakeConfigurationController.notifyConfigurationChanged()
- advanceUntilIdle()
-
- val cs = ConstraintSet()
- underTest.applyDefaultConstraints(cs)
-
- assertLargeClockTop(cs, KEYGUARD_SMARTSPACE_TOP_OFFSET)
- assertSmallClockTop(cs)
+ cs.assertSmallClock()
}
}
@@ -336,8 +225,8 @@
val cs = ConstraintSet()
underTest.applyDefaultConstraints(cs)
- assertLargeClockTop(cs, LARGE_CLOCK_TOP)
- assertSmallClockTop(cs)
+ cs.assertLargeClock(topMargin = LARGE_CLOCK_TOP)
+ cs.assertSmallClock()
}
}
@@ -360,11 +249,10 @@
val cs = ConstraintSet()
underTest.applyDefaultConstraints(cs)
- assertLargeClockTop(
- cs,
- KEYGUARD_SMARTSPACE_TOP_OFFSET + ENHANCED_SMART_SPACE_HEIGHT,
+ cs.assertLargeClock(
+ topMargin = KEYGUARD_SMARTSPACE_TOP_OFFSET + ENHANCED_SMART_SPACE_HEIGHT
)
- assertSmallClockTop(cs)
+ cs.assertSmallClock()
}
}
@@ -386,8 +274,9 @@
val cs = ConstraintSet()
underTest.applyDefaultConstraints(cs)
- assertLargeClockTop(cs, LARGE_CLOCK_TOP)
- assertSmallClockTop(cs)
+
+ cs.assertLargeClock(topMargin = LARGE_CLOCK_TOP)
+ cs.assertSmallClock()
}
}
@@ -409,11 +298,11 @@
val cs = ConstraintSet()
underTest.applyDefaultConstraints(cs)
- assertLargeClockTop(
- cs,
- KEYGUARD_SMARTSPACE_TOP_OFFSET + ENHANCED_SMART_SPACE_HEIGHT,
+
+ cs.assertLargeClock(
+ topMargin = KEYGUARD_SMARTSPACE_TOP_OFFSET + ENHANCED_SMART_SPACE_HEIGHT
)
- assertSmallClockTop(cs)
+ cs.assertSmallClock()
}
}
@@ -429,8 +318,7 @@
val cs = ConstraintSet()
underTest.applyDefaultConstraints(cs)
- val referencedIds =
- cs.getReferencedIds(R.id.weather_clock_date_and_icons_barrier_bottom)
+ val referencedIds = cs.getReferencedIds(ClockViewIds.WEATHER_CLOCK_DATE_BARRIER_BOTTOM)
referencedIds.contentEquals(
intArrayOf(com.android.systemui.shared.R.id.bc_smartspace_view)
)
@@ -448,9 +336,8 @@
val cs = ConstraintSet()
underTest.applyDefaultConstraints(cs)
- val referencedIds =
- cs.getReferencedIds(R.id.weather_clock_date_and_icons_barrier_bottom)
- referencedIds.contentEquals(intArrayOf(customR.id.lockscreen_clock_view))
+ val referencedIds = cs.getReferencedIds(ClockViewIds.WEATHER_CLOCK_DATE_BARRIER_BOTTOM)
+ referencedIds.contentEquals(intArrayOf(ClockViewIds.LOCKSCREEN_CLOCK_VIEW_SMALL))
}
@Test
@@ -464,8 +351,7 @@
val cs = ConstraintSet()
underTest.applyDefaultConstraints(cs)
- val referencedIds =
- cs.getReferencedIds(R.id.weather_clock_date_and_icons_barrier_bottom)
+ val referencedIds = cs.getReferencedIds(ClockViewIds.WEATHER_CLOCK_DATE_BARRIER_BOTTOM)
referencedIds.contentEquals(
intArrayOf(
com.android.systemui.shared.R.id.bc_smartspace_view,
@@ -474,24 +360,24 @@
)
}
- private fun assertLargeClockTop(cs: ConstraintSet, expectedLargeClockTopMargin: Int) {
- val largeClockConstraint = cs.getConstraint(customR.id.lockscreen_clock_view_large)
- assertThat(largeClockConstraint.layout.topToTop).isEqualTo(ConstraintSet.PARENT_ID)
- assertThat(largeClockConstraint.layout.topMargin).isEqualTo(expectedLargeClockTopMargin)
+ private fun ConstraintSet.assertLargeClock(
+ targetId: Int = ConstraintSet.PARENT_ID,
+ topMargin: Int = 0,
+ ) {
+ val largeClockConstraint = getConstraint(ClockViewIds.LOCKSCREEN_CLOCK_VIEW_LARGE)
+ assertThat(largeClockConstraint.layout.topToTop).isEqualTo(targetId)
+ assertThat(largeClockConstraint.layout.topMargin).isEqualTo(topMargin)
}
- private fun assertSmallClockTop(cs: ConstraintSet) {
- val smallClockGuidelineConstraint = cs.getConstraint(R.id.small_clock_guideline_top)
+ private fun ConstraintSet.assertSmallClock(
+ targetId: Int = R.id.small_clock_guideline_top,
+ topMargin: Int = 0,
+ ) {
+ val smallClockGuidelineConstraint = getConstraint(targetId)
assertThat(smallClockGuidelineConstraint.layout.topToTop).isEqualTo(-1)
- val smallClockConstraint = cs.getConstraint(customR.id.lockscreen_clock_view)
- assertThat(smallClockConstraint.layout.topToBottom)
- .isEqualTo(R.id.small_clock_guideline_top)
- assertThat(smallClockConstraint.layout.topMargin).isEqualTo(0)
- }
-
- companion object {
- private const val SMART_SPACE_DATE_WEATHER_HEIGHT = 10
- private const val ENHANCED_SMART_SPACE_HEIGHT = 11
+ val smallClockConstraint = getConstraint(ClockViewIds.LOCKSCREEN_CLOCK_VIEW_SMALL)
+ assertThat(smallClockConstraint.layout.topToBottom).isEqualTo(targetId)
+ assertThat(smallClockConstraint.layout.topMargin).isEqualTo(topMargin)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSectionTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSectionTest.kt
index 2df1184..b1e2c2f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSectionTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSectionTest.kt
@@ -27,18 +27,16 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.customization.R as customR
import com.android.systemui.keyguard.KeyguardUnlockAnimationController
import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardSmartspaceInteractor
import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardSmartspaceViewModel
+import com.android.systemui.plugins.clocks.ClockViewIds
import com.android.systemui.res.R
import com.android.systemui.shared.R as sharedR
import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController
-import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import dagger.Lazy
import kotlinx.coroutines.flow.MutableStateFlow
@@ -47,6 +45,8 @@
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.any
+import org.mockito.kotlin.whenever
@RunWith(AndroidJUnit4::class)
@SmallTest
@@ -72,6 +72,7 @@
private val shouldDateWeatherBeBelowSmallClock = MutableStateFlow(true)
private val isWeatherVisibleFlow = MutableStateFlow(false)
private val isShadeLayoutWide = MutableStateFlow(false)
+ private val isLargeClockVisible = MutableStateFlow(true)
@Before
fun setup() {
@@ -96,6 +97,7 @@
.thenReturn(dateView)
whenever(keyguardClockViewModel.hasCustomWeatherDataDisplay)
.thenReturn(hasCustomWeatherDataDisplay)
+ whenever(keyguardClockViewModel.isLargeClockVisible).thenReturn(isLargeClockVisible)
whenever(keyguardClockViewModel.shouldDateWeatherBeBelowSmallClock)
.thenReturn(shouldDateWeatherBeBelowSmallClock)
whenever(keyguardClockViewModel.clockShouldBeCentered).thenReturn(clockShouldBeCentered)
@@ -170,7 +172,8 @@
assertThat(smartspaceConstraints.layout.topToBottom).isEqualTo(dateView.id)
val dateConstraints = constraintSet.getConstraint(dateView.id)
- assertThat(dateConstraints.layout.topToBottom).isEqualTo(customR.id.lockscreen_clock_view)
+ assertThat(dateConstraints.layout.topToBottom)
+ .isEqualTo(ClockViewIds.LOCKSCREEN_CLOCK_VIEW_SMALL)
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModelTest.kt
index 0e13d01..ac903a8 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModelTest.kt
@@ -20,6 +20,7 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectValues
+import com.android.systemui.flags.DisableSceneContainer
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionState
@@ -33,6 +34,7 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
+@DisableSceneContainer
class AodToGoneTransitionViewModelTest : SysuiTestCase() {
val kosmos = testKosmos()
val testScope = kosmos.testScope
@@ -48,7 +50,7 @@
repository.sendTransitionSteps(
from = KeyguardState.AOD,
to = KeyguardState.GONE,
- testScope
+ testScope,
)
assertThat(alpha[0]).isEqualTo(0.5f)
@@ -75,14 +77,14 @@
private fun step(
value: Float,
- state: TransitionState = TransitionState.RUNNING
+ state: TransitionState = TransitionState.RUNNING,
): TransitionStep {
return TransitionStep(
from = KeyguardState.AOD,
to = KeyguardState.GONE,
value = value,
transitionState = state,
- ownerName = "AodToGoneTransitionViewModelTest"
+ ownerName = "AodToGoneTransitionViewModelTest",
)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsTest.kt
index 7a9bd92..a445f5e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsTest.kt
@@ -97,12 +97,7 @@
whenever(primaryBouncerInteractor.willRunDismissFromKeyguard()).thenReturn(true)
keyguardTransitionRepository.sendTransitionSteps(
- listOf(
- step(0f, TransitionState.STARTED),
- step(0.3f),
- step(0.6f),
- step(1f),
- ),
+ listOf(step(0f, TransitionState.STARTED), step(0.3f), step(0.6f), step(1f)),
testScope,
)
@@ -124,12 +119,7 @@
whenever(primaryBouncerInteractor.willRunDismissFromKeyguard()).thenReturn(true)
keyguardTransitionRepository.sendTransitionSteps(
- listOf(
- step(0f, TransitionState.STARTED),
- step(0.3f),
- step(0.6f),
- step(1f),
- ),
+ listOf(step(0f, TransitionState.STARTED), step(0.3f), step(0.6f), step(1f)),
testScope,
)
@@ -147,18 +137,14 @@
sysuiStatusBarStateController.setLeaveOpenOnKeyguardHide(true)
keyguardTransitionRepository.sendTransitionSteps(
- listOf(
- step(0f, TransitionState.STARTED),
- step(0.3f),
- step(0.6f),
- step(1f),
- ),
+ listOf(step(0f, TransitionState.STARTED), step(0.3f), step(0.6f), step(1f)),
testScope,
)
assertThat(values.size).isEqualTo(4)
values.forEach {
- assertThat(it).isEqualTo(ScrimAlpha(notificationsAlpha = 1f, behindAlpha = 1f))
+ assertThat(it.notificationsAlpha).isWithin(0.05f).of(1f)
+ assertThat(it.behindAlpha).isWithin(0.05f).of(1f)
}
}
@@ -204,12 +190,7 @@
runCurrent()
keyguardTransitionRepository.sendTransitionSteps(
- listOf(
- step(0f, TransitionState.STARTED),
- step(0.3f),
- step(0.6f),
- step(1f),
- ),
+ listOf(step(0f, TransitionState.STARTED), step(0.3f), step(0.6f), step(1f)),
testScope,
)
@@ -222,14 +203,14 @@
private fun step(
value: Float,
- state: TransitionState = TransitionState.RUNNING
+ state: TransitionState = TransitionState.RUNNING,
): TransitionStep {
return TransitionStep(
from = KeyguardState.PRIMARY_BOUNCER,
to = KeyguardState.GONE,
value = value,
transitionState = state,
- ownerName = "PrimaryBouncerToGoneTransitionViewModelTest"
+ ownerName = "PrimaryBouncerToGoneTransitionViewModelTest",
)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DozingToGoneTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DozingToGoneTransitionViewModelTest.kt
index 61a50ba..b144804 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DozingToGoneTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DozingToGoneTransitionViewModelTest.kt
@@ -20,6 +20,7 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectValues
+import com.android.systemui.flags.DisableSceneContainer
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
@@ -49,6 +50,7 @@
}
@Test
+ @DisableSceneContainer
fun lockscreenAlpha() =
testScope.runTest {
val viewState = ViewStateAccessor()
@@ -57,7 +59,7 @@
keyguardTransitionRepository.sendTransitionSteps(
from = KeyguardState.DOZING,
to = KeyguardState.GONE,
- testScope
+ testScope,
)
// Remain at zero throughout
@@ -91,7 +93,7 @@
to = KeyguardState.GONE,
value = value,
transitionState = state,
- ownerName = "DozingToGoneTransitionViewModelTest"
+ ownerName = "DozingToGoneTransitionViewModelTest",
)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToGlanceableHubTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToGlanceableHubTransitionViewModelTest.kt
index cf86c25..31cee772 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToGlanceableHubTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToGlanceableHubTransitionViewModelTest.kt
@@ -22,7 +22,6 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
-import com.android.systemui.coroutines.collectValues
import com.android.systemui.flags.DisableSceneContainer
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
@@ -42,6 +41,7 @@
import org.mockito.kotlin.whenever
@SmallTest
+@DisableSceneContainer
@RunWith(AndroidJUnit4::class)
class DreamingToGlanceableHubTransitionViewModelTest : SysuiTestCase() {
val kosmos = testKosmos()
@@ -97,7 +97,6 @@
}
@Test
- @DisableSceneContainer
fun blurBecomesMaxValueImmediately() =
kosmos.runTest {
val values by collectValues(underTest.windowBlurRadius)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToPrimaryBouncerTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToPrimaryBouncerTransitionViewModelTest.kt
index c515fc3..dc7600c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToPrimaryBouncerTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToPrimaryBouncerTransitionViewModelTest.kt
@@ -19,6 +19,7 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectValues
+import com.android.systemui.flags.DisableSceneContainer
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionState.RUNNING
@@ -31,6 +32,7 @@
import org.junit.runner.RunWith
@SmallTest
+@DisableSceneContainer
@RunWith(AndroidJUnit4::class)
class DreamingToPrimaryBouncerTransitionViewModelTest : SysuiTestCase() {
private val kosmos = testKosmos()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToDreamingTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToDreamingTransitionViewModelTest.kt
index dd9563f..e5d0cf5 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToDreamingTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToDreamingTransitionViewModelTest.kt
@@ -41,6 +41,7 @@
import org.mockito.kotlin.whenever
@SmallTest
+@DisableSceneContainer
@RunWith(AndroidJUnit4::class)
class GlanceableHubToDreamingTransitionViewModelTest : SysuiTestCase() {
val kosmos = testKosmos()
@@ -96,7 +97,6 @@
}
@Test
- @DisableSceneContainer
fun blurBecomesMinValueImmediately() =
kosmos.runTest {
val values by collectValues(underTest.windowBlurRadius)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToLockscreenTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToLockscreenTransitionViewModelTest.kt
index cdd093a..a7a3386 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToLockscreenTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToLockscreenTransitionViewModelTest.kt
@@ -57,6 +57,7 @@
import platform.test.runner.parameterized.Parameters
@SmallTest
+@DisableSceneContainer
@RunWith(ParameterizedAndroidJunit4::class)
class GlanceableHubToLockscreenTransitionViewModelTest(flags: FlagsParameterization) :
SysuiTestCase() {
@@ -358,7 +359,6 @@
}
@Test
- @DisableSceneContainer
fun blurBecomesMinValueImmediately() =
kosmos.runTest {
val values by collectValues(underTest.windowBlurRadius)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModelTest.kt
index ce234ccb..810d444 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModelTest.kt
@@ -21,6 +21,7 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.data.repository.fingerprintPropertyRepository
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.flags.DisableSceneContainer
import com.android.systemui.keyguard.data.repository.biometricSettingsRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
@@ -41,6 +42,7 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
+@DisableSceneContainer
class GoneToAodTransitionViewModelTest : SysuiTestCase() {
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
@@ -57,7 +59,7 @@
rawState = WakefulnessState.STARTING_TO_SLEEP,
lastWakeReason = WakeSleepReason.POWER_BUTTON,
lastSleepReason = WakeSleepReason.POWER_BUTTON,
- powerButtonLaunchGestureTriggered = false
+ powerButtonLaunchGestureTriggered = false,
)
val pixels = -100f
@@ -73,7 +75,7 @@
from = KeyguardState.GONE,
to = KeyguardState.AOD,
transitionState = TransitionState.STARTED,
- value = pixels
+ value = pixels,
)
)
@@ -91,7 +93,7 @@
from = KeyguardState.GONE,
to = KeyguardState.AOD,
transitionState = TransitionState.RUNNING,
- value = 0f
+ value = 0f,
)
)
}
@@ -103,7 +105,7 @@
rawState = WakefulnessState.STARTING_TO_SLEEP,
lastWakeReason = WakeSleepReason.POWER_BUTTON,
lastSleepReason = WakeSleepReason.FOLD,
- powerButtonLaunchGestureTriggered = false
+ powerButtonLaunchGestureTriggered = false,
)
val pixels = -100f
@@ -131,7 +133,7 @@
rawState = WakefulnessState.STARTING_TO_SLEEP,
lastWakeReason = WakeSleepReason.POWER_BUTTON,
lastSleepReason = WakeSleepReason.FOLD,
- powerButtonLaunchGestureTriggered = false
+ powerButtonLaunchGestureTriggered = false,
)
val pixels = -100f
@@ -147,7 +149,7 @@
from = KeyguardState.GONE,
to = KeyguardState.AOD,
transitionState = TransitionState.STARTED,
- value = pixels
+ value = pixels,
)
)
@@ -165,7 +167,7 @@
from = KeyguardState.GONE,
to = KeyguardState.AOD,
transitionState = TransitionState.RUNNING,
- value = 0f
+ value = 0f,
)
)
}
@@ -288,14 +290,14 @@
private fun step(
value: Float,
- state: TransitionState = TransitionState.RUNNING
+ state: TransitionState = TransitionState.RUNNING,
): TransitionStep {
return TransitionStep(
from = KeyguardState.GONE,
to = KeyguardState.AOD,
value = value,
transitionState = state,
- ownerName = "GoneToAodTransitionViewModelTest"
+ ownerName = "GoneToAodTransitionViewModelTest",
)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDozingTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDozingTransitionViewModelTest.kt
index c7c0369..ab8d186 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDozingTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDozingTransitionViewModelTest.kt
@@ -23,6 +23,7 @@
import com.android.systemui.biometrics.data.repository.fakeFingerprintPropertyRepository
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
+import com.android.systemui.flags.DisableSceneContainer
import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.data.repository.fakeBiometricSettingsRepository
@@ -42,6 +43,7 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
+@DisableSceneContainer
class GoneToDozingTransitionViewModelTest : SysuiTestCase() {
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
@@ -128,7 +130,7 @@
to = KeyguardState.DOZING,
value = value,
transitionState = state,
- ownerName = "GoneToDozingTransitionViewModelTest"
+ ownerName = "GoneToDozingTransitionViewModelTest",
)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelTest.kt
index 36b26a4..d2525c98 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelTest.kt
@@ -20,8 +20,9 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectValues
-import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.flags.DisableSceneContainer
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
@@ -35,6 +36,7 @@
import org.junit.runner.RunWith
@SmallTest
+@DisableSceneContainer
@RunWith(AndroidJUnit4::class)
class GoneToDreamingTransitionViewModelTest : SysuiTestCase() {
private val kosmos = testKosmos()
@@ -87,7 +89,7 @@
step(0.3f),
step(0.5f),
// And a final reset event on CANCEL
- step(0.8f, TransitionState.CANCELED)
+ step(0.8f, TransitionState.CANCELED),
),
testScope,
)
@@ -98,14 +100,14 @@
private fun step(
value: Float,
- state: TransitionState = TransitionState.RUNNING
+ state: TransitionState = TransitionState.RUNNING,
): TransitionStep {
return TransitionStep(
from = KeyguardState.GONE,
to = KeyguardState.DREAMING,
value = value,
transitionState = state,
- ownerName = "GoneToDreamingTransitionViewModelTest"
+ ownerName = "GoneToDreamingTransitionViewModelTest",
)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardMediaViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardMediaViewModelTest.kt
index 583fd1e..c98e761 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardMediaViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardMediaViewModelTest.kt
@@ -58,7 +58,7 @@
kosmos.runTest {
val userMedia = MediaData(active = true)
- mediaFilterRepository.addSelectedUserMediaEntry(userMedia)
+ mediaFilterRepository.addCurrentUserMediaEntry(userMedia)
keyguardRepository.setIsDozing(true)
assertThat(underTest.isMediaVisible).isFalse()
@@ -69,7 +69,7 @@
kosmos.runTest {
val userMedia = MediaData(active = true)
- mediaFilterRepository.addSelectedUserMediaEntry(userMedia)
+ mediaFilterRepository.addCurrentUserMediaEntry(userMedia)
keyguardRepository.setIsDozing(false)
assertThat(underTest.isMediaVisible).isTrue()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSmartspaceViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSmartspaceViewModelTest.kt
index 007ec0c..99c36a0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSmartspaceViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSmartspaceViewModelTest.kt
@@ -69,6 +69,21 @@
}
@Test
+ fun testWhenWeatherEnabled_notCustomWeatherDataDisplay_isWeatherVisible_smallClock_shouldBeTrue() =
+ testScope.runTest {
+ val isWeatherVisible by collectLastValue(underTest.isWeatherVisible)
+ whenever(clockController.smallClock.config.hasCustomWeatherDataDisplay)
+ .thenReturn(false)
+
+ with(kosmos) {
+ keyguardSmartspaceRepository.setIsWeatherEnabled(true)
+ keyguardClockRepository.setClockSize(ClockSize.SMALL)
+ }
+
+ assertThat(isWeatherVisible).isEqualTo(true)
+ }
+
+ @Test
fun testWhenWeatherEnabled_hasCustomWeatherDataDisplay_isWeatherVisible_shouldBeFalse() =
testScope.runTest {
val isWeatherVisible by collectLastValue(underTest.isWeatherVisible)
@@ -83,6 +98,20 @@
}
@Test
+ fun testWhenWeatherEnabled_hasCustomWeatherDataDisplay_isWeatherVisible_smallClock_shouldBeTrue() =
+ testScope.runTest {
+ val isWeatherVisible by collectLastValue(underTest.isWeatherVisible)
+ whenever(clockController.smallClock.config.hasCustomWeatherDataDisplay).thenReturn(true)
+
+ with(kosmos) {
+ keyguardSmartspaceRepository.setIsWeatherEnabled(true)
+ keyguardClockRepository.setClockSize(ClockSize.SMALL)
+ }
+
+ assertThat(isWeatherVisible).isEqualTo(true)
+ }
+
+ @Test
fun testWhenWeatherEnabled_notCustomWeatherDataDisplay_notIsWeatherVisible_shouldBeFalse() =
testScope.runTest {
val isWeatherVisible by collectLastValue(underTest.isWeatherVisible)
@@ -98,6 +127,62 @@
}
@Test
+ fun isDateVisible_notCustomWeatherDataDisplay_largeClock_shouldBeTrue() =
+ testScope.runTest {
+ val isDateVisible by collectLastValue(underTest.isDateVisible)
+ whenever(clockController.largeClock.config.hasCustomWeatherDataDisplay)
+ .thenReturn(false)
+
+ with(kosmos) {
+ keyguardClockRepository.setClockSize(ClockSize.LARGE)
+ }
+
+ assertThat(isDateVisible).isEqualTo(true)
+ }
+
+ @Test
+ fun isDateVisible_hasCustomWeatherDataDisplay_largeClock_shouldBeFalse() =
+ testScope.runTest {
+ val isDateVisible by collectLastValue(underTest.isDateVisible)
+ whenever(clockController.largeClock.config.hasCustomWeatherDataDisplay)
+ .thenReturn(true)
+
+ with(kosmos) {
+ keyguardClockRepository.setClockSize(ClockSize.LARGE)
+ }
+
+ assertThat(isDateVisible).isEqualTo(false)
+ }
+
+ @Test
+ fun isDateVisible_hasCustomWeatherDataDisplay_smallClock_shouldBeTrue() =
+ testScope.runTest {
+ val isDateVisible by collectLastValue(underTest.isDateVisible)
+ whenever(clockController.smallClock.config.hasCustomWeatherDataDisplay)
+ .thenReturn(true)
+
+ with(kosmos) {
+ keyguardClockRepository.setClockSize(ClockSize.SMALL)
+ }
+
+ assertThat(isDateVisible).isEqualTo(true)
+ }
+
+ @Test
+ fun isDateVisible_notCustomWeatherDataDisplay_smallClock_shouldBeTrue() =
+ testScope.runTest {
+ val isDateVisible by collectLastValue(underTest.isDateVisible)
+ whenever(clockController.smallClock.config.hasCustomWeatherDataDisplay)
+ .thenReturn(false)
+
+ with(kosmos) {
+ keyguardClockRepository.setClockSize(ClockSize.SMALL)
+ }
+
+ assertThat(isDateVisible).isEqualTo(true)
+ }
+
+ @Test
fun isShadeLayoutWide_withConfigTrue_true() =
with(kosmos) {
testScope.runTest {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGlanceableHubTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGlanceableHubTransitionViewModelTest.kt
index aefa2eb..4d7072d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGlanceableHubTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGlanceableHubTransitionViewModelTest.kt
@@ -41,6 +41,7 @@
import org.mockito.kotlin.whenever
@SmallTest
+@DisableSceneContainer
@RunWith(AndroidJUnit4::class)
class LockscreenToGlanceableHubTransitionViewModelTest : SysuiTestCase() {
val kosmos = testKosmos()
@@ -108,7 +109,6 @@
}
@Test
- @DisableSceneContainer
fun blurBecomesMaxValueImmediately() =
kosmos.runTest {
val values by collectValues(underTest.windowBlurRadius)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModelTest.kt
index a82d01f..29d1969 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModelTest.kt
@@ -21,6 +21,7 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
+import com.android.systemui.flags.DisableSceneContainer
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionState
@@ -35,6 +36,7 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
+@DisableSceneContainer
class LockscreenToGoneTransitionViewModelTest : SysuiTestCase() {
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
@@ -106,14 +108,14 @@
private fun step(
value: Float,
- state: TransitionState = TransitionState.RUNNING
+ state: TransitionState = TransitionState.RUNNING,
): TransitionStep {
return TransitionStep(
from = KeyguardState.LOCKSCREEN,
to = KeyguardState.GONE,
value = value,
transitionState = state,
- ownerName = "LockscreenToGoneTransitionViewModelTest"
+ ownerName = "LockscreenToGoneTransitionViewModelTest",
)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt
index 5357c28..6514191 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt
@@ -22,6 +22,7 @@
import com.android.systemui.biometrics.data.repository.fingerprintPropertyRepository
import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
import com.android.systemui.coroutines.collectValues
+import com.android.systemui.flags.DisableSceneContainer
import com.android.systemui.keyguard.data.repository.biometricSettingsRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
@@ -80,7 +81,7 @@
testScope.runTest {
configurationRepository.setDimensionPixelSize(
R.dimen.occluded_to_lockscreen_transition_lockscreen_translation_y,
- 100
+ 100,
)
val values by collectValues(underTest.lockscreenTranslationY)
runCurrent()
@@ -101,11 +102,12 @@
}
@Test
+ @DisableSceneContainer // onCancel values are not emitted when the scene container is enabled.
fun lockscreenTranslationYResettedAfterJobCancelled() =
testScope.runTest {
configurationRepository.setDimensionPixelSize(
R.dimen.occluded_to_lockscreen_transition_lockscreen_translation_y,
- 100
+ 100,
)
val values by collectValues(underTest.lockscreenTranslationY)
runCurrent()
@@ -162,14 +164,14 @@
private fun step(
value: Float,
- state: TransitionState = TransitionState.RUNNING
+ state: TransitionState = TransitionState.RUNNING,
): TransitionStep {
return TransitionStep(
from = KeyguardState.OCCLUDED,
to = KeyguardState.LOCKSCREEN,
value = value,
transitionState = state,
- ownerName = "OccludedToLockscreenTransitionViewModelTest"
+ ownerName = "OccludedToLockscreenTransitionViewModelTest",
)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToDreamingTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToDreamingTransitionViewModelTest.kt
index 9c2c3c3..8e71587 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToDreamingTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToDreamingTransitionViewModelTest.kt
@@ -16,10 +16,13 @@
package com.android.systemui.keyguard.ui.viewmodel
+import android.platform.test.annotations.EnableFlags
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectValues
+import com.android.systemui.flags.DisableSceneContainer
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
@@ -35,6 +38,7 @@
import org.junit.runner.RunWith
@SmallTest
+@DisableSceneContainer
@RunWith(AndroidJUnit4::class)
class PrimaryBouncerToDreamingTransitionViewModelTest : SysuiTestCase() {
private val kosmos = testKosmos()
@@ -52,6 +56,7 @@
fun blurRadiusGoesToMinImmediately() =
testScope.runTest {
val values by collectValues(underTest.windowBlurRadius)
+ kosmos.keyguardWindowBlurTestUtil.shadeExpanded(false)
kosmos.keyguardWindowBlurTestUtil.assertTransitionToBlurRadius(
transitionProgress = listOf(0.0f, 0.2f, 0.3f, 0.65f, 0.7f, 1.0f),
@@ -62,6 +67,38 @@
)
}
+ @Test
+ @EnableFlags(Flags.FLAG_NOTIFICATION_SHADE_BLUR)
+ fun blurRadiusRemainsAtMaxIfShadeIsExpandedAndShadeBlurIsEnabled() =
+ testScope.runTest {
+ val values by collectValues(underTest.windowBlurRadius)
+ kosmos.keyguardWindowBlurTestUtil.shadeExpanded(true)
+
+ kosmos.keyguardWindowBlurTestUtil.assertTransitionToBlurRadius(
+ transitionProgress = listOf(0.0f, 0.2f, 0.3f, 0.65f, 0.7f, 1.0f),
+ startValue = kosmos.blurConfig.maxBlurRadiusPx,
+ endValue = kosmos.blurConfig.maxBlurRadiusPx,
+ actualValuesProvider = { values },
+ transitionFactory = ::step,
+ checkInterpolatedValues = false,
+ )
+ }
+
+ @Test
+ fun notificationBlurDropsToMinWhenGoingBackFromPrimaryBouncerToDreaming() =
+ testScope.runTest {
+ val values by collectValues(underTest.notificationBlurRadius)
+
+ kosmos.keyguardWindowBlurTestUtil.assertTransitionToBlurRadius(
+ transitionProgress = listOf(0.0f, 0.2f, 0.3f, 0.65f, 0.7f, 1.0f),
+ startValue = kosmos.blurConfig.minBlurRadiusPx,
+ endValue = kosmos.blurConfig.minBlurRadiusPx,
+ actualValuesProvider = { values },
+ transitionFactory = ::step,
+ checkInterpolatedValues = false,
+ )
+ }
+
private fun step(value: Float, state: TransitionState = RUNNING): TransitionStep {
return TransitionStep(
from = KeyguardState.PRIMARY_BOUNCER,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt
index 97709a7..cc80c9e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt
@@ -21,6 +21,7 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.bouncer.domain.interactor.mockPrimaryBouncerInteractor
import com.android.systemui.coroutines.collectValues
+import com.android.systemui.flags.DisableSceneContainer
import com.android.systemui.flags.Flags
import com.android.systemui.flags.fakeFeatureFlagsClassic
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
@@ -41,6 +42,7 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
+@DisableSceneContainer
class PrimaryBouncerToGoneTransitionViewModelTest : SysuiTestCase() {
val kosmos =
testKosmos().apply {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/lowlightclock/LowLightMonitorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/lowlightclock/LowLightMonitorTest.kt
index 285c009..f8bbad9 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/lowlightclock/LowLightMonitorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/lowlightclock/LowLightMonitorTest.kt
@@ -27,15 +27,20 @@
import com.android.systemui.biometrics.domain.interactor.displayStateInteractor
import com.android.systemui.display.data.repository.displayRepository
import com.android.systemui.dreams.domain.interactor.dreamSettingsInteractorKosmos
+import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
+import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
+import com.android.systemui.keyguard.shared.model.DozeStateModel
+import com.android.systemui.keyguard.shared.model.DozeTransitionModel
import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.kosmos.backgroundScope
+import com.android.systemui.kosmos.collectLastValue
import com.android.systemui.kosmos.runTest
-import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.useUnconfinedTestDispatcher
import com.android.systemui.log.logcatLogBuffer
-import com.android.systemui.shared.condition.Condition
-import com.android.systemui.shared.condition.Monitor
+import com.android.systemui.lowlight.ambientLightModeMonitor
+import com.android.systemui.lowlight.fake
+import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest
+import com.android.systemui.power.domain.interactor.powerInteractor
import com.android.systemui.statusbar.commandline.commandRegistry
import com.android.systemui.testKosmos
import com.android.systemui.user.data.repository.fakeUserRepository
@@ -45,14 +50,13 @@
import com.google.common.truth.Truth.assertThat
import java.io.PrintWriter
import java.io.StringWriter
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.asExecutor
+import kotlinx.coroutines.flow.MutableStateFlow
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.kotlin.clearInvocations
+import org.mockito.kotlin.any
+import org.mockito.kotlin.doAnswer
import org.mockito.kotlin.mock
-import org.mockito.kotlin.verify
@SmallTest
@RunWith(AndroidJUnit4::class)
@@ -63,23 +67,27 @@
.apply { mainResources = mContext.orCreateTestableResources.resources }
.useUnconfinedTestDispatcher()
- private val Kosmos.lowLightDreamManager: LowLightDreamManager by
- Kosmos.Fixture { mock<LowLightDreamManager>() }
+ private val ambientLightMode: MutableStateFlow<Int> =
+ MutableStateFlow(LowLightDreamManager.AMBIENT_LIGHT_MODE_UNKNOWN)
- private val Kosmos.monitor: Monitor by Kosmos.Fixture { Monitor(testDispatcher.asExecutor()) }
+ private val Kosmos.lowLightDreamManager: LowLightDreamManager by
+ Kosmos.Fixture {
+ mock<LowLightDreamManager> {
+ on { setAmbientLightMode(any()) } doAnswer
+ { invocation ->
+ val mode = invocation.arguments[0] as Int
+ ambientLightMode.value = mode
+ }
+ }
+ }
private val Kosmos.logger: LowLightLogger by
Kosmos.Fixture { LowLightLogger(logcatLogBuffer()) }
- private val Kosmos.condition: FakeCondition by
- Kosmos.Fixture { FakeCondition(scope = applicationCoroutineScope, initialValue = null) }
-
private val Kosmos.underTest: LowLightMonitor by
Kosmos.Fixture {
LowLightMonitor(
lowLightDreamManager = { lowLightDreamManager },
- conditionsMonitor = monitor,
- lowLightConditions = { setOf(condition) },
dreamSettingsInteractor = dreamSettingsInteractorKosmos,
displayStateInteractor = displayStateInteractor,
logger = logger,
@@ -88,6 +96,10 @@
scope = backgroundScope,
commandRegistry = commandRegistry,
userLockedInteractor = userLockedInteractor,
+ keyguardInteractor = keyguardInteractor,
+ powerInteractor = powerInteractor,
+ ambientLightModeMonitor = ambientLightModeMonitor,
+ uiEventLogger = mock(),
)
}
@@ -126,6 +138,8 @@
fun setUp() {
kosmos.setDisplayOn(false)
kosmos.setUserUnlocked(true)
+ kosmos.powerInteractor.setAwakeForTest()
+ kosmos.fakeKeyguardRepository.setKeyguardShowing(true)
// Activate dreams on charge by default
mContext.orCreateTestableResources.addOverride(
@@ -149,35 +163,36 @@
@Test
fun testSetAmbientLowLightWhenInLowLight() =
kosmos.runTest {
+ val mode by collectLastValue(ambientLightMode)
underTest.start()
// Turn on screen
setDisplayOn(true)
- // Set conditions to true
- condition.setValue(true)
+ assertThat(mode).isEqualTo(LowLightDreamManager.AMBIENT_LIGHT_MODE_REGULAR)
+
+ setLowLightFromSensor(true)
// Verify setting low light when condition is true
- verify(lowLightDreamManager)
- .setAmbientLightMode(LowLightDreamManager.AMBIENT_LIGHT_MODE_LOW_LIGHT)
+ assertThat(mode).isEqualTo(LowLightDreamManager.AMBIENT_LIGHT_MODE_LOW_LIGHT)
}
@Test
fun testExitAmbientLowLightWhenNotInLowLight() =
kosmos.runTest {
+ val mode by collectLastValue(ambientLightMode)
underTest.start()
// Turn on screen
setDisplayOn(true)
// Set conditions to true then false
- condition.setValue(true)
- clearInvocations(lowLightDreamManager)
- condition.setValue(false)
+ setLowLightFromSensor(true)
+ assertThat(mode).isEqualTo(LowLightDreamManager.AMBIENT_LIGHT_MODE_LOW_LIGHT)
+ setLowLightFromSensor(false)
// Verify ambient light toggles back to light mode regular
- verify(lowLightDreamManager)
- .setAmbientLightMode(LowLightDreamManager.AMBIENT_LIGHT_MODE_REGULAR)
+ assertThat(mode).isEqualTo(LowLightDreamManager.AMBIENT_LIGHT_MODE_REGULAR)
}
@Test
@@ -186,11 +201,11 @@
underTest.start()
setDisplayOn(true)
- assertThat(condition.started).isTrue()
+ assertThat(ambientLightModeMonitor.fake.started).isTrue()
// Verify removing subscription when screen turns off.
setDisplayOn(false)
- assertThat(condition.started).isFalse()
+ assertThat(ambientLightModeMonitor.fake.started).isFalse()
}
@Test
@@ -201,11 +216,11 @@
setDisplayOn(true)
setDreamEnabled(true)
- assertThat(condition.started).isTrue()
+ assertThat(ambientLightModeMonitor.fake.started).isTrue()
setDreamEnabled(false)
// Verify removing subscription when dream disabled.
- assertThat(condition.started).isFalse()
+ assertThat(ambientLightModeMonitor.fake.started).isFalse()
}
@Test
@@ -214,7 +229,64 @@
setDisplayOn(true)
underTest.start()
- assertThat(condition.started).isTrue()
+ assertThat(ambientLightModeMonitor.fake.started).isTrue()
+ }
+
+ @Test
+ fun testDoNotEnterLowLightIfDeviceNotIdle() =
+ kosmos.runTest {
+ val mode by collectLastValue(ambientLightMode)
+ setDisplayOn(true)
+ setUserUnlocked(true)
+ setLowLightFromSensor(true)
+
+ fakeKeyguardRepository.setKeyguardShowing(true)
+ fakeKeyguardRepository.setDreaming(false)
+
+ underTest.start()
+ assertThat(mode).isEqualTo(LowLightDreamManager.AMBIENT_LIGHT_MODE_LOW_LIGHT)
+
+ fakeKeyguardRepository.setKeyguardShowing(false)
+ assertThat(mode).isEqualTo(LowLightDreamManager.AMBIENT_LIGHT_MODE_REGULAR)
+ }
+
+ @Test
+ fun testDoNotEnterLowLightIfNotDreaming() =
+ kosmos.runTest {
+ val mode by collectLastValue(ambientLightMode)
+ setDisplayOn(true)
+ setUserUnlocked(true)
+ fakeKeyguardRepository.setKeyguardShowing(false)
+ fakeKeyguardRepository.setDreaming(true)
+ setLowLightFromSensor(true)
+
+ underTest.start()
+ assertThat(mode).isEqualTo(LowLightDreamManager.AMBIENT_LIGHT_MODE_LOW_LIGHT)
+
+ fakeKeyguardRepository.setDreaming(false)
+ assertThat(mode).isEqualTo(LowLightDreamManager.AMBIENT_LIGHT_MODE_REGULAR)
+ }
+
+ @Test
+ fun testDoNotEnterLowLightWhenDozingAndAsleep() =
+ kosmos.runTest {
+ val mode by collectLastValue(ambientLightMode)
+ underTest.start()
+
+ setDisplayOn(true)
+ setUserUnlocked(true)
+ fakeKeyguardRepository.setKeyguardShowing(false)
+ fakeKeyguardRepository.setDreaming(true)
+ setLowLightFromSensor(true)
+
+ assertThat(mode).isEqualTo(LowLightDreamManager.AMBIENT_LIGHT_MODE_LOW_LIGHT)
+
+ // Dozing started
+ fakeKeyguardRepository.setDozeTransitionModel(
+ DozeTransitionModel(from = DozeStateModel.UNINITIALIZED, to = DozeStateModel.DOZE)
+ )
+
+ assertThat(mode).isEqualTo(LowLightDreamManager.AMBIENT_LIGHT_MODE_REGULAR)
}
@Test
@@ -224,101 +296,76 @@
dreamComponent = null
underTest.start()
- assertThat(condition.started).isFalse()
+ assertThat(ambientLightModeMonitor.fake.started).isFalse()
}
@Test
fun testForceLowlightToTrue() =
kosmos.runTest {
+ val mode by collectLastValue(ambientLightMode)
setDisplayOn(true)
// low-light condition not met
- condition.setValue(false)
+ setLowLightFromSensor(false)
underTest.start()
- verify(lowLightDreamManager)
- .setAmbientLightMode(LowLightDreamManager.AMBIENT_LIGHT_MODE_REGULAR)
- clearInvocations(lowLightDreamManager)
+ assertThat(mode).isEqualTo(LowLightDreamManager.AMBIENT_LIGHT_MODE_REGULAR)
// force state to true
sendDebugCommand(true)
- verify(lowLightDreamManager)
- .setAmbientLightMode(LowLightDreamManager.AMBIENT_LIGHT_MODE_LOW_LIGHT)
- clearInvocations(lowLightDreamManager)
+ assertThat(mode).isEqualTo(LowLightDreamManager.AMBIENT_LIGHT_MODE_LOW_LIGHT)
// clear forced state
sendDebugCommand(null)
- verify(lowLightDreamManager)
- .setAmbientLightMode(LowLightDreamManager.AMBIENT_LIGHT_MODE_REGULAR)
+ assertThat(mode).isEqualTo(LowLightDreamManager.AMBIENT_LIGHT_MODE_REGULAR)
}
@Test
fun testForceLowlightToFalse() =
kosmos.runTest {
+ val mode by collectLastValue(ambientLightMode)
setDisplayOn(true)
// low-light condition is met
- condition.setValue(true)
+ setLowLightFromSensor(true)
underTest.start()
- verify(lowLightDreamManager)
- .setAmbientLightMode(LowLightDreamManager.AMBIENT_LIGHT_MODE_LOW_LIGHT)
- clearInvocations(lowLightDreamManager)
+ assertThat(mode).isEqualTo(LowLightDreamManager.AMBIENT_LIGHT_MODE_LOW_LIGHT)
// force state to false
sendDebugCommand(false)
- verify(lowLightDreamManager)
- .setAmbientLightMode(LowLightDreamManager.AMBIENT_LIGHT_MODE_REGULAR)
- clearInvocations(lowLightDreamManager)
+ assertThat(mode).isEqualTo(LowLightDreamManager.AMBIENT_LIGHT_MODE_REGULAR)
// clear forced state and ensure we go back to low-light
sendDebugCommand(null)
- verify(lowLightDreamManager)
- .setAmbientLightMode(LowLightDreamManager.AMBIENT_LIGHT_MODE_LOW_LIGHT)
+ assertThat(mode).isEqualTo(LowLightDreamManager.AMBIENT_LIGHT_MODE_LOW_LIGHT)
}
@Test
fun testLowlightForcedToTrueWhenUserLocked() =
kosmos.runTest {
+ val mode by collectLastValue(ambientLightMode)
setDisplayOn(true)
// low-light condition is false
- condition.setValue(false)
+ setLowLightFromSensor(false)
underTest.start()
- verify(lowLightDreamManager)
- .setAmbientLightMode(LowLightDreamManager.AMBIENT_LIGHT_MODE_REGULAR)
- clearInvocations(lowLightDreamManager)
+ assertThat(mode).isEqualTo(LowLightDreamManager.AMBIENT_LIGHT_MODE_REGULAR)
// locked user forces lowlight
setUserUnlocked(false)
- verify(lowLightDreamManager)
- .setAmbientLightMode(LowLightDreamManager.AMBIENT_LIGHT_MODE_LOW_LIGHT)
- clearInvocations(lowLightDreamManager)
+ assertThat(mode).isEqualTo(LowLightDreamManager.AMBIENT_LIGHT_MODE_LOW_LIGHT)
// clear forced state and ensure we go back to regular mode
setUserUnlocked(true)
- verify(lowLightDreamManager)
- .setAmbientLightMode(LowLightDreamManager.AMBIENT_LIGHT_MODE_REGULAR)
+ assertThat(mode).isEqualTo(LowLightDreamManager.AMBIENT_LIGHT_MODE_REGULAR)
}
- private class FakeCondition(
- scope: CoroutineScope,
- initialValue: Boolean?,
- overriding: Boolean = false,
- @StartStrategy override val startStrategy: Int = START_EAGERLY,
- ) : Condition(scope, initialValue, overriding) {
- private var _started = false
- val started: Boolean
- get() = _started
-
- override suspend fun start() {
- _started = true
- }
-
- override fun stop() {
- _started = false
- }
-
- fun setValue(value: Boolean?) {
- value?.also { updateCondition(value) } ?: clearCondition()
- }
+ private fun Kosmos.setLowLightFromSensor(lowlight: Boolean) {
+ val lightMode =
+ if (lowlight) {
+ AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK
+ } else {
+ AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT
+ }
+ ambientLightModeMonitor.fake.setAmbientLightMode(lightMode)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/NotificationMediaManagerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/NotificationMediaManagerTest.kt
index 10b0085..1238e88 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/NotificationMediaManagerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/NotificationMediaManagerTest.kt
@@ -20,12 +20,9 @@
import android.media.session.MediaController
import android.media.session.MediaSession
import android.os.fakeExecutorHandler
-import android.platform.test.annotations.DisableFlags
-import android.platform.test.annotations.EnableFlags
import android.service.notification.NotificationListenerService
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.dumpManager
import com.android.systemui.media.controls.domain.pipeline.MediaDataManager
@@ -87,7 +84,6 @@
}
@Test
- @EnableFlags(Flags.FLAG_MEDIA_CONTROLS_USER_INITIATED_DELETEINTENT)
fun mediaDataRemoved_userInitiated_dismissNotif() {
val notifEntryCaptor = argumentCaptor<NotificationEntry>()
val notifEntry = mock<NotificationEntry>()
@@ -102,7 +98,6 @@
}
@Test
- @EnableFlags(Flags.FLAG_MEDIA_CONTROLS_USER_INITIATED_DELETEINTENT)
fun mediaDataRemoved_notUserInitiated_doesNotDismissNotif() {
listenerCaptor.lastValue.onMediaDataRemoved(KEY, false)
@@ -110,21 +105,6 @@
}
@Test
- @DisableFlags(Flags.FLAG_MEDIA_CONTROLS_USER_INITIATED_DELETEINTENT)
- fun mediaDataRemoved_notUserInitiated_flagOff_dismissNotif() {
- val notifEntryCaptor = argumentCaptor<NotificationEntry>()
- val notifEntry = mock<NotificationEntry>()
- whenever(notifEntry.key).thenReturn(KEY)
- whenever(notifEntry.ranking).thenReturn(NotificationListenerService.Ranking())
- whenever(notifPipeline.allNotifs).thenReturn(listOf(notifEntry))
-
- listenerCaptor.lastValue.onMediaDataRemoved(KEY, false)
-
- verify(notifCollection).dismissNotification(notifEntryCaptor.capture(), any())
- assertThat(notifEntryCaptor.lastValue.key).isEqualTo(KEY)
- }
-
- @Test
fun clearMediaNotification_resetMediaMetadata() {
// set up media metadata.
notificationMediaManager.mMediaListener.onMetadataChanged(MediaMetadata.Builder().build())
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/data/repository/MediaFilterRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/data/repository/MediaFilterRepositoryTest.kt
index 0a4911f..7f0ec4c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/data/repository/MediaFilterRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/data/repository/MediaFilterRepositoryTest.kt
@@ -41,88 +41,88 @@
private val underTest: MediaFilterRepository = with(kosmos) { mediaFilterRepository }
@Test
- fun addSelectedUserMediaEntry_activeThenInactivate() =
+ fun addCurrentUserMediaEntry_activeThenInactivate() =
testScope.runTest {
- val selectedUserEntries by collectLastValue(underTest.selectedUserEntries)
+ val currentUserEntries by collectLastValue(underTest.currentUserEntries)
val instanceId = InstanceId.fakeInstanceId(123)
val userMedia = MediaData().copy(active = true, instanceId = instanceId)
- underTest.addSelectedUserMediaEntry(userMedia)
+ underTest.addCurrentUserMediaEntry(userMedia)
- assertThat(selectedUserEntries?.get(instanceId)).isEqualTo(userMedia)
+ assertThat(currentUserEntries?.get(instanceId)).isEqualTo(userMedia)
assertThat(underTest.hasActiveMedia()).isTrue()
assertThat(underTest.hasAnyMedia()).isTrue()
- underTest.addSelectedUserMediaEntry(userMedia.copy(active = false))
+ underTest.addCurrentUserMediaEntry(userMedia.copy(active = false))
- assertThat(selectedUserEntries?.get(instanceId)).isNotEqualTo(userMedia)
- assertThat(selectedUserEntries?.get(instanceId)?.active).isFalse()
+ assertThat(currentUserEntries?.get(instanceId)).isNotEqualTo(userMedia)
+ assertThat(currentUserEntries?.get(instanceId)?.active).isFalse()
assertThat(underTest.hasActiveMedia()).isFalse()
assertThat(underTest.hasAnyMedia()).isTrue()
}
@Test
- fun addSelectedUserMediaEntry_thenRemove_returnsBoolean() =
+ fun addCurrentUserMediaEntry_thenRemove_returnsBoolean() =
testScope.runTest {
- val selectedUserEntries by collectLastValue(underTest.selectedUserEntries)
+ val currentUserEntries by collectLastValue(underTest.currentUserEntries)
val instanceId = InstanceId.fakeInstanceId(123)
val userMedia = MediaData().copy(instanceId = instanceId)
- underTest.addSelectedUserMediaEntry(userMedia)
+ underTest.addCurrentUserMediaEntry(userMedia)
- assertThat(selectedUserEntries?.get(instanceId)).isEqualTo(userMedia)
+ assertThat(currentUserEntries?.get(instanceId)).isEqualTo(userMedia)
assertThat(underTest.hasActiveMedia()).isTrue()
assertThat(underTest.hasAnyMedia()).isTrue()
- assertThat(underTest.removeSelectedUserMediaEntry(instanceId, userMedia)).isTrue()
+ assertThat(underTest.removeCurrentUserMediaEntry(instanceId, userMedia)).isTrue()
assertThat(underTest.hasActiveMedia()).isFalse()
assertThat(underTest.hasAnyMedia()).isFalse()
}
@Test
- fun addSelectedUserMediaEntry_thenRemove_returnsValue() =
+ fun addCurrentUserMediaEntry_thenRemove_returnsValue() =
testScope.runTest {
- val selectedUserEntries by collectLastValue(underTest.selectedUserEntries)
+ val currentUserEntries by collectLastValue(underTest.currentUserEntries)
val instanceId = InstanceId.fakeInstanceId(123)
val userMedia = MediaData().copy(instanceId = instanceId)
- underTest.addSelectedUserMediaEntry(userMedia)
+ underTest.addCurrentUserMediaEntry(userMedia)
- assertThat(selectedUserEntries?.get(instanceId)).isEqualTo(userMedia)
+ assertThat(currentUserEntries?.get(instanceId)).isEqualTo(userMedia)
- assertThat(underTest.removeSelectedUserMediaEntry(instanceId)).isEqualTo(userMedia)
+ assertThat(underTest.removeCurrentUserMediaEntry(instanceId)).isEqualTo(userMedia)
}
@Test
- fun addAllUserMediaEntry_activeThenInactivate() =
+ fun addMediaEntry_activeThenInactivate() =
testScope.runTest {
- val allUserEntries by collectLastValue(underTest.allUserEntries)
+ val allMediaEntries by collectLastValue(underTest.allMediaEntries)
val userMedia = MediaData().copy(active = true)
underTest.addMediaEntry(KEY, userMedia)
- assertThat(allUserEntries?.get(KEY)).isEqualTo(userMedia)
+ assertThat(allMediaEntries?.get(KEY)).isEqualTo(userMedia)
underTest.addMediaEntry(KEY, userMedia.copy(active = false))
- assertThat(allUserEntries?.get(KEY)).isNotEqualTo(userMedia)
- assertThat(allUserEntries?.get(KEY)?.active).isFalse()
+ assertThat(allMediaEntries?.get(KEY)).isNotEqualTo(userMedia)
+ assertThat(allMediaEntries?.get(KEY)?.active).isFalse()
}
@Test
- fun addAllUserMediaEntry_thenRemove_returnsValue() =
+ fun addMediaEntry_thenRemove_returnsValue() =
testScope.runTest {
- val allUserEntries by collectLastValue(underTest.allUserEntries)
+ val allMediaEntries by collectLastValue(underTest.allMediaEntries)
val userMedia = MediaData()
underTest.addMediaEntry(KEY, userMedia)
- assertThat(allUserEntries?.get(KEY)).isEqualTo(userMedia)
+ assertThat(allMediaEntries?.get(KEY)).isEqualTo(userMedia)
assertThat(underTest.removeMediaEntry(KEY)).isEqualTo(userMedia)
}
@@ -136,17 +136,9 @@
val playingData = createMediaData("app1", true, LOCAL, false, playingInstanceId)
val remoteData = createMediaData("app2", true, REMOTE, false, remoteInstanceId)
- underTest.addSelectedUserMediaEntry(playingData)
- underTest.addMediaDataLoadingState(
- MediaDataLoadingModel.Loaded(playingInstanceId),
- false,
- )
+ underTest.addCurrentUserMediaEntry(playingData)
- underTest.addSelectedUserMediaEntry(remoteData)
- underTest.addMediaDataLoadingState(
- MediaDataLoadingModel.Loaded(remoteInstanceId),
- false,
- )
+ underTest.addCurrentUserMediaEntry(remoteData)
assertThat(currentMedia?.size).isEqualTo(2)
assertThat(currentMedia)
@@ -166,10 +158,8 @@
var playingData1 = createMediaData("app1", true, LOCAL, false, playingInstanceId1)
var playingData2 = createMediaData("app2", false, LOCAL, false, playingInstanceId2)
- underTest.addSelectedUserMediaEntry(playingData1)
- underTest.addMediaDataLoadingState(MediaDataLoadingModel.Loaded(playingInstanceId1))
- underTest.addSelectedUserMediaEntry(playingData2)
- underTest.addMediaDataLoadingState(MediaDataLoadingModel.Loaded(playingInstanceId2))
+ underTest.addCurrentUserMediaEntry(playingData1)
+ underTest.addCurrentUserMediaEntry(playingData2)
assertThat(currentMedia?.size).isEqualTo(2)
assertThat(currentMedia)
@@ -182,10 +172,8 @@
playingData1 = createMediaData("app1", false, LOCAL, false, playingInstanceId1)
playingData2 = createMediaData("app2", true, LOCAL, false, playingInstanceId2)
- underTest.addSelectedUserMediaEntry(playingData1)
- underTest.addMediaDataLoadingState(MediaDataLoadingModel.Loaded(playingInstanceId1))
- underTest.addSelectedUserMediaEntry(playingData2)
- underTest.addMediaDataLoadingState(MediaDataLoadingModel.Loaded(playingInstanceId2))
+ underTest.addCurrentUserMediaEntry(playingData1)
+ underTest.addCurrentUserMediaEntry(playingData2)
assertThat(currentMedia?.size).isEqualTo(2)
assertThat(currentMedia)
@@ -221,20 +209,15 @@
val stoppedAndRemoteData = createMediaData("app4", false, REMOTE, false, instanceId4)
val canResumeData = createMediaData("app5", false, LOCAL, true, instanceId5)
- underTest.addSelectedUserMediaEntry(stoppedAndLocalData)
- underTest.addMediaDataLoadingState(MediaDataLoadingModel.Loaded(instanceId3))
+ underTest.addCurrentUserMediaEntry(stoppedAndLocalData)
- underTest.addSelectedUserMediaEntry(stoppedAndRemoteData)
- underTest.addMediaDataLoadingState(MediaDataLoadingModel.Loaded(instanceId4))
+ underTest.addCurrentUserMediaEntry(stoppedAndRemoteData)
- underTest.addSelectedUserMediaEntry(canResumeData)
- underTest.addMediaDataLoadingState(MediaDataLoadingModel.Loaded(instanceId5))
+ underTest.addCurrentUserMediaEntry(canResumeData)
- underTest.addSelectedUserMediaEntry(playingAndLocalData)
- underTest.addMediaDataLoadingState(MediaDataLoadingModel.Loaded(instanceId1))
+ underTest.addCurrentUserMediaEntry(playingAndLocalData)
- underTest.addSelectedUserMediaEntry(playingAndRemoteData)
- underTest.addMediaDataLoadingState(MediaDataLoadingModel.Loaded(instanceId2))
+ underTest.addCurrentUserMediaEntry(playingAndRemoteData)
underTest.setOrderedMedia()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaCarouselInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaCarouselInteractorTest.kt
index 0b146cc..50a2a93 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaCarouselInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaCarouselInteractorTest.kt
@@ -26,7 +26,6 @@
import com.android.systemui.media.controls.domain.pipeline.interactor.MediaCarouselInteractor
import com.android.systemui.media.controls.domain.pipeline.interactor.mediaCarouselInteractor
import com.android.systemui.media.controls.shared.model.MediaData
-import com.android.systemui.media.controls.shared.model.MediaDataLoadingModel
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runTest
@@ -58,13 +57,13 @@
val userMedia = MediaData(active = true)
- mediaFilterRepository.addSelectedUserMediaEntry(userMedia)
+ mediaFilterRepository.addCurrentUserMediaEntry(userMedia)
assertThat(hasActiveMedia).isTrue()
assertThat(underTest.hasActiveMedia()).isTrue()
assertThat(underTest.hasAnyMedia()).isTrue()
- mediaFilterRepository.addSelectedUserMediaEntry(userMedia.copy(active = false))
+ mediaFilterRepository.addCurrentUserMediaEntry(userMedia.copy(active = false))
assertThat(hasActiveMedia).isFalse()
assertThat(underTest.hasActiveMedia()).isFalse()
@@ -79,18 +78,14 @@
val userMedia = MediaData(active = false)
val instanceId = userMedia.instanceId
- mediaFilterRepository.addSelectedUserMediaEntry(userMedia)
- mediaFilterRepository.addMediaDataLoadingState(MediaDataLoadingModel.Loaded(instanceId))
+ mediaFilterRepository.addCurrentUserMediaEntry(userMedia)
assertThat(hasActiveMedia).isFalse()
assertThat(underTest.hasActiveMedia()).isFalse()
assertThat(underTest.hasAnyMedia()).isTrue()
- assertThat(mediaFilterRepository.removeSelectedUserMediaEntry(instanceId, userMedia))
+ assertThat(mediaFilterRepository.removeCurrentUserMediaEntry(instanceId, userMedia))
.isTrue()
- mediaFilterRepository.addMediaDataLoadingState(
- MediaDataLoadingModel.Removed(instanceId)
- )
assertThat(hasActiveMedia).isFalse()
assertThat(underTest.hasActiveMedia()).isFalse()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataCombineLatestTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataCombineLatestTest.java
index 1065b30..1789c50 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataCombineLatestTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataCombineLatestTest.java
@@ -34,7 +34,7 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.media.controls.shared.model.MediaData;
import com.android.systemui.media.controls.shared.model.MediaDeviceData;
-import com.android.systemui.media.controls.shared.model.SuggestedMediaDeviceData;
+import com.android.systemui.media.controls.shared.model.SuggestionData;
import org.junit.Before;
import org.junit.Rule;
@@ -69,7 +69,7 @@
private MediaData mMediaData;
private MediaDeviceData mDeviceData;
- @Mock private SuggestedMediaDeviceData mSuggestedDeviceData;
+ @Mock private SuggestionData mSuggestionData;
@Before
public void setUp() {
@@ -157,12 +157,12 @@
mManager.onMediaDeviceChanged(KEY, OLD_KEY, mDeviceData);
reset(mListener);
// WHEN suggestion event is received
- mManager.onSuggestedMediaDeviceChanged(KEY, null, mSuggestedDeviceData);
+ mManager.onSuggestionDataChanged(KEY, null, mSuggestionData);
// THEN the listener receives a combined event
ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class);
verify(mListener)
.onMediaDataLoaded(eq(KEY), any(), captor.capture(), anyBoolean());
- assertThat(captor.getValue().getSuggestedDevice()).isNotNull();
+ assertThat(captor.getValue().getSuggestionData()).isNotNull();
}
@Test
@@ -198,16 +198,16 @@
// GIVEN that media and device info has already been received
mManager.onMediaDataLoaded(OLD_KEY, null, mMediaData, true /* immediately */);
mManager.onMediaDeviceChanged(OLD_KEY, null, mDeviceData);
- mManager.onSuggestedMediaDeviceChanged(OLD_KEY, null, mSuggestedDeviceData);
+ mManager.onSuggestionDataChanged(OLD_KEY, null, mSuggestionData);
reset(mListener);
// WHEN a key migration event is received
- mManager.onSuggestedMediaDeviceChanged(KEY, OLD_KEY, mSuggestedDeviceData);
+ mManager.onSuggestionDataChanged(KEY, OLD_KEY, mSuggestionData);
// THEN the listener receives a combined event
ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class);
verify(mListener)
.onMediaDataLoaded(
eq(KEY), eq(OLD_KEY), captor.capture(), anyBoolean());
- assertThat(captor.getValue().getSuggestedDevice()).isNotNull();
+ assertThat(captor.getValue().getSuggestionData()).isNotNull();
}
@Test
@@ -245,16 +245,16 @@
// GIVEN that media and device info has already been received
mManager.onMediaDataLoaded(OLD_KEY, null, mMediaData, true /* immediately */);
mManager.onMediaDeviceChanged(OLD_KEY, null, mDeviceData);
- mManager.onSuggestedMediaDeviceChanged(OLD_KEY, null, mSuggestedDeviceData);
+ mManager.onSuggestionDataChanged(OLD_KEY, null, mSuggestionData);
mManager.onMediaDataLoaded(KEY, OLD_KEY, mMediaData, true /* immediately */);
reset(mListener);
// WHEN a second key migration event is received for the device
- mManager.onSuggestedMediaDeviceChanged(KEY, OLD_KEY, mSuggestedDeviceData);
+ mManager.onSuggestionDataChanged(KEY, OLD_KEY, mSuggestionData);
// THEN the key has already be migrated
ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class);
verify(mListener)
.onMediaDataLoaded(eq(KEY), eq(KEY), captor.capture(), anyBoolean());
- assertThat(captor.getValue().getSuggestedDevice()).isNotNull();
+ assertThat(captor.getValue().getSuggestionData()).isNotNull();
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/view/MediaCarouselScrollHandlerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/view/MediaCarouselScrollHandlerTest.kt
index 8592c42..bdd6f9f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/view/MediaCarouselScrollHandlerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/view/MediaCarouselScrollHandlerTest.kt
@@ -34,6 +34,7 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers.anyFloat
import org.mockito.Mock
import org.mockito.Mockito.anyInt
@@ -61,6 +62,7 @@
@Mock lateinit var seekBarUpdateListener: (visibleToUser: Boolean) -> Unit
@Mock lateinit var closeGuts: (immediate: Boolean) -> Unit
@Mock lateinit var falsingManager: FalsingManager
+ @Mock lateinit var onCarouselVisibleToUser: () -> Unit
@Mock lateinit var logger: MediaUiEventLogger
@Mock lateinit var contentContainer: ViewGroup
@Mock lateinit var settingsButton: View
@@ -90,6 +92,7 @@
seekBarUpdateListener,
closeGuts,
falsingManager,
+ onCarouselVisibleToUser,
logger,
)
mediaCarouselScrollHandler.playerWidthPlusPadding = carouselWidth
@@ -248,6 +251,19 @@
verify(mediaCarousel, never()).animationTargetX = anyFloat()
}
+ @Test
+ fun testCarouselScrollToNewIndex_onCarouselVisibleToUser() {
+ setupMediaContainer(visibleIndex = 0)
+ whenever(mediaCarousel.relativeScrollX).thenReturn(carouselWidth)
+ mediaCarouselScrollHandler.visibleToUser = true
+ val captor = ArgumentCaptor.forClass(View.OnScrollChangeListener::class.java)
+ verify(mediaCarousel).setOnScrollChangeListener(captor.capture())
+
+ captor.value.onScrollChange(null, 0, 0, 0, 0)
+
+ verify(onCarouselVisibleToUser).invoke()
+ }
+
private fun setupMediaContainer(visibleIndex: Int, showsSettingsButton: Boolean = true) {
whenever(contentContainer.childCount).thenReturn(2)
val child1: View = mock()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/remedia/data/repository/MediaRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/remedia/data/repository/MediaRepositoryTest.kt
new file mode 100644
index 0000000..e7440ab
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/remedia/data/repository/MediaRepositoryTest.kt
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.media.remedia.data.repository
+
+import android.media.session.MediaController
+import android.media.session.MediaSession
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.internal.logging.InstanceId
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.media.controls.shared.model.MediaData
+import com.android.systemui.media.remedia.data.model.MediaDataModel
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class MediaRepositoryTest : SysuiTestCase() {
+
+ private val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
+ private val session = MediaSession(context, "MediaRepositoryTestSession")
+
+ private val underTest: MediaRepositoryImpl = kosmos.mediaRepository
+
+ @Test
+ fun addCurrentUserMediaEntry_activeThenInactivate() =
+ testScope.runTest {
+ val currentUserEntries by collectLastValue(underTest.currentUserEntries)
+
+ val instanceId = InstanceId.fakeInstanceId(123)
+ val userMedia =
+ MediaData()
+ .copy(token = session.sessionToken, active = true, instanceId = instanceId)
+
+ underTest.addCurrentUserMediaEntry(userMedia)
+
+ assertThat(currentUserEntries?.get(instanceId)).isEqualTo(userMedia)
+
+ underTest.addCurrentUserMediaEntry(userMedia.copy(active = false))
+
+ assertThat(currentUserEntries?.get(instanceId)).isNotEqualTo(userMedia)
+ assertThat(currentUserEntries?.get(instanceId)?.active).isFalse()
+ }
+
+ @Test
+ fun addCurrentUserMediaEntry_thenRemove_returnsBoolean() =
+ testScope.runTest {
+ val currentUserEntries by collectLastValue(underTest.currentUserEntries)
+
+ val instanceId = InstanceId.fakeInstanceId(123)
+ val userMedia = MediaData().copy(token = session.sessionToken, instanceId = instanceId)
+
+ underTest.addCurrentUserMediaEntry(userMedia)
+
+ assertThat(currentUserEntries?.get(instanceId)).isEqualTo(userMedia)
+ assertThat(underTest.removeCurrentUserMediaEntry(instanceId, userMedia)).isTrue()
+ }
+
+ @Test
+ fun addCurrentUserMediaEntry_thenRemove_returnsValue() =
+ testScope.runTest {
+ val currentUserEntries by collectLastValue(underTest.currentUserEntries)
+
+ val instanceId = InstanceId.fakeInstanceId(123)
+ val userMedia = MediaData().copy(token = session.sessionToken, instanceId = instanceId)
+
+ underTest.addCurrentUserMediaEntry(userMedia)
+
+ assertThat(currentUserEntries?.get(instanceId)).isEqualTo(userMedia)
+
+ assertThat(underTest.removeCurrentUserMediaEntry(instanceId)).isEqualTo(userMedia)
+ }
+
+ @Test
+ fun addMediaEntry_activeThenInactivate() =
+ testScope.runTest {
+ val allMediaEntries by collectLastValue(underTest.allMediaEntries)
+
+ val userMedia = MediaData().copy(active = true)
+
+ underTest.addMediaEntry(KEY, userMedia)
+
+ assertThat(allMediaEntries?.get(KEY)).isEqualTo(userMedia)
+
+ underTest.addMediaEntry(KEY, userMedia.copy(active = false))
+
+ assertThat(allMediaEntries?.get(KEY)).isNotEqualTo(userMedia)
+ assertThat(allMediaEntries?.get(KEY)?.active).isFalse()
+ }
+
+ @Test
+ fun addMediaEntry_thenRemove_returnsValue() =
+ testScope.runTest {
+ val allMediaEntries by collectLastValue(underTest.allMediaEntries)
+
+ val userMedia = MediaData()
+
+ underTest.addMediaEntry(KEY, userMedia)
+
+ assertThat(allMediaEntries?.get(KEY)).isEqualTo(userMedia)
+
+ assertThat(underTest.removeMediaEntry(KEY)).isEqualTo(userMedia)
+ }
+
+ @Test
+ fun addMediaControlPlayingThenRemote() =
+ testScope.runTest {
+ val currentMedia by collectLastValue(underTest.currentMedia)
+ val playingInstanceId = InstanceId.fakeInstanceId(123)
+ val remoteInstanceId = InstanceId.fakeInstanceId(321)
+ val playingData = createMediaData("app1", true, LOCAL, false, playingInstanceId)
+ val remoteData = createMediaData("app2", true, REMOTE, false, remoteInstanceId)
+
+ underTest.addCurrentUserMediaEntry(playingData)
+
+ underTest.addCurrentUserMediaEntry(remoteData)
+
+ assertThat(currentMedia?.size).isEqualTo(2)
+ assertThat(currentMedia)
+ .containsExactly(
+ playingData.toDataModel(currentMedia!![0].controller),
+ remoteData.toDataModel(currentMedia!![1].controller),
+ )
+ .inOrder()
+ }
+
+ @Test
+ fun switchMediaControlsPlaying() =
+ testScope.runTest {
+ val currentMedia by collectLastValue(underTest.currentMedia)
+ val playingInstanceId1 = InstanceId.fakeInstanceId(123)
+ val playingInstanceId2 = InstanceId.fakeInstanceId(321)
+ var playingData1 = createMediaData("app1", true, LOCAL, false, playingInstanceId1)
+ var playingData2 = createMediaData("app2", false, LOCAL, false, playingInstanceId2)
+
+ underTest.addCurrentUserMediaEntry(playingData1)
+ underTest.addCurrentUserMediaEntry(playingData2)
+
+ assertThat(currentMedia?.size).isEqualTo(2)
+ assertThat(currentMedia)
+ .containsExactly(
+ playingData1.toDataModel(currentMedia!![0].controller),
+ playingData2.toDataModel(currentMedia!![1].controller),
+ )
+ .inOrder()
+
+ playingData1 = createMediaData("app1", false, LOCAL, false, playingInstanceId1)
+ playingData2 = createMediaData("app2", true, LOCAL, false, playingInstanceId2)
+
+ underTest.addCurrentUserMediaEntry(playingData1)
+ underTest.addCurrentUserMediaEntry(playingData2)
+
+ assertThat(currentMedia?.size).isEqualTo(2)
+ assertThat(currentMedia)
+ .containsExactly(
+ playingData1.toDataModel(currentMedia!![0].controller),
+ playingData2.toDataModel(currentMedia!![1].controller),
+ )
+ .inOrder()
+
+ underTest.reorderMedia()
+
+ assertThat(currentMedia?.size).isEqualTo(2)
+ assertThat(currentMedia)
+ .containsExactly(
+ playingData2.toDataModel(currentMedia!![0].controller),
+ playingData1.toDataModel(currentMedia!![1].controller),
+ )
+ .inOrder()
+ }
+
+ @Test
+ fun fullOrderTest() =
+ testScope.runTest {
+ val currentMedia by collectLastValue(underTest.currentMedia)
+ val instanceId1 = InstanceId.fakeInstanceId(123)
+ val instanceId2 = InstanceId.fakeInstanceId(456)
+ val instanceId3 = InstanceId.fakeInstanceId(321)
+ val instanceId4 = InstanceId.fakeInstanceId(654)
+ val instanceId5 = InstanceId.fakeInstanceId(124)
+ val playingAndLocalData = createMediaData("app1", true, LOCAL, false, instanceId1)
+ val playingAndRemoteData = createMediaData("app2", true, REMOTE, false, instanceId2)
+ val stoppedAndLocalData = createMediaData("app3", false, LOCAL, false, instanceId3)
+ val stoppedAndRemoteData = createMediaData("app4", false, REMOTE, false, instanceId4)
+ val canResumeData = createMediaData("app5", false, LOCAL, true, instanceId5)
+
+ underTest.addCurrentUserMediaEntry(stoppedAndLocalData)
+
+ underTest.addCurrentUserMediaEntry(stoppedAndRemoteData)
+
+ underTest.addCurrentUserMediaEntry(canResumeData)
+
+ underTest.addCurrentUserMediaEntry(playingAndLocalData)
+
+ underTest.addCurrentUserMediaEntry(playingAndRemoteData)
+
+ underTest.reorderMedia()
+
+ assertThat(currentMedia?.size).isEqualTo(5)
+ assertThat(currentMedia)
+ .containsExactly(
+ playingAndLocalData.toDataModel(currentMedia!![0].controller),
+ playingAndRemoteData.toDataModel(currentMedia!![1].controller),
+ stoppedAndRemoteData.toDataModel(currentMedia!![2].controller),
+ stoppedAndLocalData.toDataModel(currentMedia!![3].controller),
+ canResumeData.toDataModel(currentMedia!![4].controller),
+ )
+ .inOrder()
+ }
+
+ private fun createMediaData(
+ app: String,
+ playing: Boolean,
+ playbackLocation: Int,
+ isResume: Boolean,
+ instanceId: InstanceId,
+ ): MediaData {
+ return MediaData(
+ token = session.sessionToken,
+ playbackLocation = playbackLocation,
+ resumption = isResume,
+ notificationKey = "key: $app",
+ isPlaying = playing,
+ instanceId = instanceId,
+ )
+ }
+
+ private fun MediaData.toDataModel(mediaController: MediaController): MediaDataModel {
+ return MediaDataModel(
+ instanceId = instanceId,
+ appUid = appUid,
+ packageName = packageName,
+ appName = app.toString(),
+ appIcon = null,
+ background = null,
+ title = song.toString(),
+ subtitle = artist.toString(),
+ notificationActions = actions,
+ playbackStateActions = semanticActions,
+ outputDevice = device,
+ clickIntent = clickIntent,
+ controller = mediaController,
+ canBeDismissed = isClearable,
+ isActive = active,
+ isResume = resumption,
+ resumeAction = resumeAction,
+ isExplicit = isExplicit,
+ )
+ }
+
+ companion object {
+ private const val LOCAL = MediaData.PLAYBACK_LOCAL
+ private const val REMOTE = MediaData.PLAYBACK_CAST_LOCAL
+ private const val KEY = "KEY"
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepositoryTest.kt
index 63942072..2d0ff3c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepositoryTest.kt
@@ -29,7 +29,6 @@
import android.view.Display
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.Flags.FLAG_STATUS_BAR_SHOW_AUDIO_ONLY_PROJECTION_CHIP
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.applicationCoroutineScope
@@ -82,19 +81,7 @@
}
@Test
- @DisableFlags(FLAG_STATUS_BAR_SHOW_AUDIO_ONLY_PROJECTION_CHIP)
- fun mediaProjectionState_onStart_flagOff_emitsNotProjecting() =
- testScope.runTest {
- val state by collectLastValue(repo.mediaProjectionState)
-
- fakeMediaProjectionManager.dispatchOnStart()
-
- assertThat(state).isEqualTo(MediaProjectionState.NotProjecting)
- }
-
- @Test
- @EnableFlags(FLAG_STATUS_BAR_SHOW_AUDIO_ONLY_PROJECTION_CHIP)
- fun mediaProjectionState_onStart_flagOn_emitsProjectingNoScreen() =
+ fun mediaProjectionState_onStart_emitsProjectingNoScreen() =
testScope.runTest {
val state by collectLastValue(repo.mediaProjectionState)
@@ -104,7 +91,6 @@
}
@Test
- @EnableFlags(FLAG_STATUS_BAR_SHOW_AUDIO_ONLY_PROJECTION_CHIP)
fun mediaProjectionState_noScreen_hasHostPackage() =
testScope.runTest {
val state by collectLastValue(repo.mediaProjectionState)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/model/SceneContainerPluginTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/model/SceneContainerPluginTest.kt
index b2e29cf..08cf541 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/model/SceneContainerPluginTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/model/SceneContainerPluginTest.kt
@@ -41,7 +41,7 @@
private val shadeDisplayRepository = kosmos.fakeShadeDisplaysRepository
private val sceneDataSource = kosmos.fakeSceneDataSource
- private val underTest = kosmos.sceneContainerPlugin
+ private val underTest: SceneContainerPlugin = kosmos.sceneContainerPluginImpl
@Test
@EnableFlags(Flags.FLAG_SHADE_WINDOW_GOES_AROUND)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/model/SysUiStateTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/model/SysUiStateTest.java
deleted file mode 100644
index 5bb7f36..0000000
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/model/SysUiStateTest.java
+++ /dev/null
@@ -1,165 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.model;
-
-
-import static android.view.Display.DEFAULT_DISPLAY;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.eq;
-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 android.view.Display;
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.dump.DumpManager;
-import com.android.systemui.kosmos.KosmosJavaAdapter;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class SysUiStateTest extends SysuiTestCase {
- private static final int FLAG_1 = 1;
- private static final int FLAG_2 = 1 << 1;
- private static final int FLAG_3 = 1 << 2;
- private static final int FLAG_4 = 1 << 3;
- private static final int DISPLAY_ID = DEFAULT_DISPLAY;
-
- private KosmosJavaAdapter mKosmos;
- private SysUiState.SysUiStateCallback mCallback;
- private SysUiState mFlagsContainer;
- private SceneContainerPlugin mSceneContainerPlugin;
- private DumpManager mDumpManager;
- private SysUIStateDispatcher mSysUIStateDispatcher;
-
- private SysUiState createInstance(int displayId) {
- var sysuiState = new SysUiStateImpl(displayId, mSceneContainerPlugin, mDumpManager,
- mSysUIStateDispatcher);
- sysuiState.addCallback(mCallback);
- return sysuiState;
- }
-
- @Before
- public void setup() {
- mKosmos = new KosmosJavaAdapter(this);
- mFlagsContainer = mKosmos.getSysuiState();
- mSceneContainerPlugin = mKosmos.getSceneContainerPlugin();
- mCallback = mock(SysUiState.SysUiStateCallback.class);
- mDumpManager = mock(DumpManager.class);
- mSysUIStateDispatcher = mKosmos.getSysUIStateDispatcher();
- mFlagsContainer = createInstance(DEFAULT_DISPLAY);
- }
-
- @Test
- public void addSingle_setFlag() {
- setFlags(FLAG_1);
-
- verify(mCallback, times(1)).onSystemUiStateChanged(FLAG_1, DEFAULT_DISPLAY);
- }
-
- @Test
- public void addMultiple_setFlag() {
- setFlags(FLAG_1);
- setFlags(FLAG_2);
-
- verify(mCallback, times(1)).onSystemUiStateChanged(FLAG_1, DEFAULT_DISPLAY);
- verify(mCallback, times(1)).onSystemUiStateChanged(FLAG_1 | FLAG_2, DEFAULT_DISPLAY);
- }
-
- @Test
- public void addMultipleRemoveOne_setFlag() {
- setFlags(FLAG_1);
- setFlags(FLAG_2);
- mFlagsContainer.setFlag(FLAG_1, false).commitUpdate(DISPLAY_ID);
-
- verify(mCallback, times(1)).onSystemUiStateChanged(FLAG_1, DEFAULT_DISPLAY);
- verify(mCallback, times(1)).onSystemUiStateChanged(FLAG_1 | FLAG_2, DEFAULT_DISPLAY);
- verify(mCallback, times(1)).onSystemUiStateChanged(FLAG_2, DEFAULT_DISPLAY);
- }
-
- @Test
- public void addMultiple_setFlags() {
- setFlags(FLAG_1, FLAG_2, FLAG_3, FLAG_4);
-
- int expected = FLAG_1 | FLAG_2 | FLAG_3 | FLAG_4;
- verify(mCallback, times(1)).onSystemUiStateChanged(expected, DEFAULT_DISPLAY);
- }
-
- @Test
- public void addMultipleRemoveOne_setFlags() {
- setFlags(FLAG_1, FLAG_2, FLAG_3, FLAG_4);
- mFlagsContainer.setFlag(FLAG_2, false).commitUpdate(DISPLAY_ID);
-
- int expected1 = FLAG_1 | FLAG_2 | FLAG_3 | FLAG_4;
- verify(mCallback, times(1)).onSystemUiStateChanged(expected1, DEFAULT_DISPLAY);
- int expected2 = FLAG_1 | FLAG_3 | FLAG_4;
- verify(mCallback, times(1)).onSystemUiStateChanged(expected2, DEFAULT_DISPLAY);
- }
-
- @Test
- public void removeCallback() {
- mFlagsContainer.removeCallback(mCallback);
- setFlags(FLAG_1, FLAG_2, FLAG_3, FLAG_4);
-
- int expected = FLAG_1 | FLAG_2 | FLAG_3 | FLAG_4;
- verify(mCallback, times(0)).onSystemUiStateChanged(expected, DEFAULT_DISPLAY);
- }
-
- @Test
- public void setFlag_receivedForDefaultDisplay() {
- setFlags(FLAG_1);
-
- verify(mCallback, times(1)).onSystemUiStateChanged(FLAG_1, DEFAULT_DISPLAY);
- }
-
-
- @Test
- public void init_registersWithDumpManager() {
- mFlagsContainer.start();
-
- verify(mDumpManager).registerNormalDumpable(any(), eq(mFlagsContainer));
- }
-
- @Test
- public void destroy_unregistersWithDumpManager() {
- mFlagsContainer.destroy();
-
- verify(mDumpManager).unregisterDumpable(anyString());
- }
-
- private void setFlags(int... flags) {
- setFlags(mFlagsContainer, flags);
- }
-
- private void setFlags(SysUiState instance, int... flags) {
- for (int flag : flags) {
- instance.setFlag(flag, true);
- }
- instance.commitUpdate();
- }
-}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/model/SysUiStateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/model/SysUiStateTest.kt
new file mode 100644
index 0000000..009193a
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/model/SysUiStateTest.kt
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.model
+
+import android.view.Display
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.dump.dumpManager
+import com.android.systemui.kosmos.runTest
+import com.android.systemui.model.SysUiState.SysUiStateCallback
+import com.android.systemui.testKosmos
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyString
+import org.mockito.kotlin.any
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.times
+import org.mockito.kotlin.verify
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+open class SysUiStateTest : SysuiTestCase() {
+
+ private val kosmos = testKosmos()
+ private val callback: SysUiStateCallback = mock()
+
+ private lateinit var underTest: SysUiState
+
+ @Before
+ fun setup() {
+ underTest = createInstance(Display.DEFAULT_DISPLAY)
+ }
+
+ @Test
+ fun addSingle_setFlag() =
+ kosmos.runTest {
+ setFlags(FLAG_1)
+
+ verify(callback, times(1)).onSystemUiStateChanged(FLAG_1, Display.DEFAULT_DISPLAY)
+ }
+
+ @Test
+ fun addMultiple_setFlag() =
+ kosmos.runTest {
+ setFlags(FLAG_1)
+ setFlags(FLAG_2)
+
+ verify(callback, times(1)).onSystemUiStateChanged(FLAG_1, Display.DEFAULT_DISPLAY)
+ verify(callback, times(1))
+ .onSystemUiStateChanged((FLAG_1 or FLAG_2), Display.DEFAULT_DISPLAY)
+ }
+
+ @Test
+ fun addMultipleRemoveOne_setFlag() =
+ kosmos.runTest {
+ setFlags(FLAG_1)
+ setFlags(FLAG_2)
+ underTest.setFlag(FLAG_1, false).commitUpdate(DISPLAY_ID)
+
+ verify(callback, times(1)).onSystemUiStateChanged(FLAG_1, Display.DEFAULT_DISPLAY)
+ verify(callback, times(1))
+ .onSystemUiStateChanged((FLAG_1 or FLAG_2), Display.DEFAULT_DISPLAY)
+ verify(callback, times(1)).onSystemUiStateChanged(FLAG_2, Display.DEFAULT_DISPLAY)
+ }
+
+ @Test
+ fun addMultiple_setFlags() =
+ kosmos.runTest {
+ setFlags(FLAG_1, FLAG_2, FLAG_3, FLAG_4)
+
+ val expected = FLAG_1 or FLAG_2 or FLAG_3 or FLAG_4
+ verify(callback, times(1)).onSystemUiStateChanged(expected, Display.DEFAULT_DISPLAY)
+ }
+
+ @Test
+ fun addMultipleRemoveOne_setFlags() =
+ kosmos.runTest {
+ setFlags(FLAG_1, FLAG_2, FLAG_3, FLAG_4)
+ underTest.setFlag(FLAG_2, false).commitUpdate(DISPLAY_ID)
+
+ val expected1 = FLAG_1 or FLAG_2 or FLAG_3 or FLAG_4
+ verify(callback, times(1)).onSystemUiStateChanged(expected1, Display.DEFAULT_DISPLAY)
+ val expected2 = FLAG_1 or FLAG_3 or FLAG_4
+ verify(callback, times(1)).onSystemUiStateChanged(expected2, Display.DEFAULT_DISPLAY)
+ }
+
+ @Test
+ fun removeCallback() =
+ kosmos.runTest {
+ underTest.removeCallback(callback)
+ setFlags(FLAG_1, FLAG_2, FLAG_3, FLAG_4)
+
+ val expected = FLAG_1 or FLAG_2 or FLAG_3 or FLAG_4
+ verify(callback, times(0)).onSystemUiStateChanged(expected, Display.DEFAULT_DISPLAY)
+ }
+
+ @Test
+ fun setFlag_receivedForDefaultDisplay() =
+ kosmos.runTest {
+ setFlags(FLAG_1)
+
+ verify(callback, times(1)).onSystemUiStateChanged(FLAG_1, Display.DEFAULT_DISPLAY)
+ }
+
+ @Test
+ fun init_registersWithDumpManager() =
+ kosmos.runTest {
+ underTest.start()
+
+ verify(dumpManager).registerNormalDumpable(any(), eq(underTest))
+ }
+
+ @Test
+ fun destroy_unregistersWithDumpManager() =
+ kosmos.runTest {
+ underTest.destroy()
+
+ verify(dumpManager).unregisterDumpable(anyString())
+ }
+
+ private fun createInstance(displayId: Int): SysUiState {
+ return SysUiStateImpl(
+ displayId,
+ kosmos.fakeSceneContainerPlugin,
+ kosmos.dumpManager,
+ kosmos.sysUIStateDispatcher,
+ )
+ .apply { addCallback(callback) }
+ }
+
+ private fun setFlags(vararg flags: Long) {
+ setFlags(underTest, *flags)
+ }
+
+ private fun setFlags(instance: SysUiState, vararg flags: Long) {
+ for (flag in flags) {
+ instance.setFlag(flag, true)
+ }
+ instance.commitUpdate()
+ }
+
+ companion object {
+ private const val FLAG_1 = 1L
+ private const val FLAG_2 = 1L shl 1
+ private const val FLAG_3 = 1L shl 2
+ private const val FLAG_4 = 1L shl 3
+ private const val DISPLAY_ID = Display.DEFAULT_DISPLAY
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayContentViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayContentViewModelTest.kt
index cd7b658..be191ca 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayContentViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayContentViewModelTest.kt
@@ -115,7 +115,7 @@
@Test
fun showMedia_activeMedia_true() =
testScope.runTest {
- kosmos.mediaFilterRepository.addSelectedUserMediaEntry(MediaData(active = true))
+ kosmos.mediaFilterRepository.addCurrentUserMediaEntry(MediaData(active = true))
runCurrent()
assertThat(underTest.showMedia).isTrue()
@@ -124,7 +124,7 @@
@Test
fun showMedia_InactiveMedia_false() =
testScope.runTest {
- kosmos.mediaFilterRepository.addSelectedUserMediaEntry(MediaData(active = false))
+ kosmos.mediaFilterRepository.addCurrentUserMediaEntry(MediaData(active = false))
runCurrent()
assertThat(underTest.showMedia).isFalse()
@@ -133,8 +133,8 @@
@Test
fun showMedia_noMedia_false() =
testScope.runTest {
- kosmos.mediaFilterRepository.addSelectedUserMediaEntry(MediaData(active = true))
- kosmos.mediaFilterRepository.clearSelectedUserMedia()
+ kosmos.mediaFilterRepository.addCurrentUserMediaEntry(MediaData(active = true))
+ kosmos.mediaFilterRepository.clearCurrentUserMedia()
runCurrent()
assertThat(underTest.showMedia).isFalse()
@@ -143,7 +143,7 @@
@Test
fun showMedia_qsDisabled_false() =
testScope.runTest {
- kosmos.mediaFilterRepository.addSelectedUserMediaEntry(MediaData(active = true))
+ kosmos.mediaFilterRepository.addCurrentUserMediaEntry(MediaData(active = true))
kosmos.fakeDisableFlagsRepository.disableFlags.update {
it.copy(disable2 = DISABLE2_QUICK_SETTINGS)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/FgsManagerControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/FgsManagerControllerTest.java
index 3d9fb0a..8c20d53 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/FgsManagerControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/FgsManagerControllerTest.java
@@ -42,7 +42,6 @@
import android.os.Binder;
import android.os.RemoteException;
import android.os.UserHandle;
-import android.platform.test.annotations.EnableFlags;
import android.provider.DeviceConfig;
import android.testing.TestableLooper;
@@ -50,7 +49,6 @@
import androidx.test.filters.SmallTest;
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
-import com.android.systemui.Flags;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.DialogTransitionAnimator;
import com.android.systemui.broadcast.BroadcastDispatcher;
@@ -320,7 +318,6 @@
}
@Test
- @EnableFlags(Flags.FLAG_STOPPABLE_FGS_SYSTEM_APP)
public void testButtonVisibilityOfStoppableApps() throws Exception {
setUserProfiles(0);
setBackgroundRestrictionExemptionReason("pkg", 12345, REASON_ALLOWLISTED_PACKAGE);
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/IconAndNameCustomRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/IconAndNameCustomRepositoryTest.kt
index 93ba265..7d8501a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/IconAndNameCustomRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/IconAndNameCustomRepositoryTest.kt
@@ -62,6 +62,7 @@
tileService1,
drawable1,
appName1,
+ appIcon1,
)
private val service2 =
@@ -70,6 +71,7 @@
tileService2,
drawable2,
appName2,
+ appIcon2,
)
private val underTest =
@@ -78,6 +80,7 @@
installedTilesRepository,
userTracker,
mainCoroutineContext,
+ appIconRepositoryFactory,
)
}
@@ -85,7 +88,7 @@
fun setUp() {
kosmos.fakeInstalledTilesRepository.setInstalledServicesForUser(
userTracker.userId,
- listOf(service1, service2)
+ listOf(service1, service2),
)
}
@@ -100,6 +103,7 @@
Icon.Loaded(drawable1, ContentDescription.Loaded(tileService1)),
Text.Loaded(tileService1),
Text.Loaded(appName1),
+ Icon.Loaded(appIcon1, ContentDescription.Loaded(null)),
TileCategory.PROVIDED_BY_APP,
)
val expectedData2 =
@@ -108,6 +112,7 @@
Icon.Loaded(drawable2, ContentDescription.Loaded(tileService2)),
Text.Loaded(tileService2),
Text.Loaded(appName2),
+ Icon.Loaded(appIcon2, ContentDescription.Loaded(null)),
TileCategory.PROVIDED_BY_APP,
)
@@ -131,13 +136,10 @@
with(kosmos) {
testScope.runTest {
val serviceNullIcon =
- FakeInstalledTilesComponentRepository.ServiceInfo(
- component2,
- tileService2,
- )
+ FakeInstalledTilesComponentRepository.ServiceInfo(component2, tileService2)
fakeInstalledTilesRepository.setInstalledServicesForUser(
userTracker.userId,
- listOf(service1, serviceNullIcon)
+ listOf(service1, serviceNullIcon),
)
val expectedData1 =
@@ -146,6 +148,7 @@
Icon.Loaded(drawable1, ContentDescription.Loaded(tileService1)),
Text.Loaded(tileService1),
Text.Loaded(appName1),
+ Icon.Loaded(appIcon1, ContentDescription.Loaded(null)),
TileCategory.PROVIDED_BY_APP,
)
@@ -156,11 +159,13 @@
private companion object {
val drawable1 = TestStubDrawable("drawable1")
+ val appIcon1 = TestStubDrawable("appIcon1")
val appName1 = "App1"
val tileService1 = "Tile Service 1"
val component1 = ComponentName("pkg1", "srv1")
val drawable2 = TestStubDrawable("drawable2")
+ val appIcon2 = TestStubDrawable("appIcon2")
val appName2 = "App2"
val tileService2 = "Tile Service 2"
val component2 = ComponentName("pkg2", "srv2")
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/EditTilesListInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/EditTilesListInteractorTest.kt
index 5f8adf0..49cc799 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/EditTilesListInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/EditTilesListInteractorTest.kt
@@ -129,6 +129,7 @@
label = Text.Loaded(tileName),
appName = Text.Loaded(appName),
category = TileCategory.PROVIDED_BY_APP,
+ appIcon = null,
)
assertThat(editTiles.customTiles).hasSize(1)
@@ -180,6 +181,7 @@
label = Text.Loaded(spec),
appName = null,
category = TileCategory.UNKNOWN,
+ appIcon = null,
)
}
@@ -191,6 +193,7 @@
label = Text.Resource(uiConfig.labelRes),
appName = null,
category = category,
+ appIcon = null,
)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/EditTileListStateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/EditTileListStateTest.kt
index 81e6b9d..88228a9 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/EditTileListStateTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/EditTileListStateTest.kt
@@ -230,7 +230,9 @@
label = AnnotatedString("unused"),
appName = null,
isCurrent = true,
+ isDualTarget = false,
availableEditActions = emptySet(),
+ appIcon = null,
category = TileCategory.UNKNOWN,
)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModelTest.kt
index 71ab0a2..345dd8d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModelTest.kt
@@ -30,9 +30,10 @@
import com.android.systemui.common.ui.compose.toAnnotatedString
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.kosmos.runCurrent
import com.android.systemui.kosmos.runTest
import com.android.systemui.kosmos.testScope
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
+import com.android.systemui.plugins.qs.QSTile
import com.android.systemui.qs.FakeQSFactory
import com.android.systemui.qs.FakeQSTile
import com.android.systemui.qs.QSEditEvent
@@ -64,7 +65,6 @@
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOf
-import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
@@ -80,7 +80,7 @@
mSetFlagsRule.setFlagsParameterization(flags)
}
- private val kosmos = testKosmos()
+ private val kosmos = testKosmos().useUnconfinedTestDispatcher()
// Only have some configurations so we can test the effect of missing configurations.
// As the configurations are injected by dagger, we'll have all the existing configurations
@@ -514,7 +514,6 @@
)
currentTilesInteractor.setTiles(currentTiles)
underTest.startEditing()
- runCurrent()
// Move flashlight tile to index 3
underTest.addTile(TileSpec.create("flashlight"), 3)
@@ -539,7 +538,6 @@
)
currentTilesInteractor.setTiles(currentTiles)
underTest.startEditing()
- runCurrent()
// Move alarm tile to index 0
underTest.addTile(TileSpec.create("alarm"), 0)
@@ -588,7 +586,6 @@
val internetTile = TileSpec.create("internet")
val customTile = TileSpec.create(component2)
currentTilesInteractor.setTiles(listOf(flashlightTile))
- runCurrent()
underTest.addTile(airplaneTile)
underTest.addTile(internetTile, position = 0)
@@ -620,7 +617,6 @@
val airplaneTile = TileSpec.create("airplane")
val internetTile = TileSpec.create("internet")
currentTilesInteractor.setTiles(listOf(flashlightTile, airplaneTile, internetTile))
- runCurrent()
underTest.addTile(flashlightTile) // adding at the end, should use correct position
underTest.addTile(internetTile, 0)
@@ -647,7 +643,6 @@
val airplaneTile = TileSpec.create("airplane")
val internetTile = TileSpec.create("internet")
currentTilesInteractor.setTiles(listOf(flashlightTile, airplaneTile, internetTile))
- runCurrent()
underTest.removeTile(airplaneTile)
@@ -668,19 +663,15 @@
val alarmTile = TileSpec.create("alarm")
currentTilesInteractor.setTiles(listOf(flashlightTile, airplaneTile, internetTile))
- runCurrent()
// 0. Move flashlightTile to position 2
underTest.setTiles(listOf(airplaneTile, internetTile, flashlightTile))
- runCurrent()
// 1. Add alarm tile at position 1
underTest.setTiles(listOf(airplaneTile, alarmTile, internetTile, flashlightTile))
- runCurrent()
// 2. Remove internetTile
underTest.setTiles(listOf(airplaneTile, alarmTile, flashlightTile))
- runCurrent()
assertThat(uiEventLoggerFake.numLogs()).isEqualTo(3)
@@ -700,6 +691,65 @@
}
}
+ @Test
+ fun currentTiles_checksForDualTarget() =
+ with(kosmos) {
+ testScope.runTest {
+ val tiles by collectLastValue(underTest.tiles)
+ val currentTiles =
+ mutableListOf(
+ TileSpec.create("flashlight"),
+ TileSpec.create("airplane"),
+ TileSpec.create("internet"),
+ TileSpec.create("alarm"),
+ )
+ currentTilesInteractor.setTiles(currentTiles)
+
+ val tile =
+ currentTilesInteractor.currentTiles.value.find { it.spec.spec == "flashlight" }
+
+ (tile!!.tile as FakeQSTile).changeState(
+ QSTile.State().apply { handlesSecondaryClick = true }
+ )
+
+ underTest.startEditing()
+
+ assertThat(tiles!!.filter { it.isDualTarget }.map { it.tileSpec.spec })
+ .containsExactly("flashlight")
+ }
+ }
+
+ @Test
+ fun removeTile_noLongerDualTarget() =
+ with(kosmos) {
+ testScope.runTest {
+ val tiles by collectLastValue(underTest.tiles)
+ val currentTiles =
+ mutableListOf(
+ TileSpec.create("flashlight"),
+ TileSpec.create("airplane"),
+ TileSpec.create("internet"),
+ TileSpec.create("alarm"),
+ )
+ currentTilesInteractor.setTiles(currentTiles)
+
+ val tile =
+ currentTilesInteractor.currentTiles.value.find { it.spec.spec == "flashlight" }
+ (tile!!.tile as FakeQSTile).changeState(
+ QSTile.State().apply { handlesSecondaryClick = true }
+ )
+
+ underTest.startEditing()
+
+ assertThat(tiles!!.filter { it.isDualTarget }.map { it.tileSpec.spec })
+ .containsExactly("flashlight")
+
+ underTest.removeTile(TileSpec.create("flashlight"))
+
+ assertThat(tiles!!.filter { it.isDualTarget }.map { it.tileSpec.spec }).isEmpty()
+ }
+ }
+
companion object {
private val drawable1 = TestStubDrawable("drawable1")
private val appName1 = "App1"
@@ -717,6 +767,7 @@
icon = Icon.Resource(R.drawable.star_on, ContentDescription.Loaded(spec)),
label = Text.Loaded(spec),
appName = null,
+ appIcon = null,
category = TileCategory.UNKNOWN,
)
}
@@ -728,6 +779,7 @@
Icon.Resource(uiConfig.iconRes, ContentDescription.Resource(uiConfig.labelRes)),
label = Text.Resource(uiConfig.labelRes),
appName = null,
+ appIcon = null,
category = category,
)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/TileUiStateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/TileUiStateTest.kt
index b144f06..f91316d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/TileUiStateTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/TileUiStateTest.kt
@@ -168,7 +168,7 @@
}
@Test
- fun switchInactive_emptySecondaryLabel_defaultOff() {
+ fun switchInactive_emptySecondaryLabel_defaultOff_onlyOnStateDescription() {
val state =
QSTile.State().apply {
expandedAccessibilityClassName = Switch::class.java.name
@@ -178,11 +178,13 @@
val uiState = state.toUiState()
- assertThat(uiState.secondaryLabel).isEqualTo(resources.getString(R.string.switch_bar_off))
+ assertThat(uiState.secondaryLabel).isEmpty()
+ assertThat(uiState.accessibilityUiState.stateDescription)
+ .isEqualTo(resources.getString(R.string.switch_bar_off))
}
@Test
- fun switchActive_emptySecondaryLabel_defaultOn() {
+ fun switchActive_emptySecondaryLabel_defaultOn_onlyOnStateDescription() {
val state =
QSTile.State().apply {
expandedAccessibilityClassName = Switch::class.java.name
@@ -192,7 +194,9 @@
val uiState = state.toUiState()
- assertThat(uiState.secondaryLabel).isEqualTo(resources.getString(R.string.switch_bar_on))
+ assertThat(uiState.secondaryLabel).isEmpty()
+ assertThat(uiState.accessibilityUiState.stateDescription)
+ .isEqualTo(resources.getString(R.string.switch_bar_on))
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/CameraToggleTileTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/CameraToggleTileTest.kt
index 6bb6713..f08cbb0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/CameraToggleTileTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/CameraToggleTileTest.kt
@@ -148,6 +148,8 @@
keyguardStateController,
safetyCenterManager,
)
+ cameraTile.initialize()
+ testableLooper.processAllMessages()
assertThat(cameraTile.longClickIntent?.action).isEqualTo(Settings.ACTION_PRIVACY_CONTROLS)
cameraTile.destroy()
testableLooper.processAllMessages()
@@ -171,6 +173,8 @@
keyguardStateController,
safetyCenterManager,
)
+ cameraTile.initialize()
+ testableLooper.processAllMessages()
assertThat(tile.longClickIntent?.action).isEqualTo(Settings.ACTION_PRIVACY_SETTINGS)
cameraTile.destroy()
testableLooper.processAllMessages()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/MicrophoneToggleTileTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/MicrophoneToggleTileTest.kt
index a39692d..0ac255b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/MicrophoneToggleTileTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/MicrophoneToggleTileTest.kt
@@ -148,6 +148,8 @@
keyguardStateController,
safetyCenterManager,
)
+ micTile.initialize()
+ testableLooper.processAllMessages()
assertThat(micTile.longClickIntent?.action).isEqualTo(Settings.ACTION_PRIVACY_CONTROLS)
micTile.destroy()
testableLooper.processAllMessages()
@@ -171,6 +173,8 @@
keyguardStateController,
safetyCenterManager,
)
+ micTile.initialize()
+ testableLooper.processAllMessages()
assertThat(micTile.longClickIntent?.action).isEqualTo(Settings.ACTION_PRIVACY_SETTINGS)
micTile.destroy()
testableLooper.processAllMessages()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/dialog/CastDetailsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/dialog/CastDetailsViewModelTest.kt
index 468c3dc..90da1cb 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/dialog/CastDetailsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/dialog/CastDetailsViewModelTest.kt
@@ -16,7 +16,7 @@
package com.android.systemui.qs.tiles.dialog
-import android.content.Context
+import android.graphics.drawable.Drawable
import android.media.MediaRouter
import android.provider.Settings
import android.testing.TestableLooper.RunWithLooper
@@ -27,8 +27,10 @@
import com.android.systemui.qs.tiles.base.domain.actions.FakeQSTileIntentUserInputHandler
import com.android.systemui.qs.tiles.base.domain.actions.intentInputs
import com.google.common.truth.Truth.assertThat
+import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyInt
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock
import org.mockito.kotlin.stub
@@ -38,13 +40,22 @@
@RunWith(AndroidJUnit4::class)
class CastDetailsViewModelTest : SysuiTestCase() {
var inputHandler: FakeQSTileIntentUserInputHandler = FakeQSTileIntentUserInputHandler()
- private var context: Context = mock()
- private var mediaRouter: MediaRouter = mock()
private var selectedRoute: MediaRouter.RouteInfo = mock()
+ private var mediaRouter: MediaRouter =
+ mock<MediaRouter> { on { selectedRoute } doReturn selectedRoute }
+
+ @Before
+ fun SetUp() {
+ // We need to set up a fake system service here since shouldShowChooserDialog access's
+ // context system service, and we want to use the mocked selectedRoute to test this
+ // function's behavior.
+ context.addMockSystemService(MediaRouter::class.java, mediaRouter)
+ }
@Test
fun testClickOnSettingsButton() {
- var viewModel = CastDetailsViewModel(inputHandler, context, MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY)
+ val viewModel =
+ CastDetailsViewModel(inputHandler, context, MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY)
viewModel.clickOnSettingsButton()
@@ -56,14 +67,7 @@
@Test
fun testShouldShowChooserDialog() {
- context.stub {
- on { getSystemService(MediaRouter::class.java) } doReturn mediaRouter
- }
- mediaRouter.stub {
- on { selectedRoute } doReturn selectedRoute
- }
-
- var viewModel =
+ val viewModel =
CastDetailsViewModel(inputHandler, context, MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY)
assertThat(viewModel.shouldShowChooserDialog())
@@ -74,4 +78,58 @@
)
)
}
+
+ @Test
+ fun shouldShowChooserDialogFalse_subTitleEmpty() {
+ selectedRoute.stub {
+ on { isDefault } doReturn false
+ on { matchesTypes(anyInt()) } doReturn true
+ }
+ val viewModel =
+ CastDetailsViewModel(inputHandler, context, MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY)
+
+ assertThat(viewModel.shouldShowChooserDialog()).isEqualTo(false)
+ assertThat(viewModel.subTitle).isEqualTo("")
+ }
+
+ @Test
+ fun shouldShowChooserDialogTrue_useDefaultSubTitle() {
+ selectedRoute.stub { on { isDefault } doReturn true }
+ val viewModel =
+ CastDetailsViewModel(inputHandler, context, MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY)
+
+ assertThat(viewModel.shouldShowChooserDialog()).isEqualTo(true)
+ assertThat(viewModel.subTitle).isEqualTo("Searching for devices...")
+ }
+
+ @Test
+ fun shouldShowChooserDialogTrue_useDefaultTitle() {
+ selectedRoute.stub { on { isDefault } doReturn true }
+ val viewModel =
+ CastDetailsViewModel(inputHandler, context, MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY)
+
+ assertThat(viewModel.shouldShowChooserDialog()).isEqualTo(true)
+ assertThat(viewModel.title).isEqualTo("Cast screen to device")
+ }
+
+ @Test
+ fun setMediaRouteDeviceTitle() {
+ val viewModel =
+ CastDetailsViewModel(inputHandler, context, MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY)
+
+ viewModel.setMediaRouteDeviceTitle("test")
+
+ assertThat(viewModel.title).isEqualTo("test")
+ }
+
+ @Test
+ fun setMediaRouteDeviceIcon() {
+ val viewModel =
+ CastDetailsViewModel(inputHandler, context, MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY)
+ val testIcon = mock<Drawable>()
+
+ viewModel.setMediaRouteDeviceIcon(testIcon)
+
+ assertThat(viewModel.deviceIcon).isEqualTo(testIcon)
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/dialog/InternetAdapterTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/dialog/InternetAdapterTest.java
index cfbc812..f36dbae 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/dialog/InternetAdapterTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/dialog/InternetAdapterTest.java
@@ -18,14 +18,18 @@
import android.view.View;
import android.widget.LinearLayout;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.res.R;
import com.android.wifitrackerlib.WifiEntry;
+import com.google.common.collect.ImmutableList;
+
import kotlinx.coroutines.CoroutineScope;
+import platform.test.runner.parameterized.Parameter;
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
+import platform.test.runner.parameterized.Parameters;
import org.junit.Before;
import org.junit.Rule;
@@ -37,10 +41,11 @@
import org.mockito.junit.MockitoRule;
import java.util.Arrays;
+import java.util.Collection;
import java.util.List;
@SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(ParameterizedAndroidJunit4.class)
public class InternetAdapterTest extends SysuiTestCase {
private static final String WIFI_KEY = "Wi-Fi_Key";
@@ -75,6 +80,14 @@
private InternetAdapter mInternetAdapter;
private InternetAdapter.InternetViewHolder mViewHolder;
+ @Parameters(name = "isInDetailsView = {0}")
+ public static Collection<Boolean> data() {
+ return ImmutableList.of(true, false);
+ }
+
+ @Parameter
+ public boolean isInDetailsView;
+
@Before
public void setUp() {
mTestableResources = mContext.getOrCreateTestableResources();
@@ -86,7 +99,8 @@
when(mWifiEntry.getTitle()).thenReturn(WIFI_TITLE);
when(mWifiEntry.getSummary(false)).thenReturn(WIFI_SUMMARY);
- mInternetAdapter = new InternetAdapter(mInternetDetailsContentController, mScope);
+ mInternetAdapter = new InternetAdapter(mInternetDetailsContentController, mScope,
+ isInDetailsView);
mViewHolder = mInternetAdapter.onCreateViewHolder(new LinearLayout(mContext), 0);
mInternetAdapter.setWifiEntries(Arrays.asList(mWifiEntry), 1 /* wifiEntriesCount */);
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/airplane/ui/mapper/AirplaneModeTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/airplane/ui/mapper/AirplaneModeTileMapperTest.kt
index ccb2386..62b49c0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/airplane/ui/mapper/AirplaneModeTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/airplane/ui/mapper/AirplaneModeTileMapperTest.kt
@@ -17,7 +17,6 @@
package com.android.systemui.qs.tiles.impl.airplane.ui.mapper
import android.graphics.drawable.TestStubDrawable
-import android.service.quicksettings.Tile
import android.widget.Switch
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
@@ -64,7 +63,6 @@
val expectedState =
createAirplaneModeState(
QSTileState.ActivationState.ACTIVE,
- context.resources.getStringArray(R.array.tile_states_airplane)[Tile.STATE_ACTIVE],
R.drawable.qs_airplane_icon_on,
)
QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState)
@@ -79,7 +77,6 @@
val expectedState =
createAirplaneModeState(
QSTileState.ActivationState.INACTIVE,
- context.resources.getStringArray(R.array.tile_states_airplane)[Tile.STATE_INACTIVE],
R.drawable.qs_airplane_icon_off,
)
QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState)
@@ -87,7 +84,6 @@
private fun createAirplaneModeState(
activationState: QSTileState.ActivationState,
- secondaryLabel: String,
iconRes: Int,
): QSTileState {
val label = context.getString(R.string.airplane_mode)
@@ -95,10 +91,10 @@
Icon.Loaded(context.getDrawable(iconRes)!!, null, iconRes),
label,
activationState,
- secondaryLabel,
+ secondaryLabel = null,
setOf(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK),
label,
- null,
+ stateDescription = null,
QSTileState.SideViewIcon.None,
QSTileState.EnabledState.ENABLED,
Switch::class.qualifiedName,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/ui/mapper/ColorCorrectionTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/ui/mapper/ColorCorrectionTileMapperTest.kt
index 124e013..4eabd49 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/ui/mapper/ColorCorrectionTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/ui/mapper/ColorCorrectionTileMapperTest.kt
@@ -36,9 +36,7 @@
class ColorCorrectionTileMapperTest : SysuiTestCase() {
private val kosmos = testKosmos()
private val colorCorrectionTileConfig = kosmos.qsColorCorrectionTileConfig
- private val subtitleArray by lazy {
- context.resources.getStringArray(R.array.tile_states_color_correction)
- }
+
// Using lazy (versus =) to make sure we override the right context -- see b/311612168
private val mapper by lazy {
ColorCorrectionTileMapper(
@@ -55,8 +53,7 @@
val outputState = mapper.map(colorCorrectionTileConfig, inputModel)
- val expectedState =
- createColorCorrectionTileState(QSTileState.ActivationState.INACTIVE, subtitleArray[1])
+ val expectedState = createColorCorrectionTileState(QSTileState.ActivationState.INACTIVE)
QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState)
}
@@ -66,14 +63,12 @@
val outputState = mapper.map(colorCorrectionTileConfig, inputModel)
- val expectedState =
- createColorCorrectionTileState(QSTileState.ActivationState.ACTIVE, subtitleArray[2])
+ val expectedState = createColorCorrectionTileState(QSTileState.ActivationState.ACTIVE)
QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState)
}
private fun createColorCorrectionTileState(
- activationState: QSTileState.ActivationState,
- secondaryLabel: String,
+ activationState: QSTileState.ActivationState
): QSTileState {
val label = context.getString(R.string.quick_settings_color_correction_label)
return QSTileState(
@@ -84,10 +79,10 @@
),
label,
activationState,
- secondaryLabel,
+ secondaryLabel = null,
setOf(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK),
label,
- null,
+ stateDescription = null,
QSTileState.SideViewIcon.None,
QSTileState.EnabledState.ENABLED,
Switch::class.qualifiedName,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/inversion/ui/mapper/ColorInversionTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/inversion/ui/mapper/ColorInversionTileMapperTest.kt
index 509a178..451be8c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/inversion/ui/mapper/ColorInversionTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/inversion/ui/mapper/ColorInversionTileMapperTest.kt
@@ -22,7 +22,6 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.qs.tileimpl.SubtitleArrayMapping
import com.android.systemui.qs.tiles.base.shared.model.QSTileState
import com.android.systemui.qs.tiles.base.ui.model.QSTileStateSubject
import com.android.systemui.qs.tiles.impl.inversion.domain.model.ColorInversionTileModel
@@ -37,9 +36,7 @@
class ColorInversionTileMapperTest : SysuiTestCase() {
private val kosmos = testKosmos()
private val colorInversionTileConfig = kosmos.qsColorInversionTileConfig
- private val subtitleArrayId =
- SubtitleArrayMapping.getSubtitleId(colorInversionTileConfig.tileSpec.spec)
- private val subtitleArray by lazy { context.resources.getStringArray(subtitleArrayId) }
+
// Using lazy (versus =) to make sure we override the right context -- see b/311612168
private val mapper by lazy {
ColorInversionTileMapper(
@@ -62,7 +59,6 @@
val expectedState =
createColorInversionTileState(
QSTileState.ActivationState.INACTIVE,
- subtitleArray[1],
R.drawable.qs_invert_colors_icon_off,
)
QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState)
@@ -77,7 +73,6 @@
val expectedState =
createColorInversionTileState(
QSTileState.ActivationState.ACTIVE,
- subtitleArray[2],
R.drawable.qs_invert_colors_icon_on,
)
QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState)
@@ -85,7 +80,6 @@
private fun createColorInversionTileState(
activationState: QSTileState.ActivationState,
- secondaryLabel: String,
iconRes: Int,
): QSTileState {
val label = context.getString(R.string.quick_settings_inversion_label)
@@ -93,10 +87,10 @@
Icon.Loaded(context.getDrawable(iconRes)!!, null, iconRes),
label,
activationState,
- secondaryLabel,
+ secondaryLabel = null,
setOf(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK),
label,
- null,
+ stateDescription = null,
QSTileState.SideViewIcon.None,
QSTileState.EnabledState.ENABLED,
Switch::class.qualifiedName,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/night/ui/mapper/NightDisplayTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/night/ui/mapper/NightDisplayTileMapperTest.kt
index 25c7016..3bb7c3b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/night/ui/mapper/NightDisplayTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/night/ui/mapper/NightDisplayTileMapperTest.kt
@@ -17,7 +17,6 @@
package com.android.systemui.qs.tiles.impl.night.ui.mapper
import android.graphics.drawable.TestStubDrawable
-import android.service.quicksettings.Tile
import android.text.TextUtils
import android.widget.Switch
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -70,11 +69,7 @@
val outputState = mapper.map(config, inputModel)
- val expectedState =
- createNightDisplayTileState(
- QSTileState.ActivationState.INACTIVE,
- context.resources.getStringArray(R.array.tile_states_night)[Tile.STATE_INACTIVE],
- )
+ val expectedState = createNightDisplayTileState(QSTileState.ActivationState.INACTIVE, null)
QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState)
}
@@ -85,11 +80,7 @@
val outputState = mapper.map(config, inputModel)
- val expectedState =
- createNightDisplayTileState(
- QSTileState.ActivationState.INACTIVE,
- context.resources.getStringArray(R.array.tile_states_night)[Tile.STATE_INACTIVE],
- )
+ val expectedState = createNightDisplayTileState(QSTileState.ActivationState.INACTIVE, null)
QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState)
}
@@ -99,11 +90,7 @@
val outputState = mapper.map(config, inputModel)
- val expectedState =
- createNightDisplayTileState(
- QSTileState.ActivationState.ACTIVE,
- context.resources.getStringArray(R.array.tile_states_night)[Tile.STATE_ACTIVE],
- )
+ val expectedState = createNightDisplayTileState(QSTileState.ActivationState.ACTIVE, null)
QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState)
}
@@ -113,11 +100,7 @@
val outputState = mapper.map(config, inputModel)
- val expectedState =
- createNightDisplayTileState(
- QSTileState.ActivationState.ACTIVE,
- context.resources.getStringArray(R.array.tile_states_night)[Tile.STATE_ACTIVE],
- )
+ val expectedState = createNightDisplayTileState(QSTileState.ActivationState.ACTIVE, null)
QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/onehanded/ui/mapper/OneHandedModeTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/onehanded/ui/mapper/OneHandedModeTileMapperTest.kt
index fe10457..c7dc72e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/onehanded/ui/mapper/OneHandedModeTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/onehanded/ui/mapper/OneHandedModeTileMapperTest.kt
@@ -22,7 +22,6 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.qs.tileimpl.SubtitleArrayMapping
import com.android.systemui.qs.tiles.base.shared.model.QSTileState
import com.android.systemui.qs.tiles.base.ui.model.QSTileStateSubject
import com.android.systemui.qs.tiles.impl.onehanded.domain.model.OneHandedModeTileModel
@@ -38,8 +37,6 @@
class OneHandedModeTileMapperTest : SysuiTestCase() {
private val kosmos = testKosmos()
private val config = kosmos.qsOneHandedModeTileConfig
- private val subtitleArrayId = SubtitleArrayMapping.getSubtitleId(config.tileSpec.spec)
- private val subtitleArray by lazy { context.resources.getStringArray(subtitleArrayId) }
private lateinit var mapper: OneHandedModeTileMapper
@@ -65,8 +62,7 @@
val outputState = mapper.map(config, inputModel)
- val expectedState =
- createOneHandedModeTileState(QSTileState.ActivationState.INACTIVE, subtitleArray[1])
+ val expectedState = createOneHandedModeTileState(QSTileState.ActivationState.INACTIVE)
QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState)
}
@@ -76,14 +72,12 @@
val outputState = mapper.map(config, inputModel)
- val expectedState =
- createOneHandedModeTileState(QSTileState.ActivationState.ACTIVE, subtitleArray[2])
+ val expectedState = createOneHandedModeTileState(QSTileState.ActivationState.ACTIVE)
QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState)
}
private fun createOneHandedModeTileState(
- activationState: QSTileState.ActivationState,
- secondaryLabel: String,
+ activationState: QSTileState.ActivationState
): QSTileState {
val label = context.getString(R.string.quick_settings_onehanded_label)
return QSTileState(
@@ -94,10 +88,10 @@
),
label,
activationState,
- secondaryLabel,
+ secondaryLabel = null,
setOf(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK),
label,
- null,
+ stateDescription = null,
QSTileState.SideViewIcon.None,
QSTileState.EnabledState.ENABLED,
Switch::class.qualifiedName,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/ui/mapper/ReduceBrightColorsTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/ui/mapper/ReduceBrightColorsTileMapperTest.kt
index dabf0b0..e253b06 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/ui/mapper/ReduceBrightColorsTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/ui/mapper/ReduceBrightColorsTileMapperTest.kt
@@ -17,7 +17,6 @@
package com.android.systemui.qs.tiles.impl.reducebrightness.ui.mapper
import android.graphics.drawable.TestStubDrawable
-import android.service.quicksettings.Tile
import android.widget.Switch
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
@@ -88,13 +87,10 @@
Icon.Loaded(context.getDrawable(iconRes)!!, null, iconRes),
label,
activationState,
- context.resources
- .getStringArray(R.array.tile_states_reduce_brightness)[
- if (activationState == QSTileState.ActivationState.ACTIVE) Tile.STATE_ACTIVE
- else Tile.STATE_INACTIVE],
+ secondaryLabel = null,
setOf(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK),
label,
- null,
+ stateDescription = null,
QSTileState.SideViewIcon.None,
QSTileState.EnabledState.ENABLED,
Switch::class.qualifiedName,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/ui/mapper/DataSaverTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/ui/mapper/DataSaverTileMapperTest.kt
index 638b76d..b106782 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/ui/mapper/DataSaverTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/ui/mapper/DataSaverTileMapperTest.kt
@@ -83,21 +83,15 @@
iconRes: Int,
): QSTileState {
val label = context.getString(R.string.data_saver)
- val secondaryLabel =
- if (activationState == QSTileState.ActivationState.ACTIVE)
- context.resources.getStringArray(R.array.tile_states_saver)[2]
- else if (activationState == QSTileState.ActivationState.INACTIVE)
- context.resources.getStringArray(R.array.tile_states_saver)[1]
- else context.resources.getStringArray(R.array.tile_states_saver)[0]
return QSTileState(
Icon.Loaded(context.getDrawable(iconRes)!!, null, iconRes),
label,
activationState,
- secondaryLabel,
+ secondaryLabel = null,
setOf(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK),
label,
- null,
+ stateDescription = null,
QSTileState.SideViewIcon.None,
QSTileState.EnabledState.ENABLED,
Switch::class.qualifiedName,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/uimodenight/ui/mapper/UiModeNightTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/uimodenight/ui/mapper/UiModeNightTileMapperTest.kt
index e7653f2..fdd6cf5 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/uimodenight/ui/mapper/UiModeNightTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/uimodenight/ui/mapper/UiModeNightTileMapperTest.kt
@@ -129,7 +129,7 @@
val actualState: QSTileState = mapper.map(qsTileConfig, inputModel)
val expectedLabel = context.getString(R.string.quick_settings_ui_mode_night_label)
- val expectedSecondaryLabel = context.resources.getStringArray(R.array.tile_states_dark)[1]
+ val expectedSecondaryLabel = null
val expectedState =
createUiNightModeTileState(
activationState = QSTileState.ActivationState.INACTIVE,
@@ -147,7 +147,7 @@
val actualState: QSTileState = mapper.map(qsTileConfig, inputModel)
val expectedLabel = context.getString(R.string.quick_settings_ui_mode_night_label)
- val expectedSecondaryLabel = context.resources.getStringArray(R.array.tile_states_dark)[2]
+ val expectedSecondaryLabel = null
val expectedState =
createUiNightModeTileState(
iconRes = R.drawable.qs_light_dark_theme_icon_on,
@@ -166,7 +166,7 @@
val actualState: QSTileState = mapper.map(qsTileConfig, inputModel)
val expectedLabel = context.getString(R.string.quick_settings_ui_mode_night_label)
- val expectedSecondaryLabel = context.resources.getStringArray(R.array.tile_states_dark)[2]
+ val expectedSecondaryLabel = null
val expectedState =
createUiNightModeTileState(
iconRes = R.drawable.qs_light_dark_theme_icon_on,
@@ -185,7 +185,7 @@
val actualState: QSTileState = mapper.map(qsTileConfig, inputModel)
val expectedLabel = context.getString(R.string.quick_settings_ui_mode_night_label)
- val expectedSecondaryLabel = context.resources.getStringArray(R.array.tile_states_dark)[1]
+ val expectedSecondaryLabel = null
val expectedState =
createUiNightModeTileState(
iconRes = R.drawable.qs_light_dark_theme_icon_off,
@@ -204,7 +204,7 @@
val actualState: QSTileState = mapper.map(qsTileConfig, inputModel)
val expectedLabel = context.getString(R.string.quick_settings_ui_mode_night_label)
- val expectedSecondaryLabel = context.resources.getStringArray(R.array.tile_states_dark)[2]
+ val expectedSecondaryLabel = null
val expectedState =
createUiNightModeTileState(
iconRes = R.drawable.qs_light_dark_theme_icon_on,
@@ -248,7 +248,7 @@
val actualState: QSTileState = mapper.map(qsTileConfig, inputModel)
val expectedLabel = context.getString(R.string.quick_settings_ui_mode_night_label)
- val expectedSecondaryLabel = context.resources.getStringArray(R.array.tile_states_dark)[1]
+ val expectedSecondaryLabel = null
val expectedState =
createUiNightModeTileState(
iconRes = R.drawable.qs_light_dark_theme_icon_off,
@@ -367,7 +367,7 @@
val actualState: QSTileState = mapper.map(qsTileConfig, inputModel)
- val expectedSecondaryLabel = context.resources.getStringArray(R.array.tile_states_dark)[2]
+ val expectedSecondaryLabel = null
val expectedLabel = context.getString(R.string.quick_settings_ui_mode_night_label)
val expectedState =
@@ -390,7 +390,7 @@
val actualState: QSTileState = mapper.map(qsTileConfig, inputModel)
- val expectedSecondaryLabel = context.resources.getStringArray(R.array.tile_states_dark)[1]
+ val expectedSecondaryLabel = null
val expectedLabel = context.getString(R.string.quick_settings_ui_mode_night_label)
val expectedState =
createUiNightModeTileState(
@@ -417,7 +417,7 @@
val actualState: QSTileState = mapper.map(qsTileConfig, inputModel)
- val expectedSecondaryLabel = context.resources.getStringArray(R.array.tile_states_dark)[1]
+ val expectedSecondaryLabel = null
val expectedLabel = context.getString(R.string.quick_settings_ui_mode_night_label)
val expectedState =
createUiNightModeTileState(
@@ -444,7 +444,7 @@
val actualState: QSTileState = mapper.map(qsTileConfig, inputModel)
- val expectedSecondaryLabel = context.resources.getStringArray(R.array.tile_states_dark)[2]
+ val expectedSecondaryLabel = null
val expectedLabel = context.getString(R.string.quick_settings_ui_mode_night_label)
val expectedState =
createUiNightModeTileState(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsContainerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsContainerViewModelTest.kt
index d117015..e1d5907 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsContainerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsContainerViewModelTest.kt
@@ -88,7 +88,7 @@
@Test
fun showMedia_activeMedia_true() =
testScope.runTest {
- kosmos.mediaFilterRepository.addSelectedUserMediaEntry(MediaData(active = true))
+ kosmos.mediaFilterRepository.addCurrentUserMediaEntry(MediaData(active = true))
runCurrent()
assertThat(underTest.showMedia).isTrue()
@@ -97,7 +97,7 @@
@Test
fun showMedia_InactiveMedia_true() =
testScope.runTest {
- kosmos.mediaFilterRepository.addSelectedUserMediaEntry(MediaData(active = false))
+ kosmos.mediaFilterRepository.addCurrentUserMediaEntry(MediaData(active = false))
runCurrent()
assertThat(underTest.showMedia).isTrue()
@@ -106,8 +106,8 @@
@Test
fun showMedia_noMedia_false() =
testScope.runTest {
- kosmos.mediaFilterRepository.addSelectedUserMediaEntry(MediaData(active = true))
- kosmos.mediaFilterRepository.clearSelectedUserMedia()
+ kosmos.mediaFilterRepository.addCurrentUserMediaEntry(MediaData(active = true))
+ kosmos.mediaFilterRepository.clearCurrentUserMedia()
runCurrent()
assertThat(underTest.showMedia).isFalse()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneContentViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneContentViewModelTest.kt
index 1d891da..2c4ed36 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneContentViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneContentViewModelTest.kt
@@ -109,11 +109,11 @@
assertThat(isMediaVisible).isFalse()
- kosmos.mediaFilterRepository.addSelectedUserMediaEntry(userMedia)
+ kosmos.mediaFilterRepository.addCurrentUserMediaEntry(userMedia)
assertThat(isMediaVisible).isTrue()
- kosmos.mediaFilterRepository.removeSelectedUserMediaEntry(userMedia.instanceId)
+ kosmos.mediaFilterRepository.removeCurrentUserMediaEntry(userMedia.instanceId)
assertThat(isMediaVisible).isFalse()
}
@@ -126,7 +126,7 @@
assertThat(isMediaVisible).isFalse()
- kosmos.mediaFilterRepository.addSelectedUserMediaEntry(userMedia)
+ kosmos.mediaFilterRepository.addCurrentUserMediaEntry(userMedia)
assertThat(isMediaVisible).isTrue()
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/DualShadeEducationInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/DualShadeEducationInteractorTest.kt
new file mode 100644
index 0000000..46f8ee9
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/DualShadeEducationInteractorTest.kt
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.scene.domain.interactor
+
+import android.content.pm.UserInfo
+import androidx.test.filters.SmallTest
+import com.android.compose.animation.scene.OverlayKey
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.flags.EnableSceneContainer
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.advanceTimeBy
+import com.android.systemui.kosmos.currentValue
+import com.android.systemui.kosmos.runCurrent
+import com.android.systemui.kosmos.runTest
+import com.android.systemui.scene.domain.model.DualShadeEducationModel
+import com.android.systemui.scene.shared.model.Overlays
+import com.android.systemui.shade.domain.interactor.disableDualShade
+import com.android.systemui.shade.domain.interactor.enableDualShade
+import com.android.systemui.testKosmos
+import com.android.systemui.user.data.repository.fakeUserRepository
+import com.android.systemui.user.domain.interactor.selectedUserInteractor
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
+
+@SmallTest
+@RunWith(ParameterizedAndroidJunit4::class)
+@EnableSceneContainer
+class DualShadeEducationInteractorTest(private val forOverlay: OverlayKey) : SysuiTestCase() {
+ private val kosmos = testKosmos()
+ private lateinit var underTest: DualShadeEducationInteractor
+
+ @Before
+ fun setUp() {
+ kosmos.enableDualShade()
+ kosmos.fakeUserRepository.setUserInfos(USER_INFOS)
+ underTest = kosmos.dualShadeEducationInteractor
+ kosmos.runCurrent()
+ }
+
+ @Test fun initiallyNull() = kosmos.runTest { assertEducation(DualShadeEducationModel.None) }
+
+ @Test
+ fun happyPath() =
+ kosmos.runTest {
+ val otherOverlay = otherOverlay(forOverlay)
+
+ showOverlay(otherOverlay)
+ // No education before the delay.
+ assertEducation(DualShadeEducationModel.None)
+
+ // SHOW EDUCATION
+ advanceTimeBy(DualShadeEducationInteractor.TOOLTIP_APPEARANCE_DELAY_MS - 1)
+ // No education because didn't wait quite long enough yet.
+ assertEducation(DualShadeEducationModel.None)
+ advanceTimeBy(1)
+ // Expected education after the full delay.
+ assertEducation(expectedEducation(forOverlay))
+
+ // UI reports impression and dismissal of the tooltip.
+ when (forOverlay) {
+ Overlays.NotificationsShade -> {
+ underTest.recordNotificationsShadeTooltipImpression()
+ underTest.dismissNotificationsShadeTooltip()
+ }
+ Overlays.QuickSettingsShade -> {
+ underTest.recordQuickSettingsShadeTooltipImpression()
+ underTest.dismissQuickSettingsShadeTooltip()
+ }
+ }
+ assertEducation(DualShadeEducationModel.None)
+
+ // Hide and reshow overlay to try and trigger it again it shouldn't show again because
+ // it was already shown once.
+ hideOverlay(otherOverlay)
+ assertEducation(DualShadeEducationModel.None)
+ showOverlay(otherOverlay)
+ advanceTimeBy(DualShadeEducationInteractor.TOOLTIP_APPEARANCE_DELAY_MS)
+ // No education as it was already shown to the user.
+ assertEducation(DualShadeEducationModel.None)
+ }
+
+ @Test
+ fun otherOverlayHiddenBeforeEducation_noEducation() =
+ kosmos.runTest {
+ val otherOverlay = otherOverlay(forOverlay)
+ showOverlay(otherOverlay)
+ // No tooltip before the delay.
+ assertEducation(DualShadeEducationModel.None)
+
+ advanceTimeBy(DualShadeEducationInteractor.TOOLTIP_APPEARANCE_DELAY_MS - 1)
+ // No education because didn't wait quite long enough yet.
+ assertEducation(DualShadeEducationModel.None)
+
+ // Overlay hidden before the delay elapses.
+ hideOverlay(otherOverlay)
+ assertEducation(DualShadeEducationModel.None)
+
+ advanceTimeBy(1)
+ // Waited the entire delay, but the overlay was already hidden.
+ assertEducation(DualShadeEducationModel.None)
+ }
+
+ @Test
+ fun notDualShadeMode_noEducation() =
+ kosmos.runTest {
+ disableDualShade()
+ showOverlay(otherOverlay(forOverlay))
+ advanceTimeBy(DualShadeEducationInteractor.TOOLTIP_APPEARANCE_DELAY_MS)
+ assertEducation(DualShadeEducationModel.None)
+ }
+
+ @Test
+ fun reshowsEducationAndTooltip_afterUserChanged() =
+ kosmos.runTest {
+ val otherOverlay = otherOverlay(forOverlay)
+ showOverlay(otherOverlay)
+ advanceTimeBy(DualShadeEducationInteractor.TOOLTIP_APPEARANCE_DELAY_MS)
+ assertEducation(expectedEducation(forOverlay))
+ when (forOverlay) {
+ Overlays.NotificationsShade -> {
+ underTest.recordNotificationsShadeTooltipImpression()
+ underTest.dismissNotificationsShadeTooltip()
+ }
+ Overlays.QuickSettingsShade -> {
+ underTest.recordQuickSettingsShadeTooltipImpression()
+ underTest.dismissQuickSettingsShadeTooltip()
+ }
+ }
+ assertEducation(DualShadeEducationModel.None)
+ hideOverlay(otherOverlay)
+
+ selectUser(USER_INFOS[1])
+
+ showOverlay(otherOverlay)
+ advanceTimeBy(DualShadeEducationInteractor.TOOLTIP_APPEARANCE_DELAY_MS)
+ // New user, education shown again.
+ assertEducation(expectedEducation(forOverlay))
+ }
+
+ /**
+ * Returns the complementary overlay for [forOverlay]; the one that, when shown, the tooltip
+ * will show for [forOverlay].
+ */
+ private fun otherOverlay(forOverlay: OverlayKey): OverlayKey {
+ return when (forOverlay) {
+ Overlays.NotificationsShade -> Overlays.QuickSettingsShade
+ Overlays.QuickSettingsShade -> Overlays.NotificationsShade
+ else -> error("Test isn't expecting forOverlay of ${forOverlay.debugName}")
+ }
+ }
+
+ private fun Kosmos.assertEducation(expected: DualShadeEducationModel) {
+ runCurrent()
+ assertThat(underTest.education).isEqualTo(expected)
+ }
+
+ private fun expectedEducation(forOverlay: OverlayKey): DualShadeEducationModel {
+ return when (forOverlay) {
+ Overlays.NotificationsShade -> DualShadeEducationModel.ForNotificationsShade
+ Overlays.QuickSettingsShade -> DualShadeEducationModel.ForQuickSettingsShade
+ else -> DualShadeEducationModel.None
+ }
+ }
+
+ private fun Kosmos.showOverlay(overlay: OverlayKey) {
+ sceneInteractor.showOverlay(overlay, "")
+ assertThat(currentValue(sceneInteractor.currentOverlays)).contains(overlay)
+ }
+
+ private fun Kosmos.hideOverlay(overlay: OverlayKey) {
+ sceneInteractor.hideOverlay(overlay, "")
+ assertThat(currentValue(sceneInteractor.currentOverlays)).doesNotContain(overlay)
+ }
+
+ private suspend fun Kosmos.selectUser(userInfo: UserInfo) {
+ fakeUserRepository.setSelectedUserInfo(userInfo)
+ assertThat(selectedUserInteractor.getSelectedUserId()).isEqualTo(userInfo.id)
+ }
+
+ companion object {
+ private val USER_INFOS =
+ listOf(UserInfo(10, "Initial user", 0), UserInfo(11, "Other user", 0))
+
+ @JvmStatic
+ @Parameters(name = "{0}")
+ fun testParameters(): List<OverlayKey> {
+ return listOf(Overlays.NotificationsShade, Overlays.QuickSettingsShade)
+ }
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/PanelExpansionInteractorImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/PanelExpansionInteractorImplTest.kt
index d7b8aff..9ab8a59 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/PanelExpansionInteractorImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/PanelExpansionInteractorImplTest.kt
@@ -19,24 +19,17 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.compose.animation.scene.ObservableTransitionState
-import com.android.compose.animation.scene.ObservableTransitionState.Transition.ShowOrHideOverlay
-import com.android.compose.animation.scene.OverlayKey
import com.android.compose.animation.scene.SceneKey
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor
-import com.android.systemui.deviceentry.shared.model.DeviceUnlockSource
-import com.android.systemui.deviceentry.shared.model.DeviceUnlockStatus
import com.android.systemui.flags.EnableSceneContainer
-import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository
-import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
import com.android.systemui.kosmos.testScope
import com.android.systemui.scene.domain.interactor.sceneInteractor
-import com.android.systemui.scene.shared.model.Overlays
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.scene.shared.model.fakeSceneDataSource
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.TestScope
@@ -46,13 +39,13 @@
import org.junit.Test
import org.junit.runner.RunWith
+@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
class PanelExpansionInteractorImplTest : SysuiTestCase() {
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
- private val deviceUnlockedInteractor by lazy { kosmos.deviceUnlockedInteractor }
private val sceneInteractor by lazy { kosmos.sceneInteractor }
private val shadeAnimationInteractor by lazy { kosmos.shadeAnimationInteractor }
private val transitionState =
@@ -70,118 +63,6 @@
@Test
@EnableSceneContainer
- fun legacyPanelExpansion_whenIdle_whenLocked() =
- testScope.runTest {
- underTest = kosmos.panelExpansionInteractorImpl
- val panelExpansion by collectLastValue(underTest.legacyPanelExpansion)
-
- changeScene(Scenes.Lockscreen) { assertThat(panelExpansion).isEqualTo(1f) }
- assertThat(panelExpansion).isEqualTo(1f)
-
- showOverlay(Overlays.Bouncer) { assertThat(panelExpansion).isEqualTo(1f) }
- assertThat(panelExpansion).isEqualTo(1f)
-
- changeScene(Scenes.Shade) { assertThat(panelExpansion).isEqualTo(1f) }
- assertThat(panelExpansion).isEqualTo(1f)
-
- changeScene(Scenes.QuickSettings) { assertThat(panelExpansion).isEqualTo(1f) }
- assertThat(panelExpansion).isEqualTo(1f)
-
- changeScene(Scenes.Communal) { assertThat(panelExpansion).isEqualTo(1f) }
- assertThat(panelExpansion).isEqualTo(1f)
- }
-
- @Test
- @EnableSceneContainer
- fun legacyPanelExpansion_whenIdle_whenUnlocked() =
- testScope.runTest {
- underTest = kosmos.panelExpansionInteractorImpl
- val unlockStatus by collectLastValue(deviceUnlockedInteractor.deviceUnlockStatus)
- kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus(
- SuccessFingerprintAuthenticationStatus(0, true)
- )
- runCurrent()
-
- assertThat(unlockStatus)
- .isEqualTo(DeviceUnlockStatus(true, DeviceUnlockSource.Fingerprint))
-
- val panelExpansion by collectLastValue(underTest.legacyPanelExpansion)
-
- changeScene(Scenes.Gone) { assertThat(panelExpansion).isEqualTo(0f) }
- assertThat(panelExpansion).isEqualTo(0f)
-
- changeScene(Scenes.Shade) { progress -> assertThat(panelExpansion).isEqualTo(progress) }
- assertThat(panelExpansion).isEqualTo(1f)
-
- changeScene(Scenes.QuickSettings) {
- // Shade's already expanded, so moving to QS should also be 1f.
- assertThat(panelExpansion).isEqualTo(1f)
- }
- assertThat(panelExpansion).isEqualTo(1f)
-
- changeScene(Scenes.Communal) { assertThat(panelExpansion).isEqualTo(1f) }
- assertThat(panelExpansion).isEqualTo(1f)
- }
-
- @Test
- @EnableSceneContainer
- fun legacyPanelExpansion_dualShade_whenIdle_whenLocked() =
- testScope.runTest {
- underTest = kosmos.panelExpansionInteractorImpl
- val panelExpansion by collectLastValue(underTest.legacyPanelExpansion)
-
- changeScene(Scenes.Lockscreen) { assertThat(panelExpansion).isEqualTo(1f) }
- assertThat(panelExpansion).isEqualTo(1f)
-
- showOverlay(Overlays.Bouncer) { assertThat(panelExpansion).isEqualTo(1f) }
- assertThat(panelExpansion).isEqualTo(1f)
-
- showOverlay(Overlays.NotificationsShade) { assertThat(panelExpansion).isEqualTo(1f) }
- assertThat(panelExpansion).isEqualTo(1f)
-
- showOverlay(Overlays.QuickSettingsShade) { assertThat(panelExpansion).isEqualTo(1f) }
- assertThat(panelExpansion).isEqualTo(1f)
-
- changeScene(Scenes.Communal) { assertThat(panelExpansion).isEqualTo(1f) }
- assertThat(panelExpansion).isEqualTo(1f)
- }
-
- @Test
- @EnableSceneContainer
- fun legacyPanelExpansion_dualShade_whenIdle_whenUnlocked() =
- testScope.runTest {
- underTest = kosmos.panelExpansionInteractorImpl
- val unlockStatus by collectLastValue(deviceUnlockedInteractor.deviceUnlockStatus)
- kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus(
- SuccessFingerprintAuthenticationStatus(0, true)
- )
- runCurrent()
-
- assertThat(unlockStatus)
- .isEqualTo(DeviceUnlockStatus(true, DeviceUnlockSource.Fingerprint))
-
- val panelExpansion by collectLastValue(underTest.legacyPanelExpansion)
-
- changeScene(Scenes.Gone) { assertThat(panelExpansion).isEqualTo(0f) }
- assertThat(panelExpansion).isEqualTo(0f)
-
- showOverlay(Overlays.NotificationsShade) { progress ->
- assertThat(panelExpansion).isEqualTo(progress)
- }
- assertThat(panelExpansion).isEqualTo(1f)
-
- showOverlay(Overlays.QuickSettingsShade) {
- // Notification shade is already expanded, so moving to QS shade should also be 1f.
- assertThat(panelExpansion).isEqualTo(1f)
- }
- assertThat(panelExpansion).isEqualTo(1f)
-
- changeScene(Scenes.Communal) { assertThat(panelExpansion).isEqualTo(1f) }
- assertThat(panelExpansion).isEqualTo(1f)
- }
-
- @Test
- @EnableSceneContainer
fun shouldHideStatusBarIconsWhenExpanded_goneScene() =
testScope.runTest {
underTest = kosmos.panelExpansionInteractorImpl
@@ -250,72 +131,4 @@
assertThat(currentScene).isEqualTo(toScene)
}
-
- private fun TestScope.showOverlay(
- toOverlay: OverlayKey,
- assertDuringProgress: ((progress: Float) -> Unit) = {},
- ) {
- val currentScene by collectLastValue(sceneInteractor.currentScene)
- val currentOverlays by collectLastValue(sceneInteractor.currentOverlays)
- val progressFlow = MutableStateFlow(0f)
- transitionState.value =
- if (checkNotNull(currentOverlays).isEmpty()) {
- ShowOrHideOverlay(
- overlay = toOverlay,
- fromContent = checkNotNull(currentScene),
- toContent = toOverlay,
- currentScene = checkNotNull(currentScene),
- currentOverlays = flowOf(emptySet()),
- progress = progressFlow,
- isInitiatedByUserInput = true,
- isUserInputOngoing = flowOf(true),
- previewProgress = flowOf(0f),
- isInPreviewStage = flowOf(false),
- )
- } else {
- ObservableTransitionState.Transition.ReplaceOverlay(
- fromOverlay = checkNotNull(currentOverlays).first(),
- toOverlay = toOverlay,
- currentScene = checkNotNull(currentScene),
- currentOverlays = flowOf(emptySet()),
- progress = progressFlow,
- isInitiatedByUserInput = true,
- isUserInputOngoing = flowOf(true),
- previewProgress = flowOf(0f),
- isInPreviewStage = flowOf(false),
- )
- }
- runCurrent()
- assertDuringProgress(progressFlow.value)
-
- progressFlow.value = 0.2f
- runCurrent()
- assertDuringProgress(progressFlow.value)
-
- progressFlow.value = 0.6f
- runCurrent()
- assertDuringProgress(progressFlow.value)
-
- progressFlow.value = 1f
- runCurrent()
- assertDuringProgress(progressFlow.value)
-
- transitionState.value =
- ObservableTransitionState.Idle(
- currentScene = checkNotNull(currentScene),
- currentOverlays = setOf(toOverlay),
- )
- if (checkNotNull(currentOverlays).isEmpty()) {
- fakeSceneDataSource.showOverlay(toOverlay)
- } else {
- fakeSceneDataSource.replaceOverlay(
- from = checkNotNull(currentOverlays).first(),
- to = toOverlay,
- )
- }
- runCurrent()
- assertDuringProgress(progressFlow.value)
-
- assertThat(currentOverlays).containsExactly(toOverlay)
- }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractorImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractorImplTest.kt
index d26e195..4b5f86b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractorImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractorImplTest.kt
@@ -101,24 +101,4 @@
assertThat(shadeMode).isNotEqualTo(ShadeMode.Dual)
assertThat(underTest.isDualShade).isFalse()
}
-
- @Test
- fun getTopEdgeSplitFraction_narrowScreen_splitInHalf() =
- testScope.runTest {
- val shadeMode by collectLastValue(underTest.shadeMode)
- kosmos.enableDualShade(wideLayout = false)
-
- assertThat(shadeMode).isEqualTo(ShadeMode.Dual)
- assertThat(underTest.getTopEdgeSplitFraction()).isEqualTo(0.5f)
- }
-
- @Test
- fun getTopEdgeSplitFraction_wideScreen_splitInHalf() =
- testScope.runTest {
- val shadeMode by collectLastValue(underTest.shadeMode)
- kosmos.enableDualShade(wideLayout = true)
-
- assertThat(shadeMode).isEqualTo(ShadeMode.Dual)
- assertThat(underTest.getTopEdgeSplitFraction()).isEqualTo(0.5f)
- }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/startable/ShadeStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/startable/ShadeStartableTest.kt
index dde8678..d16a77c6 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/startable/ShadeStartableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/startable/ShadeStartableTest.kt
@@ -17,13 +17,13 @@
package com.android.systemui.shade.domain.startable
import android.platform.test.flag.junit.FlagsParameterization
+import android.testing.TestableLooper.RunWithLooper
import androidx.test.filters.SmallTest
import com.android.compose.animation.scene.ObservableTransitionState
import com.android.compose.animation.scene.SceneKey
import com.android.systemui.SysuiTestCase
import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
-import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.display.data.repository.displayStateRepository
import com.android.systemui.flags.EnableSceneContainer
@@ -31,29 +31,33 @@
import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository
import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
import com.android.systemui.kosmos.testScope
-import com.android.systemui.res.R
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.scene.shared.model.fakeSceneDataSource
import com.android.systemui.shade.ShadeExpansionChangeEvent
import com.android.systemui.shade.ShadeExpansionListener
-import com.android.systemui.shade.domain.interactor.disableDualShade
import com.android.systemui.shade.domain.interactor.enableDualShade
+import com.android.systemui.shade.domain.interactor.enableSingleShade
+import com.android.systemui.shade.domain.interactor.enableSplitShade
import com.android.systemui.shade.domain.interactor.shadeMode
+import com.android.systemui.shade.domain.interactor.shadeModeInteractor
import com.android.systemui.shade.shared.model.ShadeMode
import com.android.systemui.statusbar.notification.stack.notificationStackScrollLayoutController
+import com.android.systemui.statusbar.notificationShadeDepthController
import com.android.systemui.statusbar.phone.scrimController
import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
+import kotlin.math.max
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
+import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.verify
@@ -62,16 +66,20 @@
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
+@RunWithLooper(setAsMainLooper = true)
@RunWith(ParameterizedAndroidJunit4::class)
class ShadeStartableTest(flags: FlagsParameterization) : SysuiTestCase() {
+
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
private val sceneInteractor by lazy { kosmos.sceneInteractor }
- private val shadeExpansionStateManager by lazy { kosmos.shadeExpansionStateManager }
- private val fakeConfigurationRepository by lazy { kosmos.fakeConfigurationRepository }
private val fakeSceneDataSource by lazy { kosmos.fakeSceneDataSource }
+ private val shadeDepthController by lazy { kosmos.notificationShadeDepthController }
+ private val shadeExpansionStateManager by lazy {
+ kosmos.shadeExpansionStateManager.also { it.addExpansionListener(shadeDepthController) }
+ }
- private val underTest: ShadeStartable = kosmos.shadeStartable
+ private lateinit var underTest: ShadeStartable
companion object {
@JvmStatic
@@ -85,43 +93,50 @@
mSetFlagsRule.setFlagsParameterization(flags)
}
+ @Before
+ fun setUp() {
+ underTest = kosmos.shadeStartable
+ }
+
@Test
fun hydrateShadeMode_dualShadeDisabled() =
testScope.runTest {
- overrideResource(R.bool.config_use_split_notification_shade, false)
- kosmos.disableDualShade()
val shadeMode by collectLastValue(kosmos.shadeMode)
-
+ val isShadeLayoutWide by collectLastValue(kosmos.shadeModeInteractor.isShadeLayoutWide)
underTest.start()
- assertThat(shadeMode).isEqualTo(ShadeMode.Single)
- overrideResource(R.bool.config_use_split_notification_shade, true)
- fakeConfigurationRepository.onAnyConfigurationChange()
+ kosmos.enableSingleShade()
+ assertThat(shadeMode).isEqualTo(ShadeMode.Single)
+ assertThat(isShadeLayoutWide).isFalse()
+
+ kosmos.enableSplitShade()
assertThat(shadeMode).isEqualTo(ShadeMode.Split)
+ assertThat(isShadeLayoutWide).isTrue()
- overrideResource(R.bool.config_use_split_notification_shade, false)
- fakeConfigurationRepository.onAnyConfigurationChange()
+ kosmos.enableSingleShade()
assertThat(shadeMode).isEqualTo(ShadeMode.Single)
+ assertThat(isShadeLayoutWide).isFalse()
}
@Test
@EnableSceneContainer
fun hydrateShadeMode_dualShadeEnabled() =
testScope.runTest {
- overrideResource(R.bool.config_use_split_notification_shade, false)
- kosmos.enableDualShade()
val shadeMode by collectLastValue(kosmos.shadeMode)
-
+ val isShadeLayoutWide by collectLastValue(kosmos.shadeModeInteractor.isShadeLayoutWide)
underTest.start()
- assertThat(shadeMode).isEqualTo(ShadeMode.Dual)
- overrideResource(R.bool.config_use_split_notification_shade, true)
- fakeConfigurationRepository.onAnyConfigurationChange()
+ kosmos.enableDualShade(wideLayout = false)
assertThat(shadeMode).isEqualTo(ShadeMode.Dual)
+ assertThat(isShadeLayoutWide).isFalse()
- overrideResource(R.bool.config_use_split_notification_shade, false)
- fakeConfigurationRepository.onAnyConfigurationChange()
+ kosmos.enableDualShade(wideLayout = true)
assertThat(shadeMode).isEqualTo(ShadeMode.Dual)
+ assertThat(isShadeLayoutWide).isTrue()
+
+ kosmos.enableDualShade(wideLayout = false)
+ assertThat(shadeMode).isEqualTo(ShadeMode.Dual)
+ assertThat(isShadeLayoutWide).isFalse()
}
@Test
@@ -163,7 +178,34 @@
changeScene(Scenes.Shade, transitionState) { progress ->
assertThat(latestChangeEvent?.fraction).isEqualTo(progress)
+ assertThat(shadeDepthController.qsPanelExpansion).isZero()
+ assertThat(shadeDepthController.shadeExpansion).isEqualTo(progress)
+ assertThat(shadeDepthController.transitionToFullShadeProgress).isEqualTo(progress)
}
+ assertThat(currentScene).isEqualTo(Scenes.Shade)
+
+ changeScene(Scenes.QuickSettings, transitionState) { progress ->
+ assertThat(latestChangeEvent?.fraction).isEqualTo(1 - progress)
+ assertThat(shadeDepthController.qsPanelExpansion).isEqualTo(progress)
+ assertThat(shadeDepthController.shadeExpansion).isEqualTo(1 - progress)
+ assertThat(shadeDepthController.transitionToFullShadeProgress)
+ .isEqualTo(
+ max(
+ shadeDepthController.qsPanelExpansion,
+ shadeDepthController.shadeExpansion,
+ )
+ )
+ }
+ assertThat(currentScene).isEqualTo(Scenes.QuickSettings)
+
+ changeScene(Scenes.Lockscreen, transitionState) { progress ->
+ assertThat(latestChangeEvent?.fraction).isZero()
+ assertThat(shadeDepthController.qsPanelExpansion).isEqualTo(1 - progress)
+ assertThat(shadeDepthController.shadeExpansion).isZero()
+ assertThat(shadeDepthController.transitionToFullShadeProgress)
+ .isEqualTo(1 - progress)
+ }
+ assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneContentViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneContentViewModelTest.kt
index 623a0eb..316d988 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneContentViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneContentViewModelTest.kt
@@ -122,11 +122,11 @@
assertThat(isMediaVisible).isFalse()
- kosmos.mediaFilterRepository.addSelectedUserMediaEntry(userMedia)
+ kosmos.mediaFilterRepository.addCurrentUserMediaEntry(userMedia)
assertThat(isMediaVisible).isTrue()
- kosmos.mediaFilterRepository.removeSelectedUserMediaEntry(userMedia.instanceId)
+ kosmos.mediaFilterRepository.removeCurrentUserMediaEntry(userMedia.instanceId)
assertThat(isMediaVisible).isFalse()
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
index 2f2fafa..5604c8c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
@@ -133,7 +133,7 @@
override fun getClocks() = metadata
- override fun createClock(settings: ClockSettings): ClockController {
+ override fun createClock(ctx: Context, settings: ClockSettings): ClockController {
val clockId = settings.clockId ?: throw IllegalArgumentException("No clockId specified")
return createCallbacks[clockId]?.invoke(clockId)
?: throw NotImplementedError("No callback for '$clockId'")
@@ -243,7 +243,7 @@
@Test
fun noPlugins_createDefaultClock() {
- val clock = registry.createCurrentClock()
+ val clock = registry.createCurrentClock(mockContext)
assertEquals(mockDefaultClock, clock)
}
@@ -270,8 +270,8 @@
list.toSet(),
)
- assertEquals(mockClock, registry.createExampleClock("clock_1"))
- assertEquals(mockClock, registry.createExampleClock("clock_2"))
+ assertEquals(mockClock, registry.createExampleClock(mockContext, "clock_1"))
+ assertEquals(mockClock, registry.createExampleClock(mockContext, "clock_2"))
assertEquals(pickerConfig, registry.getClockPickerConfig("clock_1"))
assertEquals(pickerConfig, registry.getClockPickerConfig("clock_2"))
verify(lifecycle1, never()).unloadPlugin()
@@ -290,7 +290,7 @@
pluginListener.onPluginLoaded(plugin1, mockContext, lifecycle1)
pluginListener.onPluginLoaded(plugin2, mockContext, lifecycle2)
- val clock = registry.createCurrentClock()
+ val clock = registry.createCurrentClock(mockContext)
assertEquals(mockClock, clock)
}
@@ -324,7 +324,7 @@
pluginListener.onPluginLoaded(plugin2, mockContext, lifecycle2)
pluginListener.onPluginUnloaded(plugin2, lifecycle2)
- val clock = registry.createCurrentClock()
+ val clock = registry.createCurrentClock(mockContext)
assertEquals(mockDefaultClock, clock)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt
index 0642467..a39b133 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.shared.clocks
+import android.content.Context
import android.content.res.Resources
import android.graphics.Color
import android.graphics.drawable.Drawable
@@ -54,8 +55,12 @@
import org.mockito.Mockito.`when` as whenever
import org.mockito.junit.MockitoJUnit
-private fun DefaultClockProvider.createClock(id: ClockId): DefaultClockController =
- createClock(ClockSettings(id, null)) as DefaultClockController
+private fun DefaultClockProvider.createClock(
+ context: Context,
+ id: ClockId,
+): DefaultClockController {
+ return createClock(context, ClockSettings(id, null)) as DefaultClockController
+}
@RunWith(AndroidJUnit4::class)
@SmallTest
@@ -84,14 +89,14 @@
whenever(mockSmallClockView.getLayoutParams()).thenReturn(FrameLayout.LayoutParams(10, 10))
whenever(mockLargeClockView.getLayoutParams()).thenReturn(FrameLayout.LayoutParams(10, 10))
- provider = DefaultClockProvider(context, layoutInflater, resources, vibrator = null)
+ provider = DefaultClockProvider(layoutInflater, resources, vibrator = null)
}
@Test
fun providedClocks_matchesFactory() {
// All providers need to provide clocks & thumbnails for exposed clocks
for (metadata in provider.getClocks()) {
- assertNotNull(provider.createClock(metadata.clockId))
+ assertNotNull(provider.createClock(context, metadata.clockId))
assertNotNull(provider.getClockPickerConfig(ClockSettings(metadata.clockId)))
}
}
@@ -99,7 +104,7 @@
@Test
fun defaultClock_alwaysProvided() {
// Default clock provider must always provide the default clock
- val clock = provider.createClock(DEFAULT_CLOCK_ID)
+ val clock = provider.createClock(context, DEFAULT_CLOCK_ID)
assertNotNull(clock)
assertEquals(mockSmallClockView, clock.smallClock.view)
assertEquals(mockLargeClockView, clock.largeClock.view)
@@ -108,7 +113,7 @@
@Test
@DisableFlags(Flags.FLAG_AMBIENT_AOD)
fun defaultClock_initialize_flagOff() {
- val clock = provider.createClock(DEFAULT_CLOCK_ID)
+ val clock = provider.createClock(context, DEFAULT_CLOCK_ID)
verify(mockSmallClockView).setColors(DOZE_COLOR, Color.MAGENTA)
verify(mockLargeClockView).setColors(DOZE_COLOR, Color.MAGENTA)
@@ -127,15 +132,16 @@
@Test
@EnableFlags(Flags.FLAG_AMBIENT_AOD)
fun defaultClock_initialize() {
- val clock = provider.createClock(DEFAULT_CLOCK_ID)
- verify(mockSmallClockView).setColors(DOZE_COLOR, Color.MAGENTA)
- verify(mockLargeClockView).setColors(DOZE_COLOR, Color.MAGENTA)
+ val expectedAodColor = context.resources.getColor(android.R.color.system_accent1_100)
+ val clock = provider.createClock(context, DEFAULT_CLOCK_ID)
+ verify(mockSmallClockView).setColors(expectedAodColor, Color.MAGENTA)
+ verify(mockLargeClockView).setColors(expectedAodColor, Color.MAGENTA)
clock.initialize(true, 0f, 0f, null)
val expectedColor = Color.MAGENTA
- verify(mockSmallClockView).setColors(DOZE_COLOR, expectedColor)
- verify(mockLargeClockView).setColors(DOZE_COLOR, expectedColor)
+ verify(mockSmallClockView).setColors(expectedAodColor, expectedColor)
+ verify(mockLargeClockView).setColors(expectedAodColor, expectedColor)
verify(mockSmallClockView).onTimeZoneChanged(notNull())
verify(mockLargeClockView).onTimeZoneChanged(notNull())
verify(mockSmallClockView).refreshTime()
@@ -144,7 +150,7 @@
@Test
fun defaultClock_events_onTimeTick() {
- val clock = provider.createClock(DEFAULT_CLOCK_ID)
+ val clock = provider.createClock(context, DEFAULT_CLOCK_ID)
clock.smallClock.events.onTimeTick()
clock.largeClock.events.onTimeTick()
@@ -154,7 +160,7 @@
@Test
fun defaultClock_events_onTimeFormatChanged() {
- val clock = provider.createClock(DEFAULT_CLOCK_ID)
+ val clock = provider.createClock(context, DEFAULT_CLOCK_ID)
clock.events.onTimeFormatChanged(true)
verify(mockSmallClockView).refreshFormat(true)
@@ -164,7 +170,7 @@
@Test
fun defaultClock_events_onTimeZoneChanged() {
val timeZone = mock<TimeZone>()
- val clock = provider.createClock(DEFAULT_CLOCK_ID)
+ val clock = provider.createClock(context, DEFAULT_CLOCK_ID)
clock.events.onTimeZoneChanged(timeZone)
verify(mockSmallClockView).onTimeZoneChanged(timeZone)
@@ -173,7 +179,7 @@
@Test
fun defaultSmallClock_events_onFontSettingChanged() {
- val clock = provider.createClock(DEFAULT_CLOCK_ID)
+ val clock = provider.createClock(context, DEFAULT_CLOCK_ID)
clock.smallClock.events.onFontSettingChanged(100f)
verify(mockSmallClockView).setTextSize(eq(TypedValue.COMPLEX_UNIT_PX), eq(100f))
@@ -181,7 +187,7 @@
@Test
fun defaultLargeClock_events_onFontSettingChanged() {
- val clock = provider.createClock(DEFAULT_CLOCK_ID)
+ val clock = provider.createClock(context, DEFAULT_CLOCK_ID)
clock.largeClock.events.onFontSettingChanged(200f)
verify(mockLargeClockView).setTextSize(eq(TypedValue.COMPLEX_UNIT_PX), eq(200f))
@@ -192,7 +198,7 @@
fun defaultClock_events_onThemeChanged_noSeed_flagOff() {
// This is the default darkTheme color
val expectedColor = context.resources.getColor(android.R.color.system_accent1_100)
- val clock = provider.createClock(DEFAULT_CLOCK_ID)
+ val clock = provider.createClock(context, DEFAULT_CLOCK_ID)
verify(mockSmallClockView).setColors(DOZE_COLOR, Color.MAGENTA)
verify(mockLargeClockView).setColors(DOZE_COLOR, Color.MAGENTA)
@@ -208,23 +214,26 @@
@EnableFlags(Flags.FLAG_AMBIENT_AOD)
fun defaultClock_events_onThemeChanged_noSeedn() {
val expectedColor = Color.TRANSPARENT
- val clock = provider.createClock(DEFAULT_CLOCK_ID)
+ val clock = provider.createClock(context, DEFAULT_CLOCK_ID)
- verify(mockSmallClockView).setColors(DOZE_COLOR, Color.MAGENTA)
- verify(mockLargeClockView).setColors(DOZE_COLOR, Color.MAGENTA)
+ val expectedAodColor = context.resources.getColor(android.R.color.system_accent1_100)
+
+ verify(mockSmallClockView).setColors(expectedAodColor, Color.MAGENTA)
+ verify(mockLargeClockView).setColors(expectedAodColor, Color.MAGENTA)
clock.smallClock.events.onThemeChanged(ThemeConfig(true, null))
clock.largeClock.events.onThemeChanged(ThemeConfig(true, null))
- verify(mockSmallClockView).setColors(DOZE_COLOR, Color.MAGENTA)
- verify(mockLargeClockView).setColors(DOZE_COLOR, Color.MAGENTA)
+ verify(mockSmallClockView).setColors(expectedAodColor, Color.MAGENTA)
+ verify(mockLargeClockView).setColors(expectedAodColor, Color.MAGENTA)
}
@Test
- fun defaultClock_events_onThemeChanged_newSeed() {
+ @DisableFlags(Flags.FLAG_AMBIENT_AOD)
+ fun defaultClock_events_onThemeChanged_newSeed_flagOff() {
val initSeedColor = 10
val newSeedColor = 20
- val clock = provider.createClock(ClockSettings(DEFAULT_CLOCK_ID, initSeedColor))
+ val clock = provider.createClock(context, ClockSettings(DEFAULT_CLOCK_ID, initSeedColor))
verify(mockSmallClockView).setColors(DOZE_COLOR, initSeedColor)
verify(mockLargeClockView).setColors(DOZE_COLOR, initSeedColor)
@@ -237,8 +246,27 @@
}
@Test
+ @EnableFlags(Flags.FLAG_AMBIENT_AOD)
+ fun defaultClock_events_onThemeChanged_newSeed() {
+ val initSeedColor = 10
+ val newSeedColor = 20
+ val clock = provider.createClock(context, ClockSettings(DEFAULT_CLOCK_ID, initSeedColor))
+
+ val expectedAodColor = context.resources.getColor(android.R.color.system_accent1_100)
+
+ verify(mockSmallClockView).setColors(expectedAodColor, initSeedColor)
+ verify(mockLargeClockView).setColors(expectedAodColor, initSeedColor)
+
+ clock.smallClock.events.onThemeChanged(ThemeConfig(true, newSeedColor))
+ clock.largeClock.events.onThemeChanged(ThemeConfig(true, newSeedColor))
+
+ verify(mockSmallClockView).setColors(expectedAodColor, newSeedColor)
+ verify(mockLargeClockView).setColors(expectedAodColor, newSeedColor)
+ }
+
+ @Test
fun defaultClock_events_onLocaleChanged() {
- val clock = provider.createClock(DEFAULT_CLOCK_ID)
+ val clock = provider.createClock(context, DEFAULT_CLOCK_ID)
clock.events.onLocaleChanged(Locale.getDefault())
verify(mockSmallClockView, times(2)).setLineSpacingScale(anyFloat())
@@ -249,7 +277,7 @@
@Test
fun test_aodClock_always_whiteColor() {
- val clock = provider.createClock(DEFAULT_CLOCK_ID)
+ val clock = provider.createClock(context, DEFAULT_CLOCK_ID)
clock.smallClock.animations.doze(0.9f) // set AOD mode to active
clock.smallClock.events.onThemeChanged(ThemeConfig(true, null))
verify((clock.smallClock.view as AnimatableClockView), never()).animateAppearOnLockscreen()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/KeyguardIndicationControllerBaseTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/KeyguardIndicationControllerBaseTest.java
index 99d152f..7fe6e72 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/KeyguardIndicationControllerBaseTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/KeyguardIndicationControllerBaseTest.java
@@ -18,7 +18,6 @@
import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_DEFAULT;
-import static com.android.systemui.flags.Flags.KEYGUARD_TALKBACK_FIX;
import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_TRANSIENT;
import static com.android.systemui.keyguard.ScreenLifecycle.SCREEN_ON;
@@ -286,7 +285,6 @@
}
mFlags = new FakeFeatureFlags();
- mFlags.set(KEYGUARD_TALKBACK_FIX, true);
mController = new KeyguardIndicationController(
mContext,
mTestableLooper.getLooper(),
@@ -301,7 +299,6 @@
mAlarmManager,
mUserTracker,
mock(BouncerMessageInteractor.class),
- mFlags,
mIndicationHelper,
mDeviceEntryBiometricSettingsInteractor,
KeyguardInteractorFactory.create(mFlags).getKeyguardInteractor(),
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
index 51a2682..1c2a4e2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
@@ -16,7 +16,6 @@
package com.android.systemui.statusbar;
-import static android.app.Flags.FLAG_KEYGUARD_PRIVATE_NOTIFICATIONS;
import static android.app.Notification.VISIBILITY_PRIVATE;
import static android.app.NotificationManager.IMPORTANCE_HIGH;
import static android.app.NotificationManager.VISIBILITY_NO_OVERRIDE;
@@ -130,7 +129,6 @@
public static List<FlagsParameterization> getParams() {
return FlagsParameterization.allCombinationsOf(
FLAG_ALLOW_PRIVATE_PROFILE,
- FLAG_KEYGUARD_PRIVATE_NOTIFICATIONS,
FLAG_ENABLE_PRIVATE_SPACE_FEATURES);
}
@@ -314,7 +312,6 @@
}
@Test
- @EnableFlags(FLAG_KEYGUARD_PRIVATE_NOTIFICATIONS)
public void testInit() {
when(mKeyguardManager.getPrivateNotificationsAllowed()).thenReturn(false);
mLockscreenUserManager = new TestNotificationLockscreenUserManager(mContext);
@@ -884,7 +881,6 @@
}
@Test
- @EnableFlags(FLAG_KEYGUARD_PRIVATE_NOTIFICATIONS)
public void testEarlyUserSwitch() {
mLockscreenUserManager =
new TestNotificationLockscreenUserManager(mContext);
@@ -895,7 +891,6 @@
}
@Test
- @EnableFlags(FLAG_KEYGUARD_PRIVATE_NOTIFICATIONS)
public void testKeyguardManager_noPrivateNotifications() {
Mockito.clearInvocations(mDevicePolicyManager);
// User allows notifications
@@ -1042,7 +1037,6 @@
}
@Test
- @EnableFlags(FLAG_KEYGUARD_PRIVATE_NOTIFICATIONS)
public void testShouldShowLockscreenNotifications_keyguardManagerNoPrivateNotifications_show() {
// KeyguardManager does not allow notifications
when(mKeyguardManager.getPrivateNotificationsAllowed()).thenReturn(false);
@@ -1064,50 +1058,6 @@
}
@Test
- @DisableFlags(FLAG_KEYGUARD_PRIVATE_NOTIFICATIONS)
- public void testShouldShowLockscreenNotifications_keyguardManagerNoPrivateNotifications() {
- // KeyguardManager does not allow notifications
- when(mKeyguardManager.getPrivateNotificationsAllowed()).thenReturn(false);
- // User allows notifications
- mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, mCurrentUser.id);
- changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS);
- // DevicePolicy allows notifications
- when(mDevicePolicyManager.getKeyguardDisabledFeatures(null, mCurrentUser.id))
- .thenReturn(0);
- BroadcastReceiver.PendingResult pr = new BroadcastReceiver.PendingResult(
- 0, null, null, 0, true, false, null, mCurrentUser.id, 0);
- mLockscreenUserManager.mAllUsersReceiver.setPendingResult(pr);
- mLockscreenUserManager.mAllUsersReceiver.onReceive(mContext,
- new Intent(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED));
-
- assertFalse(mLockscreenUserManager.shouldShowLockscreenNotifications());
- }
-
- @Test
- @DisableFlags(FLAG_KEYGUARD_PRIVATE_NOTIFICATIONS)
- public void testUserAllowsNotificationsInPublic_keyguardManagerNoPrivateNotifications() {
- // DevicePolicy allows notifications
- when(mDevicePolicyManager.getKeyguardDisabledFeatures(null, mCurrentUser.id))
- .thenReturn(0);
- BroadcastReceiver.PendingResult pr = new BroadcastReceiver.PendingResult(
- 0, null, null, 0, true, false, null, mCurrentUser.id, 0);
- mLockscreenUserManager.mAllUsersReceiver.setPendingResult(pr);
- mLockscreenUserManager.mAllUsersReceiver.onReceive(mContext,
- new Intent(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED));
-
- // KeyguardManager does not allow notifications
- when(mKeyguardManager.getPrivateNotificationsAllowed()).thenReturn(false);
-
- // User allows notifications
- mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, mCurrentUser.id);
- // We shouldn't need to call this method, but getPrivateNotificationsAllowed has no
- // callback, so it's only updated when the setting is
- changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS);
-
- assertFalse(mLockscreenUserManager.userAllowsNotificationsInPublic(mCurrentUser.id));
- }
-
- @Test
public void testUserAllowsNotificationsInPublic_settingsChange() {
// User allows notifications
mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, mCurrentUser.id);
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
index 3fe8817..544299c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
@@ -478,7 +478,11 @@
}
@Test
- @DisableFlags(Flags.FLAG_BOUNCER_UI_REVAMP, Flags.FLAG_GLANCEABLE_HUB_BLURRED_BACKGROUND)
+ @DisableFlags(
+ Flags.FLAG_BOUNCER_UI_REVAMP,
+ Flags.FLAG_GLANCEABLE_HUB_BLURRED_BACKGROUND,
+ Flags.FLAG_SPATIAL_MODEL_APP_PUSHBACK
+ )
fun brightnessMirror_hidesShadeBlur() {
// Brightness mirror is fully visible
`when`(brightnessSpring.ratio).thenReturn(1f)
@@ -496,7 +500,27 @@
}
@Test
+ @DisableFlags(Flags.FLAG_BOUNCER_UI_REVAMP, Flags.FLAG_GLANCEABLE_HUB_BLURRED_BACKGROUND)
+ @EnableFlags(Flags.FLAG_SPATIAL_MODEL_APP_PUSHBACK)
+ fun brightnessMirror_hidesShadeBlur_withAppPushback() {
+ // Brightness mirror is fully visible
+ `when`(brightnessSpring.ratio).thenReturn(1f)
+ // And shade is blurred
+ notificationShadeDepthController.onPanelExpansionChanged(
+ ShadeExpansionChangeEvent(fraction = 1f, expanded = true, tracking = false)
+ )
+ `when`(shadeAnimation.radius).thenReturn(maxBlur.toFloat())
+
+ notificationShadeDepthController.updateBlurCallback.doFrame(0)
+ verify(notificationShadeWindowController).setBackgroundBlurRadius(eq(0))
+ verify(wallpaperController).setNotificationShadeZoom(eq(0f))
+ verify(blurUtils).prepareBlur(any(), eq(0))
+ verify(blurUtils).applyBlur(eq(viewRootImpl), eq(0), eq(false))
+ }
+
+ @Test
@EnableFlags(Flags.FLAG_BOUNCER_UI_REVAMP)
+ @DisableFlags(Flags.FLAG_SPATIAL_MODEL_APP_PUSHBACK)
fun brightnessMirror_hidesShadeBlur_withWindowBlurFlag() {
// Brightness mirror is fully visible
`when`(brightnessSpring.ratio).thenReturn(1f)
@@ -513,6 +537,23 @@
}
@Test
+ @EnableFlags(Flags.FLAG_BOUNCER_UI_REVAMP, Flags.FLAG_SPATIAL_MODEL_APP_PUSHBACK)
+ fun brightnessMirror_hidesShadeBlur_withWindowBlurFlagAndAppPushback() {
+ // Brightness mirror is fully visible
+ `when`(brightnessSpring.ratio).thenReturn(1f)
+ // And shade is blurred
+ notificationShadeDepthController.onPanelExpansionChanged(
+ ShadeExpansionChangeEvent(fraction = 1f, expanded = true, tracking = false)
+ )
+ `when`(shadeAnimation.radius).thenReturn(maxBlur.toFloat())
+
+ notificationShadeDepthController.updateBlurCallback.doFrame(0)
+ verify(notificationShadeWindowController).setBackgroundBlurRadius(eq(0))
+ verify(wallpaperController).setNotificationShadeZoom(eq(0f))
+ verify(windowRootViewBlurInteractor).requestBlurForShade(0)
+ }
+
+ @Test
fun ignoreShadeBlurUntilHidden_whennNull_ignoresIfShadeHasNoBlur() {
`when`(shadeAnimation.radius).thenReturn(0f)
notificationShadeDepthController.blursDisabledForAppLaunch = true
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationUiAdjustmentTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationUiAdjustmentTest.java
index 5f9c9df..3651beb 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationUiAdjustmentTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationUiAdjustmentTest.java
@@ -232,6 +232,22 @@
.isTrue();
}
+ @Test
+ public void needReinflate_differentBundling() {
+ assertThat(NotificationUiAdjustment.needReinflate(
+ createUiAdjustmentFromSmartReplies("first", new CharSequence[]{"a", "b"}),
+ createUiAdjustmentFromSmartReplies("first", new CharSequence[] {"b", "a"})))
+ .isTrue();
+ }
+
+ @Test
+ public void needReinflate_sameBundling() {
+ assertThat(NotificationUiAdjustment.needReinflate(
+ createUiAdjustmentForBundling("first", true),
+ createUiAdjustmentForBundling("first", true)))
+ .isFalse();
+ }
+
private Notification.Action.Builder createActionBuilder(
String title, int drawableRes, PendingIntent pendingIntent) {
return new Notification.Action.Builder(
@@ -244,16 +260,21 @@
private NotificationUiAdjustment createUiAdjustmentFromSmartActions(
String key, List<Notification.Action> actions) {
- return new NotificationUiAdjustment(key, actions, null, false);
+ return new NotificationUiAdjustment(key, actions, null, false, false);
}
private NotificationUiAdjustment createUiAdjustmentFromSmartReplies(
String key, CharSequence[] replies) {
- return new NotificationUiAdjustment(key, null, Arrays.asList(replies), false);
+ return new NotificationUiAdjustment(key, null, Arrays.asList(replies), false, false);
}
private NotificationUiAdjustment createUiAdjustmentForConversation(
String key, boolean isConversation) {
- return new NotificationUiAdjustment(key, null, null, isConversation);
+ return new NotificationUiAdjustment(key, null, null, isConversation, false);
+ }
+
+ private NotificationUiAdjustment createUiAdjustmentForBundling(
+ String key, boolean isBundle) {
+ return new NotificationUiAdjustment(key, null, null, false, isBundle);
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/StatusBarSignalPolicyTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/StatusBarSignalPolicyTest.kt
index 7de09f7..4788339f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/StatusBarSignalPolicyTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/StatusBarSignalPolicyTest.kt
@@ -21,12 +21,12 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.R
-import com.android.settingslib.mobile.TelephonyIcons
import com.android.systemui.Flags.FLAG_STATUS_BAR_SIGNAL_POLICY_REFACTOR
import com.android.systemui.SysuiTestCase
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.runTest
import com.android.systemui.kosmos.useUnconfinedTestDispatcher
+import com.android.systemui.res.R.drawable.stat_sys_airplane_mode
import com.android.systemui.statusbar.connectivity.IconState
import com.android.systemui.statusbar.connectivity.NetworkController
import com.android.systemui.statusbar.phone.StatusBarSignalPolicy
@@ -106,10 +106,10 @@
// Make sure the legacy code path does not change airplane mode when the refactor
// flag is enabled.
- underTest.setIsAirplaneMode(IconState(true, TelephonyIcons.FLIGHT_MODE_ICON, ""))
+ underTest.setIsAirplaneMode(IconState(true, stat_sys_airplane_mode, ""))
verify(statusBarIconController, never()).setIconVisibility(eq(slotAirplane), any())
- underTest.setIsAirplaneMode(IconState(false, TelephonyIcons.FLIGHT_MODE_ICON, ""))
+ underTest.setIsAirplaneMode(IconState(false, stat_sys_airplane_mode, ""))
verify(statusBarIconController, never()).setIconVisibility(eq(slotAirplane), any())
}
@@ -129,10 +129,10 @@
kosmos.runTest {
underTest.init()
- underTest.setIsAirplaneMode(IconState(true, TelephonyIcons.FLIGHT_MODE_ICON, ""))
+ underTest.setIsAirplaneMode(IconState(true, stat_sys_airplane_mode, ""))
verify(statusBarIconController).setIconVisibility(slotAirplane, true)
- underTest.setIsAirplaneMode(IconState(false, TelephonyIcons.FLIGHT_MODE_ICON, ""))
+ underTest.setIsAirplaneMode(IconState(false, stat_sys_airplane_mode, ""))
verify(statusBarIconController).setIconVisibility(slotAirplane, false)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModelTest.kt
index 9959976..da76dfe 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModelTest.kt
@@ -17,12 +17,10 @@
package com.android.systemui.statusbar.chips.casttootherdevice.ui.viewmodel
import android.content.DialogInterface
-import android.platform.test.annotations.EnableFlags
import android.view.View
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.jank.Cuj
-import com.android.systemui.Flags.FLAG_STATUS_BAR_SHOW_AUDIO_ONLY_PROJECTION_CHIP
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.DialogCuj
import com.android.systemui.animation.Expandable
@@ -149,7 +147,6 @@
}
@Test
- @EnableFlags(FLAG_STATUS_BAR_SHOW_AUDIO_ONLY_PROJECTION_CHIP)
fun chip_projectionIsAudioOnly_otherDevicePackage_isShownAsIconOnly() =
testScope.runTest {
val latest by collectLastValue(underTest.chip)
@@ -358,7 +355,6 @@
}
@Test
- @EnableFlags(FLAG_STATUS_BAR_SHOW_AUDIO_ONLY_PROJECTION_CHIP)
fun chip_projectionIsNoScreenState_normalPackage_isHidden() =
testScope.runTest {
val latest by collectLastValue(underTest.chip)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractorTest.kt
index 0dc2759..6338b52 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractorTest.kt
@@ -21,10 +21,8 @@
import android.content.packageManager
import android.content.pm.PackageManager
import android.content.pm.ResolveInfo
-import android.platform.test.annotations.EnableFlags
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.Flags.FLAG_STATUS_BAR_SHOW_AUDIO_ONLY_PROJECTION_CHIP
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.Kosmos
@@ -93,7 +91,6 @@
}
@Test
- @EnableFlags(FLAG_STATUS_BAR_SHOW_AUDIO_ONLY_PROJECTION_CHIP)
fun projection_noScreenState_otherDevicesPackage_isCastToOtherAndAudio() =
testScope.runTest {
val latest by collectLastValue(underTest.projection)
@@ -143,7 +140,6 @@
}
@Test
- @EnableFlags(FLAG_STATUS_BAR_SHOW_AUDIO_ONLY_PROJECTION_CHIP)
fun projection_noScreenState_normalPackage_isShareToAppAndAudio() =
testScope.runTest {
val latest by collectLastValue(underTest.projection)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt
index 4dcd4b2..a8d183d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt
@@ -44,6 +44,7 @@
import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationsStore
import com.android.systemui.statusbar.notification.data.repository.UnconfinedFakeHeadsUpRowRepository
import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository
+import com.android.systemui.statusbar.notification.data.repository.addNotif
import com.android.systemui.statusbar.notification.headsup.PinnedStatus
import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUi
import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentBuilder
@@ -232,16 +233,19 @@
listOf(
activeNotificationModel(
key = "notif1",
+ packageName = "notif1",
statusBarChipIcon = firstIcon,
promotedContent = PromotedNotificationContentBuilder("notif1").build(),
),
activeNotificationModel(
key = "notif2",
+ packageName = "notif2",
statusBarChipIcon = secondIcon,
promotedContent = PromotedNotificationContentBuilder("notif2").build(),
),
activeNotificationModel(
key = "notif3",
+ packageName = "notif3",
statusBarChipIcon = createStatusBarIconViewOrNull(),
promotedContent = null,
),
@@ -267,16 +271,19 @@
listOf(
activeNotificationModel(
key = firstKey,
+ packageName = firstKey,
statusBarChipIcon = null,
promotedContent = PromotedNotificationContentBuilder(firstKey).build(),
),
activeNotificationModel(
key = secondKey,
+ packageName = secondKey,
statusBarChipIcon = null,
promotedContent = PromotedNotificationContentBuilder(secondKey).build(),
),
activeNotificationModel(
key = thirdKey,
+ packageName = thirdKey,
statusBarChipIcon = null,
promotedContent = null,
),
@@ -289,6 +296,184 @@
}
@Test
+ fun chips_twoChips_samePackage_differentUids_bothIncluded() =
+ kosmos.runTest {
+ val latest by collectLastValue(underTest.chips)
+
+ fakeSystemClock.setCurrentTimeMillis(1000)
+ activeNotificationListRepository.addNotif(
+ activeNotificationModel(
+ key = "notif1",
+ packageName = "samePackage",
+ uid = 10,
+ statusBarChipIcon = createStatusBarIconViewOrNull(),
+ promotedContent = PromotedNotificationContentBuilder("notif1").build(),
+ )
+ )
+
+ fakeSystemClock.advanceTime(1000)
+ activeNotificationListRepository.addNotif(
+ activeNotificationModel(
+ key = "notif2",
+ packageName = "samePackage",
+ uid = 20,
+ statusBarChipIcon = createStatusBarIconViewOrNull(),
+ promotedContent = PromotedNotificationContentBuilder("notif2").build(),
+ )
+ )
+
+ // Notif added later takes priority
+ assertThat(latest!!.map { it.key }).containsExactly("notif2", "notif1").inOrder()
+ }
+
+ @Test
+ fun chips_twoChips_sameUid_differentPackages_bothIncluded() =
+ kosmos.runTest {
+ val latest by collectLastValue(underTest.chips)
+
+ fakeSystemClock.setCurrentTimeMillis(1000)
+ activeNotificationListRepository.addNotif(
+ activeNotificationModel(
+ key = "notif1",
+ packageName = "onePackage",
+ uid = 10,
+ statusBarChipIcon = createStatusBarIconViewOrNull(),
+ promotedContent = PromotedNotificationContentBuilder("notif1").build(),
+ )
+ )
+
+ fakeSystemClock.advanceTime(1000)
+ activeNotificationListRepository.addNotif(
+ activeNotificationModel(
+ key = "notif2",
+ packageName = "anotherPackage",
+ uid = 10,
+ statusBarChipIcon = createStatusBarIconViewOrNull(),
+ promotedContent = PromotedNotificationContentBuilder("notif2").build(),
+ )
+ )
+
+ // Notif added later takes priority
+ assertThat(latest!!.map { it.key }).containsExactly("notif2", "notif1").inOrder()
+ }
+
+ @Test
+ fun chips_twoChips_samePackage_andSameUid_onlyLaterOneIncluded() =
+ kosmos.runTest {
+ val latest by collectLastValue(underTest.chips)
+
+ val uid = 3
+ fakeSystemClock.setCurrentTimeMillis(1000)
+ activeNotificationListRepository.addNotif(
+ activeNotificationModel(
+ key = "notif1",
+ packageName = "samePackage",
+ uid = 3,
+ statusBarChipIcon = createStatusBarIconViewOrNull(),
+ promotedContent = PromotedNotificationContentBuilder("notif1").build(),
+ )
+ )
+
+ fakeSystemClock.advanceTime(1000)
+ activeNotificationListRepository.addNotif(
+ activeNotificationModel(
+ key = "notif2",
+ packageName = "samePackage",
+ uid = 3,
+ statusBarChipIcon = createStatusBarIconViewOrNull(),
+ promotedContent = PromotedNotificationContentBuilder("notif2").build(),
+ )
+ )
+
+ // Notif added later takes priority
+ assertThat(latest!!.map { it.key }).containsExactly("notif2").inOrder()
+ }
+
+ @Test
+ fun chips_multipleChipsFromMultiplePackagesAndUids_higherPriorityOfEachIncluded() =
+ kosmos.runTest {
+ val latest by collectLastValue(underTest.chips)
+
+ // Two notifs from "firstPackage"
+ fakeSystemClock.setCurrentTimeMillis(1000)
+ activeNotificationListRepository.addNotif(
+ activeNotificationModel(
+ key = "firstPackage.1",
+ packageName = "firstPackage",
+ uid = 1,
+ statusBarChipIcon = createStatusBarIconViewOrNull(),
+ promotedContent = PromotedNotificationContentBuilder("firstPackage.1").build(),
+ )
+ )
+
+ fakeSystemClock.advanceTime(1000)
+ activeNotificationListRepository.addNotif(
+ activeNotificationModel(
+ key = "firstPackage.2",
+ packageName = "firstPackage",
+ uid = 1,
+ statusBarChipIcon = createStatusBarIconViewOrNull(),
+ promotedContent = PromotedNotificationContentBuilder("firstPackage.2").build(),
+ )
+ )
+
+ // Three notifs from "secondPackage"
+ fakeSystemClock.advanceTime(1000)
+ activeNotificationListRepository.addNotif(
+ activeNotificationModel(
+ key = "secondPackage.1",
+ packageName = "secondPackage",
+ uid = 2,
+ statusBarChipIcon = createStatusBarIconViewOrNull(),
+ promotedContent = PromotedNotificationContentBuilder("secondPackage.1").build(),
+ )
+ )
+
+ fakeSystemClock.advanceTime(1000)
+ activeNotificationListRepository.addNotif(
+ activeNotificationModel(
+ key = "secondPackage.2",
+ packageName = "secondPackage",
+ uid = 2,
+ statusBarChipIcon = createStatusBarIconViewOrNull(),
+ promotedContent = PromotedNotificationContentBuilder("secondPackage.2").build(),
+ )
+ )
+
+ fakeSystemClock.advanceTime(1000)
+ activeNotificationListRepository.addNotif(
+ activeNotificationModel(
+ key = "secondPackage.3",
+ packageName = "secondPackage",
+ uid = 2,
+ statusBarChipIcon = createStatusBarIconViewOrNull(),
+ promotedContent = PromotedNotificationContentBuilder("secondPackage.3").build(),
+ )
+ )
+
+ fakeSystemClock.advanceTime(1000)
+ activeNotificationListRepository.addNotif(
+ activeNotificationModel(
+ key = "secondPackage.andDifferentUid",
+ packageName = "secondPackage",
+ uid = 200,
+ statusBarChipIcon = createStatusBarIconViewOrNull(),
+ promotedContent =
+ PromotedNotificationContentBuilder("secondPackage.andDifferentUid").build(),
+ )
+ )
+
+ // Notifs added later take priority
+ assertThat(latest!!.map { it.key })
+ .containsExactly(
+ "secondPackage.andDifferentUid",
+ "secondPackage.3",
+ "firstPackage.2",
+ )
+ .inOrder()
+ }
+
+ @Test
fun chips_notifTimeAndSystemTimeBothUpdated_modelNotRecreated() =
kosmos.runTest {
val latest by collectLastValue(underTest.chips)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractorTest.kt
index f8c5765..74c0342 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractorTest.kt
@@ -16,11 +16,8 @@
package com.android.systemui.statusbar.chips.screenrecord.domain.interactor
-import android.platform.test.annotations.DisableFlags
-import android.platform.test.annotations.EnableFlags
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.Flags.FLAG_STATUS_BAR_AUTO_START_SCREEN_RECORD_CHIP
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.testScope
@@ -161,25 +158,7 @@
}
@Test
- @DisableFlags(FLAG_STATUS_BAR_AUTO_START_SCREEN_RECORD_CHIP)
- fun screenRecordState_flagOff_doesNotAutomaticallySwitchToRecordingBasedOnTime() =
- testScope.runTest {
- val latest by collectLastValue(underTest.screenRecordState)
-
- // WHEN screen record should start in 900ms
- screenRecordRepo.screenRecordState.value = ScreenRecordModel.Starting(900)
- assertThat(latest).isEqualTo(ScreenRecordChipModel.Starting(900))
-
- // WHEN 900ms has elapsed
- advanceTimeBy(901)
-
- // THEN we don't automatically update to the recording state if the flag is off
- assertThat(latest).isEqualTo(ScreenRecordChipModel.Starting(900))
- }
-
- @Test
- @EnableFlags(FLAG_STATUS_BAR_AUTO_START_SCREEN_RECORD_CHIP)
- fun screenRecordState_flagOn_automaticallySwitchesToRecordingBasedOnTime() =
+ fun screenRecordState_automaticallySwitchesToRecordingBasedOnTime() =
testScope.runTest {
val latest by collectLastValue(underTest.screenRecordState)
@@ -195,7 +174,6 @@
}
@Test
- @EnableFlags(FLAG_STATUS_BAR_AUTO_START_SCREEN_RECORD_CHIP)
fun screenRecordState_recordingBeginsEarly_switchesToRecording() =
testScope.runTest {
val latest by collectLastValue(underTest.screenRecordState)
@@ -227,7 +205,6 @@
}
@Test
- @EnableFlags(FLAG_STATUS_BAR_AUTO_START_SCREEN_RECORD_CHIP)
fun screenRecordState_secondRecording_doesNotAutomaticallyStart() =
testScope.runTest {
val latest by collectLastValue(underTest.screenRecordState)
@@ -250,7 +227,6 @@
}
@Test
- @EnableFlags(FLAG_STATUS_BAR_AUTO_START_SCREEN_RECORD_CHIP)
fun screenRecordState_startingButThenDoingNothing_doesNotAutomaticallyStart() =
testScope.runTest {
val latest by collectLastValue(underTest.screenRecordState)
@@ -269,7 +245,6 @@
}
@Test
- @EnableFlags(FLAG_STATUS_BAR_AUTO_START_SCREEN_RECORD_CHIP)
fun screenRecordState_multipleStartingValues_autoStartResets() =
testScope.runTest {
val latest by collectLastValue(underTest.screenRecordState)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModelTest.kt
index 7fcf0a8..7169939e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModelTest.kt
@@ -23,7 +23,6 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.jank.Cuj
-import com.android.systemui.Flags.FLAG_STATUS_BAR_SHOW_AUDIO_ONLY_PROJECTION_CHIP
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.DialogCuj
import com.android.systemui.animation.Expandable
@@ -185,10 +184,7 @@
}
@Test
- @EnableFlags(
- com.android.media.projection.flags.Flags.FLAG_SHOW_STOP_DIALOG_POST_CALL_END,
- FLAG_STATUS_BAR_SHOW_AUDIO_ONLY_PROJECTION_CHIP,
- )
+ @EnableFlags(com.android.media.projection.flags.Flags.FLAG_SHOW_STOP_DIALOG_POST_CALL_END)
fun stopDialog_projectingAudio_flagEnabled_eventEmitted_showsGenericStopDialog() =
kosmos.runTest {
val latest by collectLastValue(underTest.stopDialogToShow)
@@ -334,7 +330,6 @@
}
@Test
- @EnableFlags(FLAG_STATUS_BAR_SHOW_AUDIO_ONLY_PROJECTION_CHIP)
fun chip_noScreenState_otherDevicesPackage_isHidden() =
testScope.runTest {
val latest by collectLastValue(underTest.chip)
@@ -375,7 +370,6 @@
}
@Test
- @EnableFlags(FLAG_STATUS_BAR_SHOW_AUDIO_ONLY_PROJECTION_CHIP)
fun chip_noScreenState_normalPackage_isShownAsIconOnly() =
testScope.runTest {
val latest by collectLastValue(underTest.chip)
@@ -525,7 +519,6 @@
}
@Test
- @EnableFlags(FLAG_STATUS_BAR_SHOW_AUDIO_ONLY_PROJECTION_CHIP)
@DisableChipsModernization
fun chip_noScreen_clickListenerShowsGenericShareDialog() =
testScope.runTest {
@@ -657,7 +650,6 @@
}
@Test
- @EnableFlags(FLAG_STATUS_BAR_SHOW_AUDIO_ONLY_PROJECTION_CHIP)
@EnableChipsModernization
fun chip_noScreen_clickBehaviorShowsGenericShareDialog() =
testScope.runTest {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithNotifsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithNotifsViewModelTest.kt
index 4e29946..673af75 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithNotifsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithNotifsViewModelTest.kt
@@ -21,7 +21,9 @@
import android.content.res.Configuration
import android.content.res.mainResources
import android.graphics.Bitmap
+import android.graphics.RectF
import android.graphics.drawable.BitmapDrawable
+import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.view.View
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -42,6 +44,7 @@
import com.android.systemui.res.R
import com.android.systemui.screenrecord.data.model.ScreenRecordModel
import com.android.systemui.screenrecord.data.repository.screenRecordRepository
+import com.android.systemui.statusbar.chips.StatusBarChipToHunAnimation
import com.android.systemui.statusbar.chips.call.ui.viewmodel.CallChipViewModel
import com.android.systemui.statusbar.chips.call.ui.viewmodel.CallChipViewModelTest.Companion.createStatusBarIconViewOrNull
import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractorTest.Companion.NORMAL_PACKAGE
@@ -175,9 +178,9 @@
}
@Test
- fun visibleChipKeys_allInactive() =
+ fun visibleChipsWithBounds_allInactive() =
kosmos.runTest {
- val latest by collectLastValue(underTest.visibleChipKeys)
+ val latest by collectLastValue(underTest.visibleChipsWithBounds)
screenRecordState.value = ScreenRecordModel.DoingNothing
mediaProjectionState.value = MediaProjectionState.NotProjecting
@@ -261,15 +264,16 @@
}
@Test
- fun visibleChipKeys_screenRecordShowAndCallShow_hasBothKeys() =
+ @DisableFlags(StatusBarChipToHunAnimation.FLAG_NAME)
+ fun visibleChipsWithBounds_screenRecordShowAndCallShow_animFlagOff_hasBothKeys() =
kosmos.runTest {
- val latest by collectLastValue(underTest.visibleChipKeys)
+ val latest by collectLastValue(underTest.visibleChipsWithBounds)
val callNotificationKey = "call"
screenRecordState.value = ScreenRecordModel.Recording
addOngoingCallState(callNotificationKey)
- assertThat(latest)
+ assertThat(latest!!.map { it.key })
.containsExactly(
ScreenRecordChipViewModel.KEY,
"${CallChipViewModel.KEY_PREFIX}$callNotificationKey",
@@ -277,6 +281,71 @@
.inOrder()
}
+ @Test
+ @EnableFlags(StatusBarChipToHunAnimation.FLAG_NAME)
+ fun visibleChipsWithBounds_screenRecordShowAndCallShow_animFlagOn_noBoundsSet_isEmpty() =
+ kosmos.runTest {
+ val latest by collectLastValue(underTest.visibleChipsWithBounds)
+
+ val callNotificationKey = "call"
+ screenRecordState.value = ScreenRecordModel.Recording
+ addOngoingCallState(callNotificationKey)
+
+ assertThat(latest).isEmpty()
+ }
+
+ @Test
+ @EnableFlags(StatusBarChipToHunAnimation.FLAG_NAME)
+ fun visibleChipsWithBounds_screenRecordShowAndCallShow_animFlagOn_boundsSetForOneChip_hasOnlyOneKey() =
+ kosmos.runTest {
+ val latest by collectLastValue(underTest.visibleChipsWithBounds)
+
+ val callNotificationKey = "call"
+ val callKeyForChip = "${CallChipViewModel.KEY_PREFIX}$callNotificationKey"
+ screenRecordState.value = ScreenRecordModel.Recording
+ addOngoingCallState(callNotificationKey)
+
+ underTest.onChipBoundsChanged(callKeyForChip, RectF(1f, 2f, 3f, 4f))
+
+ assertThat(latest!![callKeyForChip]).isEqualTo(RectF(1f, 2f, 3f, 4f))
+ assertThat(latest).doesNotContainKey(ScreenRecordChipViewModel.KEY)
+ }
+
+ @Test
+ @EnableFlags(StatusBarChipToHunAnimation.FLAG_NAME)
+ fun visibleChipsWithBounds_screenRecordShowAndCallShow_animFlagOn_boundsUpdated_hasUpdatedBounds() =
+ kosmos.runTest {
+ val latest by collectLastValue(underTest.visibleChipsWithBounds)
+
+ val callNotificationKey = "call"
+ val callKeyForChip = "${CallChipViewModel.KEY_PREFIX}$callNotificationKey"
+ addOngoingCallState(callNotificationKey)
+
+ underTest.onChipBoundsChanged(callKeyForChip, RectF(1f, 2f, 3f, 4f))
+ assertThat(latest!![callKeyForChip]).isEqualTo(RectF(1f, 2f, 3f, 4f))
+
+ underTest.onChipBoundsChanged(callKeyForChip, RectF(10f, 20f, 30f, 40f))
+ assertThat(latest!![callKeyForChip]).isEqualTo(RectF(10f, 20f, 30f, 40f))
+ }
+
+ @Test
+ @EnableFlags(StatusBarChipToHunAnimation.FLAG_NAME)
+ fun visibleChipsWithBounds_screenRecordShowAndCallShow_animFlagOn_boundsSet_hasBothKeysAndBounds() =
+ kosmos.runTest {
+ val latest by collectLastValue(underTest.visibleChipsWithBounds)
+
+ val callNotificationKey = "call"
+ val callKeyForChip = "${CallChipViewModel.KEY_PREFIX}$callNotificationKey"
+ screenRecordState.value = ScreenRecordModel.Recording
+ addOngoingCallState(callNotificationKey)
+
+ underTest.onChipBoundsChanged(callKeyForChip, RectF(1f, 2f, 3f, 4f))
+ underTest.onChipBoundsChanged(ScreenRecordChipViewModel.KEY, RectF(5f, 6f, 7f, 8f))
+
+ assertThat(latest!![callKeyForChip]).isEqualTo(RectF(1f, 2f, 3f, 4f))
+ assertThat(latest!![ScreenRecordChipViewModel.KEY]).isEqualTo(RectF(5f, 6f, 7f, 8f))
+ }
+
@EnableChipsModernization
@Test
fun chips_screenRecordAndCallActive_inThatOrder() =
@@ -368,6 +437,7 @@
activeNotificationListRepository.addNotif(
activeNotificationModel(
key = "notif",
+ packageName = "notif",
statusBarChipIcon = createStatusBarIconViewOrNull(),
promotedContent = promotedContentBuilder.build(),
)
@@ -744,6 +814,7 @@
listOf(
activeNotificationModel(
key = "notif",
+ packageName = "notif",
statusBarChipIcon = icon,
promotedContent = PromotedNotificationContentBuilder("notif").build(),
)
@@ -768,6 +839,7 @@
listOf(
activeNotificationModel(
key = "notif",
+ packageName = "notif",
statusBarChipIcon = icon,
promotedContent = PromotedNotificationContentBuilder("notif").build(),
)
@@ -794,11 +866,13 @@
listOf(
activeNotificationModel(
key = "firstNotif",
+ packageName = "firstNotif",
statusBarChipIcon = firstIcon,
promotedContent = PromotedNotificationContentBuilder("firstNotif").build(),
),
activeNotificationModel(
key = "secondNotif",
+ packageName = "secondNotif",
statusBarChipIcon = secondIcon,
promotedContent = PromotedNotificationContentBuilder("secondNotif").build(),
),
@@ -823,11 +897,13 @@
listOf(
activeNotificationModel(
key = "firstNotif",
+ packageName = "firstNotif",
statusBarChipIcon = firstIcon,
promotedContent = PromotedNotificationContentBuilder("firstNotif").build(),
),
activeNotificationModel(
key = "secondNotif",
+ packageName = "secondNotif",
statusBarChipIcon = secondIcon,
promotedContent = PromotedNotificationContentBuilder("secondNotif").build(),
),
@@ -856,16 +932,19 @@
listOf(
activeNotificationModel(
key = "firstNotif",
+ packageName = "firstNotif",
statusBarChipIcon = firstIcon,
promotedContent = PromotedNotificationContentBuilder("firstNotif").build(),
),
activeNotificationModel(
key = "secondNotif",
+ packageName = "secondNotif",
statusBarChipIcon = secondIcon,
promotedContent = PromotedNotificationContentBuilder("secondNotif").build(),
),
activeNotificationModel(
key = "thirdNotif",
+ packageName = "thirdNotif",
statusBarChipIcon = thirdIcon,
promotedContent = PromotedNotificationContentBuilder("thirdNotif").build(),
),
@@ -892,21 +971,25 @@
listOf(
activeNotificationModel(
key = "firstNotif",
+ packageName = "firstNotif",
statusBarChipIcon = firstIcon,
promotedContent = PromotedNotificationContentBuilder("firstNotif").build(),
),
activeNotificationModel(
key = "secondNotif",
+ packageName = "secondNotif",
statusBarChipIcon = secondIcon,
promotedContent = PromotedNotificationContentBuilder("secondNotif").build(),
),
activeNotificationModel(
key = "thirdNotif",
+ packageName = "thirdNotif",
statusBarChipIcon = thirdIcon,
promotedContent = PromotedNotificationContentBuilder("thirdNotif").build(),
),
activeNotificationModel(
key = "fourthNotif",
+ packageName = "fourthNotif",
statusBarChipIcon = fourthIcon,
promotedContent = PromotedNotificationContentBuilder("fourthNotif").build(),
),
@@ -925,65 +1008,121 @@
@Test
@DisableChipsModernization
- fun visibleChipKeys_chipsModOff_threePromotedNotifs_topTwoInList() =
+ @DisableFlags(StatusBarChipToHunAnimation.FLAG_NAME)
+ fun visibleChipsWithBounds_chipsModOff_animFlagOff_threePromotedNotifs_topTwoInList() =
kosmos.runTest {
- val latest by collectLastValue(underTest.visibleChipKeys)
+ val latest by collectLastValue(underTest.visibleChipsWithBounds)
setNotifs(
listOf(
activeNotificationModel(
key = "firstNotif",
+ packageName = "firstNotif",
statusBarChipIcon = createStatusBarIconViewOrNull(),
promotedContent = PromotedNotificationContentBuilder("firstNotif").build(),
),
activeNotificationModel(
key = "secondNotif",
+ packageName = "secondNotif",
statusBarChipIcon = createStatusBarIconViewOrNull(),
promotedContent = PromotedNotificationContentBuilder("secondNotif").build(),
),
activeNotificationModel(
key = "thirdNotif",
+ packageName = "thirdNotif",
statusBarChipIcon = createStatusBarIconViewOrNull(),
promotedContent = PromotedNotificationContentBuilder("thirdNotif").build(),
),
)
)
- assertThat(latest).containsExactly("firstNotif", "secondNotif").inOrder()
+ assertThat(latest!!.keys).containsExactly("firstNotif", "secondNotif")
}
@Test
@EnableChipsModernization
- fun visibleChipKeys_chipsModOn_fourPromotedNotifs_topThreeInList() =
+ @DisableFlags(StatusBarChipToHunAnimation.FLAG_NAME)
+ fun visibleChipsWithBounds_chipsModOn_animFlagOff_fourPromotedNotifs_topThreeInList() =
kosmos.runTest {
- val latest by collectLastValue(underTest.visibleChipKeys)
+ val latest by collectLastValue(underTest.visibleChipsWithBounds)
setNotifs(
listOf(
activeNotificationModel(
key = "firstNotif",
+ packageName = "firstNotif",
statusBarChipIcon = createStatusBarIconViewOrNull(),
promotedContent = PromotedNotificationContentBuilder("firstNotif").build(),
),
activeNotificationModel(
key = "secondNotif",
+ packageName = "secondNotif",
statusBarChipIcon = createStatusBarIconViewOrNull(),
promotedContent = PromotedNotificationContentBuilder("secondNotif").build(),
),
activeNotificationModel(
key = "thirdNotif",
+ packageName = "thirdNotif",
statusBarChipIcon = createStatusBarIconViewOrNull(),
promotedContent = PromotedNotificationContentBuilder("thirdNotif").build(),
),
activeNotificationModel(
key = "fourthNotif",
+ packageName = "fourthNotif",
statusBarChipIcon = createStatusBarIconViewOrNull(),
promotedContent = PromotedNotificationContentBuilder("fourthNotif").build(),
),
)
)
- assertThat(latest).containsExactly("firstNotif", "secondNotif", "thirdNotif").inOrder()
+ assertThat(latest!!.keys).containsExactly("firstNotif", "secondNotif", "thirdNotif")
+ }
+
+ @Test
+ @EnableChipsModernization
+ @EnableFlags(StatusBarChipToHunAnimation.FLAG_NAME)
+ fun visibleChipsWithBounds_chipsModOn_animFlagOn_fourPromotedNotifs_topThreeInListWithBounds() =
+ kosmos.runTest {
+ val latest by collectLastValue(underTest.visibleChipsWithBounds)
+
+ setNotifs(
+ listOf(
+ activeNotificationModel(
+ key = "firstNotif",
+ packageName = "firstNotif",
+ statusBarChipIcon = createStatusBarIconViewOrNull(),
+ promotedContent = PromotedNotificationContentBuilder("firstNotif").build(),
+ ),
+ activeNotificationModel(
+ key = "secondNotif",
+ packageName = "secondNotif",
+ statusBarChipIcon = createStatusBarIconViewOrNull(),
+ promotedContent = PromotedNotificationContentBuilder("secondNotif").build(),
+ ),
+ activeNotificationModel(
+ key = "thirdNotif",
+ packageName = "thirdNotif",
+ statusBarChipIcon = createStatusBarIconViewOrNull(),
+ promotedContent = PromotedNotificationContentBuilder("thirdNotif").build(),
+ ),
+ activeNotificationModel(
+ key = "fourthNotif",
+ packageName = "fourthNotif",
+ statusBarChipIcon = createStatusBarIconViewOrNull(),
+ promotedContent = PromotedNotificationContentBuilder("fourthNotif").build(),
+ ),
+ )
+ )
+
+ underTest.onChipBoundsChanged("firstNotif", RectF(1f, 1f, 1f, 1f))
+ underTest.onChipBoundsChanged("secondNotif", RectF(2f, 2f, 2f, 2f))
+ underTest.onChipBoundsChanged("thirdNotif", RectF(3f, 3f, 3f, 3f))
+ underTest.onChipBoundsChanged("fourthNotif", RectF(4f, 4f, 4f, 4f))
+
+ assertThat(latest!!["firstNotif"]).isEqualTo(RectF(1f, 1f, 1f, 1f))
+ assertThat(latest!!["secondNotif"]).isEqualTo(RectF(2f, 2f, 2f, 2f))
+ assertThat(latest!!["thirdNotif"]).isEqualTo(RectF(3f, 3f, 3f, 3f))
+ assertThat(latest).doesNotContainKey("fourthNotif")
}
@DisableChipsModernization
@@ -1001,11 +1140,13 @@
listOf(
activeNotificationModel(
key = "firstNotif",
+ packageName = "firstNotif",
statusBarChipIcon = firstIcon,
promotedContent = PromotedNotificationContentBuilder("firstNotif").build(),
),
activeNotificationModel(
key = "secondNotif",
+ packageName = "secondNotif",
statusBarChipIcon = createStatusBarIconViewOrNull(),
promotedContent = PromotedNotificationContentBuilder("secondNotif").build(),
),
@@ -1033,16 +1174,19 @@
listOf(
activeNotificationModel(
key = "firstNotif",
+ packageName = "firstNotif",
statusBarChipIcon = firstIcon,
promotedContent = PromotedNotificationContentBuilder("firstNotif").build(),
),
activeNotificationModel(
key = "secondNotif",
+ packageName = "secondNotif",
statusBarChipIcon = secondIcon,
promotedContent = PromotedNotificationContentBuilder("secondNotif").build(),
),
activeNotificationModel(
key = "thirdNotif",
+ packageName = "thirdNotif",
statusBarChipIcon = thirdIcon,
promotedContent = PromotedNotificationContentBuilder("thirdNotif").build(),
),
@@ -1084,9 +1228,10 @@
@Test
@DisableChipsModernization
- fun visibleChipKeys_chipsModOff_screenRecordAndCallAndPromotedNotifs_topTwoInList() =
+ @DisableFlags(StatusBarChipToHunAnimation.FLAG_NAME)
+ fun visibleChipsWithBounds_chipsModOff_animFlagOff_screenRecordAndCallAndPromotedNotifs_topTwoInList() =
kosmos.runTest {
- val latest by collectLastValue(underTest.visibleChipKeys)
+ val latest by collectLastValue(underTest.visibleChipsWithBounds)
val callNotificationKey = "call"
addOngoingCallState(callNotificationKey)
@@ -1094,6 +1239,7 @@
activeNotificationListRepository.addNotif(
activeNotificationModel(
key = "notif1",
+ packageName = "notif1",
statusBarChipIcon = createStatusBarIconViewOrNull(),
promotedContent = PromotedNotificationContentBuilder("notif1").build(),
)
@@ -1101,12 +1247,13 @@
activeNotificationListRepository.addNotif(
activeNotificationModel(
key = "notif2",
+ packageName = "notif2",
statusBarChipIcon = createStatusBarIconViewOrNull(),
promotedContent = PromotedNotificationContentBuilder("notif2").build(),
)
)
- assertThat(latest)
+ assertThat(latest!!.map { it.key })
.containsExactly(
ScreenRecordChipViewModel.KEY,
"${CallChipViewModel.KEY_PREFIX}$callNotificationKey",
@@ -1116,9 +1263,10 @@
@Test
@EnableChipsModernization
- fun visibleChipKeys_chipsModOn_screenRecordAndCallAndPromotedNotifs_topThreeInList() =
+ @DisableFlags(StatusBarChipToHunAnimation.FLAG_NAME)
+ fun visibleChipsWithBounds_chipsModOn_animFlagOff_screenRecordAndCallAndPromotedNotifs_topThreeInList() =
kosmos.runTest {
- val latest by collectLastValue(underTest.visibleChipKeys)
+ val latest by collectLastValue(underTest.visibleChipsWithBounds)
val callNotificationKey = "call"
addOngoingCallState(callNotificationKey)
@@ -1126,6 +1274,7 @@
activeNotificationListRepository.addNotif(
activeNotificationModel(
key = "notif1",
+ packageName = "notif1",
statusBarChipIcon = createStatusBarIconViewOrNull(),
promotedContent = PromotedNotificationContentBuilder("notif1").build(),
)
@@ -1133,12 +1282,13 @@
activeNotificationListRepository.addNotif(
activeNotificationModel(
key = "notif2",
+ packageName = "notif2",
statusBarChipIcon = createStatusBarIconViewOrNull(),
promotedContent = PromotedNotificationContentBuilder("notif2").build(),
)
)
- assertThat(latest)
+ assertThat(latest!!.map { it.key })
.containsExactly(
ScreenRecordChipViewModel.KEY,
"${CallChipViewModel.KEY_PREFIX}$callNotificationKey",
@@ -1147,6 +1297,45 @@
.inOrder()
}
+ @Test
+ @EnableChipsModernization
+ @EnableFlags(StatusBarChipToHunAnimation.FLAG_NAME)
+ fun visibleChipsWithBounds_chipsModOn_animFlagOn_screenRecordAndCallAndPromotedNotifs_topThreeInListWithBounds() =
+ kosmos.runTest {
+ val latest by collectLastValue(underTest.visibleChipsWithBounds)
+
+ val callNotificationKey = "call"
+ val callKeyForChip = "${CallChipViewModel.KEY_PREFIX}$callNotificationKey"
+ addOngoingCallState(callNotificationKey)
+ screenRecordState.value = ScreenRecordModel.Recording
+ activeNotificationListRepository.addNotif(
+ activeNotificationModel(
+ key = "notif1",
+ packageName = "notif1",
+ statusBarChipIcon = createStatusBarIconViewOrNull(),
+ promotedContent = PromotedNotificationContentBuilder("notif1").build(),
+ )
+ )
+ activeNotificationListRepository.addNotif(
+ activeNotificationModel(
+ key = "notif2",
+ packageName = "notif2",
+ statusBarChipIcon = createStatusBarIconViewOrNull(),
+ promotedContent = PromotedNotificationContentBuilder("notif2").build(),
+ )
+ )
+
+ underTest.onChipBoundsChanged(ScreenRecordChipViewModel.KEY, RectF(1f, 1f, 1f, 1f))
+ underTest.onChipBoundsChanged(callKeyForChip, RectF(2f, 2f, 2f, 2f))
+ underTest.onChipBoundsChanged("notif1", RectF(3f, 3f, 3f, 3f))
+ underTest.onChipBoundsChanged("notif2", RectF(4f, 4f, 4f, 4f))
+
+ assertThat(latest!![ScreenRecordChipViewModel.KEY]).isEqualTo(RectF(1f, 1f, 1f, 1f))
+ assertThat(latest!![callKeyForChip]).isEqualTo(RectF(2f, 2f, 2f, 2f))
+ assertThat(latest!!["notif1"]).isEqualTo(RectF(3f, 3f, 3f, 3f))
+ assertThat(latest).doesNotContainKey("notif2")
+ }
+
// The ranking between different chips should stay consistent between
// OngoingActivityChipsViewModel and PromotedNotificationsInteractor.
// Make sure to also change
@@ -1165,6 +1354,7 @@
activeNotificationListRepository.addNotif(
activeNotificationModel(
key = "notif",
+ packageName = "notif",
statusBarChipIcon = notifIcon,
promotedContent = PromotedNotificationContentBuilder("notif").build(),
)
@@ -1176,6 +1366,7 @@
activeNotificationListRepository.addNotif(
activeNotificationModel(
key = "notif2",
+ packageName = "notif2",
statusBarChipIcon = notifIcon2,
promotedContent = PromotedNotificationContentBuilder("notif2").build(),
)
@@ -1201,6 +1392,7 @@
listOf(
activeNotificationModel(
key = "notif",
+ packageName = "notif",
statusBarChipIcon = notifIcon,
promotedContent = PromotedNotificationContentBuilder("notif").build(),
)
@@ -1252,6 +1444,7 @@
activeNotificationListRepository.addNotif(
activeNotificationModel(
key = "notif",
+ packageName = "notif",
statusBarChipIcon = notifIcon,
promotedContent = PromotedNotificationContentBuilder("notif").build(),
)
@@ -1291,6 +1484,7 @@
activeNotificationListRepository.addNotif(
activeNotificationModel(
key = "notif",
+ packageName = "notif",
statusBarChipIcon = notifIcon,
promotedContent = PromotedNotificationContentBuilder("notif").build(),
)
@@ -1369,6 +1563,7 @@
listOf(
activeNotificationModel(
key = "notif1",
+ packageName = "notif1",
statusBarChipIcon = notif1Icon,
promotedContent = PromotedNotificationContentBuilder("notif1").build(),
)
@@ -1423,6 +1618,7 @@
activeNotificationListRepository.addNotif(
activeNotificationModel(
key = "notif2",
+ packageName = "notif2",
statusBarChipIcon = notif2Icon,
promotedContent = PromotedNotificationContentBuilder("notif2").build(),
)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/connectivity/CallbackHandlerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/connectivity/CallbackHandlerTest.java
index 5fce08b..ffd5efa 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/connectivity/CallbackHandlerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/connectivity/CallbackHandlerTest.java
@@ -179,7 +179,7 @@
@Test
public void testSignalCallback_setIsAirplaneMode() {
IconState state =
- new IconState(true, com.android.settingslib.R.drawable.stat_sys_airplane_mode, "Test Description");
+ new IconState(true, com.android.systemui.res.R.drawable.stat_sys_airplane_mode, "Test Description");
mHandler.setIsAirplaneMode(state);
waitForCallbacks();
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/events/SystemEventCoordinatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/events/SystemEventCoordinatorTest.kt
index 01e8dfe..c62751f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/events/SystemEventCoordinatorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/events/SystemEventCoordinatorTest.kt
@@ -22,6 +22,7 @@
import com.android.systemui.display.domain.interactor.ConnectedDisplayInteractor
import com.android.systemui.display.domain.interactor.ConnectedDisplayInteractor.PendingDisplay
import com.android.systemui.privacy.PrivacyItemController
+import com.android.systemui.statusbar.featurepods.vc.domain.interactor.AvControlsChipInteractor
import com.android.systemui.statusbar.policy.BatteryController
import com.android.systemui.util.mockito.any
import com.android.systemui.util.time.FakeSystemClock
@@ -50,9 +51,11 @@
@Mock lateinit var batteryController: BatteryController
@Mock lateinit var privacyController: PrivacyItemController
+ @Mock lateinit var avControlsChipInteractor: AvControlsChipInteractor
@Mock lateinit var scheduler: SystemStatusAnimationScheduler
private lateinit var systemEventCoordinator: SystemEventCoordinator
+
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
@@ -61,9 +64,10 @@
fakeSystemClock,
batteryController,
privacyController,
+ avControlsChipInteractor,
context,
TestScope(UnconfinedTestDispatcher()),
- connectedDisplayInteractor
+ connectedDisplayInteractor,
)
.apply { attachScheduler(scheduler) }
}
@@ -97,13 +101,18 @@
class FakeConnectedDisplayInteractor : ConnectedDisplayInteractor {
private val flow = MutableSharedFlow<Unit>()
+
suspend fun emit() = flow.emit(Unit)
+
override val connectedDisplayState: Flow<ConnectedDisplayInteractor.State>
get() = MutableSharedFlow<ConnectedDisplayInteractor.State>()
+
override val connectedDisplayAddition: Flow<Unit>
get() = flow
+
override val pendingDisplay: Flow<PendingDisplay?>
get() = MutableSharedFlow<PendingDisplay>()
+
override val concurrentDisplaysInProgress: Flow<Boolean>
get() = TODO("Not yet implemented")
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/featurepods/media/domain/interactor/MediaControlChipInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/featurepods/media/domain/interactor/MediaControlChipInteractorTest.kt
index 6409a20..3af8337 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/featurepods/media/domain/interactor/MediaControlChipInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/featurepods/media/domain/interactor/MediaControlChipInteractorTest.kt
@@ -30,7 +30,6 @@
import com.android.systemui.media.controls.shared.model.MediaAction
import com.android.systemui.media.controls.shared.model.MediaButton
import com.android.systemui.media.controls.shared.model.MediaData
-import com.android.systemui.media.controls.shared.model.MediaDataLoadingModel
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
@@ -191,8 +190,7 @@
private fun updateMedia(mediaData: MediaData) {
if (SceneContainerFlag.isEnabled) {
val instanceId = mediaData.instanceId
- mediaFilterRepository.addSelectedUserMediaEntry(mediaData)
- mediaFilterRepository.addMediaDataLoadingState(MediaDataLoadingModel.Loaded(instanceId))
+ mediaFilterRepository.addCurrentUserMediaEntry(mediaData)
} else {
kosmos.underTest.updateMediaControlChipModelLegacy(mediaData)
}
@@ -201,10 +199,7 @@
private fun removeMedia(mediaData: MediaData) {
if (SceneContainerFlag.isEnabled) {
val instanceId = mediaData.instanceId
- mediaFilterRepository.removeSelectedUserMediaEntry(instanceId, mediaData)
- mediaFilterRepository.addMediaDataLoadingState(
- MediaDataLoadingModel.Removed(instanceId)
- )
+ mediaFilterRepository.removeCurrentUserMediaEntry(instanceId, mediaData)
} else {
kosmos.underTest.updateMediaControlChipModelLegacy(null)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/featurepods/media/ui/viewmodel/MediaControlChipViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/featurepods/media/ui/viewmodel/MediaControlChipViewModelTest.kt
index d4518e7..6521c70 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/featurepods/media/ui/viewmodel/MediaControlChipViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/featurepods/media/ui/viewmodel/MediaControlChipViewModelTest.kt
@@ -28,7 +28,6 @@
import com.android.systemui.media.controls.data.repository.mediaFilterRepository
import com.android.systemui.media.controls.domain.pipeline.MediaDataManager
import com.android.systemui.media.controls.shared.model.MediaData
-import com.android.systemui.media.controls.shared.model.MediaDataLoadingModel
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.statusbar.featurepods.media.domain.interactor.mediaControlChipInteractor
import com.android.systemui.statusbar.featurepods.popups.shared.model.PopupChipModel
@@ -106,11 +105,7 @@
private fun updateMedia(mediaData: MediaData) {
if (SceneContainerFlag.isEnabled) {
- val instanceId = mediaData.instanceId
- kosmos.mediaFilterRepository.addSelectedUserMediaEntry(mediaData)
- kosmos.mediaFilterRepository.addMediaDataLoadingState(
- MediaDataLoadingModel.Loaded(instanceId)
- )
+ kosmos.mediaFilterRepository.addCurrentUserMediaEntry(mediaData)
} else {
mediaControlChipInteractor.updateMediaControlChipModelLegacy(mediaData)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/featurepods/media/ui/viewmodel/StatusBarPopupChipsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/featurepods/media/ui/viewmodel/StatusBarPopupChipsViewModelTest.kt
index 134ab93..71d4e84 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/featurepods/media/ui/viewmodel/StatusBarPopupChipsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/featurepods/media/ui/viewmodel/StatusBarPopupChipsViewModelTest.kt
@@ -27,7 +27,6 @@
import com.android.systemui.lifecycle.activateIn
import com.android.systemui.media.controls.data.repository.mediaFilterRepository
import com.android.systemui.media.controls.shared.model.MediaData
-import com.android.systemui.media.controls.shared.model.MediaDataLoadingModel
import com.android.systemui.statusbar.featurepods.popups.StatusBarPopupChips
import com.android.systemui.statusbar.featurepods.popups.shared.model.PopupChipId
import com.android.systemui.statusbar.featurepods.popups.ui.viewmodel.statusBarPopupChipsViewModelFactory
@@ -61,10 +60,8 @@
kosmos.runTest {
val shownPopupChips = underTest.shownPopupChips
val userMedia = MediaData(active = true, song = "test")
- val instanceId = userMedia.instanceId
- mediaFilterRepository.addSelectedUserMediaEntry(userMedia)
- mediaFilterRepository.addMediaDataLoadingState(MediaDataLoadingModel.Loaded(instanceId))
+ mediaFilterRepository.addCurrentUserMediaEntry(userMedia)
Snapshot.takeSnapshot {
assertThat(shownPopupChips).hasSize(1)
@@ -78,10 +75,8 @@
val shownPopupChips = underTest.shownPopupChips
val userMedia = MediaData(active = true, song = "test")
- val instanceId = userMedia.instanceId
- mediaFilterRepository.addSelectedUserMediaEntry(userMedia)
- mediaFilterRepository.addMediaDataLoadingState(MediaDataLoadingModel.Loaded(instanceId))
+ mediaFilterRepository.addCurrentUserMediaEntry(userMedia)
Snapshot.takeSnapshot {
assertThat(shownPopupChips).hasSize(1)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryAdapterTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryAdapterTest.kt
index cdaf985..8ba057d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryAdapterTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryAdapterTest.kt
@@ -18,6 +18,8 @@
import android.app.ActivityManager
import android.app.Notification
+import android.app.NotificationChannel
+import android.app.NotificationChannel.NEWS_ID
import android.app.NotificationManager
import android.app.PendingIntent
import android.os.UserHandle
@@ -563,4 +565,20 @@
assertThat(underTest.remoteInputEntryAdapter)
.isSameInstanceAs(entry.remoteInputEntryAdapter)
}
+
+ @Test
+ fun isBundled() {
+ val notification: Notification =
+ Notification.Builder(mContext, "")
+ .setSmallIcon(R.drawable.ic_person)
+ .build()
+
+ val entry = NotificationEntryBuilder()
+ .setNotification(notification)
+ .setChannel(NotificationChannel(NEWS_ID, NEWS_ID, 2))
+ .build()
+
+ underTest = factory.create(entry) as NotificationEntryAdapter
+ assertThat(underTest.isBundled).isTrue()
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/BundleCoordinatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/BundleCoordinatorTest.kt
index 7321808..d4d28f9 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/BundleCoordinatorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/BundleCoordinatorTest.kt
@@ -27,6 +27,7 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
+import com.android.systemui.statusbar.notification.collection.render.BundleBarn
import com.android.systemui.statusbar.notification.collection.render.NodeController
import com.google.common.truth.Truth.assertThat
import org.junit.Before
@@ -44,6 +45,7 @@
@Mock private lateinit var socialController: NodeController
@Mock private lateinit var recsController: NodeController
@Mock private lateinit var promoController: NodeController
+ @Mock private lateinit var bundleBarn: BundleBarn
private lateinit var coordinator: BundleCoordinator
@@ -55,7 +57,8 @@
newsController,
socialController,
recsController,
- promoController
+ promoController,
+ bundleBarn
)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProviderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProviderTest.kt
index 356eedb..1809b853 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProviderTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProviderTest.kt
@@ -15,6 +15,9 @@
*/
package com.android.systemui.statusbar.notification.collection.inflation
+import android.app.NotificationChannel
+import android.app.NotificationChannel.SOCIAL_MEDIA_ID
+import android.app.NotificationManager.IMPORTANCE_LOW
import android.database.ContentObserver
import android.os.Handler
import android.platform.test.annotations.DisableFlags
@@ -281,4 +284,41 @@
// Then: Need re-inflation
assertTrue(NotifUiAdjustment.needReinflate(oldAdjustment, newAdjustment))
}
+
+ @Test
+ @EnableFlags(android.app.Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI)
+ fun changeIsBundled_needReInflation_becomesBundled() {
+ // Given: an Entry that is not bundled
+ val oldAdjustment = adjustmentProvider.calculateAdjustment(entry)
+
+ // When: the Entry is now bundled
+ val rb = RankingBuilder(entry.ranking)
+ rb.setChannel(NotificationChannel(SOCIAL_MEDIA_ID, "social", IMPORTANCE_LOW))
+ entry.ranking = rb.build()
+ val newAdjustment = adjustmentProvider.calculateAdjustment(entry)
+ assertThat(newAdjustment).isNotEqualTo(oldAdjustment)
+
+ // Then: Need re-inflation
+ assertTrue(NotifUiAdjustment.needReinflate(oldAdjustment, newAdjustment))
+ }
+
+ @Test
+ @EnableFlags(android.app.Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI)
+ fun changeIsBundled_needReInflation_becomesUnbundled() {
+ // Given: an Entry that is bundled
+ val rb = RankingBuilder(entry.ranking)
+ rb.setChannel(NotificationChannel(SOCIAL_MEDIA_ID, "social", IMPORTANCE_LOW))
+ entry.ranking = rb.build()
+ val oldAdjustment = adjustmentProvider.calculateAdjustment(entry)
+
+ // When: the Entry is now not bundled
+ val rb2 = RankingBuilder(entry.ranking)
+ rb2.setChannel(NotificationChannel("anything", "anything", IMPORTANCE_LOW))
+ entry.ranking = rb2.build()
+ val newAdjustment = adjustmentProvider.calculateAdjustment(entry)
+ assertThat(newAdjustment).isNotEqualTo(oldAdjustment)
+
+ // Then: Need re-inflation
+ assertTrue(NotifUiAdjustment.needReinflate(oldAdjustment, newAdjustment))
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilderTest.kt
index d9c9177..a9d6bc7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilderTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilderTest.kt
@@ -51,6 +51,7 @@
private val sectionsFeatureManager: NotificationSectionsFeatureManager = mock()
private val sectionHeaderVisibilityProvider: SectionHeaderVisibilityProvider = mock()
private val viewBarn: NotifViewBarn = mock()
+ private val bundleBarn: BundleBarn = mock()
private val logger = NodeSpecBuilderLogger(mock(), logcatLogBuffer())
private var rootController: NodeController = buildFakeController("rootController")
@@ -81,7 +82,7 @@
}
specBuilder = NodeSpecBuilder(mediaContainerController, sectionsFeatureManager,
- sectionHeaderVisibilityProvider, viewBarn, logger)
+ sectionHeaderVisibilityProvider, viewBarn, bundleBarn, logger)
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/headsup/AvalancheControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/headsup/AvalancheControllerTest.kt
index 4309f6c..4b04aca 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/headsup/AvalancheControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/headsup/AvalancheControllerTest.kt
@@ -400,6 +400,7 @@
}
@Test
+ @DisableFlags(AvalancheReplaceHunWhenCritical.FLAG_NAME)
fun testGetDuration_nextEntryHigherPriority_500() {
// Entry is showing
val showingEntry = createHeadsUpEntry(id = 0)
@@ -410,17 +411,28 @@
mAvalancheController.addToNext(nextEntry, runnableMock!!)
// Next entry has higher priority
- if (AvalancheReplaceHunWhenCritical.isEnabled) {
- assertThat(showingEntry.getNextHunPriority(nextEntry))
- .isInstanceOf(NextHunPriority.HigherPriority::class.java)
- }
assertThat(nextEntry.compareNonTimeFields(showingEntry)).isEqualTo(-1)
-
val durationMs = mAvalancheController.getDuration(showingEntry, autoDismissMsValue = 5000)
assertThat((durationMs as RemainingDuration.UpdatedDuration).duration).isEqualTo(500)
}
@Test
+ @EnableFlags(AvalancheReplaceHunWhenCritical.FLAG_NAME)
+ fun testGetDuration_currentNotCritival_nextEntryFsi_hideImmediately() {
+ // Normal HUN Entry is showing
+ val showingEntry = createHeadsUpEntry(id = 0)
+ mAvalancheController.headsUpEntryShowing = showingEntry
+
+ // There's another FSI entry waiting to show next
+ val nextEntry = createFsiHeadsUpEntry(id = 1)
+ mAvalancheController.addToNext(nextEntry, runnableMock!!)
+
+ // Then: should hide immediately
+ val duration = mAvalancheController.getDuration(showingEntry, autoDismissMsValue = 5000)
+ assertThat(duration).isEqualTo(RemainingDuration.HideImmediately)
+ }
+
+ @Test
@DisableFlags(PromotedNotificationUi.FLAG_NAME)
fun testGetDuration_nextEntryIsPinnedByUser_flagOff_1000() {
// Entry is showing
@@ -529,7 +541,10 @@
@JvmStatic
@Parameters(name = "{0}")
fun getParams(): List<FlagsParameterization> {
- return FlagsParameterization.allCombinationsOf(PromotedNotificationUi.FLAG_NAME)
+ return FlagsParameterization.allCombinationsOf(
+ PromotedNotificationUi.FLAG_NAME,
+ AvalancheReplaceHunWhenCritical.FLAG_NAME,
+ )
}
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/BundledNotificationInfoTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/BundledNotificationInfoTest.kt
new file mode 100644
index 0000000..977a372
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/BundledNotificationInfoTest.kt
@@ -0,0 +1,270 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+package com.android.systemui.statusbar.notification.row
+
+import android.app.INotificationManager
+import android.app.Notification
+import android.app.NotificationChannel
+import android.app.NotificationChannel.SOCIAL_MEDIA_ID
+import android.app.NotificationManager.IMPORTANCE_LOW
+import android.content.ComponentName
+import android.content.mockPackageManager
+import android.content.pm.ActivityInfo
+import android.content.pm.ApplicationInfo
+import android.content.pm.PackageInfo
+import android.content.pm.PackageManager
+import android.content.pm.ResolveInfo
+import android.os.UserHandle
+import android.os.testableLooper
+import android.print.PrintManager
+import android.service.notification.StatusBarNotification
+import android.telecom.TelecomManager
+import android.testing.TestableLooper.RunWithLooper
+import android.view.LayoutInflater
+import android.view.View
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.internal.logging.MetricsLogger
+import com.android.internal.logging.UiEventLogger
+import com.android.internal.logging.metricsLogger
+import com.android.internal.logging.uiEventLoggerFake
+import com.android.systemui.Dependency
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.kosmos.testCase
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.notification.AssistantFeedbackController
+import com.android.systemui.statusbar.notification.collection.EntryAdapter
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
+import com.android.systemui.statusbar.notification.promoted.domain.interactor.PackageDemotionInteractor
+import com.android.systemui.statusbar.notification.row.icon.AppIconProvider
+import com.android.systemui.statusbar.notification.row.icon.NotificationIconStyleProvider
+import com.android.systemui.statusbar.notification.row.icon.mockAppIconProvider
+import com.android.systemui.statusbar.notification.row.icon.mockNotificationIconStyleProvider
+import com.android.systemui.testKosmos
+import com.android.telecom.telecomManager
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyBoolean
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.anyString
+import org.mockito.kotlin.any
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.never
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@RunWithLooper
+class BundledNotificationInfoTest : SysuiTestCase() {
+ private val kosmos = testKosmos().also { it.testCase = this }
+
+ private lateinit var underTest: NotificationInfo
+ private lateinit var notificationChannel: NotificationChannel
+ private lateinit var defaultNotificationChannel: NotificationChannel
+ private lateinit var classifiedNotificationChannel: NotificationChannel
+ private lateinit var sbn: StatusBarNotification
+ private lateinit var entry: NotificationEntry
+ private lateinit var entryAdapter: EntryAdapter
+
+ private val mockPackageManager = kosmos.mockPackageManager
+ private val mockAppIconProvider = kosmos.mockAppIconProvider
+ private val mockIconStyleProvider = kosmos.mockNotificationIconStyleProvider
+ private val uiEventLogger = kosmos.uiEventLoggerFake
+ private val testableLooper by lazy { kosmos.testableLooper }
+
+ private val onUserInteractionCallback = mock<OnUserInteractionCallback>()
+ private val mockINotificationManager = mock<INotificationManager>()
+ private val channelEditorDialogController = mock<ChannelEditorDialogController>()
+ private val packageDemotionInteractor = mock<PackageDemotionInteractor>()
+ private val assistantFeedbackController = mock<AssistantFeedbackController>()
+
+ @Before
+ fun setUp() {
+ mContext.addMockSystemService(TelecomManager::class.java, kosmos.telecomManager)
+
+ mDependency.injectTestDependency(Dependency.BG_LOOPER, testableLooper.looper)
+
+ // Inflate the layout
+ val inflater = LayoutInflater.from(mContext)
+ underTest = inflater.inflate(R.layout.bundled_notification_info, null) as NotificationInfo
+
+ underTest.setGutsParent(mock<NotificationGuts>())
+
+ // Our view is never attached to a window so the View#post methods in NotificationInfo never
+ // get called. Setting this will skip the post and do the action immediately.
+ underTest.mSkipPost = true
+
+ // PackageManager must return a packageInfo and applicationInfo.
+ val packageInfo = PackageInfo()
+ packageInfo.packageName = TEST_PACKAGE_NAME
+ whenever(mockPackageManager.getPackageInfo(eq(TEST_PACKAGE_NAME), anyInt()))
+ .thenReturn(packageInfo)
+ val applicationInfo = ApplicationInfo()
+ applicationInfo.uid = TEST_UID // non-zero
+ val systemPackageInfo = PackageInfo()
+ systemPackageInfo.packageName = TEST_SYSTEM_PACKAGE_NAME
+ whenever(mockPackageManager.getPackageInfo(eq(TEST_SYSTEM_PACKAGE_NAME), anyInt()))
+ .thenReturn(systemPackageInfo)
+ whenever(mockPackageManager.getPackageInfo(eq("android"), anyInt())).thenReturn(packageInfo)
+
+ val assistant = ComponentName("package", "service")
+ whenever(mockINotificationManager.allowedNotificationAssistant).thenReturn(assistant)
+ val ri = ResolveInfo()
+ ri.activityInfo = ActivityInfo()
+ ri.activityInfo.packageName = assistant.packageName
+ ri.activityInfo.name = "activity"
+ whenever(mockPackageManager.queryIntentActivities(any(), anyInt())).thenReturn(listOf(ri))
+
+ // Package has one channel by default.
+ whenever(
+ mockINotificationManager.getNumNotificationChannelsForPackage(
+ eq(TEST_PACKAGE_NAME),
+ eq(TEST_UID),
+ anyBoolean(),
+ )
+ )
+ .thenReturn(1)
+
+ // Some test channels.
+ notificationChannel = NotificationChannel(TEST_CHANNEL, TEST_CHANNEL_NAME, IMPORTANCE_LOW)
+ defaultNotificationChannel =
+ NotificationChannel(
+ NotificationChannel.DEFAULT_CHANNEL_ID,
+ TEST_CHANNEL_NAME,
+ IMPORTANCE_LOW,
+ )
+ classifiedNotificationChannel =
+ NotificationChannel(SOCIAL_MEDIA_ID, "social", IMPORTANCE_LOW)
+
+ val notification = Notification()
+ notification.extras.putParcelable(
+ Notification.EXTRA_BUILDER_APPLICATION_INFO,
+ applicationInfo,
+ )
+ sbn =
+ StatusBarNotification(
+ TEST_PACKAGE_NAME,
+ TEST_PACKAGE_NAME,
+ 0,
+ null,
+ TEST_UID,
+ 0,
+ notification,
+ UserHandle.getUserHandleForUid(TEST_UID),
+ null,
+ 0,
+ )
+ entry =
+ NotificationEntryBuilder()
+ .setSbn(sbn)
+ .updateRanking { it.setChannel(notificationChannel) }
+ .build()
+ entryAdapter = kosmos.entryAdapterFactory.create(entry)
+ whenever(assistantFeedbackController.isFeedbackEnabled).thenReturn(false)
+ whenever(assistantFeedbackController.getInlineDescriptionResource(any()))
+ .thenReturn(R.string.notification_channel_summary_automatic)
+ }
+
+
+ @Test
+ fun testHandleCloseControls_DoesNotMakeBinderCalllIfUnchanged() {
+ bindNotification()
+
+ underTest.handleCloseControls(true, false)
+ testableLooper.processAllMessages()
+ verify(mockINotificationManager, never())
+ .setAdjustmentSupportedForPackage(anyString(), anyString(), anyBoolean())
+ }
+
+ @Test
+ fun testToggleCallsUpdate() {
+ whenever(mockINotificationManager.isAdjustmentSupportedForPackage(
+ anyString(), anyString())).thenReturn(true)
+
+ bindNotification()
+
+ underTest.findViewById<View>(R.id.feature_toggle).performClick()
+ underTest.findViewById<View>(R.id.done).performClick()
+ underTest.handleCloseControls(true, false)
+
+ testableLooper.processAllMessages()
+ verify(mockINotificationManager)
+ .setAdjustmentSupportedForPackage(anyString(), anyString(), eq(false))
+ }
+
+ private fun bindNotification(
+ pm: PackageManager = this.mockPackageManager,
+ iNotificationManager: INotificationManager = this.mockINotificationManager,
+ appIconProvider: AppIconProvider = this.mockAppIconProvider,
+ iconStyleProvider: NotificationIconStyleProvider = this.mockIconStyleProvider,
+ onUserInteractionCallback: OnUserInteractionCallback = this.onUserInteractionCallback,
+ channelEditorDialogController: ChannelEditorDialogController =
+ this.channelEditorDialogController,
+ packageDemotionInteractor: PackageDemotionInteractor = this.packageDemotionInteractor,
+ pkg: String = TEST_PACKAGE_NAME,
+ entry: NotificationEntry = this.entry,
+ entryAdapter: EntryAdapter = this.entryAdapter,
+ onSettingsClick: NotificationInfo.OnSettingsClickListener? = mock(),
+ onAppSettingsClick: NotificationInfo.OnAppSettingsClickListener? = mock(),
+ onFeedbackClickListener: NotificationInfo.OnFeedbackClickListener? = mock(),
+ uiEventLogger: UiEventLogger = this.uiEventLogger,
+ isDeviceProvisioned: Boolean = true,
+ isNonblockable: Boolean = false,
+ isDismissable: Boolean = true,
+ wasShownHighPriority: Boolean = true,
+ assistantFeedbackController: AssistantFeedbackController = this.assistantFeedbackController,
+ metricsLogger: MetricsLogger = kosmos.metricsLogger,
+ onCloseClick: View.OnClickListener? = mock(),
+ ) {
+ underTest.bindNotification(
+ pm,
+ iNotificationManager,
+ appIconProvider,
+ iconStyleProvider,
+ onUserInteractionCallback,
+ channelEditorDialogController,
+ packageDemotionInteractor,
+ pkg,
+ entry.ranking,
+ entry.sbn,
+ entry,
+ entryAdapter,
+ onSettingsClick,
+ onAppSettingsClick,
+ onFeedbackClickListener,
+ uiEventLogger,
+ isDeviceProvisioned,
+ isNonblockable,
+ isDismissable,
+ wasShownHighPriority,
+ assistantFeedbackController,
+ metricsLogger,
+ onCloseClick,
+ )
+ }
+
+ companion object {
+ private const val TEST_PACKAGE_NAME = "test_package"
+ private const val TEST_SYSTEM_PACKAGE_NAME = PrintManager.PRINT_SPOOLER_PACKAGE_NAME
+ private const val TEST_UID = 1
+ private const val TEST_CHANNEL = "test_channel"
+ private const val TEST_CHANNEL_NAME = "TEST CHANNEL NAME"
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt
index ac428d7..ce72c80 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt
@@ -78,9 +78,6 @@
@TestableLooper.RunWithLooper
class ExpandableNotificationRowControllerTest : SysuiTestCase() {
- private val appName = "MyApp"
- private val notifKey = "MyNotifKey"
-
private val kosmos = testKosmos()
private lateinit var entry: NotificationEntry
@@ -143,8 +140,7 @@
smartReplyController,
pluginManager,
systemClock,
- appName,
- notifKey,
+ context,
keyguardBypassController,
groupMembershipManager,
groupExpansionManager,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/HeadsUpStyleProviderImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/HeadsUpStyleProviderImplTest.kt
index c325791..5fcd874 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/HeadsUpStyleProviderImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/HeadsUpStyleProviderImplTest.kt
@@ -16,9 +16,6 @@
package com.android.systemui.statusbar.notification.row
-import android.app.Flags.FLAG_COMPACT_HEADS_UP_NOTIFICATION
-import android.platform.test.annotations.DisableFlags
-import android.platform.test.annotations.EnableFlags
import android.platform.test.flag.junit.SetFlagsRule
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
@@ -48,13 +45,6 @@
}
@Test
- @DisableFlags(FLAG_COMPACT_HEADS_UP_NOTIFICATION)
- fun shouldApplyCompactStyle_returnsFalse_whenCompactFlagDisabled() {
- assertThat(headsUpStyleProvider.shouldApplyCompactStyle()).isFalse()
- }
-
- @Test
- @EnableFlags(FLAG_COMPACT_HEADS_UP_NOTIFICATION)
fun shouldApplyCompactStyle_returnsTrue_whenImmersiveModeEnabled() {
// GIVEN
statusBarModeRepositoryStore.defaultDisplay.isInFullscreenMode.value = true
@@ -64,7 +54,6 @@
}
@Test
- @EnableFlags(FLAG_COMPACT_HEADS_UP_NOTIFICATION)
fun shouldApplyCompactStyle_returnsFalse_whenImmersiveModeDisabled() {
// GIVEN
statusBarModeRepositoryStore.defaultDisplay.isInFullscreenMode.value = false
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.kt
index 31b5102..62c1805 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.kt
@@ -125,7 +125,10 @@
// Inflate the layout
val inflater = LayoutInflater.from(mContext)
- underTest = inflater.inflate(R.layout.notification_info, null) as NotificationInfo
+ val layoutId =
+ if (Flags.notificationsRedesignTemplates()) R.layout.notification_2025_info
+ else R.layout.notification_info
+ underTest = inflater.inflate(layoutId, null) as NotificationInfo
underTest.setGutsParent(mock<NotificationGuts>())
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java
index 172bb94..0bb5099 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java
@@ -30,6 +30,7 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.app.NotificationChannel;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.provider.Settings;
@@ -73,7 +74,9 @@
mRow = mock(ExpandableNotificationRow.class);
mView = mock(View.class);
mPeopleNotificationIdentifier = mock(PeopleNotificationIdentifier.class);
- NotificationEntry entry = new NotificationEntryBuilder().build();
+ NotificationEntry entry = new NotificationEntryBuilder()
+ .setChannel(new NotificationChannel("hi", "hi", 2))
+ .build();
if (NotificationBundleUi.isEnabled()) {
EntryAdapter entryAdapter = mKosmos.getEntryAdapterFactory().create(entry);
when(mRow.getEntryAdapter()).thenReturn(entryAdapter);
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/PartialConversationInfoTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/PartialConversationInfoTest.java
index 0eb6016..faf6888 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/PartialConversationInfoTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/PartialConversationInfoTest.java
@@ -31,6 +31,7 @@
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
+import android.app.Flags;
import android.app.INotificationManager;
import android.app.Notification;
import android.app.NotificationChannel;
@@ -40,7 +41,6 @@
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.os.UserHandle;
-import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import android.testing.TestableLooper;
import android.text.SpannableString;
@@ -109,7 +109,10 @@
mDependency.injectTestDependency(MetricsLogger.class, mMetricsLogger);
// Inflate the layout
final LayoutInflater layoutInflater = LayoutInflater.from(mContext);
- mInfo = (PartialConversationInfo) layoutInflater.inflate(R.layout.partial_conversation_info,
+ int layoutId = Flags.notificationsRedesignTemplates()
+ ? R.layout.notification_2025_partial_conversation_info
+ : R.layout.partial_conversation_info;
+ mInfo = (PartialConversationInfo) layoutInflater.inflate(layoutId,
null);
mInfo.setGutsParent(mock(NotificationGuts.class));
// Our view is never attached to a window so the View#post methods in NotificationInfo never
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/MagneticNotificationRowManagerImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/MagneticNotificationRowManagerImplTest.kt
index da8b1a9..aaeb2a1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/MagneticNotificationRowManagerImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/MagneticNotificationRowManagerImplTest.kt
@@ -410,8 +410,8 @@
private fun MagneticRowListener.asTestableListener(rowIndex: Int): MagneticRowListener {
val delegate = this
return object : MagneticRowListener {
- override fun setMagneticTranslation(translation: Float, trackEagerly: Boolean) {
- delegate.setMagneticTranslation(translation, trackEagerly)
+ override fun setMagneticTranslation(translation: Float) {
+ delegate.setMagneticTranslation(translation)
}
override fun triggerMagneticForce(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
index 96d7e4d..d17494e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
@@ -23,6 +23,8 @@
import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_ALL;
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_ALL;
+import static com.google.common.truth.Truth.assertThat;
+
import static kotlinx.coroutines.flow.FlowKt.emptyFlow;
import static org.mockito.ArgumentMatchers.any;
@@ -67,6 +69,7 @@
import com.android.systemui.power.domain.interactor.PowerInteractor;
import com.android.systemui.scene.ui.view.WindowRootView;
import com.android.systemui.shade.ShadeController;
+import com.android.systemui.statusbar.DragDownHelper;
import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener;
@@ -149,6 +152,7 @@
@Mock private NotifCollection mNotifCollection;
@Mock private UiEventLogger mUiEventLogger;
@Mock private LockscreenShadeTransitionController mLockscreenShadeTransitionController;
+ @Mock private DragDownHelper mDragDownHelper;
@Mock private VisibilityLocationProviderDelegator mVisibilityLocationProviderDelegator;
@Mock private ShadeController mShadeController;
@Mock private Provider<WindowRootView> mWindowRootView;
@@ -759,6 +763,54 @@
));
}
+ @Test
+ @EnableSceneContainer
+ public void onTouchEvent_lockScreenExpandSwallowsIt() {
+ initController(/* viewIsAttached= */ true);
+ when(mNotificationStackScrollLayout.getExpandHelper()).thenReturn(mExpandHelper);
+ when(mNotificationStackScrollLayout.isExpanded()).thenReturn(true);
+ NotificationStackScrollLayoutController.TouchHandler touchHandler =
+ mController.getTouchHandler();
+
+ MotionEvent event = MotionEvent.obtain(
+ /* downTime= */ 0,
+ /* eventTime= */ 0,
+ MotionEvent.ACTION_DOWN,
+ 0,
+ 0,
+ /* metaState= */ 0
+ );
+ when(mDragDownHelper.onTouchEvent(event)).thenReturn(true);
+ boolean touchHandled = touchHandler.onTouchEvent(event);
+
+ assertThat(touchHandled).isTrue();
+ verify(mNotificationStackScrollLayout, never()).onScrollTouch(any());
+ }
+
+ @Test
+ @EnableSceneContainer
+ public void onInterceptTouchEvent_lockScreenExpandSwallowsIt() {
+ initController(/* viewIsAttached= */ true);
+ when(mNotificationStackScrollLayout.getExpandHelper()).thenReturn(mExpandHelper);
+ when(mNotificationStackScrollLayout.isExpanded()).thenReturn(true);
+ NotificationStackScrollLayoutController.TouchHandler touchHandler =
+ mController.getTouchHandler();
+
+ MotionEvent event = MotionEvent.obtain(
+ /* downTime= */ 0,
+ /* eventTime= */ 0,
+ MotionEvent.ACTION_DOWN,
+ 0,
+ 0,
+ /* metaState= */ 0
+ );
+ when(mDragDownHelper.onInterceptTouchEvent(event)).thenReturn(true);
+ boolean touchIntercepted = touchHandler.onInterceptTouchEvent(event);
+
+ assertThat(touchIntercepted).isTrue();
+ verify(mNotificationStackScrollLayout, never()).onInterceptTouchEventScroll(event);
+ }
+
private LogMaker logMatcher(int category, int type) {
return argThat(new LogMatcher(category, type));
}
@@ -771,6 +823,7 @@
when(mNotificationStackScrollLayout.getContext()).thenReturn(getContext());
when(mNotificationStackScrollLayout.getHeadsUpCallback()).thenReturn(mHeadsUpCallback);
when(mHeadsUpCallback.getContext()).thenReturn(getContext());
+ when(mLockscreenShadeTransitionController.getTouchHelper()).thenReturn(mDragDownHelper);
mController = new NotificationStackScrollLayoutController(
mNotificationStackScrollLayout,
true,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt
index 1f67945..030d006 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt
@@ -23,6 +23,7 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.kosmos.testScope
import com.android.systemui.media.controls.domain.pipeline.MediaDataManager
import com.android.systemui.res.R
@@ -383,6 +384,35 @@
}
@Test
+ @EnableSceneContainer
+ fun getSpaceNeeded_onLockscreenAndUserLocked_intrinsicHeight() {
+ // GIVEN: No divider height since we're testing one element where index = 0
+ setGapHeight(0f)
+
+ // AND: the row has its max height
+ val expandableView = createMockRow(rowHeight)
+
+ // AND: the user is dragging down on the Notification
+ whenever(expandableView.isUserLocked).thenReturn(true)
+
+ // AND: the row has a smaller min height, that we won't use here
+ whenever(expandableView.getMinHeight(any())).thenReturn(1)
+
+ // WHEN: we calculate the space for the Lockscreen
+ val space =
+ sizeCalculator.getSpaceNeeded(
+ expandableView,
+ visibleIndex = 0,
+ previousView = null,
+ stack = stackLayout,
+ onLockscreen = true,
+ )
+
+ // THEN: the row gets its unrestricted height (if there's enough space)
+ assertThat(space.whenEnoughSpace).isEqualTo(rowHeight)
+ }
+
+ @Test
fun getSpaceNeeded_notOnLockscreen_intrinsicHeight() {
setGapHeight(0f)
// No divider height since we're testing one element where index = 0
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java
index 4ce707d..0968d42 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java
@@ -13,8 +13,6 @@
package com.android.systemui.statusbar.notification.stack;
-import static com.android.systemui.Flags.FLAG_IGNORE_TOUCHES_NEXT_TO_NOTIFICATION_SHELF;
-
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
@@ -688,8 +686,7 @@
}
@Test
- @EnableFlags(FLAG_IGNORE_TOUCHES_NEXT_TO_NOTIFICATION_SHELF)
- public void testIsTouchInView_notificationShelf_flagEnabled() {
+ public void testIsTouchInView_notificationShelf() {
doReturn(500).when(mShelf).getWidth();
doReturn(FAKE_ROW_WIDTH).when(mShelf).getActualWidth();
doReturn(FAKE_ROW_HEIGHT).when(mShelf).getHeight();
@@ -712,30 +709,6 @@
}
@Test
- @DisableFlags(FLAG_IGNORE_TOUCHES_NEXT_TO_NOTIFICATION_SHELF)
- public void testIsTouchInView_notificationShelf_flagDisabled() {
- doReturn(500).when(mShelf).getWidth();
- doReturn(FAKE_ROW_WIDTH).when(mShelf).getActualWidth();
- doReturn(FAKE_ROW_HEIGHT).when(mShelf).getHeight();
- doReturn(FAKE_ROW_HEIGHT).when(mShelf).getActualHeight();
-
- Answer answer = (Answer) invocation -> {
- int[] arr = invocation.getArgument(0);
- arr[0] = 0;
- arr[1] = 0;
- return null;
- };
-
- doReturn(5f).when(mEvent).getRawX();
- doReturn(10f).when(mEvent).getRawY();
- doAnswer(answer).when(mShelf).getLocationOnScreen(any());
- assertTrue("Touch is within the view", mSwipeHelper.isTouchInView(mEvent, mShelf));
-
- doReturn(50f).when(mEvent).getRawX();
- assertTrue("Touch is within the view", mSwipeHelper.isTouchInView(mEvent, mShelf));
- }
-
- @Test
public void testContentAlphaRemainsUnchangedWhenNotificationIsNotDismissible() {
doReturn(FAKE_ROW_WIDTH).when(mNotificationRow).getMeasuredWidth();
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt
index 911b1b5..70e36b5 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt
@@ -32,6 +32,8 @@
import com.android.systemui.power.shared.model.WakefulnessState
import com.android.systemui.res.R
import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.shade.domain.interactor.enableDualShade
+import com.android.systemui.shade.domain.interactor.enableSingleShade
import com.android.systemui.shade.shadeTestUtil
import com.android.systemui.statusbar.data.repository.fakeRemoteInputRepository
import com.android.systemui.statusbar.notification.data.repository.FakeHeadsUpRowRepository
@@ -46,6 +48,7 @@
import com.android.systemui.util.ui.isAnimating
import com.android.systemui.util.ui.value
import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
@@ -56,6 +59,7 @@
import platform.test.runner.parameterized.ParameterizedAndroidJunit4
import platform.test.runner.parameterized.Parameters
+@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(ParameterizedAndroidJunit4::class)
class NotificationListViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
@@ -452,6 +456,60 @@
}
@Test
+ @EnableSceneContainer
+ fun shouldShowFooterView_dualShade_trueWhenShadeIsExpanded() =
+ testScope.runTest {
+ kosmos.enableDualShade()
+ runCurrent()
+
+ val shouldShow by collectLastValue(underTest.shouldShowFooterView)
+
+ // WHEN shade is open
+ fakeKeyguardRepository.setStatusBarState(StatusBarState.SHADE)
+ shadeTestUtil.setShadeExpansion(1f)
+ runCurrent()
+
+ // THEN footer is shown
+ assertThat(shouldShow?.value).isTrue()
+ }
+
+ @Test
+ @EnableSceneContainer
+ fun shouldShowFooterView_singleShade_falseWhenNoNotifs() =
+ testScope.runTest {
+ kosmos.enableSingleShade()
+ val shouldShow by collectLastValue(underTest.shouldShowFooterView)
+
+ // WHEN shade is open, has no notifs
+ fakeKeyguardRepository.setStatusBarState(StatusBarState.SHADE)
+ shadeTestUtil.setShadeExpansion(1f)
+ activeNotificationListRepository.setActiveNotifs(count = 0)
+ runCurrent()
+
+ // THEN footer is hidden
+ assertThat(shouldShow?.value).isFalse()
+ }
+
+ @Test
+ @EnableSceneContainer
+ fun shouldShowFooterView_dualShade_trueWhenNoNotifs() =
+ testScope.runTest {
+ kosmos.enableDualShade()
+ runCurrent()
+
+ val shouldShow by collectLastValue(underTest.shouldShowFooterView)
+
+ // WHEN shade is open, has no notifs
+ fakeKeyguardRepository.setStatusBarState(StatusBarState.SHADE)
+ shadeTestUtil.setShadeExpansion(1f)
+ activeNotificationListRepository.setActiveNotifs(count = 0)
+ runCurrent()
+
+ // THEN footer is shown
+ assertThat(shouldShow?.value).isTrue()
+ }
+
+ @Test
@DisableSceneContainer
fun shouldHideFooterView_trueWhenShadeIsClosed() =
testScope.runTest {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
index d3a5eff..0894104 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
@@ -1127,6 +1127,31 @@
}
@Test
+ fun shadeCollapseFadeIn_doesNotRunIfTransitioningToDream() =
+ testScope.runTest {
+ val fadeIn by collectValues(underTest.shadeCollapseFadeIn)
+
+ // Start on lockscreen without the shade
+ showLockscreen()
+ assertThat(fadeIn[0]).isEqualTo(false)
+
+ // ... then the shade expands
+ showLockscreenWithShadeExpanded()
+ assertThat(fadeIn[0]).isEqualTo(false)
+
+ // ... then user hits power to go to dream
+ keyguardTransitionRepository.sendTransitionSteps(
+ from = LOCKSCREEN,
+ to = DREAMING,
+ testScope,
+ )
+ // ... followed by a shade collapse
+ showLockscreen()
+ // ... does not trigger a fade in
+ assertThat(fadeIn[0]).isEqualTo(false)
+ }
+
+ @Test
@BrokenWithSceneContainer(330311871)
fun alpha_isZero_fromPrimaryBouncerToGoneWhileCommunalSceneVisible() =
testScope.runTest {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextViewTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextViewTest.java
index 109cd94..6deb452 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextViewTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextViewTest.java
@@ -16,9 +16,6 @@
package com.android.systemui.statusbar.phone;
-import static android.view.View.ACCESSIBILITY_LIVE_REGION_NONE;
-import static android.view.View.ACCESSIBILITY_LIVE_REGION_POLITE;
-
import static com.google.common.truth.Truth.assertThat;
import android.testing.TestableLooper;
@@ -49,7 +46,9 @@
@After
public void tearDown() {
- mKeyguardIndicationTextView.setAnimationsEnabled(true);
+ if (mKeyguardIndicationTextView != null) {
+ mKeyguardIndicationTextView.setAnimationsEnabled(true);
+ }
}
@Test
@@ -60,22 +59,6 @@
}
@Test
- public void alwaysAnnounce_setsLiveRegionToNone() {
- mKeyguardIndicationTextView.setAlwaysAnnounceEnabled(true);
-
- assertThat(mKeyguardIndicationTextView.getAccessibilityLiveRegion()).isEqualTo(
- ACCESSIBILITY_LIVE_REGION_NONE);
- }
-
- @Test
- public void alwaysAnnounce_setsLiveRegionToDefaultPolite_whenDisabled() {
- mKeyguardIndicationTextView.setAlwaysAnnounceEnabled(false);
-
- assertThat(mKeyguardIndicationTextView.getAccessibilityLiveRegion()).isEqualTo(
- ACCESSIBILITY_LIVE_REGION_POLITE);
- }
-
- @Test
public void switchIndication_emptyText_hideIndication() {
mKeyguardIndicationTextView.switchIndication("" /* text */, null);
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImplTest.kt
index dffb64b..d9b82ab 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImplTest.kt
@@ -49,6 +49,7 @@
import com.android.systemui.shade.data.repository.FakeShadeRepository
import com.android.systemui.shade.data.repository.ShadeAnimationRepository
import com.android.systemui.shade.domain.interactor.ShadeAnimationInteractorLegacyImpl
+import com.android.systemui.shade.domain.interactor.ShadeDialogContextInteractor
import com.android.systemui.shared.Flags as SharedFlags
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.NotificationLockscreenUserManager
@@ -96,6 +97,7 @@
@Mock private lateinit var commandQueue: CommandQueue
@Mock private lateinit var statusBarKeyguardViewManager: StatusBarKeyguardViewManager
@Mock private lateinit var activityTransitionAnimator: ActivityTransitionAnimator
+ @Mock private lateinit var shadeDialogContextInteractor: ShadeDialogContextInteractor
@Mock private lateinit var lockScreenUserManager: NotificationLockscreenUserManager
@Mock private lateinit var statusBarWindowController: StatusBarWindowController
@Mock private lateinit var statusBarWindowControllerStore: StatusBarWindowControllerStore
@@ -132,8 +134,7 @@
statusBarKeyguardViewManagerLazy = { statusBarKeyguardViewManager },
notifShadeWindowControllerLazy = { notifShadeWindowController },
activityTransitionAnimator = activityTransitionAnimator,
- context = context,
- displayId = DISPLAY_ID,
+ contextInteractor = shadeDialogContextInteractor,
lockScreenUserManager = lockScreenUserManager,
statusBarWindowControllerStore = statusBarWindowControllerStore,
wakefulnessLifecycle = wakefulnessLifecycle,
@@ -151,6 +152,7 @@
`when`(communalSceneInteractor.isCommunalVisible).thenReturn(MutableStateFlow(false))
`when`(communalSceneInteractor.isIdleOnCommunal).thenReturn(MutableStateFlow(false))
`when`(communalSceneInteractor.isLaunchingWidget).thenReturn(MutableStateFlow(false))
+ `when`(shadeDialogContextInteractor.context).thenReturn(context)
}
@EnableFlags(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewModel.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewModel.kt
index b96a6be..505ab1f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewModel.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewModel.kt
@@ -18,6 +18,7 @@
import android.graphics.Color
import android.graphics.Rect
+import android.graphics.RectF
import android.view.View
import androidx.compose.runtime.getValue
import com.android.systemui.lifecycle.ExclusiveActivatable
@@ -57,6 +58,8 @@
override val ongoingActivityChips =
ChipsVisibilityModel(MultipleOngoingActivityChipsModel(), areChipsAllowed = false)
+ override fun onChipBoundsChanged(key: String, bounds: RectF) {}
+
override val ongoingActivityChipsLegacy =
MutableStateFlow(MultipleOngoingActivityChipsModelLegacy())
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/RotationLockControllerImplTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/RotationLockControllerImplTest.java
index 83da1d6..a81f6d0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/RotationLockControllerImplTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/RotationLockControllerImplTest.java
@@ -107,5 +107,6 @@
mFakeExecutor,
mFakeExecutor
);
+ mFakeExecutor.runAllReady();
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/systemstatusicons/ethernet/ui/viewmodel/EthernetIconViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/systemstatusicons/ethernet/ui/viewmodel/EthernetIconViewModelTest.kt
new file mode 100644
index 0000000..6dd837a
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/systemstatusicons/ethernet/ui/viewmodel/EthernetIconViewModelTest.kt
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.systemstatusicons.ethernet.ui.viewmodel
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.settingslib.AccessibilityContentDescriptions
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.shared.model.ContentDescription
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.kosmos.runTest
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
+import com.android.systemui.lifecycle.activateIn
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
+import com.android.systemui.statusbar.pipeline.shared.data.repository.connectivityRepository
+import com.android.systemui.statusbar.pipeline.shared.data.repository.fake
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class EthernetIconViewModelTest : SysuiTestCase() {
+
+ private val kosmos = testKosmos().useUnconfinedTestDispatcher()
+ private val fakeConnectivityRepository: FakeConnectivityRepository by lazy {
+ kosmos.connectivityRepository.fake
+ }
+
+ private val underTest = kosmos.ethernetIconViewModelFactory.create()
+
+ @Before
+ fun setUp() {
+ underTest.activateIn(kosmos.testScope)
+ }
+
+ @Test
+ fun icon_default_validated() =
+ kosmos.runTest {
+ // Ethernet is the default connection and validated
+ fakeConnectivityRepository.setEthernetConnected(default = true, validated = true)
+
+ // Icon is the 'fully connected' ethernet icon
+ val expected =
+ Icon.Resource(
+ R.drawable.stat_sys_ethernet_fully,
+ ContentDescription.Resource(
+ AccessibilityContentDescriptions.ETHERNET_CONNECTION_VALUES[1]
+ ),
+ )
+
+ assertThat(underTest.icon).isEqualTo(expected)
+ }
+
+ @Test
+ fun icon_default_notValidated() =
+ kosmos.runTest {
+ // Ethernet is the default connection but not validated
+ fakeConnectivityRepository.setEthernetConnected(default = true, validated = false)
+
+ // Icon is the 'connected, not validated' ethernet icon
+ val expected =
+ Icon.Resource(
+ R.drawable.stat_sys_ethernet,
+ ContentDescription.Resource(
+ AccessibilityContentDescriptions.ETHERNET_CONNECTION_VALUES[0]
+ ),
+ )
+
+ assertThat(underTest.icon).isEqualTo(expected)
+ }
+
+ @Test
+ fun icon_notDefault_validated() =
+ kosmos.runTest {
+ // Ethernet is connected and validated, but NOT the default connection
+ fakeConnectivityRepository.setEthernetConnected(default = false, validated = true)
+
+ // Icon is null (we only show the icon for the default connection)
+ assertThat(underTest.icon).isNull()
+ }
+
+ @Test
+ fun icon_notDefault_notValidated() =
+ kosmos.runTest {
+ // Ethernet is connected, but NOT validated and NOT the default connection
+ fakeConnectivityRepository.setEthernetConnected(default = false, validated = false)
+
+ // Icon is null
+ assertThat(underTest.icon).isNull()
+ }
+
+ @Test
+ fun icon_updatesWhenConnectivityChanges() =
+ kosmos.runTest {
+ // Start default and validated
+ fakeConnectivityRepository.setEthernetConnected(default = true, validated = true)
+ val expectedFully =
+ Icon.Resource(
+ R.drawable.stat_sys_ethernet_fully,
+ ContentDescription.Resource(
+ AccessibilityContentDescriptions.ETHERNET_CONNECTION_VALUES[1]
+ ),
+ )
+ assertThat(underTest.icon).isEqualTo(expectedFully)
+
+ // Change to default but not validated
+ fakeConnectivityRepository.setEthernetConnected(default = true, validated = false)
+ val expectedNotValidated =
+ Icon.Resource(
+ R.drawable.stat_sys_ethernet,
+ ContentDescription.Resource(
+ AccessibilityContentDescriptions.ETHERNET_CONNECTION_VALUES[0]
+ ),
+ )
+ assertThat(underTest.icon).isEqualTo(expectedNotValidated)
+
+ // Change to not default
+ fakeConnectivityRepository.setEthernetConnected(default = false, validated = false)
+ assertThat(underTest.icon).isNull()
+
+ // Change back to default and validated
+ fakeConnectivityRepository.setEthernetConnected(default = true, validated = true)
+ assertThat(underTest.icon).isEqualTo(expectedFully)
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/topwindoweffects/TopLevelWindowEffectsTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/topwindoweffects/TopLevelWindowEffectsTest.kt
index 7a608d1..2412a6d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/topwindoweffects/TopLevelWindowEffectsTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/topwindoweffects/TopLevelWindowEffectsTest.kt
@@ -30,14 +30,15 @@
import com.android.systemui.kosmos.testScope
import com.android.systemui.kosmos.useUnconfinedTestDispatcher
import com.android.systemui.testKosmos
+import com.android.systemui.topwindoweffects.data.repository.DEFAULT_INITIAL_DELAY_MILLIS
+import com.android.systemui.topwindoweffects.data.repository.DEFAULT_LONG_PRESS_POWER_DURATION_MILLIS
import com.android.systemui.topwindoweffects.data.repository.fakeSqueezeEffectRepository
import com.android.systemui.topwindoweffects.domain.interactor.SqueezeEffectInteractor
import com.android.systemui.topwindoweffects.ui.compose.EffectsWindowRoot
import com.android.systemui.topwindoweffects.ui.viewmodel.SqueezeEffectViewModel
+import java.util.Optional
import kotlin.time.Duration
import kotlin.time.Duration.Companion.milliseconds
-import kotlinx.coroutines.test.advanceTimeBy
-import kotlinx.coroutines.test.runCurrent
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -71,10 +72,11 @@
viewModelFactory = viewModelFactory,
squeezeEffectInteractor =
SqueezeEffectInteractor(squeezeEffectRepository = fakeSqueezeEffectRepository),
+ appZoomOutOptional = Optional.empty(),
)
}
- private fun Kosmos.waitFor(duration: Duration) {
+ private fun Kosmos.advanceTime(duration: Duration) {
advanceTimeBy(duration)
runCurrent()
}
@@ -101,12 +103,15 @@
@Test
fun addViewToWindowWhenSqueezeEffectEnabled_withDelayMoreThan100Millis() =
kosmos.runTest {
+ val expectedDelay = 100L
fakeSqueezeEffectRepository.isSqueezeEffectEnabled.value = true
+ fakeSqueezeEffectRepository.invocationEffectInitialDelayMs = expectedDelay
fakeKeyEventRepository.setPowerButtonDown(true)
underTest.start()
- waitFor(101.milliseconds)
+ // add additional 1ms time to simulate initial delay duration has passed
+ advanceTime((expectedDelay + 1).milliseconds)
verify(windowManager, times(1)).addView(any<View>(), any<WindowManager.LayoutParams>())
}
@@ -114,12 +119,15 @@
@Test
fun addViewToWindowWhenSqueezeEffectEnabled_withDelayLessThan100Millis() =
kosmos.runTest {
+ val expectedDelay = 100L
fakeSqueezeEffectRepository.isSqueezeEffectEnabled.value = true
+ fakeSqueezeEffectRepository.invocationEffectInitialDelayMs = expectedDelay
fakeKeyEventRepository.setPowerButtonDown(true)
underTest.start()
- waitFor(99.milliseconds)
+ // subtract 1ms time to simulate initial delay duration is yet not finished
+ advanceTime((expectedDelay - 1).milliseconds)
verify(windowManager, never()).addView(any<View>(), any<WindowManager.LayoutParams>())
}
@@ -127,12 +135,15 @@
@Test
fun addViewToWindowWhenSqueezeEffectEnabled_upEventReceivedBefore100Millis() =
kosmos.runTest {
+ val expectedDelay = 100L
fakeSqueezeEffectRepository.isSqueezeEffectEnabled.value = true
+ fakeSqueezeEffectRepository.invocationEffectInitialDelayMs = expectedDelay
fakeKeyEventRepository.setPowerButtonDown(true)
underTest.start()
- waitFor(99.milliseconds)
+ // subtract 1ms time to simulate initial delay duration is yet not finished
+ advanceTime((expectedDelay - 1).milliseconds)
fakeKeyEventRepository.setPowerButtonDown(false)
@@ -144,12 +155,15 @@
@Test
fun addViewToWindowWhenSqueezeEffectEnabled_upEventReceivedAfter100Millis() =
kosmos.runTest {
+ val expectedDelay = 100L
fakeSqueezeEffectRepository.isSqueezeEffectEnabled.value = true
+ fakeSqueezeEffectRepository.invocationEffectInitialDelayMs = expectedDelay
fakeKeyEventRepository.setPowerButtonDown(true)
underTest.start()
- waitFor(101.milliseconds)
+ // add additional 1ms time to simulate initial delay duration has passed
+ advanceTime((expectedDelay + 1).milliseconds)
fakeKeyEventRepository.setPowerButtonDown(false)
@@ -157,4 +171,46 @@
verify(windowManager, times(1)).addView(any<View>(), any<WindowManager.LayoutParams>())
}
+
+ @Test
+ fun addViewToWindowWhenSqueezeEffectEnabled_upEventReceivedAfterLpp_withIncreasedLppDuration_afterInitialDelay() =
+ kosmos.runTest {
+ val expectedDelay =
+ DEFAULT_INITIAL_DELAY_MILLIS + 750 - DEFAULT_LONG_PRESS_POWER_DURATION_MILLIS
+ fakeSqueezeEffectRepository.isSqueezeEffectEnabled.value = true
+ fakeSqueezeEffectRepository.invocationEffectInitialDelayMs = expectedDelay
+ fakeKeyEventRepository.setPowerButtonDown(true)
+
+ underTest.start()
+
+ // add additional 1ms time to simulate initial delay duration has passed
+ advanceTime((expectedDelay + 1).milliseconds)
+
+ fakeKeyEventRepository.setPowerButtonDown(false)
+
+ runCurrent()
+
+ verify(windowManager, times(1)).addView(any<View>(), any<WindowManager.LayoutParams>())
+ }
+
+ @Test
+ fun addViewToWindowWhenSqueezeEffectEnabled_upEventReceivedAfterLpp_withIncreasedLppDuration_beforeInitialDelay() =
+ kosmos.runTest {
+ val expectedDelay =
+ DEFAULT_INITIAL_DELAY_MILLIS + 750 - DEFAULT_LONG_PRESS_POWER_DURATION_MILLIS
+ fakeSqueezeEffectRepository.isSqueezeEffectEnabled.value = true
+ fakeSqueezeEffectRepository.invocationEffectInitialDelayMs = expectedDelay
+ fakeKeyEventRepository.setPowerButtonDown(true)
+
+ underTest.start()
+
+ // subtract 1ms time to simulate initial delay duration is yet not finished
+ advanceTime((expectedDelay - 1).milliseconds)
+
+ fakeKeyEventRepository.setPowerButtonDown(false)
+
+ runCurrent()
+
+ verify(windowManager, never()).addView(any<View>(), any<WindowManager.LayoutParams>())
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/underlay/data/repository/UnderlayRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/underlay/data/repository/UnderlayRepositoryTest.kt
new file mode 100644
index 0000000..bd07e7d
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/underlay/data/repository/UnderlayRepositoryTest.kt
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.underlay.data.repository
+
+import android.content.Intent
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.broadcast.broadcastDispatcher
+import com.android.systemui.kosmos.collectLastValue
+import com.android.systemui.kosmos.runCurrent
+import com.android.systemui.kosmos.runTest
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runCurrent
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class UnderlayRepositoryTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
+
+ @Test
+ fun isUnderlayAttached_whenCreated_true() =
+ kosmos.runTest {
+ val isUnderlayAttached by collectLastValue(underlayRepository.isUnderlayAttached)
+ runCurrent()
+
+ broadcastDispatcher.sendIntentToMatchingReceiversOnly(
+ context,
+ Intent(UnderlayRepository.ACTION_CREATE_UNDERLAY),
+ )
+
+ assertThat(isUnderlayAttached).isTrue()
+ }
+
+ @Test
+ fun isUnderlayAttached_whenDestroyed_false() =
+ kosmos.runTest {
+ val isUnderlayAttached by collectLastValue(underlayRepository.isUnderlayAttached)
+ runCurrent()
+
+ broadcastDispatcher.sendIntentToMatchingReceiversOnly(
+ context,
+ Intent(UnderlayRepository.ACTION_DESTROY_UNDERLAY),
+ )
+
+ assertThat(isUnderlayAttached).isFalse()
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/underlay/domain/interactor/UnderlayInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/underlay/domain/interactor/UnderlayInteractorTest.kt
new file mode 100644
index 0000000..9a4740f
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/underlay/domain/interactor/UnderlayInteractorTest.kt
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.underlay.domain.interactor
+
+import android.content.Intent
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.broadcast.broadcastDispatcher
+import com.android.systemui.kosmos.collectLastValue
+import com.android.systemui.kosmos.runCurrent
+import com.android.systemui.kosmos.runTest
+import com.android.systemui.testKosmos
+import com.android.systemui.underlay.data.repository.UnderlayRepository
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class UnderlayInteractorTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
+
+ @Test
+ fun isUnderlayAttached_whenCreated_true() =
+ kosmos.runTest {
+ val isUnderlayAttached by collectLastValue(underlayInteractor.isUnderlayAttached)
+ runCurrent()
+
+ broadcastDispatcher.sendIntentToMatchingReceiversOnly(
+ context,
+ Intent(UnderlayRepository.ACTION_CREATE_UNDERLAY),
+ )
+
+ assertThat(isUnderlayAttached).isTrue()
+ }
+
+ @Test
+ fun isUnderlayAttached_whenDestroyed_false() =
+ kosmos.runTest {
+ val isUnderlayAttached by collectLastValue(underlayInteractor.isUnderlayAttached)
+ runCurrent()
+
+ broadcastDispatcher.sendIntentToMatchingReceiversOnly(
+ context,
+ Intent(UnderlayRepository.ACTION_DESTROY_UNDERLAY),
+ )
+
+ assertThat(isUnderlayAttached).isFalse()
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt
index 48ab6e8..232ce39 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt
@@ -476,7 +476,7 @@
private fun setUserSwitchingMustGoThroughLoginScreen(enabled: Boolean) {
context.orCreateTestableResources.addOverride(
- R.bool.config_userSwitchingMustGoThroughLoginScreen,
+ com.android.internal.R.bool.config_userSwitchingMustGoThroughLoginScreen,
enabled,
)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/volume/domain/interactor/AudioSlidersInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/volume/domain/interactor/AudioSlidersInteractorTest.kt
index 5a814fc..8b9c3c7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/volume/domain/interactor/AudioSlidersInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/volume/domain/interactor/AudioSlidersInteractorTest.kt
@@ -17,12 +17,10 @@
package com.android.systemui.volume.panel.component.volume.domain.interactor
import android.media.AudioManager
-import android.platform.test.annotations.EnableFlags
import android.testing.TestableLooper
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.settingslib.volume.shared.model.AudioStream
-import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.Kosmos
@@ -95,7 +93,6 @@
@Test
- @EnableFlags(Flags.FLAG_ONLY_SHOW_MEDIA_STREAM_SLIDER_IN_SINGLE_VOLUME_MODE)
fun shouldAddMusicStreamOnly_singleVolumeMode() =
with(kosmos) {
testScope.runTest {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/ui/viewmodel/WallpaperFocalAreaViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/ui/viewmodel/WallpaperFocalAreaViewModelTest.kt
index 0e2be51d..82c72db 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/ui/viewmodel/WallpaperFocalAreaViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/ui/viewmodel/WallpaperFocalAreaViewModelTest.kt
@@ -216,6 +216,15 @@
assertThat(bounds).isNotNull()
}
+ @Test
+ fun onReboot_sendFocalBounds() =
+ testScope.runTest {
+ val bounds by collectLastValue(underTest.wallpaperFocalAreaBounds)
+ kosmos.wallpaperFocalAreaRepository.setHasFocalArea(true)
+ setTestFocalAreaBounds()
+ assertThat(bounds).isNotNull()
+ }
+
private fun setTestFocalAreaBounds(
shadeLayoutWide: Boolean = false,
activeNotifs: Int = 0,
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockConfig.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockConfig.kt
index 812a964..5e4a85a 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockConfig.kt
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockConfig.kt
@@ -13,11 +13,14 @@
*/
package com.android.systemui.plugins.clocks
+import com.android.internal.annotations.Keep
+
/**
* Exposes the rendering capabilities of this clock to SystemUI so that it can be hosted and render
* correctly in SystemUI's process. Ideally all clocks could be rendered identically, but in
* practice we different clocks require different behavior from SystemUI.
*/
+@Keep
data class ClockConfig(
val id: ClockId,
@@ -35,6 +38,7 @@
)
/** Render configuration options for a specific clock face. */
+@Keep
data class ClockFaceConfig(
/** Expected interval between calls to onTimeTick. Can always reduce to PER_MINUTE in AOD. */
val tickRate: ClockTickRate = ClockTickRate.PER_MINUTE,
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockFaceEvents.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockFaceEvents.kt
index a658c15..437d91b 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockFaceEvents.kt
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockFaceEvents.kt
@@ -15,6 +15,7 @@
import android.content.Context
import android.graphics.Rect
+import com.android.internal.annotations.Keep
import com.android.systemui.plugins.annotations.ProtectedInterface
/** Events that have specific data about the related face */
@@ -53,6 +54,7 @@
}
/** Contains Theming information for the clock face */
+@Keep
data class ThemeConfig(
/** True if the clock should use dark theme (light text on dark background) */
val isDarkTheme: Boolean,
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockFaceLayout.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockFaceLayout.kt
index 37b611f..0f27815 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockFaceLayout.kt
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockFaceLayout.kt
@@ -13,21 +13,12 @@
*/
package com.android.systemui.plugins.clocks
-import android.util.DisplayMetrics
import android.view.View
import androidx.constraintlayout.widget.ConstraintSet
-import androidx.constraintlayout.widget.ConstraintSet.BOTTOM
-import androidx.constraintlayout.widget.ConstraintSet.END
-import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
-import androidx.constraintlayout.widget.ConstraintSet.START
-import androidx.constraintlayout.widget.ConstraintSet.TOP
-import androidx.constraintlayout.widget.ConstraintSet.WRAP_CONTENT
-import com.android.internal.policy.SystemBarUtils
+import com.android.internal.annotations.Keep
import com.android.systemui.plugins.annotations.GeneratedImport
import com.android.systemui.plugins.annotations.ProtectedInterface
import com.android.systemui.plugins.annotations.ProtectedReturn
-import com.android.systemui.plugins.clocks.ContextExt.getDimen
-import com.android.systemui.plugins.clocks.ContextExt.getId
/** Specifies layout information for the clock face */
@ProtectedInterface
@@ -54,6 +45,7 @@
}
/** Data class to contain AOD BurnIn information for correct aod rendering */
+@Keep
data class AodClockBurnInModel(
/** Scale that the clock should render at to mitigate burnin */
val scale: Float,
@@ -64,102 +56,3 @@
/** Y-Translation for the clock to mitigate burnin */
val translationY: Float,
)
-
-/** A ClockFaceLayout that applies the default lockscreen layout to a single view */
-class DefaultClockFaceLayout(val view: View) : ClockFaceLayout {
- override val views = listOf(view)
-
- override fun applyConstraints(constraints: ConstraintSet): ConstraintSet {
- if (views.size != 1) {
- throw IllegalArgumentException(
- "Should have only one container view when using DefaultClockFaceLayout"
- )
- }
- return constraints
- }
-
- override fun applyPreviewConstraints(
- clockPreviewConfig: ClockPreviewConfig,
- constraints: ConstraintSet,
- ): ConstraintSet {
- return applyDefaultPreviewConstraints(clockPreviewConfig, constraints)
- }
-
- override fun applyAodBurnIn(aodBurnInModel: AodClockBurnInModel) {
- // Default clock doesn't need detailed control of view
- }
-
- companion object {
- fun applyDefaultPreviewConstraints(
- clockPreviewConfig: ClockPreviewConfig,
- constraints: ConstraintSet,
- ): ConstraintSet {
- constraints.apply {
- val context = clockPreviewConfig.context
- val lockscreenClockViewLargeId = context.getId("lockscreen_clock_view_large")
- constrainWidth(lockscreenClockViewLargeId, WRAP_CONTENT)
- constrainHeight(lockscreenClockViewLargeId, WRAP_CONTENT)
- constrainMaxHeight(lockscreenClockViewLargeId, 0)
-
- val largeClockTopMargin =
- SystemBarUtils.getStatusBarHeight(context) +
- context.getDimen("small_clock_padding_top") +
- context.getDimen("keyguard_smartspace_top_offset") +
- context.getDimen("date_weather_view_height") +
- context.getDimen("enhanced_smartspace_height")
- connect(lockscreenClockViewLargeId, TOP, PARENT_ID, TOP, largeClockTopMargin)
- connect(lockscreenClockViewLargeId, START, PARENT_ID, START)
- connect(lockscreenClockViewLargeId, END, PARENT_ID, END)
-
- clockPreviewConfig.udfpsTop?.let {
- val screenHeight = context.resources.displayMetrics.heightPixels
- connect(
- lockscreenClockViewLargeId,
- BOTTOM,
- PARENT_ID,
- BOTTOM,
- (screenHeight - it).toInt(),
- )
- }
- ?: run {
- // Copied calculation codes from applyConstraints in
- // DefaultDeviceEntrySection
- clockPreviewConfig.lockId?.let { lockId ->
- connect(lockscreenClockViewLargeId, BOTTOM, lockId, TOP)
- }
- ?: run {
- val bottomPaddingPx = context.getDimen("lock_icon_margin_bottom")
- val defaultDensity =
- DisplayMetrics.DENSITY_DEVICE_STABLE.toFloat() /
- DisplayMetrics.DENSITY_DEFAULT.toFloat()
- val lockIconRadiusPx = (defaultDensity * 36).toInt()
- val clockBottomMargin = bottomPaddingPx + 2 * lockIconRadiusPx
- connect(
- lockscreenClockViewLargeId,
- BOTTOM,
- PARENT_ID,
- BOTTOM,
- clockBottomMargin,
- )
- }
- }
-
- val smallClockViewId = context.getId("lockscreen_clock_view")
- constrainWidth(smallClockViewId, WRAP_CONTENT)
- constrainHeight(smallClockViewId, context.getDimen("small_clock_height"))
- connect(
- smallClockViewId,
- START,
- PARENT_ID,
- START,
- context.getDimen("clock_padding_start") +
- context.getDimen("status_view_margin_horizontal"),
- )
-
- val smallClockTopMargin = clockPreviewConfig.getSmallClockTopPadding()
- connect(smallClockViewId, TOP, PARENT_ID, TOP, smallClockTopMargin)
- }
- return constraints
- }
- }
-}
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockMessageBuffers.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockMessageBuffers.kt
index bec589a..cc77076 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockMessageBuffers.kt
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockMessageBuffers.kt
@@ -13,9 +13,11 @@
*/
package com.android.systemui.plugins.clocks
+import com.android.internal.annotations.Keep
import com.android.systemui.log.core.MessageBuffer
/** MessageBuffers for clocks that want to log information to SystemUI dumps */
+@Keep
data class ClockMessageBuffers(
/** Message buffer for general infrastructure */
val infraMessageBuffer: MessageBuffer,
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockPickerConfig.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockPickerConfig.kt
index 2147ca1..d589eea 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockPickerConfig.kt
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockPickerConfig.kt
@@ -14,7 +14,9 @@
package com.android.systemui.plugins.clocks
import android.graphics.drawable.Drawable
+import com.android.internal.annotations.Keep
+@Keep
data class ClockPickerConfig
@JvmOverloads
constructor(
@@ -39,6 +41,7 @@
val presetConfig: AxisPresetConfig? = null,
)
+@Keep
data class AxisPresetConfig(
/** Groups of Presets. Each group can be used together in a single control. */
val groups: List<Group>,
@@ -84,6 +87,7 @@
}
/** Represents an Axis that can be modified */
+@Keep
data class ClockFontAxis(
/** Axis key, not user renderable */
val key: String,
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockPreviewConfig.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockPreviewConfig.kt
index abc8e15..f373685 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockPreviewConfig.kt
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockPreviewConfig.kt
@@ -16,26 +16,23 @@
package com.android.systemui.plugins.clocks
-import android.content.Context
-import com.android.internal.policy.SystemBarUtils
-import com.android.systemui.plugins.clocks.ContextExt.getDimen
+import com.android.internal.annotations.Keep
+@Keep
data class ClockPreviewConfig(
- val context: Context,
val isShadeLayoutWide: Boolean,
- val isSceneContainerFlagEnabled: Boolean = false,
- val lockId: Int? = null,
+ val isSceneContainerFlagEnabled: Boolean,
+ val statusBarHeight: Int,
+ val splitShadeTopMargin: Int,
+ val clockTopMargin: Int,
+ val lockViewId: Int? = null,
val udfpsTop: Float? = null,
) {
- fun getSmallClockTopPadding(
- statusBarHeight: Int = SystemBarUtils.getStatusBarHeight(context)
- ): Int {
+ fun getSmallClockTopPadding(statusBarHeight: Int = this.statusBarHeight): Int {
return if (isShadeLayoutWide) {
- context.getDimen("keyguard_split_shade_top_margin") -
- if (isSceneContainerFlagEnabled) statusBarHeight else 0
+ splitShadeTopMargin - if (isSceneContainerFlagEnabled) statusBarHeight else 0
} else {
- context.getDimen("keyguard_clock_top_margin") +
- if (!isSceneContainerFlagEnabled) statusBarHeight else 0
+ clockTopMargin + if (!isSceneContainerFlagEnabled) statusBarHeight else 0
}
}
}
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockProviderPlugin.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockProviderPlugin.kt
index 0ef62a3..6cdfb74 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockProviderPlugin.kt
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockProviderPlugin.kt
@@ -13,6 +13,8 @@
*/
package com.android.systemui.plugins.clocks
+import android.content.Context
+import com.android.internal.annotations.Keep
import com.android.systemui.plugins.Plugin
import com.android.systemui.plugins.annotations.GeneratedImport
import com.android.systemui.plugins.annotations.ProtectedInterface
@@ -43,7 +45,7 @@
@ProtectedReturn("return null;")
/** Initializes and returns the target clock design */
- fun createClock(settings: ClockSettings): ClockController?
+ fun createClock(ctx: Context, settings: ClockSettings): ClockController?
@ProtectedReturn("return new ClockPickerConfig(\"\", \"\", \"\", null);")
/** Settings configuration parameters for the clock */
@@ -54,6 +56,7 @@
typealias ClockId = String
/** Some metadata about a clock design */
+@Keep
data class ClockMetadata(
/** Id for the clock design. */
val clockId: ClockId,
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockViewIds.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockViewIds.kt
new file mode 100644
index 0000000..986bea5
--- /dev/null
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockViewIds.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.plugins.clocks
+
+import android.view.View
+import com.android.internal.annotations.Keep
+
+/**
+ * Defines several view ids which are useful for sharing views between the host process and the
+ * client. Normally these would be defined in ids.xml, but android_library is incapable of being
+ * dynamically referenced by the plugin apks. This approach means the identifiers are no longer
+ * compile-time constants, but is preferable as it eliminates our need to look them up them by name.
+ */
+@Keep
+object ClockViewIds {
+ val LOCKSCREEN_CLOCK_VIEW_LARGE = View.generateViewId()
+ val LOCKSCREEN_CLOCK_VIEW_SMALL = View.generateViewId()
+
+ // View ids for different digit views
+ val HOUR_DIGIT_PAIR = View.generateViewId()
+ val MINUTE_DIGIT_PAIR = View.generateViewId()
+ val HOUR_FIRST_DIGIT = View.generateViewId()
+ val HOUR_SECOND_DIGIT = View.generateViewId()
+ val MINUTE_FIRST_DIGIT = View.generateViewId()
+ val MINUTE_SECOND_DIGIT = View.generateViewId()
+ val TIME_FULL_FORMAT = View.generateViewId()
+ val DATE_FORMAT = View.generateViewId()
+
+ // View ids for elements in large weather clock
+ // TODO(b/364680879): Move these to the weather clock apk w/ WeatherClockSection
+ val WEATHER_CLOCK_TIME = View.generateViewId()
+ val WEATHER_CLOCK_DATE = View.generateViewId()
+ val WEATHER_CLOCK_ICON = View.generateViewId()
+ val WEATHER_CLOCK_TEMP = View.generateViewId()
+ val WEATHER_CLOCK_ALARM_DND = View.generateViewId()
+ val WEATHER_CLOCK_DATE_BARRIER_BOTTOM = View.generateViewId()
+}
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ContextExt.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ContextExt.kt
deleted file mode 100644
index 17a1bfc..0000000
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ContextExt.kt
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the
- * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the specific language governing
- * permissions and limitations under the License.
- */
-package com.android.systemui.plugins.clocks
-
-import android.content.Context
-
-object ContextExt {
- fun Context.getId(name: String): Int {
- val res = packageManager.getResourcesForApplication(packageName)
- return res.getIdentifier(name, "id", packageName)
- }
-
- fun Context.getDimen(name: String): Int {
- val id = resources.getIdentifier(name, "dimen", packageName)
- return if (id == 0) 0 else resources.getDimensionPixelSize(id)
- }
-}
diff --git a/packages/SystemUI/proguard_common.flags b/packages/SystemUI/proguard_common.flags
index 4c261e1..af588dc 100644
--- a/packages/SystemUI/proguard_common.flags
+++ b/packages/SystemUI/proguard_common.flags
@@ -48,8 +48,3 @@
static *** v(...);
}
-maximumremovedandroidloglevel 2
-
-#Keep the R
--keepclassmembers class com.android.systemui.customization.R$* {
- public static <fields>;
-}
diff --git a/packages/SystemUI/res-keyguard/values-iw/strings.xml b/packages/SystemUI/res-keyguard/values-iw/strings.xml
index 67ee3ed..d366e6e 100644
--- a/packages/SystemUI/res-keyguard/values-iw/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-iw/strings.xml
@@ -50,7 +50,7 @@
<string name="error_disable_esim_title" msgid="3802652622784813119">"לא ניתן להשבית את כרטיס ה-eSIM"</string>
<string name="error_disable_esim_msg" msgid="2441188596467999327">"לא ניתן להשבית את כרטיס ה-eSIM עקב שגיאה."</string>
<string name="keyboardview_keycode_enter" msgid="6727192265631761174">"Enter"</string>
- <string name="kg_wrong_pattern" msgid="5907301342430102842">"קו ביטול נעילה שגוי"</string>
+ <string name="kg_wrong_pattern" msgid="5907301342430102842">"קו פתיחת נעילה שגוי"</string>
<string name="kg_wrong_pattern_try_again" msgid="3603524940234151881">"קו ביטול נעילה שגוי. יש לנסות שוב."</string>
<string name="kg_wrong_password" msgid="4143127991071670512">"סיסמה שגויה"</string>
<string name="kg_wrong_password_try_again" msgid="6602878676125765920">"סיסמה שגויה. יש לנסות שוב."</string>
diff --git a/packages/SystemUI/res-keyguard/values-zh-rCN/strings.xml b/packages/SystemUI/res-keyguard/values-zh-rCN/strings.xml
index dbfc494..a63de09 100644
--- a/packages/SystemUI/res-keyguard/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-zh-rCN/strings.xml
@@ -50,7 +50,7 @@
<string name="error_disable_esim_title" msgid="3802652622784813119">"无法停用 eSIM 卡"</string>
<string name="error_disable_esim_msg" msgid="2441188596467999327">"出现错误,无法停用 eSIM 卡。"</string>
<string name="keyboardview_keycode_enter" msgid="6727192265631761174">"输入"</string>
- <string name="kg_wrong_pattern" msgid="5907301342430102842">"图案错误"</string>
+ <string name="kg_wrong_pattern" msgid="5907301342430102842">"解锁图案错误"</string>
<string name="kg_wrong_pattern_try_again" msgid="3603524940234151881">"图案有误。请重试。"</string>
<string name="kg_wrong_password" msgid="4143127991071670512">"密码错误"</string>
<string name="kg_wrong_password_try_again" msgid="6602878676125765920">"密码有误。请重试。"</string>
diff --git a/packages/SystemUI/res/drawable/ic_qs_category_system_apps.xml b/packages/SystemUI/res/drawable/ic_qs_category_system_apps.xml
new file mode 100644
index 0000000..8a6457d2d
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_qs_category_system_apps.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2025 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:tint="?attr/colorControlNormal"
+ android:viewportHeight="960"
+ android:viewportWidth="960">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M280,920Q247,920 223.5,896.5Q200,873 200,840L200,120Q200,87 223.5,63.5Q247,40 280,40L680,40Q713,40 736.5,63.5Q760,87 760,120L760,840Q760,873 736.5,896.5Q713,920 680,920L280,920ZM280,720L680,720L680,240L280,240L280,720Z" />
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/media_carousel_settings_bg.xml b/packages/SystemUI/res/drawable/media_carousel_settings_bg.xml
new file mode 100644
index 0000000..b20e69f
--- /dev/null
+++ b/packages/SystemUI/res/drawable/media_carousel_settings_bg.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2025 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT 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="oval" >
+ <size android:height="40dp" android:width="40dp" />
+ <solid android:color="@*android:color/surface_effect_1" />
+ <corners android:radius="20dp" />
+</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/notification_2025_guts_feature_toggle_bg.xml b/packages/SystemUI/res/drawable/notification_2025_guts_feature_toggle_bg.xml
new file mode 100644
index 0000000..abf1692
--- /dev/null
+++ b/packages/SystemUI/res/drawable/notification_2025_guts_feature_toggle_bg.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2025 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<shape
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:shape="rectangle" >
+
+ <solid
+ android:color="@androidprv:color/materialColorSecondaryContainer" />
+
+ <corners android:radius="20dp" />
+</shape>
diff --git a/packages/SystemUI/res/drawable/notification_2025_guts_priority_button_bg.xml b/packages/SystemUI/res/drawable/notification_2025_guts_priority_button_bg.xml
index 1de8c2b..166b2e0 100644
--- a/packages/SystemUI/res/drawable/notification_2025_guts_priority_button_bg.xml
+++ b/packages/SystemUI/res/drawable/notification_2025_guts_priority_button_bg.xml
@@ -14,14 +14,7 @@
~ 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" >
- <solid
- android:color="@color/notification_guts_priority_button_bg_fill" />
-
- <stroke
- android:width="1.5dp"
- android:color="@color/notification_guts_priority_button_bg_stroke" />
-
- <corners android:radius="16dp" />
-</shape>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_selected="true" android:drawable="@drawable/notification_2025_guts_priority_button_bg_selected" />
+ <item android:drawable="@drawable/notification_2025_guts_priority_button_bg_unselected" />
+</selector>
diff --git a/packages/SystemUI/res/drawable/notification_2025_guts_priority_button_bg_selected.xml b/packages/SystemUI/res/drawable/notification_2025_guts_priority_button_bg_selected.xml
new file mode 100644
index 0000000..2e3fb8c
--- /dev/null
+++ b/packages/SystemUI/res/drawable/notification_2025_guts_priority_button_bg_selected.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2025 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:shape="rectangle" >
+ <solid android:color="@androidprv:color/materialColorSecondaryContainer" />
+
+ <stroke android:width="1dp" android:color="@androidprv:color/materialColorOutline" />
+
+ <corners android:radius="28dp" />
+</shape>
diff --git a/packages/SystemUI/res/drawable/notification_2025_guts_priority_button_bg_unselected.xml b/packages/SystemUI/res/drawable/notification_2025_guts_priority_button_bg_unselected.xml
new file mode 100644
index 0000000..fd5aa86
--- /dev/null
+++ b/packages/SystemUI/res/drawable/notification_2025_guts_priority_button_bg_unselected.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2025 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:shape="rectangle" >
+ <solid android:color="@color/transparent" />
+
+ <stroke android:width="1dp" android:color="@androidprv:color/materialColorOutlineVariant" />
+
+ <corners android:radius="16dp" />
+</shape>
diff --git a/packages/SystemUI/res/drawable/rounded_corner_bottom.xml b/packages/SystemUI/res/drawable/rounded_corner_bottom.xml
index ef1a82f..1aa6ad2 100644
--- a/packages/SystemUI/res/drawable/rounded_corner_bottom.xml
+++ b/packages/SystemUI/res/drawable/rounded_corner_bottom.xml
@@ -12,5 +12,14 @@
limitations under the License.
-->
<!-- Overlay this resource to change rounded_corners_bottom -->
-<inset xmlns:android="http://schemas.android.com/apk/res/android"
- android:drawable="@drawable/rounded"/>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="8dp"
+ android:height="8dp"
+ android:viewportWidth="8"
+ android:viewportHeight="8">
+
+ <path
+ android:fillColor="#000000"
+ android:pathData="M8,0H0v8C0,3.6,3.6,0,8,0z" />
+
+</vector>
diff --git a/packages/SystemUI/res/drawable/rounded_corner_top.xml b/packages/SystemUI/res/drawable/rounded_corner_top.xml
index 7934892..fecd559 100644
--- a/packages/SystemUI/res/drawable/rounded_corner_top.xml
+++ b/packages/SystemUI/res/drawable/rounded_corner_top.xml
@@ -12,5 +12,14 @@
limitations under the License.
-->
<!-- Overlay this resource to change rounded_corners_top -->
-<inset xmlns:android="http://schemas.android.com/apk/res/android"
- android:drawable="@drawable/rounded"/>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="8dp"
+ android:height="8dp"
+ android:viewportWidth="8"
+ android:viewportHeight="8">
+
+ <path
+ android:fillColor="#000000"
+ android:pathData="M8,0H0v8C0,3.6,3.6,0,8,0z" />
+
+</vector>
diff --git a/packages/SystemUI/res/drawable/settingslib_entry_bg_off_end.xml b/packages/SystemUI/res/drawable/settingslib_entry_bg_off_end.xml
new file mode 100644
index 0000000..e332f1b
--- /dev/null
+++ b/packages/SystemUI/res/drawable/settingslib_entry_bg_off_end.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2025 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:color="?android:attr/colorControlHighlight">
+ <item>
+ <shape android:shape="rectangle">
+ <solid android:color="@androidprv:color/materialColorPrimaryContainer"/>
+ <corners
+ android:bottomLeftRadius="@dimen/settingslib_switch_bar_radius"
+ android:bottomRightRadius="@dimen/settingslib_switch_bar_radius"/>
+ </shape>
+ </item>
+</ripple>
diff --git a/packages/SystemUI/res/drawable/settingslib_entry_bg_off_middle.xml b/packages/SystemUI/res/drawable/settingslib_entry_bg_off_middle.xml
new file mode 100644
index 0000000..7588ff2
--- /dev/null
+++ b/packages/SystemUI/res/drawable/settingslib_entry_bg_off_middle.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2025 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:color="?android:attr/colorControlHighlight">
+ <item>
+ <shape android:shape="rectangle">
+ <solid android:color="@androidprv:color/materialColorPrimaryContainer"/>
+ </shape>
+ </item>
+</ripple>
diff --git a/packages/SystemUI/res/drawable/settingslib_entry_bg_off_start.xml b/packages/SystemUI/res/drawable/settingslib_entry_bg_off_start.xml
new file mode 100644
index 0000000..20e0d15a
--- /dev/null
+++ b/packages/SystemUI/res/drawable/settingslib_entry_bg_off_start.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2025 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:color="?android:attr/colorControlHighlight">
+ <item>
+ <shape android:shape="rectangle">
+ <solid android:color="@androidprv:color/materialColorPrimaryContainer"/>
+ <corners
+ android:topLeftRadius="@dimen/settingslib_switch_bar_radius"
+ android:topRightRadius="@dimen/settingslib_switch_bar_radius"/>
+ </shape>
+ </item>
+</ripple>
diff --git a/packages/SettingsLib/res/drawable/stat_sys_airplane_mode.xml b/packages/SystemUI/res/drawable/stat_sys_airplane_mode.xml
similarity index 100%
rename from packages/SettingsLib/res/drawable/stat_sys_airplane_mode.xml
rename to packages/SystemUI/res/drawable/stat_sys_airplane_mode.xml
diff --git "a/packages/SystemUI/res/flag\050com.android.systemui.icon_refresh_2025\051/drawable/qs_flashlight_icon_off.xml" "b/packages/SystemUI/res/flag\050com.android.systemui.icon_refresh_2025\051/drawable/qs_flashlight_icon_off.xml"
new file mode 100644
index 0000000..fca7b5b
--- /dev/null
+++ "b/packages/SystemUI/res/flag\050com.android.systemui.icon_refresh_2025\051/drawable/qs_flashlight_icon_off.xml"
@@ -0,0 +1,312 @@
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:aapt="http://schemas.android.com/aapt">
+ <target android:name="_R_G_L_0_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="17"
+ android:propertyName="pathData"
+ android:startOffset="0"
+ android:valueFrom=" M0.25 74.63 C0.25,74.63 0.25,48.38 0.25,48.38 C0.25,35.04 4.85,23.79 14.05,14.63 C23.66,5.04 35.16,0.25 48.54,0.25 C48.54,0.25 252.97,0.25 252.97,0.25 C266.34,0.25 277.63,5.04 286.83,14.63 C296.44,23.79 301.25,35.04 301.25,48.38 C301.25,48.38 301.25,74.63 301.25,74.63 C301.25,74.63 0.25,74.63 0.25,74.63c M150.75 398.56 C160.78,398.56 169.35,395.02 176.46,387.94 C183.57,380.86 187.12,372.31 187.12,362.31 C187.12,352.31 183.57,343.77 176.46,336.69 C169.35,329.19 160.78,325.44 150.75,325.44 C140.72,325.44 132.15,328.98 125.04,336.06 C117.93,343.15 114.38,351.9 114.38,362.31 C114.38,372.31 117.93,380.86 125.04,387.94 C132.15,395.02 140.72,398.56 150.75,398.56c M51.67 452.13 C51.67,452.13 51.67,224.63 51.67,224.63 C51.67,224.63 7.78,160.88 7.78,160.88 C5.27,156.71 3.38,152.54 2.13,148.38 C0.88,143.79 0.25,139 0.25,134 C0.25,134 0.25,122.75 0.25,122.75 C0.25,122.75 301.25,122.75 301.25,122.75 C301.25,122.75 301.25,134 301.25,134 C301.25,139 300.62,143.79 299.37,148.38 C298.12,152.54 296.23,156.71 293.73,160.88 C293.73,160.88 249.83,224.63 249.83,224.63 C249.83,224.63 249.83,452.13 249.83,452.13 C249.83,465.46 245.02,476.92 235.41,486.5 C226.21,495.67 215.13,500.25 202.17,500.25 C202.17,500.25 99.33,500.25 99.33,500.25 C86.37,500.25 75.08,495.67 65.47,486.5 C56.27,476.92 51.67,465.46 51.67,452.13c "
+ android:valueTo=" M0.25 74.63 C0.25,74.63 0.25,48.38 0.25,48.38 C0.25,35.04 4.85,23.79 14.05,14.63 C23.66,5.04 35.16,0.25 48.54,0.25 C48.54,0.25 252.97,0.25 252.97,0.25 C266.34,0.25 277.63,5.04 286.83,14.63 C296.44,23.79 301.25,35.04 301.25,48.38 C301.25,48.38 301.25,74.63 301.25,74.63 C301.25,74.63 0.25,74.63 0.25,74.63c M150.75 384.73 C160.78,384.73 169.35,381.19 176.46,374.11 C183.57,367.02 187.12,358.48 187.12,348.48 C187.12,338.48 183.57,329.94 176.46,322.86 C169.35,315.36 160.78,311.61 150.75,311.61 C140.72,311.61 132.15,315.15 125.04,322.23 C117.93,329.31 114.38,338.06 114.38,348.48 C114.38,358.48 117.93,367.02 125.04,374.11 C132.15,381.19 140.72,384.73 150.75,384.73c M51.67 452.13 C51.67,452.13 51.67,224.63 51.67,224.63 C51.67,224.63 7.78,160.88 7.78,160.88 C5.27,156.71 3.38,152.54 2.13,148.38 C0.88,143.79 0.25,139 0.25,134 C0.25,134 0.25,122.75 0.25,122.75 C0.25,122.75 301.25,122.75 301.25,122.75 C301.25,122.75 301.25,134 301.25,134 C301.25,139 300.62,143.79 299.37,148.38 C298.12,152.54 296.23,156.71 293.73,160.88 C293.73,160.88 249.83,224.63 249.83,224.63 C249.83,224.63 249.83,452.13 249.83,452.13 C249.83,465.46 245.02,476.92 235.41,486.5 C226.21,495.67 215.13,500.25 202.17,500.25 C202.17,500.25 99.33,500.25 99.33,500.25 C86.37,500.25 75.08,495.67 65.47,486.5 C56.27,476.92 51.67,465.46 51.67,452.13c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="17"
+ android:propertyName="pathData"
+ android:startOffset="17"
+ android:valueFrom=" M0.25 74.63 C0.25,74.63 0.25,48.38 0.25,48.38 C0.25,35.04 4.85,23.79 14.05,14.63 C23.66,5.04 35.16,0.25 48.54,0.25 C48.54,0.25 252.97,0.25 252.97,0.25 C266.34,0.25 277.63,5.04 286.83,14.63 C296.44,23.79 301.25,35.04 301.25,48.38 C301.25,48.38 301.25,74.63 301.25,74.63 C301.25,74.63 0.25,74.63 0.25,74.63c M150.75 384.73 C160.78,384.73 169.35,381.19 176.46,374.11 C183.57,367.02 187.12,358.48 187.12,348.48 C187.12,338.48 183.57,329.94 176.46,322.86 C169.35,315.36 160.78,311.61 150.75,311.61 C140.72,311.61 132.15,315.15 125.04,322.23 C117.93,329.31 114.38,338.06 114.38,348.48 C114.38,358.48 117.93,367.02 125.04,374.11 C132.15,381.19 140.72,384.73 150.75,384.73c M51.67 452.13 C51.67,452.13 51.67,224.63 51.67,224.63 C51.67,224.63 7.78,160.88 7.78,160.88 C5.27,156.71 3.38,152.54 2.13,148.38 C0.88,143.79 0.25,139 0.25,134 C0.25,134 0.25,122.75 0.25,122.75 C0.25,122.75 301.25,122.75 301.25,122.75 C301.25,122.75 301.25,134 301.25,134 C301.25,139 300.62,143.79 299.37,148.38 C298.12,152.54 296.23,156.71 293.73,160.88 C293.73,160.88 249.83,224.63 249.83,224.63 C249.83,224.63 249.83,452.13 249.83,452.13 C249.83,465.46 245.02,476.92 235.41,486.5 C226.21,495.67 215.13,500.25 202.17,500.25 C202.17,500.25 99.33,500.25 99.33,500.25 C86.37,500.25 75.08,495.67 65.47,486.5 C56.27,476.92 51.67,465.46 51.67,452.13c "
+ android:valueTo=" M0.25 74.63 C0.25,74.63 0.25,48.38 0.25,48.38 C0.25,35.04 4.85,23.79 14.05,14.63 C23.66,5.04 35.16,0.25 48.54,0.25 C48.54,0.25 252.97,0.25 252.97,0.25 C266.34,0.25 277.63,5.04 286.83,14.63 C296.44,23.79 301.25,35.04 301.25,48.38 C301.25,48.38 301.25,74.63 301.25,74.63 C301.25,74.63 0.25,74.63 0.25,74.63c M150.75 371.16 C160.78,371.16 169.35,367.62 176.46,360.54 C183.57,353.45 187.12,344.91 187.12,334.91 C187.12,324.91 183.57,316.37 176.46,309.28 C169.35,301.78 160.78,298.03 150.75,298.03 C140.72,298.03 132.15,301.58 125.04,308.66 C117.93,315.74 114.38,324.49 114.38,334.91 C114.38,344.91 117.93,353.45 125.04,360.54 C132.15,367.62 140.72,371.16 150.75,371.16c M51.67 452.13 C51.67,452.13 51.67,224.63 51.67,224.63 C51.67,224.63 7.78,160.88 7.78,160.88 C5.27,156.71 3.38,152.54 2.13,148.38 C0.88,143.79 0.25,139 0.25,134 C0.25,134 0.25,122.75 0.25,122.75 C0.25,122.75 301.25,122.75 301.25,122.75 C301.25,122.75 301.25,134 301.25,134 C301.25,139 300.62,143.79 299.37,148.38 C298.12,152.54 296.23,156.71 293.73,160.88 C293.73,160.88 249.83,224.63 249.83,224.63 C249.83,224.63 249.83,452.13 249.83,452.13 C249.83,465.46 245.02,476.92 235.41,486.5 C226.21,495.67 215.13,500.25 202.17,500.25 C202.17,500.25 99.33,500.25 99.33,500.25 C86.37,500.25 75.08,495.67 65.47,486.5 C56.27,476.92 51.67,465.46 51.67,452.13c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="17"
+ android:propertyName="pathData"
+ android:startOffset="33"
+ android:valueFrom=" M0.25 74.63 C0.25,74.63 0.25,48.38 0.25,48.38 C0.25,35.04 4.85,23.79 14.05,14.63 C23.66,5.04 35.16,0.25 48.54,0.25 C48.54,0.25 252.97,0.25 252.97,0.25 C266.34,0.25 277.63,5.04 286.83,14.63 C296.44,23.79 301.25,35.04 301.25,48.38 C301.25,48.38 301.25,74.63 301.25,74.63 C301.25,74.63 0.25,74.63 0.25,74.63c M150.75 371.16 C160.78,371.16 169.35,367.62 176.46,360.54 C183.57,353.45 187.12,344.91 187.12,334.91 C187.12,324.91 183.57,316.37 176.46,309.28 C169.35,301.78 160.78,298.03 150.75,298.03 C140.72,298.03 132.15,301.58 125.04,308.66 C117.93,315.74 114.38,324.49 114.38,334.91 C114.38,344.91 117.93,353.45 125.04,360.54 C132.15,367.62 140.72,371.16 150.75,371.16c M51.67 452.13 C51.67,452.13 51.67,224.63 51.67,224.63 C51.67,224.63 7.78,160.88 7.78,160.88 C5.27,156.71 3.38,152.54 2.13,148.38 C0.88,143.79 0.25,139 0.25,134 C0.25,134 0.25,122.75 0.25,122.75 C0.25,122.75 301.25,122.75 301.25,122.75 C301.25,122.75 301.25,134 301.25,134 C301.25,139 300.62,143.79 299.37,148.38 C298.12,152.54 296.23,156.71 293.73,160.88 C293.73,160.88 249.83,224.63 249.83,224.63 C249.83,224.63 249.83,452.13 249.83,452.13 C249.83,465.46 245.02,476.92 235.41,486.5 C226.21,495.67 215.13,500.25 202.17,500.25 C202.17,500.25 99.33,500.25 99.33,500.25 C86.37,500.25 75.08,495.67 65.47,486.5 C56.27,476.92 51.67,465.46 51.67,452.13c "
+ android:valueTo=" M0.25 74.63 C0.25,74.63 0.25,48.38 0.25,48.38 C0.25,35.04 4.85,23.79 14.05,14.63 C23.66,5.04 35.16,0.25 48.54,0.25 C48.54,0.25 252.97,0.25 252.97,0.25 C266.34,0.25 277.63,5.04 286.83,14.63 C296.44,23.79 301.25,35.04 301.25,48.38 C301.25,48.38 301.25,74.63 301.25,74.63 C301.25,74.63 0.25,74.63 0.25,74.63c M150.75 359.06 C160.78,359.06 169.35,355.52 176.46,348.44 C183.57,341.35 187.12,332.81 187.12,322.81 C187.12,312.81 183.57,304.27 176.46,297.19 C169.35,289.69 160.78,285.94 150.75,285.94 C140.72,285.94 132.15,289.48 125.04,296.56 C117.93,303.64 114.38,312.39 114.38,322.81 C114.38,332.81 117.93,341.35 125.04,348.44 C132.15,355.52 140.72,359.06 150.75,359.06c M51.67 452.13 C51.67,452.13 51.67,224.63 51.67,224.63 C51.67,224.63 7.78,160.88 7.78,160.88 C5.27,156.71 3.38,152.54 2.13,148.38 C0.88,143.79 0.25,139 0.25,134 C0.25,134 0.25,122.75 0.25,122.75 C0.25,122.75 301.25,122.75 301.25,122.75 C301.25,122.75 301.25,134 301.25,134 C301.25,139 300.62,143.79 299.37,148.38 C298.12,152.54 296.23,156.71 293.73,160.88 C293.73,160.88 249.83,224.63 249.83,224.63 C249.83,224.63 249.83,452.13 249.83,452.13 C249.83,465.46 245.02,476.92 235.41,486.5 C226.21,495.67 215.13,500.25 202.17,500.25 C202.17,500.25 99.33,500.25 99.33,500.25 C86.37,500.25 75.08,495.67 65.47,486.5 C56.27,476.92 51.67,465.46 51.67,452.13c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="17"
+ android:propertyName="pathData"
+ android:startOffset="50"
+ android:valueFrom=" M0.25 74.63 C0.25,74.63 0.25,48.38 0.25,48.38 C0.25,35.04 4.85,23.79 14.05,14.63 C23.66,5.04 35.16,0.25 48.54,0.25 C48.54,0.25 252.97,0.25 252.97,0.25 C266.34,0.25 277.63,5.04 286.83,14.63 C296.44,23.79 301.25,35.04 301.25,48.38 C301.25,48.38 301.25,74.63 301.25,74.63 C301.25,74.63 0.25,74.63 0.25,74.63c M150.75 359.06 C160.78,359.06 169.35,355.52 176.46,348.44 C183.57,341.35 187.12,332.81 187.12,322.81 C187.12,312.81 183.57,304.27 176.46,297.19 C169.35,289.69 160.78,285.94 150.75,285.94 C140.72,285.94 132.15,289.48 125.04,296.56 C117.93,303.64 114.38,312.39 114.38,322.81 C114.38,332.81 117.93,341.35 125.04,348.44 C132.15,355.52 140.72,359.06 150.75,359.06c M51.67 452.13 C51.67,452.13 51.67,224.63 51.67,224.63 C51.67,224.63 7.78,160.88 7.78,160.88 C5.27,156.71 3.38,152.54 2.13,148.38 C0.88,143.79 0.25,139 0.25,134 C0.25,134 0.25,122.75 0.25,122.75 C0.25,122.75 301.25,122.75 301.25,122.75 C301.25,122.75 301.25,134 301.25,134 C301.25,139 300.62,143.79 299.37,148.38 C298.12,152.54 296.23,156.71 293.73,160.88 C293.73,160.88 249.83,224.63 249.83,224.63 C249.83,224.63 249.83,452.13 249.83,452.13 C249.83,465.46 245.02,476.92 235.41,486.5 C226.21,495.67 215.13,500.25 202.17,500.25 C202.17,500.25 99.33,500.25 99.33,500.25 C86.37,500.25 75.08,495.67 65.47,486.5 C56.27,476.92 51.67,465.46 51.67,452.13c "
+ android:valueTo=" M0.25 74.63 C0.25,74.63 0.25,48.38 0.25,48.38 C0.25,35.04 4.85,23.79 14.05,14.63 C23.66,5.04 35.16,0.25 48.54,0.25 C48.54,0.25 252.97,0.25 252.97,0.25 C266.34,0.25 277.63,5.04 286.83,14.63 C296.44,23.79 301.25,35.04 301.25,48.38 C301.25,48.38 301.25,74.63 301.25,74.63 C301.25,74.63 0.25,74.63 0.25,74.63c M150.75 349.35 C160.78,349.35 169.35,345.81 176.46,338.73 C183.57,331.64 187.12,323.1 187.12,313.1 C187.12,303.1 183.57,294.56 176.46,287.48 C169.35,279.98 160.78,276.23 150.75,276.23 C140.72,276.23 132.15,279.77 125.04,286.85 C117.93,293.93 114.38,302.68 114.38,313.1 C114.38,323.1 117.93,331.64 125.04,338.73 C132.15,345.81 140.72,349.35 150.75,349.35c M51.67 452.13 C51.67,452.13 51.67,224.63 51.67,224.63 C51.67,224.63 7.78,160.88 7.78,160.88 C5.27,156.71 3.38,152.54 2.13,148.38 C0.88,143.79 0.25,139 0.25,134 C0.25,134 0.25,122.75 0.25,122.75 C0.25,122.75 301.25,122.75 301.25,122.75 C301.25,122.75 301.25,134 301.25,134 C301.25,139 300.62,143.79 299.37,148.38 C298.12,152.54 296.23,156.71 293.73,160.88 C293.73,160.88 249.83,224.63 249.83,224.63 C249.83,224.63 249.83,452.13 249.83,452.13 C249.83,465.46 245.02,476.92 235.41,486.5 C226.21,495.67 215.13,500.25 202.17,500.25 C202.17,500.25 99.33,500.25 99.33,500.25 C86.37,500.25 75.08,495.67 65.47,486.5 C56.27,476.92 51.67,465.46 51.67,452.13c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="17"
+ android:propertyName="pathData"
+ android:startOffset="67"
+ android:valueFrom=" M0.25 74.63 C0.25,74.63 0.25,48.38 0.25,48.38 C0.25,35.04 4.85,23.79 14.05,14.63 C23.66,5.04 35.16,0.25 48.54,0.25 C48.54,0.25 252.97,0.25 252.97,0.25 C266.34,0.25 277.63,5.04 286.83,14.63 C296.44,23.79 301.25,35.04 301.25,48.38 C301.25,48.38 301.25,74.63 301.25,74.63 C301.25,74.63 0.25,74.63 0.25,74.63c M150.75 349.35 C160.78,349.35 169.35,345.81 176.46,338.73 C183.57,331.64 187.12,323.1 187.12,313.1 C187.12,303.1 183.57,294.56 176.46,287.48 C169.35,279.98 160.78,276.23 150.75,276.23 C140.72,276.23 132.15,279.77 125.04,286.85 C117.93,293.93 114.38,302.68 114.38,313.1 C114.38,323.1 117.93,331.64 125.04,338.73 C132.15,345.81 140.72,349.35 150.75,349.35c M51.67 452.13 C51.67,452.13 51.67,224.63 51.67,224.63 C51.67,224.63 7.78,160.88 7.78,160.88 C5.27,156.71 3.38,152.54 2.13,148.38 C0.88,143.79 0.25,139 0.25,134 C0.25,134 0.25,122.75 0.25,122.75 C0.25,122.75 301.25,122.75 301.25,122.75 C301.25,122.75 301.25,134 301.25,134 C301.25,139 300.62,143.79 299.37,148.38 C298.12,152.54 296.23,156.71 293.73,160.88 C293.73,160.88 249.83,224.63 249.83,224.63 C249.83,224.63 249.83,452.13 249.83,452.13 C249.83,465.46 245.02,476.92 235.41,486.5 C226.21,495.67 215.13,500.25 202.17,500.25 C202.17,500.25 99.33,500.25 99.33,500.25 C86.37,500.25 75.08,495.67 65.47,486.5 C56.27,476.92 51.67,465.46 51.67,452.13c "
+ android:valueTo=" M0.25 74.63 C0.25,74.63 0.25,48.38 0.25,48.38 C0.25,35.04 4.85,23.79 14.05,14.63 C23.66,5.04 35.16,0.25 48.54,0.25 C48.54,0.25 252.97,0.25 252.97,0.25 C266.34,0.25 277.63,5.04 286.83,14.63 C296.44,23.79 301.25,35.04 301.25,48.38 C301.25,48.38 301.25,74.63 301.25,74.63 C301.25,74.63 0.25,74.63 0.25,74.63c M150.75 341.71 C160.78,341.71 169.35,338.17 176.46,331.09 C183.57,324.01 187.12,315.46 187.12,305.46 C187.12,295.46 183.57,286.92 176.46,279.84 C169.35,272.34 160.78,268.59 150.75,268.59 C140.72,268.59 132.15,272.13 125.04,279.21 C117.93,286.3 114.38,295.05 114.38,305.46 C114.38,315.46 117.93,324.01 125.04,331.09 C132.15,338.17 140.72,341.71 150.75,341.71c M51.67 452.13 C51.67,452.13 51.67,224.63 51.67,224.63 C51.67,224.63 7.78,160.88 7.78,160.88 C5.27,156.71 3.38,152.54 2.13,148.38 C0.88,143.79 0.25,139 0.25,134 C0.25,134 0.25,122.75 0.25,122.75 C0.25,122.75 301.25,122.75 301.25,122.75 C301.25,122.75 301.25,134 301.25,134 C301.25,139 300.62,143.79 299.37,148.38 C298.12,152.54 296.23,156.71 293.73,160.88 C293.73,160.88 249.83,224.63 249.83,224.63 C249.83,224.63 249.83,452.13 249.83,452.13 C249.83,465.46 245.02,476.92 235.41,486.5 C226.21,495.67 215.13,500.25 202.17,500.25 C202.17,500.25 99.33,500.25 99.33,500.25 C86.37,500.25 75.08,495.67 65.47,486.5 C56.27,476.92 51.67,465.46 51.67,452.13c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="17"
+ android:propertyName="pathData"
+ android:startOffset="83"
+ android:valueFrom=" M0.25 74.63 C0.25,74.63 0.25,48.38 0.25,48.38 C0.25,35.04 4.85,23.79 14.05,14.63 C23.66,5.04 35.16,0.25 48.54,0.25 C48.54,0.25 252.97,0.25 252.97,0.25 C266.34,0.25 277.63,5.04 286.83,14.63 C296.44,23.79 301.25,35.04 301.25,48.38 C301.25,48.38 301.25,74.63 301.25,74.63 C301.25,74.63 0.25,74.63 0.25,74.63c M150.75 341.71 C160.78,341.71 169.35,338.17 176.46,331.09 C183.57,324.01 187.12,315.46 187.12,305.46 C187.12,295.46 183.57,286.92 176.46,279.84 C169.35,272.34 160.78,268.59 150.75,268.59 C140.72,268.59 132.15,272.13 125.04,279.21 C117.93,286.3 114.38,295.05 114.38,305.46 C114.38,315.46 117.93,324.01 125.04,331.09 C132.15,338.17 140.72,341.71 150.75,341.71c M51.67 452.13 C51.67,452.13 51.67,224.63 51.67,224.63 C51.67,224.63 7.78,160.88 7.78,160.88 C5.27,156.71 3.38,152.54 2.13,148.38 C0.88,143.79 0.25,139 0.25,134 C0.25,134 0.25,122.75 0.25,122.75 C0.25,122.75 301.25,122.75 301.25,122.75 C301.25,122.75 301.25,134 301.25,134 C301.25,139 300.62,143.79 299.37,148.38 C298.12,152.54 296.23,156.71 293.73,160.88 C293.73,160.88 249.83,224.63 249.83,224.63 C249.83,224.63 249.83,452.13 249.83,452.13 C249.83,465.46 245.02,476.92 235.41,486.5 C226.21,495.67 215.13,500.25 202.17,500.25 C202.17,500.25 99.33,500.25 99.33,500.25 C86.37,500.25 75.08,495.67 65.47,486.5 C56.27,476.92 51.67,465.46 51.67,452.13c "
+ android:valueTo=" M0.25 74.63 C0.25,74.63 0.25,48.38 0.25,48.38 C0.25,35.04 4.85,23.79 14.05,14.63 C23.66,5.04 35.16,0.25 48.54,0.25 C48.54,0.25 252.97,0.25 252.97,0.25 C266.34,0.25 277.63,5.04 286.83,14.63 C296.44,23.79 301.25,35.04 301.25,48.38 C301.25,48.38 301.25,74.63 301.25,74.63 C301.25,74.63 0.25,74.63 0.25,74.63c M150.75 336.34 C160.78,336.34 169.35,332.8 176.46,325.71 C183.57,318.63 187.12,310.09 187.12,300.09 C187.12,290.09 183.57,281.55 176.46,274.46 C169.35,266.96 160.78,263.21 150.75,263.21 C140.72,263.21 132.15,266.76 125.04,273.84 C117.93,280.92 114.38,289.67 114.38,300.09 C114.38,310.09 117.93,318.63 125.04,325.71 C132.15,332.8 140.72,336.34 150.75,336.34c M51.67 452.13 C51.67,452.13 51.67,224.63 51.67,224.63 C51.67,224.63 7.78,160.88 7.78,160.88 C5.27,156.71 3.38,152.54 2.13,148.38 C0.88,143.79 0.25,139 0.25,134 C0.25,134 0.25,122.75 0.25,122.75 C0.25,122.75 301.25,122.75 301.25,122.75 C301.25,122.75 301.25,134 301.25,134 C301.25,139 300.62,143.79 299.37,148.38 C298.12,152.54 296.23,156.71 293.73,160.88 C293.73,160.88 249.83,224.63 249.83,224.63 C249.83,224.63 249.83,452.13 249.83,452.13 C249.83,465.46 245.02,476.92 235.41,486.5 C226.21,495.67 215.13,500.25 202.17,500.25 C202.17,500.25 99.33,500.25 99.33,500.25 C86.37,500.25 75.08,495.67 65.47,486.5 C56.27,476.92 51.67,465.46 51.67,452.13c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="17"
+ android:propertyName="pathData"
+ android:startOffset="100"
+ android:valueFrom=" M0.25 74.63 C0.25,74.63 0.25,48.38 0.25,48.38 C0.25,35.04 4.85,23.79 14.05,14.63 C23.66,5.04 35.16,0.25 48.54,0.25 C48.54,0.25 252.97,0.25 252.97,0.25 C266.34,0.25 277.63,5.04 286.83,14.63 C296.44,23.79 301.25,35.04 301.25,48.38 C301.25,48.38 301.25,74.63 301.25,74.63 C301.25,74.63 0.25,74.63 0.25,74.63c M150.75 336.34 C160.78,336.34 169.35,332.8 176.46,325.71 C183.57,318.63 187.12,310.09 187.12,300.09 C187.12,290.09 183.57,281.55 176.46,274.46 C169.35,266.96 160.78,263.21 150.75,263.21 C140.72,263.21 132.15,266.76 125.04,273.84 C117.93,280.92 114.38,289.67 114.38,300.09 C114.38,310.09 117.93,318.63 125.04,325.71 C132.15,332.8 140.72,336.34 150.75,336.34c M51.67 452.13 C51.67,452.13 51.67,224.63 51.67,224.63 C51.67,224.63 7.78,160.88 7.78,160.88 C5.27,156.71 3.38,152.54 2.13,148.38 C0.88,143.79 0.25,139 0.25,134 C0.25,134 0.25,122.75 0.25,122.75 C0.25,122.75 301.25,122.75 301.25,122.75 C301.25,122.75 301.25,134 301.25,134 C301.25,139 300.62,143.79 299.37,148.38 C298.12,152.54 296.23,156.71 293.73,160.88 C293.73,160.88 249.83,224.63 249.83,224.63 C249.83,224.63 249.83,452.13 249.83,452.13 C249.83,465.46 245.02,476.92 235.41,486.5 C226.21,495.67 215.13,500.25 202.17,500.25 C202.17,500.25 99.33,500.25 99.33,500.25 C86.37,500.25 75.08,495.67 65.47,486.5 C56.27,476.92 51.67,465.46 51.67,452.13c "
+ android:valueTo=" M0.25 74.63 C0.25,74.63 0.25,48.38 0.25,48.38 C0.25,35.04 4.85,23.79 14.05,14.63 C23.66,5.04 35.16,0.25 48.54,0.25 C48.54,0.25 252.97,0.25 252.97,0.25 C266.34,0.25 277.63,5.04 286.83,14.63 C296.44,23.79 301.25,35.04 301.25,48.38 C301.25,48.38 301.25,74.63 301.25,74.63 C301.25,74.63 0.25,74.63 0.25,74.63c M150.75 332.78 C160.78,332.78 169.35,329.24 176.46,322.15 C183.57,315.07 187.12,306.53 187.12,296.53 C187.12,286.53 183.57,277.99 176.46,270.9 C169.35,263.4 160.78,259.65 150.75,259.65 C140.72,259.65 132.15,263.2 125.04,270.28 C117.93,277.36 114.38,286.11 114.38,296.53 C114.38,306.53 117.93,315.07 125.04,322.15 C132.15,329.24 140.72,332.78 150.75,332.78c M51.67 452.13 C51.67,452.13 51.67,224.63 51.67,224.63 C51.67,224.63 7.78,160.88 7.78,160.88 C5.27,156.71 3.38,152.54 2.13,148.38 C0.88,143.79 0.25,139 0.25,134 C0.25,134 0.25,122.75 0.25,122.75 C0.25,122.75 301.25,122.75 301.25,122.75 C301.25,122.75 301.25,134 301.25,134 C301.25,139 300.62,143.79 299.37,148.38 C298.12,152.54 296.23,156.71 293.73,160.88 C293.73,160.88 249.83,224.63 249.83,224.63 C249.83,224.63 249.83,452.13 249.83,452.13 C249.83,465.46 245.02,476.92 235.41,486.5 C226.21,495.67 215.13,500.25 202.17,500.25 C202.17,500.25 99.33,500.25 99.33,500.25 C86.37,500.25 75.08,495.67 65.47,486.5 C56.27,476.92 51.67,465.46 51.67,452.13c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="17"
+ android:propertyName="pathData"
+ android:startOffset="117"
+ android:valueFrom=" M0.25 74.63 C0.25,74.63 0.25,48.38 0.25,48.38 C0.25,35.04 4.85,23.79 14.05,14.63 C23.66,5.04 35.16,0.25 48.54,0.25 C48.54,0.25 252.97,0.25 252.97,0.25 C266.34,0.25 277.63,5.04 286.83,14.63 C296.44,23.79 301.25,35.04 301.25,48.38 C301.25,48.38 301.25,74.63 301.25,74.63 C301.25,74.63 0.25,74.63 0.25,74.63c M150.75 332.78 C160.78,332.78 169.35,329.24 176.46,322.15 C183.57,315.07 187.12,306.53 187.12,296.53 C187.12,286.53 183.57,277.99 176.46,270.9 C169.35,263.4 160.78,259.65 150.75,259.65 C140.72,259.65 132.15,263.2 125.04,270.28 C117.93,277.36 114.38,286.11 114.38,296.53 C114.38,306.53 117.93,315.07 125.04,322.15 C132.15,329.24 140.72,332.78 150.75,332.78c M51.67 452.13 C51.67,452.13 51.67,224.63 51.67,224.63 C51.67,224.63 7.78,160.88 7.78,160.88 C5.27,156.71 3.38,152.54 2.13,148.38 C0.88,143.79 0.25,139 0.25,134 C0.25,134 0.25,122.75 0.25,122.75 C0.25,122.75 301.25,122.75 301.25,122.75 C301.25,122.75 301.25,134 301.25,134 C301.25,139 300.62,143.79 299.37,148.38 C298.12,152.54 296.23,156.71 293.73,160.88 C293.73,160.88 249.83,224.63 249.83,224.63 C249.83,224.63 249.83,452.13 249.83,452.13 C249.83,465.46 245.02,476.92 235.41,486.5 C226.21,495.67 215.13,500.25 202.17,500.25 C202.17,500.25 99.33,500.25 99.33,500.25 C86.37,500.25 75.08,495.67 65.47,486.5 C56.27,476.92 51.67,465.46 51.67,452.13c "
+ android:valueTo=" M0.25 74.63 C0.25,74.63 0.25,48.38 0.25,48.38 C0.25,35.04 4.85,23.79 14.05,14.63 C23.66,5.04 35.16,0.25 48.54,0.25 C48.54,0.25 252.97,0.25 252.97,0.25 C266.34,0.25 277.63,5.04 286.83,14.63 C296.44,23.79 301.25,35.04 301.25,48.38 C301.25,48.38 301.25,74.63 301.25,74.63 C301.25,74.63 0.25,74.63 0.25,74.63c M150.75 331.03 C160.78,331.03 169.35,327.49 176.46,320.4 C183.57,313.32 187.12,304.78 187.12,294.78 C187.12,284.78 183.57,276.24 176.46,269.15 C169.35,261.65 160.78,257.9 150.75,257.9 C140.72,257.9 132.15,261.44 125.04,268.53 C117.93,275.61 114.38,284.36 114.38,294.78 C114.38,304.78 117.93,313.32 125.04,320.4 C132.15,327.49 140.72,331.03 150.75,331.03c M51.67 452.13 C51.67,452.13 51.67,224.63 51.67,224.63 C51.67,224.63 7.78,160.88 7.78,160.88 C5.27,156.71 3.38,152.54 2.13,148.38 C0.88,143.79 0.25,139 0.25,134 C0.25,134 0.25,122.75 0.25,122.75 C0.25,122.75 301.25,122.75 301.25,122.75 C301.25,122.75 301.25,134 301.25,134 C301.25,139 300.62,143.79 299.37,148.38 C298.12,152.54 296.23,156.71 293.73,160.88 C293.73,160.88 249.83,224.63 249.83,224.63 C249.83,224.63 249.83,452.13 249.83,452.13 C249.83,465.46 245.02,476.92 235.41,486.5 C226.21,495.67 215.13,500.25 202.17,500.25 C202.17,500.25 99.33,500.25 99.33,500.25 C86.37,500.25 75.08,495.67 65.47,486.5 C56.27,476.92 51.67,465.46 51.67,452.13c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="17"
+ android:propertyName="pathData"
+ android:startOffset="133"
+ android:valueFrom=" M0.25 74.63 C0.25,74.63 0.25,48.38 0.25,48.38 C0.25,35.04 4.85,23.79 14.05,14.63 C23.66,5.04 35.16,0.25 48.54,0.25 C48.54,0.25 252.97,0.25 252.97,0.25 C266.34,0.25 277.63,5.04 286.83,14.63 C296.44,23.79 301.25,35.04 301.25,48.38 C301.25,48.38 301.25,74.63 301.25,74.63 C301.25,74.63 0.25,74.63 0.25,74.63c M150.75 331.03 C160.78,331.03 169.35,327.49 176.46,320.4 C183.57,313.32 187.12,304.78 187.12,294.78 C187.12,284.78 183.57,276.24 176.46,269.15 C169.35,261.65 160.78,257.9 150.75,257.9 C140.72,257.9 132.15,261.44 125.04,268.53 C117.93,275.61 114.38,284.36 114.38,294.78 C114.38,304.78 117.93,313.32 125.04,320.4 C132.15,327.49 140.72,331.03 150.75,331.03c M51.67 452.13 C51.67,452.13 51.67,224.63 51.67,224.63 C51.67,224.63 7.78,160.88 7.78,160.88 C5.27,156.71 3.38,152.54 2.13,148.38 C0.88,143.79 0.25,139 0.25,134 C0.25,134 0.25,122.75 0.25,122.75 C0.25,122.75 301.25,122.75 301.25,122.75 C301.25,122.75 301.25,134 301.25,134 C301.25,139 300.62,143.79 299.37,148.38 C298.12,152.54 296.23,156.71 293.73,160.88 C293.73,160.88 249.83,224.63 249.83,224.63 C249.83,224.63 249.83,452.13 249.83,452.13 C249.83,465.46 245.02,476.92 235.41,486.5 C226.21,495.67 215.13,500.25 202.17,500.25 C202.17,500.25 99.33,500.25 99.33,500.25 C86.37,500.25 75.08,495.67 65.47,486.5 C56.27,476.92 51.67,465.46 51.67,452.13c "
+ android:valueTo=" M0.25 74.63 C0.25,74.63 0.25,48.38 0.25,48.38 C0.25,35.04 4.85,23.79 14.05,14.63 C23.66,5.04 35.16,0.25 48.54,0.25 C48.54,0.25 252.97,0.25 252.97,0.25 C266.34,0.25 277.63,5.04 286.83,14.63 C296.44,23.79 301.25,35.04 301.25,48.38 C301.25,48.38 301.25,74.63 301.25,74.63 C301.25,74.63 0.25,74.63 0.25,74.63c M150.75 330.13 C160.78,330.13 169.35,326.59 176.46,319.51 C183.57,312.43 187.12,303.88 187.12,293.88 C187.12,283.88 183.57,275.34 176.46,268.26 C169.35,260.76 160.78,257.01 150.75,257.01 C140.72,257.01 132.15,260.55 125.04,267.63 C117.93,274.72 114.38,283.47 114.38,293.88 C114.38,303.88 117.93,312.43 125.04,319.51 C132.15,326.59 140.72,330.13 150.75,330.13c M51.67 452.13 C51.67,452.13 51.67,224.63 51.67,224.63 C51.67,224.63 7.78,160.88 7.78,160.88 C5.27,156.71 3.38,152.54 2.13,148.38 C0.88,143.79 0.25,139 0.25,134 C0.25,134 0.25,122.75 0.25,122.75 C0.25,122.75 301.25,122.75 301.25,122.75 C301.25,122.75 301.25,134 301.25,134 C301.25,139 300.62,143.79 299.37,148.38 C298.12,152.54 296.23,156.71 293.73,160.88 C293.73,160.88 249.83,224.63 249.83,224.63 C249.83,224.63 249.83,452.13 249.83,452.13 C249.83,465.46 245.02,476.92 235.41,486.5 C226.21,495.67 215.13,500.25 202.17,500.25 C202.17,500.25 99.33,500.25 99.33,500.25 C86.37,500.25 75.08,495.67 65.47,486.5 C56.27,476.92 51.67,465.46 51.67,452.13c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="17"
+ android:propertyName="pathData"
+ android:startOffset="150"
+ android:valueFrom=" M0.25 74.63 C0.25,74.63 0.25,48.38 0.25,48.38 C0.25,35.04 4.85,23.79 14.05,14.63 C23.66,5.04 35.16,0.25 48.54,0.25 C48.54,0.25 252.97,0.25 252.97,0.25 C266.34,0.25 277.63,5.04 286.83,14.63 C296.44,23.79 301.25,35.04 301.25,48.38 C301.25,48.38 301.25,74.63 301.25,74.63 C301.25,74.63 0.25,74.63 0.25,74.63c M150.75 330.13 C160.78,330.13 169.35,326.59 176.46,319.51 C183.57,312.43 187.12,303.88 187.12,293.88 C187.12,283.88 183.57,275.34 176.46,268.26 C169.35,260.76 160.78,257.01 150.75,257.01 C140.72,257.01 132.15,260.55 125.04,267.63 C117.93,274.72 114.38,283.47 114.38,293.88 C114.38,303.88 117.93,312.43 125.04,319.51 C132.15,326.59 140.72,330.13 150.75,330.13c M51.67 452.13 C51.67,452.13 51.67,224.63 51.67,224.63 C51.67,224.63 7.78,160.88 7.78,160.88 C5.27,156.71 3.38,152.54 2.13,148.38 C0.88,143.79 0.25,139 0.25,134 C0.25,134 0.25,122.75 0.25,122.75 C0.25,122.75 301.25,122.75 301.25,122.75 C301.25,122.75 301.25,134 301.25,134 C301.25,139 300.62,143.79 299.37,148.38 C298.12,152.54 296.23,156.71 293.73,160.88 C293.73,160.88 249.83,224.63 249.83,224.63 C249.83,224.63 249.83,452.13 249.83,452.13 C249.83,465.46 245.02,476.92 235.41,486.5 C226.21,495.67 215.13,500.25 202.17,500.25 C202.17,500.25 99.33,500.25 99.33,500.25 C86.37,500.25 75.08,495.67 65.47,486.5 C56.27,476.92 51.67,465.46 51.67,452.13c "
+ android:valueTo=" M0.25 74.63 C0.25,74.63 0.25,48.38 0.25,48.38 C0.25,35.04 4.85,23.79 14.05,14.63 C23.66,5.04 35.16,0.25 48.54,0.25 C48.54,0.25 252.97,0.25 252.97,0.25 C266.34,0.25 277.63,5.04 286.83,14.63 C296.44,23.79 301.25,35.04 301.25,48.38 C301.25,48.38 301.25,74.63 301.25,74.63 C301.25,74.63 0.25,74.63 0.25,74.63c M150.75 329.94 C160.78,329.94 169.35,326.4 176.46,319.31 C183.57,312.23 187.12,303.69 187.12,293.69 C187.12,283.69 183.57,275.15 176.46,268.06 C169.35,260.56 160.78,256.81 150.75,256.81 C140.72,256.81 132.15,260.36 125.04,267.44 C117.93,274.52 114.38,283.27 114.38,293.69 C114.38,303.69 117.93,312.23 125.04,319.31 C132.15,326.4 140.72,329.94 150.75,329.94c M51.67 452.13 C51.67,452.13 51.67,224.63 51.67,224.63 C51.67,224.63 7.78,160.88 7.78,160.88 C5.27,156.71 3.38,152.54 2.13,148.38 C0.88,143.79 0.25,139 0.25,134 C0.25,134 0.25,122.75 0.25,122.75 C0.25,122.75 301.25,122.75 301.25,122.75 C301.25,122.75 301.25,134 301.25,134 C301.25,139 300.62,143.79 299.37,148.38 C298.12,152.54 296.23,156.71 293.73,160.88 C293.73,160.88 249.83,224.63 249.83,224.63 C249.83,224.63 249.83,452.13 249.83,452.13 C249.83,465.46 245.02,476.92 235.41,486.5 C226.21,495.67 215.13,500.25 202.17,500.25 C202.17,500.25 99.33,500.25 99.33,500.25 C86.37,500.25 75.08,495.67 65.47,486.5 C56.27,476.92 51.67,465.46 51.67,452.13c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="17"
+ android:propertyName="pathData"
+ android:startOffset="167"
+ android:valueFrom=" M0.25 74.63 C0.25,74.63 0.25,48.38 0.25,48.38 C0.25,35.04 4.85,23.79 14.05,14.63 C23.66,5.04 35.16,0.25 48.54,0.25 C48.54,0.25 252.97,0.25 252.97,0.25 C266.34,0.25 277.63,5.04 286.83,14.63 C296.44,23.79 301.25,35.04 301.25,48.38 C301.25,48.38 301.25,74.63 301.25,74.63 C301.25,74.63 0.25,74.63 0.25,74.63c M150.75 329.94 C160.78,329.94 169.35,326.4 176.46,319.31 C183.57,312.23 187.12,303.69 187.12,293.69 C187.12,283.69 183.57,275.15 176.46,268.06 C169.35,260.56 160.78,256.81 150.75,256.81 C140.72,256.81 132.15,260.36 125.04,267.44 C117.93,274.52 114.38,283.27 114.38,293.69 C114.38,303.69 117.93,312.23 125.04,319.31 C132.15,326.4 140.72,329.94 150.75,329.94c M51.67 452.13 C51.67,452.13 51.67,224.63 51.67,224.63 C51.67,224.63 7.78,160.88 7.78,160.88 C5.27,156.71 3.38,152.54 2.13,148.38 C0.88,143.79 0.25,139 0.25,134 C0.25,134 0.25,122.75 0.25,122.75 C0.25,122.75 301.25,122.75 301.25,122.75 C301.25,122.75 301.25,134 301.25,134 C301.25,139 300.62,143.79 299.37,148.38 C298.12,152.54 296.23,156.71 293.73,160.88 C293.73,160.88 249.83,224.63 249.83,224.63 C249.83,224.63 249.83,452.13 249.83,452.13 C249.83,465.46 245.02,476.92 235.41,486.5 C226.21,495.67 215.13,500.25 202.17,500.25 C202.17,500.25 99.33,500.25 99.33,500.25 C86.37,500.25 75.08,495.67 65.47,486.5 C56.27,476.92 51.67,465.46 51.67,452.13c "
+ android:valueTo=" M0.25 74.63 C0.25,74.63 0.25,48.38 0.25,48.38 C0.25,35.04 4.85,23.79 14.05,14.63 C23.66,5.04 35.16,0.25 48.54,0.25 C48.54,0.25 252.97,0.25 252.97,0.25 C266.34,0.25 277.63,5.04 286.83,14.63 C296.44,23.79 301.25,35.04 301.25,48.38 C301.25,48.38 301.25,74.63 301.25,74.63 C301.25,74.63 0.25,74.63 0.25,74.63c M150.75 330.46 C160.78,330.46 169.35,326.92 176.46,319.84 C183.57,312.76 187.12,304.21 187.12,294.21 C187.12,284.21 183.57,275.67 176.46,268.59 C169.35,261.09 160.78,257.34 150.75,257.34 C140.72,257.34 132.15,260.88 125.04,267.96 C117.93,275.05 114.38,283.8 114.38,294.21 C114.38,304.21 117.93,312.76 125.04,319.84 C132.15,326.92 140.72,330.46 150.75,330.46c M51.67 452.13 C51.67,452.13 51.67,224.63 51.67,224.63 C51.67,224.63 7.78,160.88 7.78,160.88 C5.27,156.71 3.38,152.54 2.13,148.38 C0.88,143.79 0.25,139 0.25,134 C0.25,134 0.25,122.75 0.25,122.75 C0.25,122.75 301.25,122.75 301.25,122.75 C301.25,122.75 301.25,134 301.25,134 C301.25,139 300.62,143.79 299.37,148.38 C298.12,152.54 296.23,156.71 293.73,160.88 C293.73,160.88 249.83,224.63 249.83,224.63 C249.83,224.63 249.83,452.13 249.83,452.13 C249.83,465.46 245.02,476.92 235.41,486.5 C226.21,495.67 215.13,500.25 202.17,500.25 C202.17,500.25 99.33,500.25 99.33,500.25 C86.37,500.25 75.08,495.67 65.47,486.5 C56.27,476.92 51.67,465.46 51.67,452.13c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="17"
+ android:propertyName="pathData"
+ android:startOffset="183"
+ android:valueFrom=" M0.25 74.63 C0.25,74.63 0.25,48.38 0.25,48.38 C0.25,35.04 4.85,23.79 14.05,14.63 C23.66,5.04 35.16,0.25 48.54,0.25 C48.54,0.25 252.97,0.25 252.97,0.25 C266.34,0.25 277.63,5.04 286.83,14.63 C296.44,23.79 301.25,35.04 301.25,48.38 C301.25,48.38 301.25,74.63 301.25,74.63 C301.25,74.63 0.25,74.63 0.25,74.63c M150.75 330.46 C160.78,330.46 169.35,326.92 176.46,319.84 C183.57,312.76 187.12,304.21 187.12,294.21 C187.12,284.21 183.57,275.67 176.46,268.59 C169.35,261.09 160.78,257.34 150.75,257.34 C140.72,257.34 132.15,260.88 125.04,267.96 C117.93,275.05 114.38,283.8 114.38,294.21 C114.38,304.21 117.93,312.76 125.04,319.84 C132.15,326.92 140.72,330.46 150.75,330.46c M51.67 452.13 C51.67,452.13 51.67,224.63 51.67,224.63 C51.67,224.63 7.78,160.88 7.78,160.88 C5.27,156.71 3.38,152.54 2.13,148.38 C0.88,143.79 0.25,139 0.25,134 C0.25,134 0.25,122.75 0.25,122.75 C0.25,122.75 301.25,122.75 301.25,122.75 C301.25,122.75 301.25,134 301.25,134 C301.25,139 300.62,143.79 299.37,148.38 C298.12,152.54 296.23,156.71 293.73,160.88 C293.73,160.88 249.83,224.63 249.83,224.63 C249.83,224.63 249.83,452.13 249.83,452.13 C249.83,465.46 245.02,476.92 235.41,486.5 C226.21,495.67 215.13,500.25 202.17,500.25 C202.17,500.25 99.33,500.25 99.33,500.25 C86.37,500.25 75.08,495.67 65.47,486.5 C56.27,476.92 51.67,465.46 51.67,452.13c "
+ android:valueTo=" M0.25 74.63 C0.25,74.63 0.25,48.38 0.25,48.38 C0.25,35.04 4.85,23.79 14.05,14.63 C23.66,5.04 35.16,0.25 48.54,0.25 C48.54,0.25 252.97,0.25 252.97,0.25 C266.34,0.25 277.63,5.04 286.83,14.63 C296.44,23.79 301.25,35.04 301.25,48.38 C301.25,48.38 301.25,74.63 301.25,74.63 C301.25,74.63 0.25,74.63 0.25,74.63c M150.75 330.99 C160.78,330.99 169.35,327.45 176.46,320.36 C183.57,313.28 187.12,304.74 187.12,294.74 C187.12,284.74 183.57,276.2 176.46,269.11 C169.35,261.61 160.78,257.86 150.75,257.86 C140.72,257.86 132.15,261.4 125.04,268.49 C117.93,275.57 114.38,284.32 114.38,294.74 C114.38,304.74 117.93,313.28 125.04,320.36 C132.15,327.45 140.72,330.99 150.75,330.99c M51.67 452.13 C51.67,452.13 51.67,224.63 51.67,224.63 C51.67,224.63 7.78,160.88 7.78,160.88 C5.27,156.71 3.38,152.54 2.13,148.38 C0.88,143.79 0.25,139 0.25,134 C0.25,134 0.25,122.75 0.25,122.75 C0.25,122.75 301.25,122.75 301.25,122.75 C301.25,122.75 301.25,134 301.25,134 C301.25,139 300.62,143.79 299.37,148.38 C298.12,152.54 296.23,156.71 293.73,160.88 C293.73,160.88 249.83,224.63 249.83,224.63 C249.83,224.63 249.83,452.13 249.83,452.13 C249.83,465.46 245.02,476.92 235.41,486.5 C226.21,495.67 215.13,500.25 202.17,500.25 C202.17,500.25 99.33,500.25 99.33,500.25 C86.37,500.25 75.08,495.67 65.47,486.5 C56.27,476.92 51.67,465.46 51.67,452.13c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="17"
+ android:propertyName="pathData"
+ android:startOffset="200"
+ android:valueFrom=" M0.25 74.63 C0.25,74.63 0.25,48.38 0.25,48.38 C0.25,35.04 4.85,23.79 14.05,14.63 C23.66,5.04 35.16,0.25 48.54,0.25 C48.54,0.25 252.97,0.25 252.97,0.25 C266.34,0.25 277.63,5.04 286.83,14.63 C296.44,23.79 301.25,35.04 301.25,48.38 C301.25,48.38 301.25,74.63 301.25,74.63 C301.25,74.63 0.25,74.63 0.25,74.63c M150.75 330.99 C160.78,330.99 169.35,327.45 176.46,320.36 C183.57,313.28 187.12,304.74 187.12,294.74 C187.12,284.74 183.57,276.2 176.46,269.11 C169.35,261.61 160.78,257.86 150.75,257.86 C140.72,257.86 132.15,261.4 125.04,268.49 C117.93,275.57 114.38,284.32 114.38,294.74 C114.38,304.74 117.93,313.28 125.04,320.36 C132.15,327.45 140.72,330.99 150.75,330.99c M51.67 452.13 C51.67,452.13 51.67,224.63 51.67,224.63 C51.67,224.63 7.78,160.88 7.78,160.88 C5.27,156.71 3.38,152.54 2.13,148.38 C0.88,143.79 0.25,139 0.25,134 C0.25,134 0.25,122.75 0.25,122.75 C0.25,122.75 301.25,122.75 301.25,122.75 C301.25,122.75 301.25,134 301.25,134 C301.25,139 300.62,143.79 299.37,148.38 C298.12,152.54 296.23,156.71 293.73,160.88 C293.73,160.88 249.83,224.63 249.83,224.63 C249.83,224.63 249.83,452.13 249.83,452.13 C249.83,465.46 245.02,476.92 235.41,486.5 C226.21,495.67 215.13,500.25 202.17,500.25 C202.17,500.25 99.33,500.25 99.33,500.25 C86.37,500.25 75.08,495.67 65.47,486.5 C56.27,476.92 51.67,465.46 51.67,452.13c "
+ android:valueTo=" M0.25 74.63 C0.25,74.63 0.25,48.38 0.25,48.38 C0.25,35.04 4.85,23.79 14.05,14.63 C23.66,5.04 35.16,0.25 48.54,0.25 C48.54,0.25 252.97,0.25 252.97,0.25 C266.34,0.25 277.63,5.04 286.83,14.63 C296.44,23.79 301.25,35.04 301.25,48.38 C301.25,48.38 301.25,74.63 301.25,74.63 C301.25,74.63 0.25,74.63 0.25,74.63c M150.75 331.89 C160.78,331.89 169.35,328.35 176.46,321.26 C183.57,314.18 187.12,305.64 187.12,295.64 C187.12,285.64 183.57,277.1 176.46,270.01 C169.35,262.51 160.78,258.76 150.75,258.76 C140.72,258.76 132.15,262.3 125.04,269.39 C117.93,276.47 114.38,285.22 114.38,295.64 C114.38,305.64 117.93,314.18 125.04,321.26 C132.15,328.35 140.72,331.89 150.75,331.89c M51.67 452.13 C51.67,452.13 51.67,224.63 51.67,224.63 C51.67,224.63 7.78,160.88 7.78,160.88 C5.27,156.71 3.38,152.54 2.13,148.38 C0.88,143.79 0.25,139 0.25,134 C0.25,134 0.25,122.75 0.25,122.75 C0.25,122.75 301.25,122.75 301.25,122.75 C301.25,122.75 301.25,134 301.25,134 C301.25,139 300.62,143.79 299.37,148.38 C298.12,152.54 296.23,156.71 293.73,160.88 C293.73,160.88 249.83,224.63 249.83,224.63 C249.83,224.63 249.83,452.13 249.83,452.13 C249.83,465.46 245.02,476.92 235.41,486.5 C226.21,495.67 215.13,500.25 202.17,500.25 C202.17,500.25 99.33,500.25 99.33,500.25 C86.37,500.25 75.08,495.67 65.47,486.5 C56.27,476.92 51.67,465.46 51.67,452.13c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="67"
+ android:propertyName="pathData"
+ android:startOffset="217"
+ android:valueFrom=" M0.25 74.63 C0.25,74.63 0.25,48.38 0.25,48.38 C0.25,35.04 4.85,23.79 14.05,14.63 C23.66,5.04 35.16,0.25 48.54,0.25 C48.54,0.25 252.97,0.25 252.97,0.25 C266.34,0.25 277.63,5.04 286.83,14.63 C296.44,23.79 301.25,35.04 301.25,48.38 C301.25,48.38 301.25,74.63 301.25,74.63 C301.25,74.63 0.25,74.63 0.25,74.63c M150.75 331.89 C160.78,331.89 169.35,328.35 176.46,321.26 C183.57,314.18 187.12,305.64 187.12,295.64 C187.12,285.64 183.57,277.1 176.46,270.01 C169.35,262.51 160.78,258.76 150.75,258.76 C140.72,258.76 132.15,262.3 125.04,269.39 C117.93,276.47 114.38,285.22 114.38,295.64 C114.38,305.64 117.93,314.18 125.04,321.26 C132.15,328.35 140.72,331.89 150.75,331.89c M51.67 452.13 C51.67,452.13 51.67,224.63 51.67,224.63 C51.67,224.63 7.78,160.88 7.78,160.88 C5.27,156.71 3.38,152.54 2.13,148.38 C0.88,143.79 0.25,139 0.25,134 C0.25,134 0.25,122.75 0.25,122.75 C0.25,122.75 301.25,122.75 301.25,122.75 C301.25,122.75 301.25,134 301.25,134 C301.25,139 300.62,143.79 299.37,148.38 C298.12,152.54 296.23,156.71 293.73,160.88 C293.73,160.88 249.83,224.63 249.83,224.63 C249.83,224.63 249.83,452.13 249.83,452.13 C249.83,465.46 245.02,476.92 235.41,486.5 C226.21,495.67 215.13,500.25 202.17,500.25 C202.17,500.25 99.33,500.25 99.33,500.25 C86.37,500.25 75.08,495.67 65.47,486.5 C56.27,476.92 51.67,465.46 51.67,452.13c "
+ android:valueTo=" M0.25 74.63 C0.25,74.63 0.25,48.38 0.25,48.38 C0.25,35.04 4.85,23.79 14.05,14.63 C23.66,5.04 35.16,0.25 48.54,0.25 C48.54,0.25 252.97,0.25 252.97,0.25 C266.34,0.25 277.63,5.04 286.83,14.63 C296.44,23.79 301.25,35.04 301.25,48.38 C301.25,48.38 301.25,74.63 301.25,74.63 C301.25,74.63 0.25,74.63 0.25,74.63c M150.75 334.92 C160.78,334.92 169.35,331.38 176.46,324.3 C183.57,317.21 187.12,308.67 187.12,298.67 C187.12,288.67 183.57,280.13 176.46,273.05 C169.35,265.55 160.78,261.8 150.75,261.8 C140.72,261.8 132.15,265.34 125.04,272.42 C117.93,279.5 114.38,288.25 114.38,298.67 C114.38,308.67 117.93,317.21 125.04,324.3 C132.15,331.38 140.72,334.92 150.75,334.92c M51.67 452.13 C51.67,452.13 51.67,224.63 51.67,224.63 C51.67,224.63 7.78,160.88 7.78,160.88 C5.27,156.71 3.38,152.54 2.13,148.38 C0.88,143.79 0.25,139 0.25,134 C0.25,134 0.25,122.75 0.25,122.75 C0.25,122.75 301.25,122.75 301.25,122.75 C301.25,122.75 301.25,134 301.25,134 C301.25,139 300.62,143.79 299.37,148.38 C298.12,152.54 296.23,156.71 293.73,160.88 C293.73,160.88 249.83,224.63 249.83,224.63 C249.83,224.63 249.83,452.13 249.83,452.13 C249.83,465.46 245.02,476.92 235.41,486.5 C226.21,495.67 215.13,500.25 202.17,500.25 C202.17,500.25 99.33,500.25 99.33,500.25 C86.37,500.25 75.08,495.67 65.47,486.5 C56.27,476.92 51.67,465.46 51.67,452.13c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="17"
+ android:propertyName="pathData"
+ android:startOffset="283"
+ android:valueFrom=" M0.25 74.63 C0.25,74.63 0.25,48.38 0.25,48.38 C0.25,35.04 4.85,23.79 14.05,14.63 C23.66,5.04 35.16,0.25 48.54,0.25 C48.54,0.25 252.97,0.25 252.97,0.25 C266.34,0.25 277.63,5.04 286.83,14.63 C296.44,23.79 301.25,35.04 301.25,48.38 C301.25,48.38 301.25,74.63 301.25,74.63 C301.25,74.63 0.25,74.63 0.25,74.63c M150.75 334.92 C160.78,334.92 169.35,331.38 176.46,324.3 C183.57,317.21 187.12,308.67 187.12,298.67 C187.12,288.67 183.57,280.13 176.46,273.05 C169.35,265.55 160.78,261.8 150.75,261.8 C140.72,261.8 132.15,265.34 125.04,272.42 C117.93,279.5 114.38,288.25 114.38,298.67 C114.38,308.67 117.93,317.21 125.04,324.3 C132.15,331.38 140.72,334.92 150.75,334.92c M51.67 452.13 C51.67,452.13 51.67,224.63 51.67,224.63 C51.67,224.63 7.78,160.88 7.78,160.88 C5.27,156.71 3.38,152.54 2.13,148.38 C0.88,143.79 0.25,139 0.25,134 C0.25,134 0.25,122.75 0.25,122.75 C0.25,122.75 301.25,122.75 301.25,122.75 C301.25,122.75 301.25,134 301.25,134 C301.25,139 300.62,143.79 299.37,148.38 C298.12,152.54 296.23,156.71 293.73,160.88 C293.73,160.88 249.83,224.63 249.83,224.63 C249.83,224.63 249.83,452.13 249.83,452.13 C249.83,465.46 245.02,476.92 235.41,486.5 C226.21,495.67 215.13,500.25 202.17,500.25 C202.17,500.25 99.33,500.25 99.33,500.25 C86.37,500.25 75.08,495.67 65.47,486.5 C56.27,476.92 51.67,465.46 51.67,452.13c "
+ android:valueTo=" M0.25 74.63 C0.25,74.63 0.25,48.38 0.25,48.38 C0.25,35.04 4.85,23.79 14.05,14.63 C23.66,5.04 35.16,0.25 48.54,0.25 C48.54,0.25 252.97,0.25 252.97,0.25 C266.34,0.25 277.63,5.04 286.83,14.63 C296.44,23.79 301.25,35.04 301.25,48.38 C301.25,48.38 301.25,74.63 301.25,74.63 C301.25,74.63 0.25,74.63 0.25,74.63c M150.75 335.59 C160.78,335.59 169.35,332.05 176.46,324.96 C183.57,317.88 187.12,309.34 187.12,299.34 C187.12,289.34 183.57,280.8 176.46,273.71 C169.35,266.21 160.78,262.46 150.75,262.46 C140.72,262.46 132.15,266.01 125.04,273.09 C117.93,280.17 114.38,288.92 114.38,299.34 C114.38,309.34 117.93,317.88 125.04,324.96 C132.15,332.05 140.72,335.59 150.75,335.59c M51.67 452.13 C51.67,452.13 51.67,224.63 51.67,224.63 C51.67,224.63 7.78,160.88 7.78,160.88 C5.27,156.71 3.38,152.54 2.13,148.38 C0.88,143.79 0.25,139 0.25,134 C0.25,134 0.25,122.75 0.25,122.75 C0.25,122.75 301.25,122.75 301.25,122.75 C301.25,122.75 301.25,134 301.25,134 C301.25,139 300.62,143.79 299.37,148.38 C298.12,152.54 296.23,156.71 293.73,160.88 C293.73,160.88 249.83,224.63 249.83,224.63 C249.83,224.63 249.83,452.13 249.83,452.13 C249.83,465.46 245.02,476.92 235.41,486.5 C226.21,495.67 215.13,500.25 202.17,500.25 C202.17,500.25 99.33,500.25 99.33,500.25 C86.37,500.25 75.08,495.67 65.47,486.5 C56.27,476.92 51.67,465.46 51.67,452.13c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="17"
+ android:propertyName="pathData"
+ android:startOffset="300"
+ android:valueFrom=" M0.25 74.63 C0.25,74.63 0.25,48.38 0.25,48.38 C0.25,35.04 4.85,23.79 14.05,14.63 C23.66,5.04 35.16,0.25 48.54,0.25 C48.54,0.25 252.97,0.25 252.97,0.25 C266.34,0.25 277.63,5.04 286.83,14.63 C296.44,23.79 301.25,35.04 301.25,48.38 C301.25,48.38 301.25,74.63 301.25,74.63 C301.25,74.63 0.25,74.63 0.25,74.63c M150.75 335.59 C160.78,335.59 169.35,332.05 176.46,324.96 C183.57,317.88 187.12,309.34 187.12,299.34 C187.12,289.34 183.57,280.8 176.46,273.71 C169.35,266.21 160.78,262.46 150.75,262.46 C140.72,262.46 132.15,266.01 125.04,273.09 C117.93,280.17 114.38,288.92 114.38,299.34 C114.38,309.34 117.93,317.88 125.04,324.96 C132.15,332.05 140.72,335.59 150.75,335.59c M51.67 452.13 C51.67,452.13 51.67,224.63 51.67,224.63 C51.67,224.63 7.78,160.88 7.78,160.88 C5.27,156.71 3.38,152.54 2.13,148.38 C0.88,143.79 0.25,139 0.25,134 C0.25,134 0.25,122.75 0.25,122.75 C0.25,122.75 301.25,122.75 301.25,122.75 C301.25,122.75 301.25,134 301.25,134 C301.25,139 300.62,143.79 299.37,148.38 C298.12,152.54 296.23,156.71 293.73,160.88 C293.73,160.88 249.83,224.63 249.83,224.63 C249.83,224.63 249.83,452.13 249.83,452.13 C249.83,465.46 245.02,476.92 235.41,486.5 C226.21,495.67 215.13,500.25 202.17,500.25 C202.17,500.25 99.33,500.25 99.33,500.25 C86.37,500.25 75.08,495.67 65.47,486.5 C56.27,476.92 51.67,465.46 51.67,452.13c "
+ android:valueTo=" M0.25 74.63 C0.25,74.63 0.25,48.38 0.25,48.38 C0.25,35.04 4.85,23.79 14.05,14.63 C23.66,5.04 35.16,0.25 48.54,0.25 C48.54,0.25 252.97,0.25 252.97,0.25 C266.34,0.25 277.63,5.04 286.83,14.63 C296.44,23.79 301.25,35.04 301.25,48.38 C301.25,48.38 301.25,74.63 301.25,74.63 C301.25,74.63 0.25,74.63 0.25,74.63c M150.75 336.16 C160.78,336.16 169.35,332.62 176.46,325.54 C183.57,318.46 187.12,309.91 187.12,299.91 C187.12,289.91 183.57,281.37 176.46,274.29 C169.35,266.79 160.78,263.04 150.75,263.04 C140.72,263.04 132.15,266.58 125.04,273.66 C117.93,280.75 114.38,289.5 114.38,299.91 C114.38,309.91 117.93,318.46 125.04,325.54 C132.15,332.62 140.72,336.16 150.75,336.16c M51.67 452.13 C51.67,452.13 51.67,224.63 51.67,224.63 C51.67,224.63 7.78,160.88 7.78,160.88 C5.27,156.71 3.38,152.54 2.13,148.38 C0.88,143.79 0.25,139 0.25,134 C0.25,134 0.25,122.75 0.25,122.75 C0.25,122.75 301.25,122.75 301.25,122.75 C301.25,122.75 301.25,134 301.25,134 C301.25,139 300.62,143.79 299.37,148.38 C298.12,152.54 296.23,156.71 293.73,160.88 C293.73,160.88 249.83,224.63 249.83,224.63 C249.83,224.63 249.83,452.13 249.83,452.13 C249.83,465.46 245.02,476.92 235.41,486.5 C226.21,495.67 215.13,500.25 202.17,500.25 C202.17,500.25 99.33,500.25 99.33,500.25 C86.37,500.25 75.08,495.67 65.47,486.5 C56.27,476.92 51.67,465.46 51.67,452.13c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="17"
+ android:propertyName="pathData"
+ android:startOffset="317"
+ android:valueFrom=" M0.25 74.63 C0.25,74.63 0.25,48.38 0.25,48.38 C0.25,35.04 4.85,23.79 14.05,14.63 C23.66,5.04 35.16,0.25 48.54,0.25 C48.54,0.25 252.97,0.25 252.97,0.25 C266.34,0.25 277.63,5.04 286.83,14.63 C296.44,23.79 301.25,35.04 301.25,48.38 C301.25,48.38 301.25,74.63 301.25,74.63 C301.25,74.63 0.25,74.63 0.25,74.63c M150.75 336.16 C160.78,336.16 169.35,332.62 176.46,325.54 C183.57,318.46 187.12,309.91 187.12,299.91 C187.12,289.91 183.57,281.37 176.46,274.29 C169.35,266.79 160.78,263.04 150.75,263.04 C140.72,263.04 132.15,266.58 125.04,273.66 C117.93,280.75 114.38,289.5 114.38,299.91 C114.38,309.91 117.93,318.46 125.04,325.54 C132.15,332.62 140.72,336.16 150.75,336.16c M51.67 452.13 C51.67,452.13 51.67,224.63 51.67,224.63 C51.67,224.63 7.78,160.88 7.78,160.88 C5.27,156.71 3.38,152.54 2.13,148.38 C0.88,143.79 0.25,139 0.25,134 C0.25,134 0.25,122.75 0.25,122.75 C0.25,122.75 301.25,122.75 301.25,122.75 C301.25,122.75 301.25,134 301.25,134 C301.25,139 300.62,143.79 299.37,148.38 C298.12,152.54 296.23,156.71 293.73,160.88 C293.73,160.88 249.83,224.63 249.83,224.63 C249.83,224.63 249.83,452.13 249.83,452.13 C249.83,465.46 245.02,476.92 235.41,486.5 C226.21,495.67 215.13,500.25 202.17,500.25 C202.17,500.25 99.33,500.25 99.33,500.25 C86.37,500.25 75.08,495.67 65.47,486.5 C56.27,476.92 51.67,465.46 51.67,452.13c "
+ android:valueTo=" M0.25 74.63 C0.25,74.63 0.25,48.38 0.25,48.38 C0.25,35.04 4.85,23.79 14.05,14.63 C23.66,5.04 35.16,0.25 48.54,0.25 C48.54,0.25 252.97,0.25 252.97,0.25 C266.34,0.25 277.63,5.04 286.83,14.63 C296.44,23.79 301.25,35.04 301.25,48.38 C301.25,48.38 301.25,74.63 301.25,74.63 C301.25,74.63 0.25,74.63 0.25,74.63c M150.75 336.74 C160.78,336.74 169.35,333.2 176.46,326.11 C183.57,319.03 187.12,310.49 187.12,300.49 C187.12,290.49 183.57,281.95 176.46,274.86 C169.35,267.36 160.78,263.61 150.75,263.61 C140.72,263.61 132.15,267.15 125.04,274.24 C117.93,281.32 114.38,290.07 114.38,300.49 C114.38,310.49 117.93,319.03 125.04,326.11 C132.15,333.2 140.72,336.74 150.75,336.74c M51.67 452.13 C51.67,452.13 51.67,224.63 51.67,224.63 C51.67,224.63 7.78,160.88 7.78,160.88 C5.27,156.71 3.38,152.54 2.13,148.38 C0.88,143.79 0.25,139 0.25,134 C0.25,134 0.25,122.75 0.25,122.75 C0.25,122.75 301.25,122.75 301.25,122.75 C301.25,122.75 301.25,134 301.25,134 C301.25,139 300.62,143.79 299.37,148.38 C298.12,152.54 296.23,156.71 293.73,160.88 C293.73,160.88 249.83,224.63 249.83,224.63 C249.83,224.63 249.83,452.13 249.83,452.13 C249.83,465.46 245.02,476.92 235.41,486.5 C226.21,495.67 215.13,500.25 202.17,500.25 C202.17,500.25 99.33,500.25 99.33,500.25 C86.37,500.25 75.08,495.67 65.47,486.5 C56.27,476.92 51.67,465.46 51.67,452.13c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="17"
+ android:propertyName="pathData"
+ android:startOffset="333"
+ android:valueFrom=" M0.25 74.63 C0.25,74.63 0.25,48.38 0.25,48.38 C0.25,35.04 4.85,23.79 14.05,14.63 C23.66,5.04 35.16,0.25 48.54,0.25 C48.54,0.25 252.97,0.25 252.97,0.25 C266.34,0.25 277.63,5.04 286.83,14.63 C296.44,23.79 301.25,35.04 301.25,48.38 C301.25,48.38 301.25,74.63 301.25,74.63 C301.25,74.63 0.25,74.63 0.25,74.63c M150.75 336.74 C160.78,336.74 169.35,333.2 176.46,326.11 C183.57,319.03 187.12,310.49 187.12,300.49 C187.12,290.49 183.57,281.95 176.46,274.86 C169.35,267.36 160.78,263.61 150.75,263.61 C140.72,263.61 132.15,267.15 125.04,274.24 C117.93,281.32 114.38,290.07 114.38,300.49 C114.38,310.49 117.93,319.03 125.04,326.11 C132.15,333.2 140.72,336.74 150.75,336.74c M51.67 452.13 C51.67,452.13 51.67,224.63 51.67,224.63 C51.67,224.63 7.78,160.88 7.78,160.88 C5.27,156.71 3.38,152.54 2.13,148.38 C0.88,143.79 0.25,139 0.25,134 C0.25,134 0.25,122.75 0.25,122.75 C0.25,122.75 301.25,122.75 301.25,122.75 C301.25,122.75 301.25,134 301.25,134 C301.25,139 300.62,143.79 299.37,148.38 C298.12,152.54 296.23,156.71 293.73,160.88 C293.73,160.88 249.83,224.63 249.83,224.63 C249.83,224.63 249.83,452.13 249.83,452.13 C249.83,465.46 245.02,476.92 235.41,486.5 C226.21,495.67 215.13,500.25 202.17,500.25 C202.17,500.25 99.33,500.25 99.33,500.25 C86.37,500.25 75.08,495.67 65.47,486.5 C56.27,476.92 51.67,465.46 51.67,452.13c "
+ android:valueTo=" M0.25 74.63 C0.25,74.63 0.25,48.38 0.25,48.38 C0.25,35.04 4.85,23.79 14.05,14.63 C23.66,5.04 35.16,0.25 48.54,0.25 C48.54,0.25 252.97,0.25 252.97,0.25 C266.34,0.25 277.63,5.04 286.83,14.63 C296.44,23.79 301.25,35.04 301.25,48.38 C301.25,48.38 301.25,74.63 301.25,74.63 C301.25,74.63 0.25,74.63 0.25,74.63c M150.75 337.18 C160.78,337.18 169.35,333.64 176.46,326.55 C183.57,319.47 187.12,310.93 187.12,300.93 C187.12,290.93 183.57,282.39 176.46,275.3 C169.35,267.8 160.78,264.05 150.75,264.05 C140.72,264.05 132.15,267.59 125.04,274.68 C117.93,281.76 114.38,290.51 114.38,300.93 C114.38,310.93 117.93,319.47 125.04,326.55 C132.15,333.64 140.72,337.18 150.75,337.18c M51.67 452.13 C51.67,452.13 51.67,224.63 51.67,224.63 C51.67,224.63 7.78,160.88 7.78,160.88 C5.27,156.71 3.38,152.54 2.13,148.38 C0.88,143.79 0.25,139 0.25,134 C0.25,134 0.25,122.75 0.25,122.75 C0.25,122.75 301.25,122.75 301.25,122.75 C301.25,122.75 301.25,134 301.25,134 C301.25,139 300.62,143.79 299.37,148.38 C298.12,152.54 296.23,156.71 293.73,160.88 C293.73,160.88 249.83,224.63 249.83,224.63 C249.83,224.63 249.83,452.13 249.83,452.13 C249.83,465.46 245.02,476.92 235.41,486.5 C226.21,495.67 215.13,500.25 202.17,500.25 C202.17,500.25 99.33,500.25 99.33,500.25 C86.37,500.25 75.08,495.67 65.47,486.5 C56.27,476.92 51.67,465.46 51.67,452.13c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="17"
+ android:propertyName="pathData"
+ android:startOffset="350"
+ android:valueFrom=" M0.25 74.63 C0.25,74.63 0.25,48.38 0.25,48.38 C0.25,35.04 4.85,23.79 14.05,14.63 C23.66,5.04 35.16,0.25 48.54,0.25 C48.54,0.25 252.97,0.25 252.97,0.25 C266.34,0.25 277.63,5.04 286.83,14.63 C296.44,23.79 301.25,35.04 301.25,48.38 C301.25,48.38 301.25,74.63 301.25,74.63 C301.25,74.63 0.25,74.63 0.25,74.63c M150.75 337.18 C160.78,337.18 169.35,333.64 176.46,326.55 C183.57,319.47 187.12,310.93 187.12,300.93 C187.12,290.93 183.57,282.39 176.46,275.3 C169.35,267.8 160.78,264.05 150.75,264.05 C140.72,264.05 132.15,267.59 125.04,274.68 C117.93,281.76 114.38,290.51 114.38,300.93 C114.38,310.93 117.93,319.47 125.04,326.55 C132.15,333.64 140.72,337.18 150.75,337.18c M51.67 452.13 C51.67,452.13 51.67,224.63 51.67,224.63 C51.67,224.63 7.78,160.88 7.78,160.88 C5.27,156.71 3.38,152.54 2.13,148.38 C0.88,143.79 0.25,139 0.25,134 C0.25,134 0.25,122.75 0.25,122.75 C0.25,122.75 301.25,122.75 301.25,122.75 C301.25,122.75 301.25,134 301.25,134 C301.25,139 300.62,143.79 299.37,148.38 C298.12,152.54 296.23,156.71 293.73,160.88 C293.73,160.88 249.83,224.63 249.83,224.63 C249.83,224.63 249.83,452.13 249.83,452.13 C249.83,465.46 245.02,476.92 235.41,486.5 C226.21,495.67 215.13,500.25 202.17,500.25 C202.17,500.25 99.33,500.25 99.33,500.25 C86.37,500.25 75.08,495.67 65.47,486.5 C56.27,476.92 51.67,465.46 51.67,452.13c "
+ android:valueTo=" M0.25 74.63 C0.25,74.63 0.25,48.38 0.25,48.38 C0.25,35.04 4.85,23.79 14.05,14.63 C23.66,5.04 35.16,0.25 48.54,0.25 C48.54,0.25 252.97,0.25 252.97,0.25 C266.34,0.25 277.63,5.04 286.83,14.63 C296.44,23.79 301.25,35.04 301.25,48.38 C301.25,48.38 301.25,74.63 301.25,74.63 C301.25,74.63 0.25,74.63 0.25,74.63c M150.75 337.79 C160.78,337.79 169.35,334.25 176.46,327.17 C183.57,320.09 187.12,311.54 187.12,301.54 C187.12,291.54 183.57,283 176.46,275.92 C169.35,268.42 160.78,264.67 150.75,264.67 C140.72,264.67 132.15,268.21 125.04,275.29 C117.93,282.38 114.38,291.13 114.38,301.54 C114.38,311.54 117.93,320.09 125.04,327.17 C132.15,334.25 140.72,337.79 150.75,337.79c M51.67 452.13 C51.67,452.13 51.67,224.63 51.67,224.63 C51.67,224.63 7.78,160.88 7.78,160.88 C5.27,156.71 3.38,152.54 2.13,148.38 C0.88,143.79 0.25,139 0.25,134 C0.25,134 0.25,122.75 0.25,122.75 C0.25,122.75 301.25,122.75 301.25,122.75 C301.25,122.75 301.25,134 301.25,134 C301.25,139 300.62,143.79 299.37,148.38 C298.12,152.54 296.23,156.71 293.73,160.88 C293.73,160.88 249.83,224.63 249.83,224.63 C249.83,224.63 249.83,452.13 249.83,452.13 C249.83,465.46 245.02,476.92 235.41,486.5 C226.21,495.67 215.13,500.25 202.17,500.25 C202.17,500.25 99.33,500.25 99.33,500.25 C86.37,500.25 75.08,495.67 65.47,486.5 C56.27,476.92 51.67,465.46 51.67,452.13c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="17"
+ android:propertyName="pathData"
+ android:startOffset="367"
+ android:valueFrom=" M0.25 74.63 C0.25,74.63 0.25,48.38 0.25,48.38 C0.25,35.04 4.85,23.79 14.05,14.63 C23.66,5.04 35.16,0.25 48.54,0.25 C48.54,0.25 252.97,0.25 252.97,0.25 C266.34,0.25 277.63,5.04 286.83,14.63 C296.44,23.79 301.25,35.04 301.25,48.38 C301.25,48.38 301.25,74.63 301.25,74.63 C301.25,74.63 0.25,74.63 0.25,74.63c M150.75 337.79 C160.78,337.79 169.35,334.25 176.46,327.17 C183.57,320.09 187.12,311.54 187.12,301.54 C187.12,291.54 183.57,283 176.46,275.92 C169.35,268.42 160.78,264.67 150.75,264.67 C140.72,264.67 132.15,268.21 125.04,275.29 C117.93,282.38 114.38,291.13 114.38,301.54 C114.38,311.54 117.93,320.09 125.04,327.17 C132.15,334.25 140.72,337.79 150.75,337.79c M51.67 452.13 C51.67,452.13 51.67,224.63 51.67,224.63 C51.67,224.63 7.78,160.88 7.78,160.88 C5.27,156.71 3.38,152.54 2.13,148.38 C0.88,143.79 0.25,139 0.25,134 C0.25,134 0.25,122.75 0.25,122.75 C0.25,122.75 301.25,122.75 301.25,122.75 C301.25,122.75 301.25,134 301.25,134 C301.25,139 300.62,143.79 299.37,148.38 C298.12,152.54 296.23,156.71 293.73,160.88 C293.73,160.88 249.83,224.63 249.83,224.63 C249.83,224.63 249.83,452.13 249.83,452.13 C249.83,465.46 245.02,476.92 235.41,486.5 C226.21,495.67 215.13,500.25 202.17,500.25 C202.17,500.25 99.33,500.25 99.33,500.25 C86.37,500.25 75.08,495.67 65.47,486.5 C56.27,476.92 51.67,465.46 51.67,452.13c "
+ android:valueTo=" M0.25 74.63 C0.25,74.63 0.25,48.38 0.25,48.38 C0.25,35.04 4.85,23.79 14.05,14.63 C23.66,5.04 35.16,0.25 48.54,0.25 C48.54,0.25 252.97,0.25 252.97,0.25 C266.34,0.25 277.63,5.04 286.83,14.63 C296.44,23.79 301.25,35.04 301.25,48.38 C301.25,48.38 301.25,74.63 301.25,74.63 C301.25,74.63 0.25,74.63 0.25,74.63c M150.75 338.13 C160.78,338.13 169.35,334.59 176.46,327.5 C183.57,320.42 187.12,311.88 187.12,301.88 C187.12,291.88 183.57,283.34 176.46,276.25 C169.35,268.75 160.78,265 150.75,265 C140.72,265 132.15,268.55 125.04,275.63 C117.93,282.71 114.38,291.46 114.38,301.88 C114.38,311.88 117.93,320.42 125.04,327.5 C132.15,334.59 140.72,338.13 150.75,338.13c M51.67 452.13 C51.67,452.13 51.67,224.63 51.67,224.63 C51.67,224.63 7.78,160.88 7.78,160.88 C5.27,156.71 3.38,152.54 2.13,148.38 C0.88,143.79 0.25,139 0.25,134 C0.25,134 0.25,122.75 0.25,122.75 C0.25,122.75 301.25,122.75 301.25,122.75 C301.25,122.75 301.25,134 301.25,134 C301.25,139 300.62,143.79 299.37,148.38 C298.12,152.54 296.23,156.71 293.73,160.88 C293.73,160.88 249.83,224.63 249.83,224.63 C249.83,224.63 249.83,452.13 249.83,452.13 C249.83,465.46 245.02,476.92 235.41,486.5 C226.21,495.67 215.13,500.25 202.17,500.25 C202.17,500.25 99.33,500.25 99.33,500.25 C86.37,500.25 75.08,495.67 65.47,486.5 C56.27,476.92 51.67,465.46 51.67,452.13c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="17"
+ android:propertyName="pathData"
+ android:startOffset="383"
+ android:valueFrom=" M0.25 74.63 C0.25,74.63 0.25,48.38 0.25,48.38 C0.25,35.04 4.85,23.79 14.05,14.63 C23.66,5.04 35.16,0.25 48.54,0.25 C48.54,0.25 252.97,0.25 252.97,0.25 C266.34,0.25 277.63,5.04 286.83,14.63 C296.44,23.79 301.25,35.04 301.25,48.38 C301.25,48.38 301.25,74.63 301.25,74.63 C301.25,74.63 0.25,74.63 0.25,74.63c M150.75 338.13 C160.78,338.13 169.35,334.59 176.46,327.5 C183.57,320.42 187.12,311.88 187.12,301.88 C187.12,291.88 183.57,283.34 176.46,276.25 C169.35,268.75 160.78,265 150.75,265 C140.72,265 132.15,268.55 125.04,275.63 C117.93,282.71 114.38,291.46 114.38,301.88 C114.38,311.88 117.93,320.42 125.04,327.5 C132.15,334.59 140.72,338.13 150.75,338.13c M51.67 452.13 C51.67,452.13 51.67,224.63 51.67,224.63 C51.67,224.63 7.78,160.88 7.78,160.88 C5.27,156.71 3.38,152.54 2.13,148.38 C0.88,143.79 0.25,139 0.25,134 C0.25,134 0.25,122.75 0.25,122.75 C0.25,122.75 301.25,122.75 301.25,122.75 C301.25,122.75 301.25,134 301.25,134 C301.25,139 300.62,143.79 299.37,148.38 C298.12,152.54 296.23,156.71 293.73,160.88 C293.73,160.88 249.83,224.63 249.83,224.63 C249.83,224.63 249.83,452.13 249.83,452.13 C249.83,465.46 245.02,476.92 235.41,486.5 C226.21,495.67 215.13,500.25 202.17,500.25 C202.17,500.25 99.33,500.25 99.33,500.25 C86.37,500.25 75.08,495.67 65.47,486.5 C56.27,476.92 51.67,465.46 51.67,452.13c "
+ android:valueTo=" M0.25 74.63 C0.25,74.63 0.25,48.38 0.25,48.38 C0.25,35.04 4.85,23.79 14.05,14.63 C23.66,5.04 35.16,0.25 48.54,0.25 C48.54,0.25 252.97,0.25 252.97,0.25 C266.34,0.25 277.63,5.04 286.83,14.63 C296.44,23.79 301.25,35.04 301.25,48.38 C301.25,48.38 301.25,74.63 301.25,74.63 C301.25,74.63 0.25,74.63 0.25,74.63c M150.75 338.34 C160.78,338.34 169.35,334.8 176.46,327.71 C183.57,320.63 187.12,312.09 187.12,302.09 C187.12,292.09 183.57,283.55 176.46,276.46 C169.35,268.96 160.78,265.21 150.75,265.21 C140.72,265.21 132.15,268.76 125.04,275.84 C117.93,282.92 114.38,291.67 114.38,302.09 C114.38,312.09 117.93,320.63 125.04,327.71 C132.15,334.8 140.72,338.34 150.75,338.34c M51.67 452.13 C51.67,452.13 51.67,224.63 51.67,224.63 C51.67,224.63 7.78,160.88 7.78,160.88 C5.27,156.71 3.38,152.54 2.13,148.38 C0.88,143.79 0.25,139 0.25,134 C0.25,134 0.25,122.75 0.25,122.75 C0.25,122.75 301.25,122.75 301.25,122.75 C301.25,122.75 301.25,134 301.25,134 C301.25,139 300.62,143.79 299.37,148.38 C298.12,152.54 296.23,156.71 293.73,160.88 C293.73,160.88 249.83,224.63 249.83,224.63 C249.83,224.63 249.83,452.13 249.83,452.13 C249.83,465.46 245.02,476.92 235.41,486.5 C226.21,495.67 215.13,500.25 202.17,500.25 C202.17,500.25 99.33,500.25 99.33,500.25 C86.37,500.25 75.08,495.67 65.47,486.5 C56.27,476.92 51.67,465.46 51.67,452.13c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="17"
+ android:propertyName="pathData"
+ android:startOffset="400"
+ android:valueFrom=" M0.25 74.63 C0.25,74.63 0.25,48.38 0.25,48.38 C0.25,35.04 4.85,23.79 14.05,14.63 C23.66,5.04 35.16,0.25 48.54,0.25 C48.54,0.25 252.97,0.25 252.97,0.25 C266.34,0.25 277.63,5.04 286.83,14.63 C296.44,23.79 301.25,35.04 301.25,48.38 C301.25,48.38 301.25,74.63 301.25,74.63 C301.25,74.63 0.25,74.63 0.25,74.63c M150.75 338.34 C160.78,338.34 169.35,334.8 176.46,327.71 C183.57,320.63 187.12,312.09 187.12,302.09 C187.12,292.09 183.57,283.55 176.46,276.46 C169.35,268.96 160.78,265.21 150.75,265.21 C140.72,265.21 132.15,268.76 125.04,275.84 C117.93,282.92 114.38,291.67 114.38,302.09 C114.38,312.09 117.93,320.63 125.04,327.71 C132.15,334.8 140.72,338.34 150.75,338.34c M51.67 452.13 C51.67,452.13 51.67,224.63 51.67,224.63 C51.67,224.63 7.78,160.88 7.78,160.88 C5.27,156.71 3.38,152.54 2.13,148.38 C0.88,143.79 0.25,139 0.25,134 C0.25,134 0.25,122.75 0.25,122.75 C0.25,122.75 301.25,122.75 301.25,122.75 C301.25,122.75 301.25,134 301.25,134 C301.25,139 300.62,143.79 299.37,148.38 C298.12,152.54 296.23,156.71 293.73,160.88 C293.73,160.88 249.83,224.63 249.83,224.63 C249.83,224.63 249.83,452.13 249.83,452.13 C249.83,465.46 245.02,476.92 235.41,486.5 C226.21,495.67 215.13,500.25 202.17,500.25 C202.17,500.25 99.33,500.25 99.33,500.25 C86.37,500.25 75.08,495.67 65.47,486.5 C56.27,476.92 51.67,465.46 51.67,452.13c "
+ android:valueTo=" M0.25 74.63 C0.25,74.63 0.25,48.38 0.25,48.38 C0.25,35.04 4.85,23.79 14.05,14.63 C23.66,5.04 35.16,0.25 48.54,0.25 C48.54,0.25 252.97,0.25 252.97,0.25 C266.34,0.25 277.63,5.04 286.83,14.63 C296.44,23.79 301.25,35.04 301.25,48.38 C301.25,48.38 301.25,74.63 301.25,74.63 C301.25,74.63 0.25,74.63 0.25,74.63c M150.75 338.62 C160.78,338.62 169.35,335.08 176.46,328 C183.57,320.91 187.12,312.37 187.12,302.37 C187.12,292.37 183.57,283.83 176.46,276.75 C169.35,269.25 160.78,265.5 150.75,265.5 C140.72,265.5 132.15,269.04 125.04,276.12 C117.93,283.21 114.38,291.96 114.38,302.37 C114.38,312.37 117.93,320.91 125.04,328 C132.15,335.08 140.72,338.62 150.75,338.62c M51.67 452.13 C51.67,452.13 51.67,224.63 51.67,224.63 C51.67,224.63 7.78,160.88 7.78,160.88 C5.27,156.71 3.38,152.54 2.13,148.38 C0.88,143.79 0.25,139 0.25,134 C0.25,134 0.25,122.75 0.25,122.75 C0.25,122.75 301.25,122.75 301.25,122.75 C301.25,122.75 301.25,134 301.25,134 C301.25,139 300.62,143.79 299.37,148.38 C298.12,152.54 296.23,156.71 293.73,160.88 C293.73,160.88 249.83,224.63 249.83,224.63 C249.83,224.63 249.83,452.13 249.83,452.13 C249.83,465.46 245.02,476.92 235.41,486.5 C226.21,495.67 215.13,500.25 202.17,500.25 C202.17,500.25 99.33,500.25 99.33,500.25 C86.37,500.25 75.08,495.67 65.47,486.5 C56.27,476.92 51.67,465.46 51.67,452.13c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="17"
+ android:propertyName="pathData"
+ android:startOffset="417"
+ android:valueFrom=" M0.25 74.63 C0.25,74.63 0.25,48.38 0.25,48.38 C0.25,35.04 4.85,23.79 14.05,14.63 C23.66,5.04 35.16,0.25 48.54,0.25 C48.54,0.25 252.97,0.25 252.97,0.25 C266.34,0.25 277.63,5.04 286.83,14.63 C296.44,23.79 301.25,35.04 301.25,48.38 C301.25,48.38 301.25,74.63 301.25,74.63 C301.25,74.63 0.25,74.63 0.25,74.63c M150.75 338.62 C160.78,338.62 169.35,335.08 176.46,328 C183.57,320.91 187.12,312.37 187.12,302.37 C187.12,292.37 183.57,283.83 176.46,276.75 C169.35,269.25 160.78,265.5 150.75,265.5 C140.72,265.5 132.15,269.04 125.04,276.12 C117.93,283.21 114.38,291.96 114.38,302.37 C114.38,312.37 117.93,320.91 125.04,328 C132.15,335.08 140.72,338.62 150.75,338.62c M51.67 452.13 C51.67,452.13 51.67,224.63 51.67,224.63 C51.67,224.63 7.78,160.88 7.78,160.88 C5.27,156.71 3.38,152.54 2.13,148.38 C0.88,143.79 0.25,139 0.25,134 C0.25,134 0.25,122.75 0.25,122.75 C0.25,122.75 301.25,122.75 301.25,122.75 C301.25,122.75 301.25,134 301.25,134 C301.25,139 300.62,143.79 299.37,148.38 C298.12,152.54 296.23,156.71 293.73,160.88 C293.73,160.88 249.83,224.63 249.83,224.63 C249.83,224.63 249.83,452.13 249.83,452.13 C249.83,465.46 245.02,476.92 235.41,486.5 C226.21,495.67 215.13,500.25 202.17,500.25 C202.17,500.25 99.33,500.25 99.33,500.25 C86.37,500.25 75.08,495.67 65.47,486.5 C56.27,476.92 51.67,465.46 51.67,452.13c "
+ android:valueTo=" M0.25 74.63 C0.25,74.63 0.25,48.38 0.25,48.38 C0.25,35.04 4.85,23.79 14.05,14.63 C23.66,5.04 35.16,0.25 48.54,0.25 C48.54,0.25 252.97,0.25 252.97,0.25 C266.34,0.25 277.63,5.04 286.83,14.63 C296.44,23.79 301.25,35.04 301.25,48.38 C301.25,48.38 301.25,74.63 301.25,74.63 C301.25,74.63 0.25,74.63 0.25,74.63c M150.75 338.87 C160.78,338.87 169.35,335.33 176.46,328.25 C183.57,321.17 187.12,312.62 187.12,302.62 C187.12,292.62 183.57,284.08 176.46,277 C169.35,269.5 160.78,265.75 150.75,265.75 C140.72,265.75 132.15,269.29 125.04,276.37 C117.93,283.46 114.38,292.21 114.38,302.62 C114.38,312.62 117.93,321.17 125.04,328.25 C132.15,335.33 140.72,338.87 150.75,338.87c M51.67 452.13 C51.67,452.13 51.67,224.63 51.67,224.63 C51.67,224.63 7.78,160.88 7.78,160.88 C5.27,156.71 3.38,152.54 2.13,148.38 C0.88,143.79 0.25,139 0.25,134 C0.25,134 0.25,122.75 0.25,122.75 C0.25,122.75 301.25,122.75 301.25,122.75 C301.25,122.75 301.25,134 301.25,134 C301.25,139 300.62,143.79 299.37,148.38 C298.12,152.54 296.23,156.71 293.73,160.88 C293.73,160.88 249.83,224.63 249.83,224.63 C249.83,224.63 249.83,452.13 249.83,452.13 C249.83,465.46 245.02,476.92 235.41,486.5 C226.21,495.67 215.13,500.25 202.17,500.25 C202.17,500.25 99.33,500.25 99.33,500.25 C86.37,500.25 75.08,495.67 65.47,486.5 C56.27,476.92 51.67,465.46 51.67,452.13c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="17"
+ android:propertyName="pathData"
+ android:startOffset="433"
+ android:valueFrom=" M0.25 74.63 C0.25,74.63 0.25,48.38 0.25,48.38 C0.25,35.04 4.85,23.79 14.05,14.63 C23.66,5.04 35.16,0.25 48.54,0.25 C48.54,0.25 252.97,0.25 252.97,0.25 C266.34,0.25 277.63,5.04 286.83,14.63 C296.44,23.79 301.25,35.04 301.25,48.38 C301.25,48.38 301.25,74.63 301.25,74.63 C301.25,74.63 0.25,74.63 0.25,74.63c M150.75 338.87 C160.78,338.87 169.35,335.33 176.46,328.25 C183.57,321.17 187.12,312.62 187.12,302.62 C187.12,292.62 183.57,284.08 176.46,277 C169.35,269.5 160.78,265.75 150.75,265.75 C140.72,265.75 132.15,269.29 125.04,276.37 C117.93,283.46 114.38,292.21 114.38,302.62 C114.38,312.62 117.93,321.17 125.04,328.25 C132.15,335.33 140.72,338.87 150.75,338.87c M51.67 452.13 C51.67,452.13 51.67,224.63 51.67,224.63 C51.67,224.63 7.78,160.88 7.78,160.88 C5.27,156.71 3.38,152.54 2.13,148.38 C0.88,143.79 0.25,139 0.25,134 C0.25,134 0.25,122.75 0.25,122.75 C0.25,122.75 301.25,122.75 301.25,122.75 C301.25,122.75 301.25,134 301.25,134 C301.25,139 300.62,143.79 299.37,148.38 C298.12,152.54 296.23,156.71 293.73,160.88 C293.73,160.88 249.83,224.63 249.83,224.63 C249.83,224.63 249.83,452.13 249.83,452.13 C249.83,465.46 245.02,476.92 235.41,486.5 C226.21,495.67 215.13,500.25 202.17,500.25 C202.17,500.25 99.33,500.25 99.33,500.25 C86.37,500.25 75.08,495.67 65.47,486.5 C56.27,476.92 51.67,465.46 51.67,452.13c "
+ android:valueTo=" M0.25 74.63 C0.25,74.63 0.25,48.38 0.25,48.38 C0.25,35.04 4.85,23.79 14.05,14.63 C23.66,5.04 35.16,0.25 48.54,0.25 C48.54,0.25 252.97,0.25 252.97,0.25 C266.34,0.25 277.63,5.04 286.83,14.63 C296.44,23.79 301.25,35.04 301.25,48.38 C301.25,48.38 301.25,74.63 301.25,74.63 C301.25,74.63 0.25,74.63 0.25,74.63c M150.75 339 C160.78,339 169.35,335.46 176.46,328.38 C183.57,321.29 187.12,312.75 187.12,302.75 C187.12,292.75 183.57,284.21 176.46,277.13 C169.35,269.63 160.78,265.88 150.75,265.88 C140.72,265.88 132.15,269.42 125.04,276.5 C117.93,283.58 114.38,292.33 114.38,302.75 C114.38,312.75 117.93,321.29 125.04,328.38 C132.15,335.46 140.72,339 150.75,339c M51.67 452.13 C51.67,452.13 51.67,224.63 51.67,224.63 C51.67,224.63 7.78,160.88 7.78,160.88 C5.27,156.71 3.38,152.54 2.13,148.38 C0.88,143.79 0.25,139 0.25,134 C0.25,134 0.25,122.75 0.25,122.75 C0.25,122.75 301.25,122.75 301.25,122.75 C301.25,122.75 301.25,134 301.25,134 C301.25,139 300.62,143.79 299.37,148.38 C298.12,152.54 296.23,156.71 293.73,160.88 C293.73,160.88 249.83,224.63 249.83,224.63 C249.83,224.63 249.83,452.13 249.83,452.13 C249.83,465.46 245.02,476.92 235.41,486.5 C226.21,495.67 215.13,500.25 202.17,500.25 C202.17,500.25 99.33,500.25 99.33,500.25 C86.37,500.25 75.08,495.67 65.47,486.5 C56.27,476.92 51.67,465.46 51.67,452.13c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="time_group">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="467"
+ android:propertyName="translateX"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType" />
+ </set>
+ </aapt:attr>
+ </target>
+ <aapt:attr name="android:drawable">
+ <vector
+ android:width="64dp"
+ android:height="64dp"
+ android:viewportHeight="64"
+ android:viewportWidth="64">
+ <group android:name="_R_G">
+ <group
+ android:name="_R_G_L_0_G"
+ android:pivotX="150.75"
+ android:pivotY="250.25"
+ android:scaleX="0.1125"
+ android:scaleY="0.1125"
+ android:translateX="-118.75"
+ android:translateY="-218.5">
+ <path
+ android:name="_R_G_L_0_G_D_0_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#000000"
+ android:fillType="nonZero"
+ android:pathData=" M0.25 74.63 C0.25,74.63 0.25,48.38 0.25,48.38 C0.25,35.04 4.85,23.79 14.05,14.63 C23.66,5.04 35.16,0.25 48.54,0.25 C48.54,0.25 252.97,0.25 252.97,0.25 C266.34,0.25 277.63,5.04 286.83,14.63 C296.44,23.79 301.25,35.04 301.25,48.38 C301.25,48.38 301.25,74.63 301.25,74.63 C301.25,74.63 0.25,74.63 0.25,74.63c M150.75 398.56 C160.78,398.56 169.35,395.02 176.46,387.94 C183.57,380.86 187.12,372.31 187.12,362.31 C187.12,352.31 183.57,343.77 176.46,336.69 C169.35,329.19 160.78,325.44 150.75,325.44 C140.72,325.44 132.15,328.98 125.04,336.06 C117.93,343.15 114.38,351.9 114.38,362.31 C114.38,372.31 117.93,380.86 125.04,387.94 C132.15,395.02 140.72,398.56 150.75,398.56c M51.67 452.13 C51.67,452.13 51.67,224.63 51.67,224.63 C51.67,224.63 7.78,160.88 7.78,160.88 C5.27,156.71 3.38,152.54 2.13,148.38 C0.88,143.79 0.25,139 0.25,134 C0.25,134 0.25,122.75 0.25,122.75 C0.25,122.75 301.25,122.75 301.25,122.75 C301.25,122.75 301.25,134 301.25,134 C301.25,139 300.62,143.79 299.37,148.38 C298.12,152.54 296.23,156.71 293.73,160.88 C293.73,160.88 249.83,224.63 249.83,224.63 C249.83,224.63 249.83,452.13 249.83,452.13 C249.83,465.46 245.02,476.92 235.41,486.5 C226.21,495.67 215.13,500.25 202.17,500.25 C202.17,500.25 99.33,500.25 99.33,500.25 C86.37,500.25 75.08,495.67 65.47,486.5 C56.27,476.92 51.67,465.46 51.67,452.13c " />
+ </group>
+ </group>
+ <group android:name="time_group" />
+ </vector>
+ </aapt:attr>
+</animated-vector>
diff --git "a/packages/SystemUI/res/flag\050com.android.systemui.icon_refresh_2025\051/drawable/qs_flashlight_icon_on.xml" "b/packages/SystemUI/res/flag\050com.android.systemui.icon_refresh_2025\051/drawable/qs_flashlight_icon_on.xml"
new file mode 100644
index 0000000..ae184d3
--- /dev/null
+++ "b/packages/SystemUI/res/flag\050com.android.systemui.icon_refresh_2025\051/drawable/qs_flashlight_icon_on.xml"
@@ -0,0 +1,268 @@
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:aapt="http://schemas.android.com/aapt">
+ <target android:name="_R_G_L_0_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="17"
+ android:propertyName="pathData"
+ android:startOffset="0"
+ android:valueFrom=" M0.25 74.63 C0.25,74.63 0.25,48.38 0.25,48.38 C0.25,35.04 4.85,23.79 14.05,14.63 C23.66,5.04 35.16,0.25 48.54,0.25 C48.54,0.25 252.97,0.25 252.97,0.25 C266.34,0.25 277.63,5.04 286.83,14.63 C296.44,23.79 301.25,35.04 301.25,48.38 C301.25,48.38 301.25,74.63 301.25,74.63 C301.25,74.63 0.25,74.63 0.25,74.63c M150.75 339 C160.78,339 169.35,335.46 176.46,328.38 C183.57,321.29 187.12,312.75 187.12,302.75 C187.12,292.75 183.57,284.21 176.46,277.13 C169.35,269.63 160.78,265.88 150.75,265.88 C140.72,265.88 132.15,269.42 125.04,276.5 C117.93,283.58 114.38,292.33 114.38,302.75 C114.38,312.75 117.93,321.29 125.04,328.38 C132.15,335.46 140.72,339 150.75,339c M51.67 452.13 C51.67,452.13 51.67,224.63 51.67,224.63 C51.67,224.63 7.78,160.88 7.78,160.88 C5.27,156.71 3.38,152.54 2.13,148.38 C0.88,143.79 0.25,139 0.25,134 C0.25,134 0.25,122.75 0.25,122.75 C0.25,122.75 301.25,122.75 301.25,122.75 C301.25,122.75 301.25,134 301.25,134 C301.25,139 300.62,143.79 299.37,148.38 C298.12,152.54 296.23,156.71 293.73,160.88 C293.73,160.88 249.83,224.63 249.83,224.63 C249.83,224.63 249.83,452.13 249.83,452.13 C249.83,465.46 245.02,476.92 235.41,486.5 C226.21,495.67 215.13,500.25 202.17,500.25 C202.17,500.25 99.33,500.25 99.33,500.25 C86.37,500.25 75.08,495.67 65.47,486.5 C56.27,476.92 51.67,465.46 51.67,452.13c "
+ android:valueTo=" M0.25 74.63 C0.25,74.63 0.25,48.38 0.25,48.38 C0.25,35.04 4.85,23.79 14.05,14.63 C23.66,5.04 35.16,0.25 48.54,0.25 C48.54,0.25 252.97,0.25 252.97,0.25 C266.34,0.25 277.63,5.04 286.83,14.63 C296.44,23.79 301.25,35.04 301.25,48.38 C301.25,48.38 301.25,74.63 301.25,74.63 C301.25,74.63 0.25,74.63 0.25,74.63c M150.75 352.97 C160.78,352.97 169.35,349.43 176.46,342.34 C183.57,335.26 187.12,326.72 187.12,316.72 C187.12,306.72 183.57,298.18 176.46,291.09 C169.35,283.59 160.78,279.84 150.75,279.84 C140.72,279.84 132.15,283.39 125.04,290.47 C117.93,297.55 114.38,306.3 114.38,316.72 C114.38,326.72 117.93,335.26 125.04,342.34 C132.15,349.43 140.72,352.97 150.75,352.97c M51.67 452.13 C51.67,452.13 51.67,224.63 51.67,224.63 C51.67,224.63 7.78,160.88 7.78,160.88 C5.27,156.71 3.38,152.54 2.13,148.38 C0.88,143.79 0.25,139 0.25,134 C0.25,134 0.25,122.75 0.25,122.75 C0.25,122.75 301.25,122.75 301.25,122.75 C301.25,122.75 301.25,134 301.25,134 C301.25,139 300.62,143.79 299.37,148.38 C298.12,152.54 296.23,156.71 293.73,160.88 C293.73,160.88 249.83,224.63 249.83,224.63 C249.83,224.63 249.83,452.13 249.83,452.13 C249.83,465.46 245.02,476.92 235.41,486.5 C226.21,495.67 215.13,500.25 202.17,500.25 C202.17,500.25 99.33,500.25 99.33,500.25 C86.37,500.25 75.08,495.67 65.47,486.5 C56.27,476.92 51.67,465.46 51.67,452.13c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="17"
+ android:propertyName="pathData"
+ android:startOffset="17"
+ android:valueFrom=" M0.25 74.63 C0.25,74.63 0.25,48.38 0.25,48.38 C0.25,35.04 4.85,23.79 14.05,14.63 C23.66,5.04 35.16,0.25 48.54,0.25 C48.54,0.25 252.97,0.25 252.97,0.25 C266.34,0.25 277.63,5.04 286.83,14.63 C296.44,23.79 301.25,35.04 301.25,48.38 C301.25,48.38 301.25,74.63 301.25,74.63 C301.25,74.63 0.25,74.63 0.25,74.63c M150.75 352.97 C160.78,352.97 169.35,349.43 176.46,342.34 C183.57,335.26 187.12,326.72 187.12,316.72 C187.12,306.72 183.57,298.18 176.46,291.09 C169.35,283.59 160.78,279.84 150.75,279.84 C140.72,279.84 132.15,283.39 125.04,290.47 C117.93,297.55 114.38,306.3 114.38,316.72 C114.38,326.72 117.93,335.26 125.04,342.34 C132.15,349.43 140.72,352.97 150.75,352.97c M51.67 452.13 C51.67,452.13 51.67,224.63 51.67,224.63 C51.67,224.63 7.78,160.88 7.78,160.88 C5.27,156.71 3.38,152.54 2.13,148.38 C0.88,143.79 0.25,139 0.25,134 C0.25,134 0.25,122.75 0.25,122.75 C0.25,122.75 301.25,122.75 301.25,122.75 C301.25,122.75 301.25,134 301.25,134 C301.25,139 300.62,143.79 299.37,148.38 C298.12,152.54 296.23,156.71 293.73,160.88 C293.73,160.88 249.83,224.63 249.83,224.63 C249.83,224.63 249.83,452.13 249.83,452.13 C249.83,465.46 245.02,476.92 235.41,486.5 C226.21,495.67 215.13,500.25 202.17,500.25 C202.17,500.25 99.33,500.25 99.33,500.25 C86.37,500.25 75.08,495.67 65.47,486.5 C56.27,476.92 51.67,465.46 51.67,452.13c "
+ android:valueTo=" M0.25 74.63 C0.25,74.63 0.25,48.38 0.25,48.38 C0.25,35.04 4.85,23.79 14.05,14.63 C23.66,5.04 35.16,0.25 48.54,0.25 C48.54,0.25 252.97,0.25 252.97,0.25 C266.34,0.25 277.63,5.04 286.83,14.63 C296.44,23.79 301.25,35.04 301.25,48.38 C301.25,48.38 301.25,74.63 301.25,74.63 C301.25,74.63 0.25,74.63 0.25,74.63c M150.75 366.31 C160.78,366.31 169.35,362.77 176.46,355.69 C183.57,348.61 187.12,340.06 187.12,330.06 C187.12,320.06 183.57,311.52 176.46,304.44 C169.35,296.94 160.78,293.19 150.75,293.19 C140.72,293.19 132.15,296.73 125.04,303.81 C117.93,310.9 114.38,319.65 114.38,330.06 C114.38,340.06 117.93,348.61 125.04,355.69 C132.15,362.77 140.72,366.31 150.75,366.31c M51.67 452.13 C51.67,452.13 51.67,224.63 51.67,224.63 C51.67,224.63 7.78,160.88 7.78,160.88 C5.27,156.71 3.38,152.54 2.13,148.38 C0.88,143.79 0.25,139 0.25,134 C0.25,134 0.25,122.75 0.25,122.75 C0.25,122.75 301.25,122.75 301.25,122.75 C301.25,122.75 301.25,134 301.25,134 C301.25,139 300.62,143.79 299.37,148.38 C298.12,152.54 296.23,156.71 293.73,160.88 C293.73,160.88 249.83,224.63 249.83,224.63 C249.83,224.63 249.83,452.13 249.83,452.13 C249.83,465.46 245.02,476.92 235.41,486.5 C226.21,495.67 215.13,500.25 202.17,500.25 C202.17,500.25 99.33,500.25 99.33,500.25 C86.37,500.25 75.08,495.67 65.47,486.5 C56.27,476.92 51.67,465.46 51.67,452.13c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="17"
+ android:propertyName="pathData"
+ android:startOffset="33"
+ android:valueFrom=" M0.25 74.63 C0.25,74.63 0.25,48.38 0.25,48.38 C0.25,35.04 4.85,23.79 14.05,14.63 C23.66,5.04 35.16,0.25 48.54,0.25 C48.54,0.25 252.97,0.25 252.97,0.25 C266.34,0.25 277.63,5.04 286.83,14.63 C296.44,23.79 301.25,35.04 301.25,48.38 C301.25,48.38 301.25,74.63 301.25,74.63 C301.25,74.63 0.25,74.63 0.25,74.63c M150.75 366.31 C160.78,366.31 169.35,362.77 176.46,355.69 C183.57,348.61 187.12,340.06 187.12,330.06 C187.12,320.06 183.57,311.52 176.46,304.44 C169.35,296.94 160.78,293.19 150.75,293.19 C140.72,293.19 132.15,296.73 125.04,303.81 C117.93,310.9 114.38,319.65 114.38,330.06 C114.38,340.06 117.93,348.61 125.04,355.69 C132.15,362.77 140.72,366.31 150.75,366.31c M51.67 452.13 C51.67,452.13 51.67,224.63 51.67,224.63 C51.67,224.63 7.78,160.88 7.78,160.88 C5.27,156.71 3.38,152.54 2.13,148.38 C0.88,143.79 0.25,139 0.25,134 C0.25,134 0.25,122.75 0.25,122.75 C0.25,122.75 301.25,122.75 301.25,122.75 C301.25,122.75 301.25,134 301.25,134 C301.25,139 300.62,143.79 299.37,148.38 C298.12,152.54 296.23,156.71 293.73,160.88 C293.73,160.88 249.83,224.63 249.83,224.63 C249.83,224.63 249.83,452.13 249.83,452.13 C249.83,465.46 245.02,476.92 235.41,486.5 C226.21,495.67 215.13,500.25 202.17,500.25 C202.17,500.25 99.33,500.25 99.33,500.25 C86.37,500.25 75.08,495.67 65.47,486.5 C56.27,476.92 51.67,465.46 51.67,452.13c "
+ android:valueTo=" M0.25 74.63 C0.25,74.63 0.25,48.38 0.25,48.38 C0.25,35.04 4.85,23.79 14.05,14.63 C23.66,5.04 35.16,0.25 48.54,0.25 C48.54,0.25 252.97,0.25 252.97,0.25 C266.34,0.25 277.63,5.04 286.83,14.63 C296.44,23.79 301.25,35.04 301.25,48.38 C301.25,48.38 301.25,74.63 301.25,74.63 C301.25,74.63 0.25,74.63 0.25,74.63c M150.75 378.19 C160.78,378.19 169.35,374.65 176.46,367.56 C183.57,360.48 187.12,351.94 187.12,341.94 C187.12,331.94 183.57,323.4 176.46,316.31 C169.35,308.81 160.78,305.06 150.75,305.06 C140.72,305.06 132.15,308.61 125.04,315.69 C117.93,322.77 114.38,331.52 114.38,341.94 C114.38,351.94 117.93,360.48 125.04,367.56 C132.15,374.65 140.72,378.19 150.75,378.19c M51.67 452.13 C51.67,452.13 51.67,224.63 51.67,224.63 C51.67,224.63 7.78,160.88 7.78,160.88 C5.27,156.71 3.38,152.54 2.13,148.38 C0.88,143.79 0.25,139 0.25,134 C0.25,134 0.25,122.75 0.25,122.75 C0.25,122.75 301.25,122.75 301.25,122.75 C301.25,122.75 301.25,134 301.25,134 C301.25,139 300.62,143.79 299.37,148.38 C298.12,152.54 296.23,156.71 293.73,160.88 C293.73,160.88 249.83,224.63 249.83,224.63 C249.83,224.63 249.83,452.13 249.83,452.13 C249.83,465.46 245.02,476.92 235.41,486.5 C226.21,495.67 215.13,500.25 202.17,500.25 C202.17,500.25 99.33,500.25 99.33,500.25 C86.37,500.25 75.08,495.67 65.47,486.5 C56.27,476.92 51.67,465.46 51.67,452.13c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="17"
+ android:propertyName="pathData"
+ android:startOffset="50"
+ android:valueFrom=" M0.25 74.63 C0.25,74.63 0.25,48.38 0.25,48.38 C0.25,35.04 4.85,23.79 14.05,14.63 C23.66,5.04 35.16,0.25 48.54,0.25 C48.54,0.25 252.97,0.25 252.97,0.25 C266.34,0.25 277.63,5.04 286.83,14.63 C296.44,23.79 301.25,35.04 301.25,48.38 C301.25,48.38 301.25,74.63 301.25,74.63 C301.25,74.63 0.25,74.63 0.25,74.63c M150.75 378.19 C160.78,378.19 169.35,374.65 176.46,367.56 C183.57,360.48 187.12,351.94 187.12,341.94 C187.12,331.94 183.57,323.4 176.46,316.31 C169.35,308.81 160.78,305.06 150.75,305.06 C140.72,305.06 132.15,308.61 125.04,315.69 C117.93,322.77 114.38,331.52 114.38,341.94 C114.38,351.94 117.93,360.48 125.04,367.56 C132.15,374.65 140.72,378.19 150.75,378.19c M51.67 452.13 C51.67,452.13 51.67,224.63 51.67,224.63 C51.67,224.63 7.78,160.88 7.78,160.88 C5.27,156.71 3.38,152.54 2.13,148.38 C0.88,143.79 0.25,139 0.25,134 C0.25,134 0.25,122.75 0.25,122.75 C0.25,122.75 301.25,122.75 301.25,122.75 C301.25,122.75 301.25,134 301.25,134 C301.25,139 300.62,143.79 299.37,148.38 C298.12,152.54 296.23,156.71 293.73,160.88 C293.73,160.88 249.83,224.63 249.83,224.63 C249.83,224.63 249.83,452.13 249.83,452.13 C249.83,465.46 245.02,476.92 235.41,486.5 C226.21,495.67 215.13,500.25 202.17,500.25 C202.17,500.25 99.33,500.25 99.33,500.25 C86.37,500.25 75.08,495.67 65.47,486.5 C56.27,476.92 51.67,465.46 51.67,452.13c "
+ android:valueTo=" M0.25 74.63 C0.25,74.63 0.25,48.38 0.25,48.38 C0.25,35.04 4.85,23.79 14.05,14.63 C23.66,5.04 35.16,0.25 48.54,0.25 C48.54,0.25 252.97,0.25 252.97,0.25 C266.34,0.25 277.63,5.04 286.83,14.63 C296.44,23.79 301.25,35.04 301.25,48.38 C301.25,48.38 301.25,74.63 301.25,74.63 C301.25,74.63 0.25,74.63 0.25,74.63c M150.75 388.42 C160.78,388.42 169.35,384.88 176.46,377.79 C183.57,370.71 187.12,362.17 187.12,352.17 C187.12,342.17 183.57,333.63 176.46,326.54 C169.35,319.04 160.78,315.29 150.75,315.29 C140.72,315.29 132.15,318.84 125.04,325.92 C117.93,333 114.38,341.75 114.38,352.17 C114.38,362.17 117.93,370.71 125.04,377.79 C132.15,384.88 140.72,388.42 150.75,388.42c M51.67 452.13 C51.67,452.13 51.67,224.63 51.67,224.63 C51.67,224.63 7.78,160.88 7.78,160.88 C5.27,156.71 3.38,152.54 2.13,148.38 C0.88,143.79 0.25,139 0.25,134 C0.25,134 0.25,122.75 0.25,122.75 C0.25,122.75 301.25,122.75 301.25,122.75 C301.25,122.75 301.25,134 301.25,134 C301.25,139 300.62,143.79 299.37,148.38 C298.12,152.54 296.23,156.71 293.73,160.88 C293.73,160.88 249.83,224.63 249.83,224.63 C249.83,224.63 249.83,452.13 249.83,452.13 C249.83,465.46 245.02,476.92 235.41,486.5 C226.21,495.67 215.13,500.25 202.17,500.25 C202.17,500.25 99.33,500.25 99.33,500.25 C86.37,500.25 75.08,495.67 65.47,486.5 C56.27,476.92 51.67,465.46 51.67,452.13c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="17"
+ android:propertyName="pathData"
+ android:startOffset="67"
+ android:valueFrom=" M0.25 74.63 C0.25,74.63 0.25,48.38 0.25,48.38 C0.25,35.04 4.85,23.79 14.05,14.63 C23.66,5.04 35.16,0.25 48.54,0.25 C48.54,0.25 252.97,0.25 252.97,0.25 C266.34,0.25 277.63,5.04 286.83,14.63 C296.44,23.79 301.25,35.04 301.25,48.38 C301.25,48.38 301.25,74.63 301.25,74.63 C301.25,74.63 0.25,74.63 0.25,74.63c M150.75 388.42 C160.78,388.42 169.35,384.88 176.46,377.79 C183.57,370.71 187.12,362.17 187.12,352.17 C187.12,342.17 183.57,333.63 176.46,326.54 C169.35,319.04 160.78,315.29 150.75,315.29 C140.72,315.29 132.15,318.84 125.04,325.92 C117.93,333 114.38,341.75 114.38,352.17 C114.38,362.17 117.93,370.71 125.04,377.79 C132.15,384.88 140.72,388.42 150.75,388.42c M51.67 452.13 C51.67,452.13 51.67,224.63 51.67,224.63 C51.67,224.63 7.78,160.88 7.78,160.88 C5.27,156.71 3.38,152.54 2.13,148.38 C0.88,143.79 0.25,139 0.25,134 C0.25,134 0.25,122.75 0.25,122.75 C0.25,122.75 301.25,122.75 301.25,122.75 C301.25,122.75 301.25,134 301.25,134 C301.25,139 300.62,143.79 299.37,148.38 C298.12,152.54 296.23,156.71 293.73,160.88 C293.73,160.88 249.83,224.63 249.83,224.63 C249.83,224.63 249.83,452.13 249.83,452.13 C249.83,465.46 245.02,476.92 235.41,486.5 C226.21,495.67 215.13,500.25 202.17,500.25 C202.17,500.25 99.33,500.25 99.33,500.25 C86.37,500.25 75.08,495.67 65.47,486.5 C56.27,476.92 51.67,465.46 51.67,452.13c "
+ android:valueTo=" M0.25 74.63 C0.25,74.63 0.25,48.38 0.25,48.38 C0.25,35.04 4.85,23.79 14.05,14.63 C23.66,5.04 35.16,0.25 48.54,0.25 C48.54,0.25 252.97,0.25 252.97,0.25 C266.34,0.25 277.63,5.04 286.83,14.63 C296.44,23.79 301.25,35.04 301.25,48.38 C301.25,48.38 301.25,74.63 301.25,74.63 C301.25,74.63 0.25,74.63 0.25,74.63c M150.75 396.09 C160.78,396.09 169.35,392.54 176.46,385.46 C183.57,378.38 187.12,369.84 187.12,359.84 C187.12,349.84 183.57,341.29 176.46,334.21 C169.35,326.71 160.78,322.96 150.75,322.96 C140.72,322.96 132.15,326.5 125.04,333.59 C117.93,340.67 114.38,349.42 114.38,359.84 C114.38,369.84 117.93,378.38 125.04,385.46 C132.15,392.54 140.72,396.09 150.75,396.09c M51.67 452.13 C51.67,452.13 51.67,224.63 51.67,224.63 C51.67,224.63 7.78,160.88 7.78,160.88 C5.27,156.71 3.38,152.54 2.13,148.38 C0.88,143.79 0.25,139 0.25,134 C0.25,134 0.25,122.75 0.25,122.75 C0.25,122.75 301.25,122.75 301.25,122.75 C301.25,122.75 301.25,134 301.25,134 C301.25,139 300.62,143.79 299.37,148.38 C298.12,152.54 296.23,156.71 293.73,160.88 C293.73,160.88 249.83,224.63 249.83,224.63 C249.83,224.63 249.83,452.13 249.83,452.13 C249.83,465.46 245.02,476.92 235.41,486.5 C226.21,495.67 215.13,500.25 202.17,500.25 C202.17,500.25 99.33,500.25 99.33,500.25 C86.37,500.25 75.08,495.67 65.47,486.5 C56.27,476.92 51.67,465.46 51.67,452.13c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="17"
+ android:propertyName="pathData"
+ android:startOffset="83"
+ android:valueFrom=" M0.25 74.63 C0.25,74.63 0.25,48.38 0.25,48.38 C0.25,35.04 4.85,23.79 14.05,14.63 C23.66,5.04 35.16,0.25 48.54,0.25 C48.54,0.25 252.97,0.25 252.97,0.25 C266.34,0.25 277.63,5.04 286.83,14.63 C296.44,23.79 301.25,35.04 301.25,48.38 C301.25,48.38 301.25,74.63 301.25,74.63 C301.25,74.63 0.25,74.63 0.25,74.63c M150.75 396.09 C160.78,396.09 169.35,392.54 176.46,385.46 C183.57,378.38 187.12,369.84 187.12,359.84 C187.12,349.84 183.57,341.29 176.46,334.21 C169.35,326.71 160.78,322.96 150.75,322.96 C140.72,322.96 132.15,326.5 125.04,333.59 C117.93,340.67 114.38,349.42 114.38,359.84 C114.38,369.84 117.93,378.38 125.04,385.46 C132.15,392.54 140.72,396.09 150.75,396.09c M51.67 452.13 C51.67,452.13 51.67,224.63 51.67,224.63 C51.67,224.63 7.78,160.88 7.78,160.88 C5.27,156.71 3.38,152.54 2.13,148.38 C0.88,143.79 0.25,139 0.25,134 C0.25,134 0.25,122.75 0.25,122.75 C0.25,122.75 301.25,122.75 301.25,122.75 C301.25,122.75 301.25,134 301.25,134 C301.25,139 300.62,143.79 299.37,148.38 C298.12,152.54 296.23,156.71 293.73,160.88 C293.73,160.88 249.83,224.63 249.83,224.63 C249.83,224.63 249.83,452.13 249.83,452.13 C249.83,465.46 245.02,476.92 235.41,486.5 C226.21,495.67 215.13,500.25 202.17,500.25 C202.17,500.25 99.33,500.25 99.33,500.25 C86.37,500.25 75.08,495.67 65.47,486.5 C56.27,476.92 51.67,465.46 51.67,452.13c "
+ android:valueTo=" M0.25 74.63 C0.25,74.63 0.25,48.38 0.25,48.38 C0.25,35.04 4.85,23.79 14.05,14.63 C23.66,5.04 35.16,0.25 48.54,0.25 C48.54,0.25 252.97,0.25 252.97,0.25 C266.34,0.25 277.63,5.04 286.83,14.63 C296.44,23.79 301.25,35.04 301.25,48.38 C301.25,48.38 301.25,74.63 301.25,74.63 C301.25,74.63 0.25,74.63 0.25,74.63c M150.75 401.5 C160.78,401.5 169.35,397.96 176.46,390.88 C183.57,383.79 187.12,375.25 187.12,365.25 C187.12,355.25 183.57,346.71 176.46,339.63 C169.35,332.13 160.78,328.38 150.75,328.38 C140.72,328.38 132.15,331.92 125.04,339 C117.93,346.09 114.38,354.84 114.38,365.25 C114.38,375.25 117.93,383.79 125.04,390.88 C132.15,397.96 140.72,401.5 150.75,401.5c M51.67 452.13 C51.67,452.13 51.67,224.63 51.67,224.63 C51.67,224.63 7.78,160.88 7.78,160.88 C5.27,156.71 3.38,152.54 2.13,148.38 C0.88,143.79 0.25,139 0.25,134 C0.25,134 0.25,122.75 0.25,122.75 C0.25,122.75 301.25,122.75 301.25,122.75 C301.25,122.75 301.25,134 301.25,134 C301.25,139 300.62,143.79 299.37,148.38 C298.12,152.54 296.23,156.71 293.73,160.88 C293.73,160.88 249.83,224.63 249.83,224.63 C249.83,224.63 249.83,452.13 249.83,452.13 C249.83,465.46 245.02,476.92 235.41,486.5 C226.21,495.67 215.13,500.25 202.17,500.25 C202.17,500.25 99.33,500.25 99.33,500.25 C86.37,500.25 75.08,495.67 65.47,486.5 C56.27,476.92 51.67,465.46 51.67,452.13c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="17"
+ android:propertyName="pathData"
+ android:startOffset="100"
+ android:valueFrom=" M0.25 74.63 C0.25,74.63 0.25,48.38 0.25,48.38 C0.25,35.04 4.85,23.79 14.05,14.63 C23.66,5.04 35.16,0.25 48.54,0.25 C48.54,0.25 252.97,0.25 252.97,0.25 C266.34,0.25 277.63,5.04 286.83,14.63 C296.44,23.79 301.25,35.04 301.25,48.38 C301.25,48.38 301.25,74.63 301.25,74.63 C301.25,74.63 0.25,74.63 0.25,74.63c M150.75 401.5 C160.78,401.5 169.35,397.96 176.46,390.88 C183.57,383.79 187.12,375.25 187.12,365.25 C187.12,355.25 183.57,346.71 176.46,339.63 C169.35,332.13 160.78,328.38 150.75,328.38 C140.72,328.38 132.15,331.92 125.04,339 C117.93,346.09 114.38,354.84 114.38,365.25 C114.38,375.25 117.93,383.79 125.04,390.88 C132.15,397.96 140.72,401.5 150.75,401.5c M51.67 452.13 C51.67,452.13 51.67,224.63 51.67,224.63 C51.67,224.63 7.78,160.88 7.78,160.88 C5.27,156.71 3.38,152.54 2.13,148.38 C0.88,143.79 0.25,139 0.25,134 C0.25,134 0.25,122.75 0.25,122.75 C0.25,122.75 301.25,122.75 301.25,122.75 C301.25,122.75 301.25,134 301.25,134 C301.25,139 300.62,143.79 299.37,148.38 C298.12,152.54 296.23,156.71 293.73,160.88 C293.73,160.88 249.83,224.63 249.83,224.63 C249.83,224.63 249.83,452.13 249.83,452.13 C249.83,465.46 245.02,476.92 235.41,486.5 C226.21,495.67 215.13,500.25 202.17,500.25 C202.17,500.25 99.33,500.25 99.33,500.25 C86.37,500.25 75.08,495.67 65.47,486.5 C56.27,476.92 51.67,465.46 51.67,452.13c "
+ android:valueTo=" M0.25 74.63 C0.25,74.63 0.25,48.38 0.25,48.38 C0.25,35.04 4.85,23.79 14.05,14.63 C23.66,5.04 35.16,0.25 48.54,0.25 C48.54,0.25 252.97,0.25 252.97,0.25 C266.34,0.25 277.63,5.04 286.83,14.63 C296.44,23.79 301.25,35.04 301.25,48.38 C301.25,48.38 301.25,74.63 301.25,74.63 C301.25,74.63 0.25,74.63 0.25,74.63c M150.75 405.07 C160.78,405.07 169.35,401.52 176.46,394.44 C183.57,387.36 187.12,378.82 187.12,368.82 C187.12,358.82 183.57,350.27 176.46,343.19 C169.35,335.69 160.78,331.94 150.75,331.94 C140.72,331.94 132.15,335.48 125.04,342.57 C117.93,349.65 114.38,358.4 114.38,368.82 C114.38,378.82 117.93,387.36 125.04,394.44 C132.15,401.52 140.72,405.07 150.75,405.07c M51.67 452.13 C51.67,452.13 51.67,224.63 51.67,224.63 C51.67,224.63 7.78,160.88 7.78,160.88 C5.27,156.71 3.38,152.54 2.13,148.38 C0.88,143.79 0.25,139 0.25,134 C0.25,134 0.25,122.75 0.25,122.75 C0.25,122.75 301.25,122.75 301.25,122.75 C301.25,122.75 301.25,134 301.25,134 C301.25,139 300.62,143.79 299.37,148.38 C298.12,152.54 296.23,156.71 293.73,160.88 C293.73,160.88 249.83,224.63 249.83,224.63 C249.83,224.63 249.83,452.13 249.83,452.13 C249.83,465.46 245.02,476.92 235.41,486.5 C226.21,495.67 215.13,500.25 202.17,500.25 C202.17,500.25 99.33,500.25 99.33,500.25 C86.37,500.25 75.08,495.67 65.47,486.5 C56.27,476.92 51.67,465.46 51.67,452.13c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="17"
+ android:propertyName="pathData"
+ android:startOffset="117"
+ android:valueFrom=" M0.25 74.63 C0.25,74.63 0.25,48.38 0.25,48.38 C0.25,35.04 4.85,23.79 14.05,14.63 C23.66,5.04 35.16,0.25 48.54,0.25 C48.54,0.25 252.97,0.25 252.97,0.25 C266.34,0.25 277.63,5.04 286.83,14.63 C296.44,23.79 301.25,35.04 301.25,48.38 C301.25,48.38 301.25,74.63 301.25,74.63 C301.25,74.63 0.25,74.63 0.25,74.63c M150.75 405.07 C160.78,405.07 169.35,401.52 176.46,394.44 C183.57,387.36 187.12,378.82 187.12,368.82 C187.12,358.82 183.57,350.27 176.46,343.19 C169.35,335.69 160.78,331.94 150.75,331.94 C140.72,331.94 132.15,335.48 125.04,342.57 C117.93,349.65 114.38,358.4 114.38,368.82 C114.38,378.82 117.93,387.36 125.04,394.44 C132.15,401.52 140.72,405.07 150.75,405.07c M51.67 452.13 C51.67,452.13 51.67,224.63 51.67,224.63 C51.67,224.63 7.78,160.88 7.78,160.88 C5.27,156.71 3.38,152.54 2.13,148.38 C0.88,143.79 0.25,139 0.25,134 C0.25,134 0.25,122.75 0.25,122.75 C0.25,122.75 301.25,122.75 301.25,122.75 C301.25,122.75 301.25,134 301.25,134 C301.25,139 300.62,143.79 299.37,148.38 C298.12,152.54 296.23,156.71 293.73,160.88 C293.73,160.88 249.83,224.63 249.83,224.63 C249.83,224.63 249.83,452.13 249.83,452.13 C249.83,465.46 245.02,476.92 235.41,486.5 C226.21,495.67 215.13,500.25 202.17,500.25 C202.17,500.25 99.33,500.25 99.33,500.25 C86.37,500.25 75.08,495.67 65.47,486.5 C56.27,476.92 51.67,465.46 51.67,452.13c "
+ android:valueTo=" M0.25 74.63 C0.25,74.63 0.25,48.38 0.25,48.38 C0.25,35.04 4.85,23.79 14.05,14.63 C23.66,5.04 35.16,0.25 48.54,0.25 C48.54,0.25 252.97,0.25 252.97,0.25 C266.34,0.25 277.63,5.04 286.83,14.63 C296.44,23.79 301.25,35.04 301.25,48.38 C301.25,48.38 301.25,74.63 301.25,74.63 C301.25,74.63 0.25,74.63 0.25,74.63c M150.75 406.72 C160.78,406.72 169.35,403.18 176.46,396.09 C183.57,389.01 187.12,380.47 187.12,370.47 C187.12,360.47 183.57,351.93 176.46,344.84 C169.35,337.34 160.78,333.59 150.75,333.59 C140.72,333.59 132.15,337.14 125.04,344.22 C117.93,351.3 114.38,360.05 114.38,370.47 C114.38,380.47 117.93,389.01 125.04,396.09 C132.15,403.18 140.72,406.72 150.75,406.72c M51.67 452.13 C51.67,452.13 51.67,224.63 51.67,224.63 C51.67,224.63 7.78,160.88 7.78,160.88 C5.27,156.71 3.38,152.54 2.13,148.38 C0.88,143.79 0.25,139 0.25,134 C0.25,134 0.25,122.75 0.25,122.75 C0.25,122.75 301.25,122.75 301.25,122.75 C301.25,122.75 301.25,134 301.25,134 C301.25,139 300.62,143.79 299.37,148.38 C298.12,152.54 296.23,156.71 293.73,160.88 C293.73,160.88 249.83,224.63 249.83,224.63 C249.83,224.63 249.83,452.13 249.83,452.13 C249.83,465.46 245.02,476.92 235.41,486.5 C226.21,495.67 215.13,500.25 202.17,500.25 C202.17,500.25 99.33,500.25 99.33,500.25 C86.37,500.25 75.08,495.67 65.47,486.5 C56.27,476.92 51.67,465.46 51.67,452.13c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="17"
+ android:propertyName="pathData"
+ android:startOffset="133"
+ android:valueFrom=" M0.25 74.63 C0.25,74.63 0.25,48.38 0.25,48.38 C0.25,35.04 4.85,23.79 14.05,14.63 C23.66,5.04 35.16,0.25 48.54,0.25 C48.54,0.25 252.97,0.25 252.97,0.25 C266.34,0.25 277.63,5.04 286.83,14.63 C296.44,23.79 301.25,35.04 301.25,48.38 C301.25,48.38 301.25,74.63 301.25,74.63 C301.25,74.63 0.25,74.63 0.25,74.63c M150.75 406.72 C160.78,406.72 169.35,403.18 176.46,396.09 C183.57,389.01 187.12,380.47 187.12,370.47 C187.12,360.47 183.57,351.93 176.46,344.84 C169.35,337.34 160.78,333.59 150.75,333.59 C140.72,333.59 132.15,337.14 125.04,344.22 C117.93,351.3 114.38,360.05 114.38,370.47 C114.38,380.47 117.93,389.01 125.04,396.09 C132.15,403.18 140.72,406.72 150.75,406.72c M51.67 452.13 C51.67,452.13 51.67,224.63 51.67,224.63 C51.67,224.63 7.78,160.88 7.78,160.88 C5.27,156.71 3.38,152.54 2.13,148.38 C0.88,143.79 0.25,139 0.25,134 C0.25,134 0.25,122.75 0.25,122.75 C0.25,122.75 301.25,122.75 301.25,122.75 C301.25,122.75 301.25,134 301.25,134 C301.25,139 300.62,143.79 299.37,148.38 C298.12,152.54 296.23,156.71 293.73,160.88 C293.73,160.88 249.83,224.63 249.83,224.63 C249.83,224.63 249.83,452.13 249.83,452.13 C249.83,465.46 245.02,476.92 235.41,486.5 C226.21,495.67 215.13,500.25 202.17,500.25 C202.17,500.25 99.33,500.25 99.33,500.25 C86.37,500.25 75.08,495.67 65.47,486.5 C56.27,476.92 51.67,465.46 51.67,452.13c "
+ android:valueTo=" M0.25 74.63 C0.25,74.63 0.25,48.38 0.25,48.38 C0.25,35.04 4.85,23.79 14.05,14.63 C23.66,5.04 35.16,0.25 48.54,0.25 C48.54,0.25 252.97,0.25 252.97,0.25 C266.34,0.25 277.63,5.04 286.83,14.63 C296.44,23.79 301.25,35.04 301.25,48.38 C301.25,48.38 301.25,74.63 301.25,74.63 C301.25,74.63 0.25,74.63 0.25,74.63c M150.75 407.56 C160.78,407.56 169.35,404.02 176.46,396.94 C183.57,389.86 187.12,381.31 187.12,371.31 C187.12,361.31 183.57,352.77 176.46,345.69 C169.35,338.19 160.78,334.44 150.75,334.44 C140.72,334.44 132.15,337.98 125.04,345.06 C117.93,352.15 114.38,360.9 114.38,371.31 C114.38,381.31 117.93,389.86 125.04,396.94 C132.15,404.02 140.72,407.56 150.75,407.56c M51.67 452.13 C51.67,452.13 51.67,224.63 51.67,224.63 C51.67,224.63 7.78,160.88 7.78,160.88 C5.27,156.71 3.38,152.54 2.13,148.38 C0.88,143.79 0.25,139 0.25,134 C0.25,134 0.25,122.75 0.25,122.75 C0.25,122.75 301.25,122.75 301.25,122.75 C301.25,122.75 301.25,134 301.25,134 C301.25,139 300.62,143.79 299.37,148.38 C298.12,152.54 296.23,156.71 293.73,160.88 C293.73,160.88 249.83,224.63 249.83,224.63 C249.83,224.63 249.83,452.13 249.83,452.13 C249.83,465.46 245.02,476.92 235.41,486.5 C226.21,495.67 215.13,500.25 202.17,500.25 C202.17,500.25 99.33,500.25 99.33,500.25 C86.37,500.25 75.08,495.67 65.47,486.5 C56.27,476.92 51.67,465.46 51.67,452.13c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="17"
+ android:propertyName="pathData"
+ android:startOffset="150"
+ android:valueFrom=" M0.25 74.63 C0.25,74.63 0.25,48.38 0.25,48.38 C0.25,35.04 4.85,23.79 14.05,14.63 C23.66,5.04 35.16,0.25 48.54,0.25 C48.54,0.25 252.97,0.25 252.97,0.25 C266.34,0.25 277.63,5.04 286.83,14.63 C296.44,23.79 301.25,35.04 301.25,48.38 C301.25,48.38 301.25,74.63 301.25,74.63 C301.25,74.63 0.25,74.63 0.25,74.63c M150.75 407.56 C160.78,407.56 169.35,404.02 176.46,396.94 C183.57,389.86 187.12,381.31 187.12,371.31 C187.12,361.31 183.57,352.77 176.46,345.69 C169.35,338.19 160.78,334.44 150.75,334.44 C140.72,334.44 132.15,337.98 125.04,345.06 C117.93,352.15 114.38,360.9 114.38,371.31 C114.38,381.31 117.93,389.86 125.04,396.94 C132.15,404.02 140.72,407.56 150.75,407.56c M51.67 452.13 C51.67,452.13 51.67,224.63 51.67,224.63 C51.67,224.63 7.78,160.88 7.78,160.88 C5.27,156.71 3.38,152.54 2.13,148.38 C0.88,143.79 0.25,139 0.25,134 C0.25,134 0.25,122.75 0.25,122.75 C0.25,122.75 301.25,122.75 301.25,122.75 C301.25,122.75 301.25,134 301.25,134 C301.25,139 300.62,143.79 299.37,148.38 C298.12,152.54 296.23,156.71 293.73,160.88 C293.73,160.88 249.83,224.63 249.83,224.63 C249.83,224.63 249.83,452.13 249.83,452.13 C249.83,465.46 245.02,476.92 235.41,486.5 C226.21,495.67 215.13,500.25 202.17,500.25 C202.17,500.25 99.33,500.25 99.33,500.25 C86.37,500.25 75.08,495.67 65.47,486.5 C56.27,476.92 51.67,465.46 51.67,452.13c "
+ android:valueTo=" M0.25 74.63 C0.25,74.63 0.25,48.38 0.25,48.38 C0.25,35.04 4.85,23.79 14.05,14.63 C23.66,5.04 35.16,0.25 48.54,0.25 C48.54,0.25 252.97,0.25 252.97,0.25 C266.34,0.25 277.63,5.04 286.83,14.63 C296.44,23.79 301.25,35.04 301.25,48.38 C301.25,48.38 301.25,74.63 301.25,74.63 C301.25,74.63 0.25,74.63 0.25,74.63c M150.75 407.78 C160.78,407.78 169.35,404.24 176.46,397.16 C183.57,390.07 187.12,381.53 187.12,371.53 C187.12,361.53 183.57,352.99 176.46,345.91 C169.35,338.41 160.78,334.66 150.75,334.66 C140.72,334.66 132.15,338.2 125.04,345.28 C117.93,352.37 114.38,361.12 114.38,371.53 C114.38,381.53 117.93,390.07 125.04,397.16 C132.15,404.24 140.72,407.78 150.75,407.78c M51.67 452.13 C51.67,452.13 51.67,224.63 51.67,224.63 C51.67,224.63 7.78,160.88 7.78,160.88 C5.27,156.71 3.38,152.54 2.13,148.38 C0.88,143.79 0.25,139 0.25,134 C0.25,134 0.25,122.75 0.25,122.75 C0.25,122.75 301.25,122.75 301.25,122.75 C301.25,122.75 301.25,134 301.25,134 C301.25,139 300.62,143.79 299.37,148.38 C298.12,152.54 296.23,156.71 293.73,160.88 C293.73,160.88 249.83,224.63 249.83,224.63 C249.83,224.63 249.83,452.13 249.83,452.13 C249.83,465.46 245.02,476.92 235.41,486.5 C226.21,495.67 215.13,500.25 202.17,500.25 C202.17,500.25 99.33,500.25 99.33,500.25 C86.37,500.25 75.08,495.67 65.47,486.5 C56.27,476.92 51.67,465.46 51.67,452.13c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="17"
+ android:propertyName="pathData"
+ android:startOffset="167"
+ android:valueFrom=" M0.25 74.63 C0.25,74.63 0.25,48.38 0.25,48.38 C0.25,35.04 4.85,23.79 14.05,14.63 C23.66,5.04 35.16,0.25 48.54,0.25 C48.54,0.25 252.97,0.25 252.97,0.25 C266.34,0.25 277.63,5.04 286.83,14.63 C296.44,23.79 301.25,35.04 301.25,48.38 C301.25,48.38 301.25,74.63 301.25,74.63 C301.25,74.63 0.25,74.63 0.25,74.63c M150.75 407.78 C160.78,407.78 169.35,404.24 176.46,397.16 C183.57,390.07 187.12,381.53 187.12,371.53 C187.12,361.53 183.57,352.99 176.46,345.91 C169.35,338.41 160.78,334.66 150.75,334.66 C140.72,334.66 132.15,338.2 125.04,345.28 C117.93,352.37 114.38,361.12 114.38,371.53 C114.38,381.53 117.93,390.07 125.04,397.16 C132.15,404.24 140.72,407.78 150.75,407.78c M51.67 452.13 C51.67,452.13 51.67,224.63 51.67,224.63 C51.67,224.63 7.78,160.88 7.78,160.88 C5.27,156.71 3.38,152.54 2.13,148.38 C0.88,143.79 0.25,139 0.25,134 C0.25,134 0.25,122.75 0.25,122.75 C0.25,122.75 301.25,122.75 301.25,122.75 C301.25,122.75 301.25,134 301.25,134 C301.25,139 300.62,143.79 299.37,148.38 C298.12,152.54 296.23,156.71 293.73,160.88 C293.73,160.88 249.83,224.63 249.83,224.63 C249.83,224.63 249.83,452.13 249.83,452.13 C249.83,465.46 245.02,476.92 235.41,486.5 C226.21,495.67 215.13,500.25 202.17,500.25 C202.17,500.25 99.33,500.25 99.33,500.25 C86.37,500.25 75.08,495.67 65.47,486.5 C56.27,476.92 51.67,465.46 51.67,452.13c "
+ android:valueTo=" M0.25 74.63 C0.25,74.63 0.25,48.38 0.25,48.38 C0.25,35.04 4.85,23.79 14.05,14.63 C23.66,5.04 35.16,0.25 48.54,0.25 C48.54,0.25 252.97,0.25 252.97,0.25 C266.34,0.25 277.63,5.04 286.83,14.63 C296.44,23.79 301.25,35.04 301.25,48.38 C301.25,48.38 301.25,74.63 301.25,74.63 C301.25,74.63 0.25,74.63 0.25,74.63c M150.75 407.54 C160.78,407.54 169.35,404 176.46,396.92 C183.57,389.84 187.12,381.29 187.12,371.29 C187.12,361.29 183.57,352.75 176.46,345.67 C169.35,338.17 160.78,334.42 150.75,334.42 C140.72,334.42 132.15,337.96 125.04,345.04 C117.93,352.13 114.38,360.88 114.38,371.29 C114.38,381.29 117.93,389.84 125.04,396.92 C132.15,404 140.72,407.54 150.75,407.54c M51.67 452.13 C51.67,452.13 51.67,224.63 51.67,224.63 C51.67,224.63 7.78,160.88 7.78,160.88 C5.27,156.71 3.38,152.54 2.13,148.38 C0.88,143.79 0.25,139 0.25,134 C0.25,134 0.25,122.75 0.25,122.75 C0.25,122.75 301.25,122.75 301.25,122.75 C301.25,122.75 301.25,134 301.25,134 C301.25,139 300.62,143.79 299.37,148.38 C298.12,152.54 296.23,156.71 293.73,160.88 C293.73,160.88 249.83,224.63 249.83,224.63 C249.83,224.63 249.83,452.13 249.83,452.13 C249.83,465.46 245.02,476.92 235.41,486.5 C226.21,495.67 215.13,500.25 202.17,500.25 C202.17,500.25 99.33,500.25 99.33,500.25 C86.37,500.25 75.08,495.67 65.47,486.5 C56.27,476.92 51.67,465.46 51.67,452.13c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="133"
+ android:propertyName="pathData"
+ android:startOffset="183"
+ android:valueFrom=" M0.25 74.63 C0.25,74.63 0.25,48.38 0.25,48.38 C0.25,35.04 4.85,23.79 14.05,14.63 C23.66,5.04 35.16,0.25 48.54,0.25 C48.54,0.25 252.97,0.25 252.97,0.25 C266.34,0.25 277.63,5.04 286.83,14.63 C296.44,23.79 301.25,35.04 301.25,48.38 C301.25,48.38 301.25,74.63 301.25,74.63 C301.25,74.63 0.25,74.63 0.25,74.63c M150.75 407.54 C160.78,407.54 169.35,404 176.46,396.92 C183.57,389.84 187.12,381.29 187.12,371.29 C187.12,361.29 183.57,352.75 176.46,345.67 C169.35,338.17 160.78,334.42 150.75,334.42 C140.72,334.42 132.15,337.96 125.04,345.04 C117.93,352.13 114.38,360.88 114.38,371.29 C114.38,381.29 117.93,389.84 125.04,396.92 C132.15,404 140.72,407.54 150.75,407.54c M51.67 452.13 C51.67,452.13 51.67,224.63 51.67,224.63 C51.67,224.63 7.78,160.88 7.78,160.88 C5.27,156.71 3.38,152.54 2.13,148.38 C0.88,143.79 0.25,139 0.25,134 C0.25,134 0.25,122.75 0.25,122.75 C0.25,122.75 301.25,122.75 301.25,122.75 C301.25,122.75 301.25,134 301.25,134 C301.25,139 300.62,143.79 299.37,148.38 C298.12,152.54 296.23,156.71 293.73,160.88 C293.73,160.88 249.83,224.63 249.83,224.63 C249.83,224.63 249.83,452.13 249.83,452.13 C249.83,465.46 245.02,476.92 235.41,486.5 C226.21,495.67 215.13,500.25 202.17,500.25 C202.17,500.25 99.33,500.25 99.33,500.25 C86.37,500.25 75.08,495.67 65.47,486.5 C56.27,476.92 51.67,465.46 51.67,452.13c "
+ android:valueTo=" M0.25 74.63 C0.25,74.63 0.25,48.38 0.25,48.38 C0.25,35.04 4.85,23.79 14.05,14.63 C23.66,5.04 35.16,0.25 48.54,0.25 C48.54,0.25 252.97,0.25 252.97,0.25 C266.34,0.25 277.63,5.04 286.83,14.63 C296.44,23.79 301.25,35.04 301.25,48.38 C301.25,48.38 301.25,74.63 301.25,74.63 C301.25,74.63 0.25,74.63 0.25,74.63c M150.75 401.57 C160.78,401.57 169.35,398.02 176.46,390.94 C183.57,383.86 187.12,375.32 187.12,365.32 C187.12,355.32 183.57,346.77 176.46,339.69 C169.35,332.19 160.78,328.44 150.75,328.44 C140.72,328.44 132.15,331.98 125.04,339.07 C117.93,346.15 114.38,354.9 114.38,365.32 C114.38,375.32 117.93,383.86 125.04,390.94 C132.15,398.02 140.72,401.57 150.75,401.57c M51.67 452.13 C51.67,452.13 51.67,224.63 51.67,224.63 C51.67,224.63 7.78,160.88 7.78,160.88 C5.27,156.71 3.38,152.54 2.13,148.38 C0.88,143.79 0.25,139 0.25,134 C0.25,134 0.25,122.75 0.25,122.75 C0.25,122.75 301.25,122.75 301.25,122.75 C301.25,122.75 301.25,134 301.25,134 C301.25,139 300.62,143.79 299.37,148.38 C298.12,152.54 296.23,156.71 293.73,160.88 C293.73,160.88 249.83,224.63 249.83,224.63 C249.83,224.63 249.83,452.13 249.83,452.13 C249.83,465.46 245.02,476.92 235.41,486.5 C226.21,495.67 215.13,500.25 202.17,500.25 C202.17,500.25 99.33,500.25 99.33,500.25 C86.37,500.25 75.08,495.67 65.47,486.5 C56.27,476.92 51.67,465.46 51.67,452.13c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="17"
+ android:propertyName="pathData"
+ android:startOffset="317"
+ android:valueFrom=" M0.25 74.63 C0.25,74.63 0.25,48.38 0.25,48.38 C0.25,35.04 4.85,23.79 14.05,14.63 C23.66,5.04 35.16,0.25 48.54,0.25 C48.54,0.25 252.97,0.25 252.97,0.25 C266.34,0.25 277.63,5.04 286.83,14.63 C296.44,23.79 301.25,35.04 301.25,48.38 C301.25,48.38 301.25,74.63 301.25,74.63 C301.25,74.63 0.25,74.63 0.25,74.63c M150.75 401.57 C160.78,401.57 169.35,398.02 176.46,390.94 C183.57,383.86 187.12,375.32 187.12,365.32 C187.12,355.32 183.57,346.77 176.46,339.69 C169.35,332.19 160.78,328.44 150.75,328.44 C140.72,328.44 132.15,331.98 125.04,339.07 C117.93,346.15 114.38,354.9 114.38,365.32 C114.38,375.32 117.93,383.86 125.04,390.94 C132.15,398.02 140.72,401.57 150.75,401.57c M51.67 452.13 C51.67,452.13 51.67,224.63 51.67,224.63 C51.67,224.63 7.78,160.88 7.78,160.88 C5.27,156.71 3.38,152.54 2.13,148.38 C0.88,143.79 0.25,139 0.25,134 C0.25,134 0.25,122.75 0.25,122.75 C0.25,122.75 301.25,122.75 301.25,122.75 C301.25,122.75 301.25,134 301.25,134 C301.25,139 300.62,143.79 299.37,148.38 C298.12,152.54 296.23,156.71 293.73,160.88 C293.73,160.88 249.83,224.63 249.83,224.63 C249.83,224.63 249.83,452.13 249.83,452.13 C249.83,465.46 245.02,476.92 235.41,486.5 C226.21,495.67 215.13,500.25 202.17,500.25 C202.17,500.25 99.33,500.25 99.33,500.25 C86.37,500.25 75.08,495.67 65.47,486.5 C56.27,476.92 51.67,465.46 51.67,452.13c "
+ android:valueTo=" M0.25 74.63 C0.25,74.63 0.25,48.38 0.25,48.38 C0.25,35.04 4.85,23.79 14.05,14.63 C23.66,5.04 35.16,0.25 48.54,0.25 C48.54,0.25 252.97,0.25 252.97,0.25 C266.34,0.25 277.63,5.04 286.83,14.63 C296.44,23.79 301.25,35.04 301.25,48.38 C301.25,48.38 301.25,74.63 301.25,74.63 C301.25,74.63 0.25,74.63 0.25,74.63c M150.75 400.51 C160.78,400.51 169.35,396.97 176.46,389.89 C183.57,382.8 187.12,374.26 187.12,364.26 C187.12,354.26 183.57,345.72 176.46,338.64 C169.35,331.14 160.78,327.39 150.75,327.39 C140.72,327.39 132.15,330.93 125.04,338.01 C117.93,345.09 114.38,353.84 114.38,364.26 C114.38,374.26 117.93,382.8 125.04,389.89 C132.15,396.97 140.72,400.51 150.75,400.51c M51.67 452.13 C51.67,452.13 51.67,224.63 51.67,224.63 C51.67,224.63 7.78,160.88 7.78,160.88 C5.27,156.71 3.38,152.54 2.13,148.38 C0.88,143.79 0.25,139 0.25,134 C0.25,134 0.25,122.75 0.25,122.75 C0.25,122.75 301.25,122.75 301.25,122.75 C301.25,122.75 301.25,134 301.25,134 C301.25,139 300.62,143.79 299.37,148.38 C298.12,152.54 296.23,156.71 293.73,160.88 C293.73,160.88 249.83,224.63 249.83,224.63 C249.83,224.63 249.83,452.13 249.83,452.13 C249.83,465.46 245.02,476.92 235.41,486.5 C226.21,495.67 215.13,500.25 202.17,500.25 C202.17,500.25 99.33,500.25 99.33,500.25 C86.37,500.25 75.08,495.67 65.47,486.5 C56.27,476.92 51.67,465.46 51.67,452.13c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="17"
+ android:propertyName="pathData"
+ android:startOffset="333"
+ android:valueFrom=" M0.25 74.63 C0.25,74.63 0.25,48.38 0.25,48.38 C0.25,35.04 4.85,23.79 14.05,14.63 C23.66,5.04 35.16,0.25 48.54,0.25 C48.54,0.25 252.97,0.25 252.97,0.25 C266.34,0.25 277.63,5.04 286.83,14.63 C296.44,23.79 301.25,35.04 301.25,48.38 C301.25,48.38 301.25,74.63 301.25,74.63 C301.25,74.63 0.25,74.63 0.25,74.63c M150.75 400.51 C160.78,400.51 169.35,396.97 176.46,389.89 C183.57,382.8 187.12,374.26 187.12,364.26 C187.12,354.26 183.57,345.72 176.46,338.64 C169.35,331.14 160.78,327.39 150.75,327.39 C140.72,327.39 132.15,330.93 125.04,338.01 C117.93,345.09 114.38,353.84 114.38,364.26 C114.38,374.26 117.93,382.8 125.04,389.89 C132.15,396.97 140.72,400.51 150.75,400.51c M51.67 452.13 C51.67,452.13 51.67,224.63 51.67,224.63 C51.67,224.63 7.78,160.88 7.78,160.88 C5.27,156.71 3.38,152.54 2.13,148.38 C0.88,143.79 0.25,139 0.25,134 C0.25,134 0.25,122.75 0.25,122.75 C0.25,122.75 301.25,122.75 301.25,122.75 C301.25,122.75 301.25,134 301.25,134 C301.25,139 300.62,143.79 299.37,148.38 C298.12,152.54 296.23,156.71 293.73,160.88 C293.73,160.88 249.83,224.63 249.83,224.63 C249.83,224.63 249.83,452.13 249.83,452.13 C249.83,465.46 245.02,476.92 235.41,486.5 C226.21,495.67 215.13,500.25 202.17,500.25 C202.17,500.25 99.33,500.25 99.33,500.25 C86.37,500.25 75.08,495.67 65.47,486.5 C56.27,476.92 51.67,465.46 51.67,452.13c "
+ android:valueTo=" M0.25 74.63 C0.25,74.63 0.25,48.38 0.25,48.38 C0.25,35.04 4.85,23.79 14.05,14.63 C23.66,5.04 35.16,0.25 48.54,0.25 C48.54,0.25 252.97,0.25 252.97,0.25 C266.34,0.25 277.63,5.04 286.83,14.63 C296.44,23.79 301.25,35.04 301.25,48.38 C301.25,48.38 301.25,74.63 301.25,74.63 C301.25,74.63 0.25,74.63 0.25,74.63c M150.75 400.04 C160.78,400.04 169.35,396.5 176.46,389.42 C183.57,382.34 187.12,373.79 187.12,363.79 C187.12,353.79 183.57,345.25 176.46,338.17 C169.35,330.67 160.78,326.92 150.75,326.92 C140.72,326.92 132.15,330.46 125.04,337.54 C117.93,344.63 114.38,353.38 114.38,363.79 C114.38,373.79 117.93,382.34 125.04,389.42 C132.15,396.5 140.72,400.04 150.75,400.04c M51.67 452.13 C51.67,452.13 51.67,224.63 51.67,224.63 C51.67,224.63 7.78,160.88 7.78,160.88 C5.27,156.71 3.38,152.54 2.13,148.38 C0.88,143.79 0.25,139 0.25,134 C0.25,134 0.25,122.75 0.25,122.75 C0.25,122.75 301.25,122.75 301.25,122.75 C301.25,122.75 301.25,134 301.25,134 C301.25,139 300.62,143.79 299.37,148.38 C298.12,152.54 296.23,156.71 293.73,160.88 C293.73,160.88 249.83,224.63 249.83,224.63 C249.83,224.63 249.83,452.13 249.83,452.13 C249.83,465.46 245.02,476.92 235.41,486.5 C226.21,495.67 215.13,500.25 202.17,500.25 C202.17,500.25 99.33,500.25 99.33,500.25 C86.37,500.25 75.08,495.67 65.47,486.5 C56.27,476.92 51.67,465.46 51.67,452.13c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="17"
+ android:propertyName="pathData"
+ android:startOffset="350"
+ android:valueFrom=" M0.25 74.63 C0.25,74.63 0.25,48.38 0.25,48.38 C0.25,35.04 4.85,23.79 14.05,14.63 C23.66,5.04 35.16,0.25 48.54,0.25 C48.54,0.25 252.97,0.25 252.97,0.25 C266.34,0.25 277.63,5.04 286.83,14.63 C296.44,23.79 301.25,35.04 301.25,48.38 C301.25,48.38 301.25,74.63 301.25,74.63 C301.25,74.63 0.25,74.63 0.25,74.63c M150.75 400.04 C160.78,400.04 169.35,396.5 176.46,389.42 C183.57,382.34 187.12,373.79 187.12,363.79 C187.12,353.79 183.57,345.25 176.46,338.17 C169.35,330.67 160.78,326.92 150.75,326.92 C140.72,326.92 132.15,330.46 125.04,337.54 C117.93,344.63 114.38,353.38 114.38,363.79 C114.38,373.79 117.93,382.34 125.04,389.42 C132.15,396.5 140.72,400.04 150.75,400.04c M51.67 452.13 C51.67,452.13 51.67,224.63 51.67,224.63 C51.67,224.63 7.78,160.88 7.78,160.88 C5.27,156.71 3.38,152.54 2.13,148.38 C0.88,143.79 0.25,139 0.25,134 C0.25,134 0.25,122.75 0.25,122.75 C0.25,122.75 301.25,122.75 301.25,122.75 C301.25,122.75 301.25,134 301.25,134 C301.25,139 300.62,143.79 299.37,148.38 C298.12,152.54 296.23,156.71 293.73,160.88 C293.73,160.88 249.83,224.63 249.83,224.63 C249.83,224.63 249.83,452.13 249.83,452.13 C249.83,465.46 245.02,476.92 235.41,486.5 C226.21,495.67 215.13,500.25 202.17,500.25 C202.17,500.25 99.33,500.25 99.33,500.25 C86.37,500.25 75.08,495.67 65.47,486.5 C56.27,476.92 51.67,465.46 51.67,452.13c "
+ android:valueTo=" M0.25 74.63 C0.25,74.63 0.25,48.38 0.25,48.38 C0.25,35.04 4.85,23.79 14.05,14.63 C23.66,5.04 35.16,0.25 48.54,0.25 C48.54,0.25 252.97,0.25 252.97,0.25 C266.34,0.25 277.63,5.04 286.83,14.63 C296.44,23.79 301.25,35.04 301.25,48.38 C301.25,48.38 301.25,74.63 301.25,74.63 C301.25,74.63 0.25,74.63 0.25,74.63c M150.75 399.61 C160.78,399.61 169.35,396.07 176.46,388.99 C183.57,381.9 187.12,373.36 187.12,363.36 C187.12,353.36 183.57,344.82 176.46,337.74 C169.35,330.24 160.78,326.49 150.75,326.49 C140.72,326.49 132.15,330.03 125.04,337.11 C117.93,344.19 114.38,352.94 114.38,363.36 C114.38,373.36 117.93,381.9 125.04,388.99 C132.15,396.07 140.72,399.61 150.75,399.61c M51.67 452.13 C51.67,452.13 51.67,224.63 51.67,224.63 C51.67,224.63 7.78,160.88 7.78,160.88 C5.27,156.71 3.38,152.54 2.13,148.38 C0.88,143.79 0.25,139 0.25,134 C0.25,134 0.25,122.75 0.25,122.75 C0.25,122.75 301.25,122.75 301.25,122.75 C301.25,122.75 301.25,134 301.25,134 C301.25,139 300.62,143.79 299.37,148.38 C298.12,152.54 296.23,156.71 293.73,160.88 C293.73,160.88 249.83,224.63 249.83,224.63 C249.83,224.63 249.83,452.13 249.83,452.13 C249.83,465.46 245.02,476.92 235.41,486.5 C226.21,495.67 215.13,500.25 202.17,500.25 C202.17,500.25 99.33,500.25 99.33,500.25 C86.37,500.25 75.08,495.67 65.47,486.5 C56.27,476.92 51.67,465.46 51.67,452.13c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="17"
+ android:propertyName="pathData"
+ android:startOffset="367"
+ android:valueFrom=" M0.25 74.63 C0.25,74.63 0.25,48.38 0.25,48.38 C0.25,35.04 4.85,23.79 14.05,14.63 C23.66,5.04 35.16,0.25 48.54,0.25 C48.54,0.25 252.97,0.25 252.97,0.25 C266.34,0.25 277.63,5.04 286.83,14.63 C296.44,23.79 301.25,35.04 301.25,48.38 C301.25,48.38 301.25,74.63 301.25,74.63 C301.25,74.63 0.25,74.63 0.25,74.63c M150.75 399.61 C160.78,399.61 169.35,396.07 176.46,388.99 C183.57,381.9 187.12,373.36 187.12,363.36 C187.12,353.36 183.57,344.82 176.46,337.74 C169.35,330.24 160.78,326.49 150.75,326.49 C140.72,326.49 132.15,330.03 125.04,337.11 C117.93,344.19 114.38,352.94 114.38,363.36 C114.38,373.36 117.93,381.9 125.04,388.99 C132.15,396.07 140.72,399.61 150.75,399.61c M51.67 452.13 C51.67,452.13 51.67,224.63 51.67,224.63 C51.67,224.63 7.78,160.88 7.78,160.88 C5.27,156.71 3.38,152.54 2.13,148.38 C0.88,143.79 0.25,139 0.25,134 C0.25,134 0.25,122.75 0.25,122.75 C0.25,122.75 301.25,122.75 301.25,122.75 C301.25,122.75 301.25,134 301.25,134 C301.25,139 300.62,143.79 299.37,148.38 C298.12,152.54 296.23,156.71 293.73,160.88 C293.73,160.88 249.83,224.63 249.83,224.63 C249.83,224.63 249.83,452.13 249.83,452.13 C249.83,465.46 245.02,476.92 235.41,486.5 C226.21,495.67 215.13,500.25 202.17,500.25 C202.17,500.25 99.33,500.25 99.33,500.25 C86.37,500.25 75.08,495.67 65.47,486.5 C56.27,476.92 51.67,465.46 51.67,452.13c "
+ android:valueTo=" M0.25 74.63 C0.25,74.63 0.25,48.38 0.25,48.38 C0.25,35.04 4.85,23.79 14.05,14.63 C23.66,5.04 35.16,0.25 48.54,0.25 C48.54,0.25 252.97,0.25 252.97,0.25 C266.34,0.25 277.63,5.04 286.83,14.63 C296.44,23.79 301.25,35.04 301.25,48.38 C301.25,48.38 301.25,74.63 301.25,74.63 C301.25,74.63 0.25,74.63 0.25,74.63c M150.75 399.16 C160.78,399.16 169.35,395.62 176.46,388.54 C183.57,381.46 187.12,372.91 187.12,362.91 C187.12,352.91 183.57,344.37 176.46,337.29 C169.35,329.79 160.78,326.04 150.75,326.04 C140.72,326.04 132.15,329.58 125.04,336.66 C117.93,343.75 114.38,352.5 114.38,362.91 C114.38,372.91 117.93,381.46 125.04,388.54 C132.15,395.62 140.72,399.16 150.75,399.16c M51.67 452.13 C51.67,452.13 51.67,224.63 51.67,224.63 C51.67,224.63 7.78,160.88 7.78,160.88 C5.27,156.71 3.38,152.54 2.13,148.38 C0.88,143.79 0.25,139 0.25,134 C0.25,134 0.25,122.75 0.25,122.75 C0.25,122.75 301.25,122.75 301.25,122.75 C301.25,122.75 301.25,134 301.25,134 C301.25,139 300.62,143.79 299.37,148.38 C298.12,152.54 296.23,156.71 293.73,160.88 C293.73,160.88 249.83,224.63 249.83,224.63 C249.83,224.63 249.83,452.13 249.83,452.13 C249.83,465.46 245.02,476.92 235.41,486.5 C226.21,495.67 215.13,500.25 202.17,500.25 C202.17,500.25 99.33,500.25 99.33,500.25 C86.37,500.25 75.08,495.67 65.47,486.5 C56.27,476.92 51.67,465.46 51.67,452.13c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="17"
+ android:propertyName="pathData"
+ android:startOffset="383"
+ android:valueFrom=" M0.25 74.63 C0.25,74.63 0.25,48.38 0.25,48.38 C0.25,35.04 4.85,23.79 14.05,14.63 C23.66,5.04 35.16,0.25 48.54,0.25 C48.54,0.25 252.97,0.25 252.97,0.25 C266.34,0.25 277.63,5.04 286.83,14.63 C296.44,23.79 301.25,35.04 301.25,48.38 C301.25,48.38 301.25,74.63 301.25,74.63 C301.25,74.63 0.25,74.63 0.25,74.63c M150.75 399.16 C160.78,399.16 169.35,395.62 176.46,388.54 C183.57,381.46 187.12,372.91 187.12,362.91 C187.12,352.91 183.57,344.37 176.46,337.29 C169.35,329.79 160.78,326.04 150.75,326.04 C140.72,326.04 132.15,329.58 125.04,336.66 C117.93,343.75 114.38,352.5 114.38,362.91 C114.38,372.91 117.93,381.46 125.04,388.54 C132.15,395.62 140.72,399.16 150.75,399.16c M51.67 452.13 C51.67,452.13 51.67,224.63 51.67,224.63 C51.67,224.63 7.78,160.88 7.78,160.88 C5.27,156.71 3.38,152.54 2.13,148.38 C0.88,143.79 0.25,139 0.25,134 C0.25,134 0.25,122.75 0.25,122.75 C0.25,122.75 301.25,122.75 301.25,122.75 C301.25,122.75 301.25,134 301.25,134 C301.25,139 300.62,143.79 299.37,148.38 C298.12,152.54 296.23,156.71 293.73,160.88 C293.73,160.88 249.83,224.63 249.83,224.63 C249.83,224.63 249.83,452.13 249.83,452.13 C249.83,465.46 245.02,476.92 235.41,486.5 C226.21,495.67 215.13,500.25 202.17,500.25 C202.17,500.25 99.33,500.25 99.33,500.25 C86.37,500.25 75.08,495.67 65.47,486.5 C56.27,476.92 51.67,465.46 51.67,452.13c "
+ android:valueTo=" M0.25 74.63 C0.25,74.63 0.25,48.38 0.25,48.38 C0.25,35.04 4.85,23.79 14.05,14.63 C23.66,5.04 35.16,0.25 48.54,0.25 C48.54,0.25 252.97,0.25 252.97,0.25 C266.34,0.25 277.63,5.04 286.83,14.63 C296.44,23.79 301.25,35.04 301.25,48.38 C301.25,48.38 301.25,74.63 301.25,74.63 C301.25,74.63 0.25,74.63 0.25,74.63c M150.75 398.98 C160.78,398.98 169.35,395.43 176.46,388.35 C183.57,381.27 187.12,372.73 187.12,362.73 C187.12,352.73 183.57,344.18 176.46,337.1 C169.35,329.6 160.78,325.85 150.75,325.85 C140.72,325.85 132.15,329.39 125.04,336.48 C117.93,343.56 114.38,352.31 114.38,362.73 C114.38,372.73 117.93,381.27 125.04,388.35 C132.15,395.43 140.72,398.98 150.75,398.98c M51.67 452.13 C51.67,452.13 51.67,224.63 51.67,224.63 C51.67,224.63 7.78,160.88 7.78,160.88 C5.27,156.71 3.38,152.54 2.13,148.38 C0.88,143.79 0.25,139 0.25,134 C0.25,134 0.25,122.75 0.25,122.75 C0.25,122.75 301.25,122.75 301.25,122.75 C301.25,122.75 301.25,134 301.25,134 C301.25,139 300.62,143.79 299.37,148.38 C298.12,152.54 296.23,156.71 293.73,160.88 C293.73,160.88 249.83,224.63 249.83,224.63 C249.83,224.63 249.83,452.13 249.83,452.13 C249.83,465.46 245.02,476.92 235.41,486.5 C226.21,495.67 215.13,500.25 202.17,500.25 C202.17,500.25 99.33,500.25 99.33,500.25 C86.37,500.25 75.08,495.67 65.47,486.5 C56.27,476.92 51.67,465.46 51.67,452.13c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="17"
+ android:propertyName="pathData"
+ android:startOffset="400"
+ android:valueFrom=" M0.25 74.63 C0.25,74.63 0.25,48.38 0.25,48.38 C0.25,35.04 4.85,23.79 14.05,14.63 C23.66,5.04 35.16,0.25 48.54,0.25 C48.54,0.25 252.97,0.25 252.97,0.25 C266.34,0.25 277.63,5.04 286.83,14.63 C296.44,23.79 301.25,35.04 301.25,48.38 C301.25,48.38 301.25,74.63 301.25,74.63 C301.25,74.63 0.25,74.63 0.25,74.63c M150.75 398.98 C160.78,398.98 169.35,395.43 176.46,388.35 C183.57,381.27 187.12,372.73 187.12,362.73 C187.12,352.73 183.57,344.18 176.46,337.1 C169.35,329.6 160.78,325.85 150.75,325.85 C140.72,325.85 132.15,329.39 125.04,336.48 C117.93,343.56 114.38,352.31 114.38,362.73 C114.38,372.73 117.93,381.27 125.04,388.35 C132.15,395.43 140.72,398.98 150.75,398.98c M51.67 452.13 C51.67,452.13 51.67,224.63 51.67,224.63 C51.67,224.63 7.78,160.88 7.78,160.88 C5.27,156.71 3.38,152.54 2.13,148.38 C0.88,143.79 0.25,139 0.25,134 C0.25,134 0.25,122.75 0.25,122.75 C0.25,122.75 301.25,122.75 301.25,122.75 C301.25,122.75 301.25,134 301.25,134 C301.25,139 300.62,143.79 299.37,148.38 C298.12,152.54 296.23,156.71 293.73,160.88 C293.73,160.88 249.83,224.63 249.83,224.63 C249.83,224.63 249.83,452.13 249.83,452.13 C249.83,465.46 245.02,476.92 235.41,486.5 C226.21,495.67 215.13,500.25 202.17,500.25 C202.17,500.25 99.33,500.25 99.33,500.25 C86.37,500.25 75.08,495.67 65.47,486.5 C56.27,476.92 51.67,465.46 51.67,452.13c "
+ android:valueTo=" M0.25 74.63 C0.25,74.63 0.25,48.38 0.25,48.38 C0.25,35.04 4.85,23.79 14.05,14.63 C23.66,5.04 35.16,0.25 48.54,0.25 C48.54,0.25 252.97,0.25 252.97,0.25 C266.34,0.25 277.63,5.04 286.83,14.63 C296.44,23.79 301.25,35.04 301.25,48.38 C301.25,48.38 301.25,74.63 301.25,74.63 C301.25,74.63 0.25,74.63 0.25,74.63c M150.75 398.87 C160.78,398.87 169.35,395.33 176.46,388.25 C183.57,381.16 187.12,372.62 187.12,362.62 C187.12,352.62 183.57,344.08 176.46,337 C169.35,329.5 160.78,325.75 150.75,325.75 C140.72,325.75 132.15,329.29 125.04,336.37 C117.93,343.45 114.38,352.2 114.38,362.62 C114.38,372.62 117.93,381.16 125.04,388.25 C132.15,395.33 140.72,398.87 150.75,398.87c M51.67 452.13 C51.67,452.13 51.67,224.63 51.67,224.63 C51.67,224.63 7.78,160.88 7.78,160.88 C5.27,156.71 3.38,152.54 2.13,148.38 C0.88,143.79 0.25,139 0.25,134 C0.25,134 0.25,122.75 0.25,122.75 C0.25,122.75 301.25,122.75 301.25,122.75 C301.25,122.75 301.25,134 301.25,134 C301.25,139 300.62,143.79 299.37,148.38 C298.12,152.54 296.23,156.71 293.73,160.88 C293.73,160.88 249.83,224.63 249.83,224.63 C249.83,224.63 249.83,452.13 249.83,452.13 C249.83,465.46 245.02,476.92 235.41,486.5 C226.21,495.67 215.13,500.25 202.17,500.25 C202.17,500.25 99.33,500.25 99.33,500.25 C86.37,500.25 75.08,495.67 65.47,486.5 C56.27,476.92 51.67,465.46 51.67,452.13c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="17"
+ android:propertyName="pathData"
+ android:startOffset="417"
+ android:valueFrom=" M0.25 74.63 C0.25,74.63 0.25,48.38 0.25,48.38 C0.25,35.04 4.85,23.79 14.05,14.63 C23.66,5.04 35.16,0.25 48.54,0.25 C48.54,0.25 252.97,0.25 252.97,0.25 C266.34,0.25 277.63,5.04 286.83,14.63 C296.44,23.79 301.25,35.04 301.25,48.38 C301.25,48.38 301.25,74.63 301.25,74.63 C301.25,74.63 0.25,74.63 0.25,74.63c M150.75 398.87 C160.78,398.87 169.35,395.33 176.46,388.25 C183.57,381.16 187.12,372.62 187.12,362.62 C187.12,352.62 183.57,344.08 176.46,337 C169.35,329.5 160.78,325.75 150.75,325.75 C140.72,325.75 132.15,329.29 125.04,336.37 C117.93,343.45 114.38,352.2 114.38,362.62 C114.38,372.62 117.93,381.16 125.04,388.25 C132.15,395.33 140.72,398.87 150.75,398.87c M51.67 452.13 C51.67,452.13 51.67,224.63 51.67,224.63 C51.67,224.63 7.78,160.88 7.78,160.88 C5.27,156.71 3.38,152.54 2.13,148.38 C0.88,143.79 0.25,139 0.25,134 C0.25,134 0.25,122.75 0.25,122.75 C0.25,122.75 301.25,122.75 301.25,122.75 C301.25,122.75 301.25,134 301.25,134 C301.25,139 300.62,143.79 299.37,148.38 C298.12,152.54 296.23,156.71 293.73,160.88 C293.73,160.88 249.83,224.63 249.83,224.63 C249.83,224.63 249.83,452.13 249.83,452.13 C249.83,465.46 245.02,476.92 235.41,486.5 C226.21,495.67 215.13,500.25 202.17,500.25 C202.17,500.25 99.33,500.25 99.33,500.25 C86.37,500.25 75.08,495.67 65.47,486.5 C56.27,476.92 51.67,465.46 51.67,452.13c "
+ android:valueTo=" M0.25 74.63 C0.25,74.63 0.25,48.38 0.25,48.38 C0.25,35.04 4.85,23.79 14.05,14.63 C23.66,5.04 35.16,0.25 48.54,0.25 C48.54,0.25 252.97,0.25 252.97,0.25 C266.34,0.25 277.63,5.04 286.83,14.63 C296.44,23.79 301.25,35.04 301.25,48.38 C301.25,48.38 301.25,74.63 301.25,74.63 C301.25,74.63 0.25,74.63 0.25,74.63c M150.75 398.66 C160.78,398.66 169.35,395.12 176.46,388.04 C183.57,380.96 187.12,372.41 187.12,362.41 C187.12,352.41 183.57,343.87 176.46,336.79 C169.35,329.29 160.78,325.54 150.75,325.54 C140.72,325.54 132.15,329.08 125.04,336.16 C117.93,343.25 114.38,352 114.38,362.41 C114.38,372.41 117.93,380.96 125.04,388.04 C132.15,395.12 140.72,398.66 150.75,398.66c M51.67 452.13 C51.67,452.13 51.67,224.63 51.67,224.63 C51.67,224.63 7.78,160.88 7.78,160.88 C5.27,156.71 3.38,152.54 2.13,148.38 C0.88,143.79 0.25,139 0.25,134 C0.25,134 0.25,122.75 0.25,122.75 C0.25,122.75 301.25,122.75 301.25,122.75 C301.25,122.75 301.25,134 301.25,134 C301.25,139 300.62,143.79 299.37,148.38 C298.12,152.54 296.23,156.71 293.73,160.88 C293.73,160.88 249.83,224.63 249.83,224.63 C249.83,224.63 249.83,452.13 249.83,452.13 C249.83,465.46 245.02,476.92 235.41,486.5 C226.21,495.67 215.13,500.25 202.17,500.25 C202.17,500.25 99.33,500.25 99.33,500.25 C86.37,500.25 75.08,495.67 65.47,486.5 C56.27,476.92 51.67,465.46 51.67,452.13c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="17"
+ android:propertyName="pathData"
+ android:startOffset="433"
+ android:valueFrom=" M0.25 74.63 C0.25,74.63 0.25,48.38 0.25,48.38 C0.25,35.04 4.85,23.79 14.05,14.63 C23.66,5.04 35.16,0.25 48.54,0.25 C48.54,0.25 252.97,0.25 252.97,0.25 C266.34,0.25 277.63,5.04 286.83,14.63 C296.44,23.79 301.25,35.04 301.25,48.38 C301.25,48.38 301.25,74.63 301.25,74.63 C301.25,74.63 0.25,74.63 0.25,74.63c M150.75 398.66 C160.78,398.66 169.35,395.12 176.46,388.04 C183.57,380.96 187.12,372.41 187.12,362.41 C187.12,352.41 183.57,343.87 176.46,336.79 C169.35,329.29 160.78,325.54 150.75,325.54 C140.72,325.54 132.15,329.08 125.04,336.16 C117.93,343.25 114.38,352 114.38,362.41 C114.38,372.41 117.93,380.96 125.04,388.04 C132.15,395.12 140.72,398.66 150.75,398.66c M51.67 452.13 C51.67,452.13 51.67,224.63 51.67,224.63 C51.67,224.63 7.78,160.88 7.78,160.88 C5.27,156.71 3.38,152.54 2.13,148.38 C0.88,143.79 0.25,139 0.25,134 C0.25,134 0.25,122.75 0.25,122.75 C0.25,122.75 301.25,122.75 301.25,122.75 C301.25,122.75 301.25,134 301.25,134 C301.25,139 300.62,143.79 299.37,148.38 C298.12,152.54 296.23,156.71 293.73,160.88 C293.73,160.88 249.83,224.63 249.83,224.63 C249.83,224.63 249.83,452.13 249.83,452.13 C249.83,465.46 245.02,476.92 235.41,486.5 C226.21,495.67 215.13,500.25 202.17,500.25 C202.17,500.25 99.33,500.25 99.33,500.25 C86.37,500.25 75.08,495.67 65.47,486.5 C56.27,476.92 51.67,465.46 51.67,452.13c "
+ android:valueTo=" M0.25 74.63 C0.25,74.63 0.25,48.38 0.25,48.38 C0.25,35.04 4.85,23.79 14.05,14.63 C23.66,5.04 35.16,0.25 48.54,0.25 C48.54,0.25 252.97,0.25 252.97,0.25 C266.34,0.25 277.63,5.04 286.83,14.63 C296.44,23.79 301.25,35.04 301.25,48.38 C301.25,48.38 301.25,74.63 301.25,74.63 C301.25,74.63 0.25,74.63 0.25,74.63c M150.75 398.69 C160.78,398.69 169.35,395.15 176.46,388.06 C183.57,380.98 187.12,372.44 187.12,362.44 C187.12,352.44 183.57,343.9 176.46,336.81 C169.35,329.31 160.78,325.56 150.75,325.56 C140.72,325.56 132.15,329.11 125.04,336.19 C117.93,343.27 114.38,352.02 114.38,362.44 C114.38,372.44 117.93,380.98 125.04,388.06 C132.15,395.15 140.72,398.69 150.75,398.69c M51.67 452.13 C51.67,452.13 51.67,224.63 51.67,224.63 C51.67,224.63 7.78,160.88 7.78,160.88 C5.27,156.71 3.38,152.54 2.13,148.38 C0.88,143.79 0.25,139 0.25,134 C0.25,134 0.25,122.75 0.25,122.75 C0.25,122.75 301.25,122.75 301.25,122.75 C301.25,122.75 301.25,134 301.25,134 C301.25,139 300.62,143.79 299.37,148.38 C298.12,152.54 296.23,156.71 293.73,160.88 C293.73,160.88 249.83,224.63 249.83,224.63 C249.83,224.63 249.83,452.13 249.83,452.13 C249.83,465.46 245.02,476.92 235.41,486.5 C226.21,495.67 215.13,500.25 202.17,500.25 C202.17,500.25 99.33,500.25 99.33,500.25 C86.37,500.25 75.08,495.67 65.47,486.5 C56.27,476.92 51.67,465.46 51.67,452.13c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="time_group">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="467"
+ android:propertyName="translateX"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType" />
+ </set>
+ </aapt:attr>
+ </target>
+ <aapt:attr name="android:drawable">
+ <vector
+ android:width="64dp"
+ android:height="64dp"
+ android:viewportHeight="64"
+ android:viewportWidth="64">
+ <group android:name="_R_G">
+ <group
+ android:name="_R_G_L_0_G"
+ android:pivotX="150.75"
+ android:pivotY="250.25"
+ android:scaleX="0.1125"
+ android:scaleY="0.1125"
+ android:translateX="-118.75"
+ android:translateY="-218.5">
+ <path
+ android:name="_R_G_L_0_G_D_0_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#000000"
+ android:fillType="nonZero"
+ android:pathData=" M0.25 74.63 C0.25,74.63 0.25,48.38 0.25,48.38 C0.25,35.04 4.85,23.79 14.05,14.63 C23.66,5.04 35.16,0.25 48.54,0.25 C48.54,0.25 252.97,0.25 252.97,0.25 C266.34,0.25 277.63,5.04 286.83,14.63 C296.44,23.79 301.25,35.04 301.25,48.38 C301.25,48.38 301.25,74.63 301.25,74.63 C301.25,74.63 0.25,74.63 0.25,74.63c M150.75 339 C160.78,339 169.35,335.46 176.46,328.38 C183.57,321.29 187.12,312.75 187.12,302.75 C187.12,292.75 183.57,284.21 176.46,277.13 C169.35,269.63 160.78,265.88 150.75,265.88 C140.72,265.88 132.15,269.42 125.04,276.5 C117.93,283.58 114.38,292.33 114.38,302.75 C114.38,312.75 117.93,321.29 125.04,328.38 C132.15,335.46 140.72,339 150.75,339c M51.67 452.13 C51.67,452.13 51.67,224.63 51.67,224.63 C51.67,224.63 7.78,160.88 7.78,160.88 C5.27,156.71 3.38,152.54 2.13,148.38 C0.88,143.79 0.25,139 0.25,134 C0.25,134 0.25,122.75 0.25,122.75 C0.25,122.75 301.25,122.75 301.25,122.75 C301.25,122.75 301.25,134 301.25,134 C301.25,139 300.62,143.79 299.37,148.38 C298.12,152.54 296.23,156.71 293.73,160.88 C293.73,160.88 249.83,224.63 249.83,224.63 C249.83,224.63 249.83,452.13 249.83,452.13 C249.83,465.46 245.02,476.92 235.41,486.5 C226.21,495.67 215.13,500.25 202.17,500.25 C202.17,500.25 99.33,500.25 99.33,500.25 C86.37,500.25 75.08,495.67 65.47,486.5 C56.27,476.92 51.67,465.46 51.67,452.13c " />
+ </group>
+ </group>
+ <group android:name="time_group" />
+ </vector>
+ </aapt:attr>
+</animated-vector>
diff --git a/packages/SystemUI/res/layout/biometric_prompt_one_pane_layout.xml b/packages/SystemUI/res/layout/biometric_prompt_one_pane_layout.xml
index 43808f2..8ca2ee0 100644
--- a/packages/SystemUI/res/layout/biometric_prompt_one_pane_layout.xml
+++ b/packages/SystemUI/res/layout/biometric_prompt_one_pane_layout.xml
@@ -11,6 +11,7 @@
android:layout_width="0dp"
android:layout_height="0dp"
android:contentDescription="@string/cancel"
+ android:importantForAccessibility="no"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
diff --git a/packages/SystemUI/res/layout/biometric_prompt_two_pane_layout.xml b/packages/SystemUI/res/layout/biometric_prompt_two_pane_layout.xml
index 51117a7..aefac9f 100644
--- a/packages/SystemUI/res/layout/biometric_prompt_two_pane_layout.xml
+++ b/packages/SystemUI/res/layout/biometric_prompt_two_pane_layout.xml
@@ -12,6 +12,7 @@
android:layout_width="0dp"
android:layout_height="0dp"
android:contentDescription="@string/cancel"
+ android:importantForAccessibility="no"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
diff --git a/packages/SystemUI/res/layout/bluetooth_tile_details.xml b/packages/SystemUI/res/layout/bluetooth_tile_details.xml
new file mode 100644
index 0000000..cfb509d
--- /dev/null
+++ b/packages/SystemUI/res/layout/bluetooth_tile_details.xml
@@ -0,0 +1,254 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2025 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<androidx.constraintlayout.widget.ConstraintLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:id="@+id/root"
+ style="@style/Widget.SliceView.Panel"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+
+ <View
+ android:id="@+id/bluetooth_tile_dialog_progress_background"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ app:layout_constraintEnd_toEndOf="@id/bluetooth_tile_dialog_progress_animation"
+ app:layout_constraintStart_toStartOf="@id/bluetooth_tile_dialog_progress_animation"
+ app:layout_constraintTop_toTopOf="@id/bluetooth_tile_dialog_progress_animation"
+ app:layout_constraintBottom_toBottomOf="@id/bluetooth_tile_dialog_progress_animation"
+ android:background="?androidprv:attr/colorSurfaceVariant" />
+
+ <ProgressBar
+ android:id="@+id/bluetooth_tile_dialog_progress_animation"
+ android:layout_width="152dp"
+ android:layout_height="4dp"
+ android:layout_marginTop="16dp"
+ style="@style/TrimmedHorizontalProgressBar"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ android:visibility="invisible"
+ android:indeterminate="true" />
+
+ <androidx.core.widget.NestedScrollView
+ android:id="@+id/scroll_view"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="21dp"
+ android:minHeight="@dimen/bluetooth_dialog_scroll_view_min_height"
+ android:fillViewport="true"
+ app:layout_constrainedHeight="true"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/bluetooth_tile_dialog_progress_animation">
+
+ <androidx.constraintlayout.widget.ConstraintLayout
+ android:id="@+id/scroll_layout"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" >
+
+ <TextView
+ android:id="@+id/bluetooth_toggle_title"
+ android:layout_width="0dp"
+ android:layout_height="68dp"
+ android:maxLines="2"
+ android:ellipsize="end"
+ android:gravity="start|center_vertical"
+ android:paddingEnd="15dp"
+ android:paddingStart="36dp"
+ android:text="@string/turn_on_bluetooth"
+ android:clickable="false"
+ android:textAppearance="@style/TextAppearance.BluetoothTileDialog"
+ android:textSize="16sp"
+ app:layout_constraintEnd_toStartOf="@+id/bluetooth_toggle"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent" />
+
+ <Switch
+ android:id="@+id/bluetooth_toggle"
+ android:layout_width="wrap_content"
+ android:layout_height="68dp"
+ android:gravity="start|center_vertical"
+ android:paddingEnd="40dp"
+ android:contentDescription="@string/turn_on_bluetooth"
+ android:switchMinWidth="@dimen/settingslib_switch_track_width"
+ android:theme="@style/MainSwitch.Settingslib"
+ android:thumb="@drawable/settingslib_switch_thumb"
+ android:track="@drawable/settingslib_switch_track"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toEndOf="@+id/bluetooth_toggle_title"
+ app:layout_constraintTop_toTopOf="parent" />
+
+ <androidx.constraintlayout.widget.Group
+ android:id="@+id/bluetooth_auto_on_toggle_layout"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:visibility="gone"
+ app:constraint_referenced_ids="bluetooth_auto_on_toggle_title,bluetooth_auto_on_toggle,bluetooth_auto_on_toggle_info_icon,bluetooth_auto_on_toggle_info_text" />
+
+ <TextView
+ android:id="@+id/bluetooth_auto_on_toggle_title"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:minHeight="68dp"
+ android:layout_marginBottom="20dp"
+ android:maxLines="2"
+ android:ellipsize="end"
+ android:text="@string/turn_on_bluetooth_auto_tomorrow"
+ android:gravity="start|center_vertical"
+ android:paddingEnd="15dp"
+ android:paddingStart="36dp"
+ android:clickable="false"
+ android:textAppearance="@style/TextAppearance.BluetoothTileDialog"
+ android:textSize="16sp"
+ app:layout_constraintEnd_toStartOf="@+id/bluetooth_auto_on_toggle"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@+id/bluetooth_toggle_title" />
+
+ <Switch
+ android:id="@+id/bluetooth_auto_on_toggle"
+ android:layout_width="wrap_content"
+ android:layout_height="68dp"
+ android:layout_marginBottom="20dp"
+ android:gravity="start|center_vertical"
+ android:paddingEnd="40dp"
+ android:contentDescription="@string/turn_on_bluetooth_auto_tomorrow"
+ android:switchMinWidth="@dimen/settingslib_switch_track_width"
+ android:theme="@style/MainSwitch.Settingslib"
+ android:thumb="@drawable/settingslib_switch_thumb"
+ android:track="@drawable/settingslib_switch_track"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toEndOf="@+id/bluetooth_auto_on_toggle_title"
+ app:layout_constraintTop_toBottomOf="@+id/bluetooth_toggle" />
+
+ <ImageView
+ android:id="@+id/bluetooth_auto_on_toggle_info_icon"
+ android:src="@drawable/ic_info_outline"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:tint="?android:attr/textColorTertiary"
+ android:paddingStart="36dp"
+ android:layout_marginTop="20dp"
+ android:layout_marginBottom="@dimen/bluetooth_dialog_layout_margin"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@+id/bluetooth_auto_on_toggle" />
+
+ <TextView
+ android:id="@+id/bluetooth_auto_on_toggle_info_text"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="20dp"
+ android:paddingStart="36dp"
+ android:paddingEnd="40dp"
+ android:textSize="14sp"
+ android:textAppearance="@style/TextAppearance.BluetoothTileDialog"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/bluetooth_auto_on_toggle_info_icon" />
+
+ <androidx.recyclerview.widget.RecyclerView
+ android:id="@+id/device_list"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintTop_toBottomOf="@+id/bluetooth_toggle" />
+
+ <Button
+ android:id="@+id/see_all_button"
+ style="@style/BluetoothTileDialog.Device"
+ android:paddingEnd="0dp"
+ android:paddingStart="26dp"
+ android:background="@drawable/bluetooth_tile_dialog_bg_off"
+ android:layout_width="0dp"
+ android:layout_height="64dp"
+ android:contentDescription="@string/accessibility_bluetooth_device_settings_see_all"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintTop_toBottomOf="@+id/device_list"
+ app:layout_constraintBottom_toTopOf="@+id/pair_new_device_button"
+ android:drawableStart="@drawable/ic_arrow_forward"
+ android:drawablePadding="26dp"
+ android:drawableTint="?android:attr/textColorPrimary"
+ android:text="@string/see_all_bluetooth_devices"
+ android:textSize="14sp"
+ android:textAppearance="@style/TextAppearance.BluetoothTileDialog"
+ android:textDirection="locale"
+ android:textAlignment="viewStart"
+ android:maxLines="1"
+ android:ellipsize="end"
+ android:visibility="gone" />
+
+ <Button
+ android:id="@+id/pair_new_device_button"
+ style="@style/BluetoothTileDialog.Device"
+ android:paddingEnd="0dp"
+ android:paddingStart="26dp"
+ android:background="@drawable/bluetooth_tile_dialog_bg_off"
+ android:layout_width="0dp"
+ android:layout_height="64dp"
+ android:contentDescription="@string/accessibility_bluetooth_device_settings_pair_new_device"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintTop_toBottomOf="@+id/see_all_button"
+ android:drawableStart="@drawable/ic_add"
+ android:drawablePadding="26dp"
+ android:drawableTint="?android:attr/textColorPrimary"
+ android:text="@string/pair_new_bluetooth_devices"
+ android:textSize="14sp"
+ android:textAppearance="@style/TextAppearance.BluetoothTileDialog"
+ android:textDirection="locale"
+ android:textAlignment="viewStart"
+ android:maxLines="1"
+ android:ellipsize="end"
+ android:visibility="gone" />
+
+ <androidx.constraintlayout.widget.Barrier
+ android:id="@+id/barrier"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ app:barrierDirection="bottom"
+ app:constraint_referenced_ids="pair_new_device_button,bluetooth_auto_on_toggle_info_text" />
+
+ <Button
+ android:id="@+id/audio_sharing_button"
+ style="@style/BluetoothTileDialog.AudioSharingButton"
+ android:textAppearance="@style/TextAppearance.BluetoothTileDialog"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="9dp"
+ android:layout_marginBottom="@dimen/dialog_bottom_padding"
+ android:layout_marginEnd="@dimen/dialog_side_padding"
+ android:layout_marginStart="@dimen/dialog_side_padding"
+ android:ellipsize="end"
+ android:maxLines="1"
+ android:text="@string/quick_settings_bluetooth_audio_sharing_button"
+ android:drawableStart="@drawable/ic_bt_le_audio_sharing_18dp"
+ android:drawablePadding="10dp"
+ app:layout_constrainedWidth="true"
+ app:layout_constraintHorizontal_bias="0"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintTop_toBottomOf="@+id/barrier"
+ app:layout_constraintVertical_bias="1"
+ android:visibility="gone" />
+ </androidx.constraintlayout.widget.ConstraintLayout>
+ </androidx.core.widget.NestedScrollView>
+</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/bundled_notification_info.xml b/packages/SystemUI/res/layout/bundled_notification_info.xml
new file mode 100644
index 0000000..9c5ec65d
--- /dev/null
+++ b/packages/SystemUI/res/layout/bundled_notification_info.xml
@@ -0,0 +1,218 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2025, The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<com.android.systemui.statusbar.notification.row.BundledNotificationInfo
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:id="@+id/notification_guts"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:focusable="true"
+ android:clipChildren="false"
+ android:clipToPadding="true"
+ android:orientation="vertical"
+ android:paddingStart="@*android:dimen/notification_2025_margin">
+
+ <!-- Package Info -->
+ <LinearLayout
+ android:id="@+id/header"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:clipChildren="false"
+ android:clipToPadding="true">
+ <ImageView
+ android:id="@+id/pkg_icon"
+ android:layout_width="@*android:dimen/notification_2025_icon_circle_size"
+ android:layout_height="@*android:dimen/notification_2025_icon_circle_size"
+ android:layout_marginTop="@*android:dimen/notification_2025_margin"
+ android:layout_marginEnd="@*android:dimen/notification_2025_margin" />
+ <LinearLayout
+ android:id="@+id/names"
+ android:layout_weight="1"
+ android:layout_width="0dp"
+ android:orientation="vertical"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@*android:dimen/notification_2025_margin"
+ android:minHeight="@*android:dimen/notification_2025_icon_circle_size">
+ <TextView
+ android:id="@+id/channel_name"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textDirection="locale"
+ style="@style/TextAppearance.NotificationImportanceChannel"/>
+ <TextView
+ android:id="@+id/group_name"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textDirection="locale"
+ android:ellipsize="end"
+ style="@style/TextAppearance.NotificationImportanceChannelGroup"/>
+ <TextView
+ android:id="@+id/pkg_name"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ style="@style/TextAppearance.NotificationImportanceApp"
+ android:ellipsize="end"
+ android:textDirection="locale"
+ android:maxLines="1"/>
+ <TextView
+ android:id="@+id/delegate_name"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ style="@style/TextAppearance.NotificationImportanceHeader"
+ android:ellipsize="end"
+ android:textDirection="locale"
+ android:text="@string/notification_delegate_header"
+ android:maxLines="1" />
+
+ </LinearLayout>
+
+ <!-- feedback for notificationassistantservice -->
+ <ImageButton
+ android:id="@+id/feedback"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:visibility="gone"
+ android:background="@drawable/ripple_drawable"
+ android:contentDescription="@string/notification_guts_bundle_feedback"
+ android:src="@*android:drawable/ic_feedback"
+ android:padding="@*android:dimen/notification_2025_margin"
+ android:tint="@androidprv:color/materialColorPrimary"/>
+
+ <!-- Optional link to app. Only appears if the channel is not disabled and the app
+ asked for it -->
+ <ImageButton
+ android:id="@+id/app_settings"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:visibility="gone"
+ android:background="@drawable/ripple_drawable"
+ android:contentDescription="@string/notification_app_settings"
+ android:src="@drawable/ic_info"
+ android:padding="@*android:dimen/notification_2025_margin"
+ android:tint="@androidprv:color/materialColorPrimary"/>
+
+ <!-- System notification settings -->
+ <ImageButton
+ android:id="@+id/info"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:contentDescription="@string/notification_more_settings"
+ android:background="@drawable/ripple_drawable"
+ android:src="@drawable/ic_settings"
+ android:padding="@*android:dimen/notification_2025_margin"
+ android:tint="@androidprv:color/materialColorPrimary" />
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/inline_controls"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="@*android:dimen/notification_2025_margin"
+ android:layout_marginTop="@*android:dimen/notification_2025_margin"
+ android:layout_marginBottom="@*android:dimen/notification_2025_margin"
+ android:clipChildren="false"
+ android:clipToPadding="false"
+ android:orientation="vertical">
+
+ <RelativeLayout
+ android:id="@+id/classification_toggle"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingVertical="@dimen/notification_2025_importance_button_padding_vertical"
+ android:paddingHorizontal="@dimen/notification_2025_importance_button_padding_horizontal"
+ android:background="@drawable/notification_2025_guts_feature_toggle_bg"
+ android:orientation="horizontal">
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:layout_alignParentStart="true"
+ android:gravity="start|center_vertical">
+ <TextView
+ android:id="@+id/feature_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:maxLines="1"
+ android:clickable="false"
+ android:focusable="false"
+ android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
+ android:textSize="16sp"
+ android:textColor="@androidprv:color/materialColorOnSecondaryContainer"
+ android:lineSpacingExtra="4sp"
+ android:text="@string/notification_guts_bundle_title"/>
+ <TextView
+ android:id="@+id/feature_summary"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:clickable="false"
+ android:focusable="false"
+ android:maxLines="1"
+ android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
+ android:textSize="14sp"
+ android:textColor="@androidprv:color/materialColorOnSecondaryContainer"
+ android:lineSpacingExtra="4sp" />
+ </LinearLayout>
+ <com.google.android.material.materialswitch.MaterialSwitch
+ android:theme="@style/Theme.Material3.DynamicColors.DayNight"
+ android:id="@+id/feature_toggle"
+ android:filterTouchesWhenObscured="false"
+ android:clickable="true"
+ android:focusable="true"
+ android:padding="8dp"
+ android:layout_height="48dp"
+ android:layout_width="wrap_content"
+ android:gravity="end|center_vertical"
+ android:layout_alignParentEnd="true"
+ style="@style/SettingslibSwitchStyle.Expressive"/>
+ </RelativeLayout>
+ <RelativeLayout
+ android:id="@+id/bottom_buttons"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="60dp"
+ android:gravity="center_vertical"
+ android:paddingStart="4dp"
+ android:paddingEnd="4dp"
+ >
+ <TextView
+ android:id="@+id/inline_dismiss"
+ android:text="@string/notification_inline_dismiss"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentStart="true"
+ android:gravity="start|center_vertical"
+ android:minWidth="@dimen/notification_importance_toggle_size"
+ android:minHeight="@dimen/notification_importance_toggle_size"
+ android:maxWidth="200dp"
+ android:paddingEnd="@dimen/notification_importance_button_padding"
+ style="@style/TextAppearance.NotificationInfo.Button"/>
+ <TextView
+ android:id="@+id/done"
+ android:text="@string/inline_ok_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentEnd="true"
+ android:gravity="end|center_vertical"
+ android:minWidth="@dimen/notification_importance_toggle_size"
+ android:minHeight="@dimen/notification_importance_toggle_size"
+ android:maxWidth="125dp"
+ style="@style/TextAppearance.NotificationInfo.Button"/>
+ </RelativeLayout>
+ </LinearLayout>
+</com.android.systemui.statusbar.notification.row.BundledNotificationInfo>
diff --git a/packages/SystemUI/res/layout/internet_connectivity_details.xml b/packages/SystemUI/res/layout/internet_connectivity_details.xml
index 91f5ec5..2fd9a09 100644
--- a/packages/SystemUI/res/layout/internet_connectivity_details.xml
+++ b/packages/SystemUI/res/layout/internet_connectivity_details.xml
@@ -339,6 +339,8 @@
<LinearLayout
android:id="@+id/see_all_layout"
style="@style/InternetDialog.Network"
+ android:background="@drawable/settingslib_entry_bg_off_end"
+ android:layout_marginTop="@dimen/tile_details_entry_gap"
android:layout_height="64dp"
android:paddingStart="20dp">
@@ -422,7 +424,6 @@
android:id="@+id/button_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginBottom="@dimen/dialog_bottom_padding"
android:layout_marginEnd="@dimen/dialog_side_padding"
android:layout_marginStart="@dimen/dialog_side_padding"
android:layout_marginTop="8dp"
@@ -435,6 +436,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="10dp"
+ android:layout_marginBottom="@dimen/dialog_bottom_padding"
android:clickable="true"
android:ellipsize="end"
android:focusable="true"
@@ -447,39 +449,93 @@
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
- <LinearLayout
- android:id="@+id/share_wifi_button"
- style="@style/InternetDialog.Network"
- android:layout_height="@dimen/tile_details_button_row_height"
- android:background="@drawable/settingslib_entry_bg_off"
- android:gravity="center">
- <FrameLayout
- android:layout_width="@dimen/tile_details_button_size"
- android:layout_height="@dimen/tile_details_button_size"
- android:clickable="false">
+ <androidx.constraintlayout.widget.ConstraintLayout
+ android:id="@+id/wifi_buttons_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="@dimen/dialog_bottom_padding">
- <ImageView
- android:contentDescription="@string/share_wifi_button_text"
- android:src="@drawable/ic_screenshot_share"
+ <LinearLayout
+ android:id="@+id/add_network_button"
+ style="@style/InternetDialog.Network"
+ android:layout_height="@dimen/tile_details_button_row_height"
+ android:layout_width="0dp"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toStartOf="@id/share_wifi_button"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"
+ android:background="@drawable/settingslib_entry_bg_off"
+ android:gravity="center"
+ android:visibility="gone">
+
+ <FrameLayout
+ android:layout_width="@dimen/tile_details_button_size"
+ android:layout_height="@dimen/tile_details_button_size"
+ android:clickable="false">
+
+ <ImageView
+ android:contentDescription="@string/add_network_button_text"
+ android:src="@drawable/ic_add"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:tint="@androidprv:color/materialColorOnSurface" />
+ </FrameLayout>
+
+ <FrameLayout
+ android:clickable="false"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:tint="@androidprv:color/materialColorOnSurface" />
- </FrameLayout>
+ android:layout_marginStart="@dimen/internet_dialog_network_layout_margin">
- <FrameLayout
- android:clickable="false"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginStart="@dimen/internet_dialog_network_layout_margin">
+ <TextView
+ android:text="@string/add_network_button_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="@style/TextAppearance.TileDetailsEntryTitle" />
+ </FrameLayout>
+ </LinearLayout>
- <TextView
- android:text="@string/share_wifi_button_text"
+ <LinearLayout
+ android:id="@+id/share_wifi_button"
+ style="@style/InternetDialog.Network"
+ android:layout_height="@dimen/tile_details_button_row_height"
+ android:layout_width="0dp"
+ app:layout_constrainedWidth="true"
+ app:layout_constraintStart_toEndOf="@id/add_network_button"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"
+ android:background="@drawable/settingslib_entry_bg_off"
+ android:gravity="center">
+
+ <FrameLayout
+ android:layout_width="@dimen/tile_details_button_size"
+ android:layout_height="@dimen/tile_details_button_size"
+ android:clickable="false">
+
+ <ImageView
+ android:contentDescription="@string/share_wifi_button_text"
+ android:src="@drawable/ic_screenshot_share"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:tint="@androidprv:color/materialColorOnSurface" />
+ </FrameLayout>
+
+ <FrameLayout
+ android:clickable="false"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:textAppearance="@style/TextAppearance.TileDetailsEntryTitle" />
- </FrameLayout>
- </LinearLayout>
+ android:layout_marginStart="@dimen/internet_dialog_network_layout_margin">
+
+ <TextView
+ android:text="@string/share_wifi_button_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="@style/TextAppearance.TileDetailsEntryTitle" />
+ </FrameLayout>
+ </LinearLayout>
+ </androidx.constraintlayout.widget.ConstraintLayout>
</LinearLayout>
</androidx.core.widget.NestedScrollView>
diff --git a/packages/SystemUI/res/layout/media_carousel_settings_button.xml b/packages/SystemUI/res/layout/media_carousel_settings_button.xml
index 4570cb1..7e78d64 100644
--- a/packages/SystemUI/res/layout/media_carousel_settings_button.xml
+++ b/packages/SystemUI/res/layout/media_carousel_settings_button.xml
@@ -1,6 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2020 The Android Open Source Project
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2025 The Android Open 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,16 +13,34 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/settings_cog"
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:id="@+id/settings_cog_layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:contentDescription="@string/controls_media_settings_button"
- android:paddingStart="30dp"
- android:paddingEnd="30dp"
- android:paddingBottom="20dp"
- android:paddingTop="20dp"
- android:src="@drawable/ic_settings"
- android:tint="@color/notification_gear_color"
android:visibility="invisible"
- android:forceHasOverlappingRendering="false"/>
+ android:forceHasOverlappingRendering="false">
+
+ <View
+ android:id="@+id/background"
+ android:layout_width="48dp"
+ android:layout_height="48dp"
+ android:layout_marginStart="2dp"
+ android:layout_marginEnd="2dp"
+ android:layout_gravity="center"
+ android:background="@drawable/media_carousel_settings_bg" />
+
+ <ImageView
+ android:id="@+id/settings_cog"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:contentDescription="@string/controls_media_settings_button"
+ android:paddingStart="30dp"
+ android:paddingEnd="30dp"
+ android:paddingBottom="20dp"
+ android:paddingTop="20dp"
+ android:src="@drawable/ic_settings"
+ android:tint="@androidprv:color/materialColorOnSurface" />
+
+</FrameLayout>
diff --git a/packages/SystemUI/res/layout/media_carousel_settings_button_legacy.xml b/packages/SystemUI/res/layout/media_carousel_settings_button_legacy.xml
new file mode 100644
index 0000000..4570cb1
--- /dev/null
+++ b/packages/SystemUI/res/layout/media_carousel_settings_button_legacy.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/settings_cog"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:contentDescription="@string/controls_media_settings_button"
+ android:paddingStart="30dp"
+ android:paddingEnd="30dp"
+ android:paddingBottom="20dp"
+ android:paddingTop="20dp"
+ android:src="@drawable/ic_settings"
+ android:tint="@color/notification_gear_color"
+ android:visibility="invisible"
+ android:forceHasOverlappingRendering="false"/>
diff --git a/packages/SystemUI/res/layout/notification_2025_conversation_info.xml b/packages/SystemUI/res/layout/notification_2025_conversation_info.xml
index efd6cdd..086e73e 100644
--- a/packages/SystemUI/res/layout/notification_2025_conversation_info.xml
+++ b/packages/SystemUI/res/layout/notification_2025_conversation_info.xml
@@ -159,20 +159,20 @@
<!-- end aligned fields -->
<ImageButton
android:id="@+id/feedback"
- android:layout_width="@dimen/notification_2025_guts_button_size"
- android:layout_height="@dimen/notification_2025_guts_button_size"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
android:visibility="gone"
android:background="@drawable/ripple_drawable"
android:contentDescription="@string/notification_guts_bundle_feedback"
android:src="@*android:drawable/ic_feedback"
- android:paddingTop="@*android:dimen/notification_2025_margin"
+ android:padding="@*android:dimen/notification_2025_margin"
android:tint="@androidprv:color/materialColorPrimary"/>
<!-- System notification settings -->
<ImageButton
android:id="@+id/info"
- android:layout_width="@dimen/notification_2025_guts_button_size"
- android:layout_height="@dimen/notification_2025_guts_button_size"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
android:contentDescription="@string/notification_more_settings"
android:background="@drawable/ripple_drawable"
android:src="@drawable/ic_settings"
@@ -234,7 +234,7 @@
android:paddingEnd="@*android:dimen/notification_2025_margin"
android:src="@drawable/ic_important_outline"
android:background="@android:color/transparent"
- android:tint="@color/notification_guts_priority_contents"
+ android:tint="@androidprv:color/materialColorOnSurface"
android:clickable="false"
android:focusable="false"/>
<LinearLayout
@@ -286,7 +286,7 @@
android:paddingEnd="@*android:dimen/notification_2025_margin"
android:src="@drawable/ic_notifications_alert"
android:background="@android:color/transparent"
- android:tint="@color/notification_guts_priority_contents"
+ android:tint="@androidprv:color/materialColorOnSurface"
android:clickable="false"
android:focusable="false"/>
<LinearLayout
@@ -339,7 +339,7 @@
android:paddingEnd="@*android:dimen/notification_2025_margin"
android:src="@drawable/ic_notifications_silence"
android:background="@android:color/transparent"
- android:tint="@color/notification_guts_priority_contents"
+ android:tint="@androidprv:color/materialColorOnSurface"
android:clickable="false"
android:focusable="false"/>
<LinearLayout
@@ -387,22 +387,24 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
- android:gravity="center_vertical"
- android:minWidth="@dimen/notification_importance_toggle_size"
- android:minHeight="@dimen/notification_importance_toggle_size"
+ android:paddingTop="8dp"
+ android:paddingBottom="@*android:dimen/notification_2025_margin"
+ android:gravity="center"
+ android:minWidth="@dimen/notification_2025_min_tap_target_size"
+ android:minHeight="@dimen/notification_2025_min_tap_target_size"
style="@style/TextAppearance.NotificationInfo.Button"/>
<TextView
android:id="@+id/done"
android:text="@string/inline_ok_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:layout_alignParentEnd="true"
android:paddingTop="8dp"
android:paddingBottom="@*android:dimen/notification_2025_margin"
android:gravity="center"
android:minWidth="@dimen/notification_2025_min_tap_target_size"
android:minHeight="@dimen/notification_2025_min_tap_target_size"
- style="@style/TextAppearance.NotificationInfo.Button"
- android:textSize="@*android:dimen/notification_2025_action_text_size"/>
+ style="@style/TextAppearance.NotificationInfo.Button"/>
</RelativeLayout>
</LinearLayout>
diff --git a/packages/SystemUI/res/layout/notification_2025_footer.xml b/packages/SystemUI/res/layout/notification_2025_footer.xml
index 9b3d67f..49d2293 100644
--- a/packages/SystemUI/res/layout/notification_2025_footer.xml
+++ b/packages/SystemUI/res/layout/notification_2025_footer.xml
@@ -36,7 +36,7 @@
android:drawablePadding="8dp"
android:gravity="center"
android:text="@string/unlock_to_see_notif_text"
- android:textAppearance="?android:attr/textAppearanceButton"
+ android:textAppearance="@style/TextAppearance.NotificationShadeText"
android:visibility="gone" />
<androidx.constraintlayout.widget.ConstraintLayout
diff --git a/packages/SystemUI/res/layout/notification_2025_info.xml b/packages/SystemUI/res/layout/notification_2025_info.xml
index d1fcdec..337d36b 100644
--- a/packages/SystemUI/res/layout/notification_2025_info.xml
+++ b/packages/SystemUI/res/layout/notification_2025_info.xml
@@ -86,33 +86,33 @@
<!-- feedback for notificationassistantservice -->
<ImageButton
android:id="@+id/feedback"
- android:layout_width="@dimen/notification_2025_guts_button_size"
- android:layout_height="@dimen/notification_2025_guts_button_size"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
android:visibility="gone"
android:background="@drawable/ripple_drawable"
android:contentDescription="@string/notification_guts_bundle_feedback"
android:src="@*android:drawable/ic_feedback"
- android:paddingTop="@*android:dimen/notification_2025_margin"
+ android:padding="@*android:dimen/notification_2025_margin"
android:tint="@androidprv:color/materialColorPrimary"/>
<!-- Optional link to app. Only appears if the channel is not disabled and the app
asked for it -->
<ImageButton
android:id="@+id/app_settings"
- android:layout_width="@dimen/notification_2025_guts_button_size"
- android:layout_height="@dimen/notification_2025_guts_button_size"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
android:visibility="gone"
android:background="@drawable/ripple_drawable"
android:contentDescription="@string/notification_app_settings"
android:src="@drawable/ic_info"
- android:paddingTop="@*android:dimen/notification_2025_margin"
+ android:padding="@*android:dimen/notification_2025_margin"
android:tint="@androidprv:color/materialColorPrimary"/>
<!-- System notification settings -->
<ImageButton
android:id="@+id/info"
- android:layout_width="@dimen/notification_2025_guts_button_size"
- android:layout_height="@dimen/notification_2025_guts_button_size"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
android:contentDescription="@string/notification_more_settings"
android:background="@drawable/ripple_drawable"
android:src="@drawable/ic_settings"
@@ -183,7 +183,7 @@
android:paddingEnd="@*android:dimen/notification_2025_margin"
android:src="@drawable/ic_notifications_automatic"
android:background="@android:color/transparent"
- android:tint="@color/notification_guts_priority_contents"
+ android:tint="@androidprv:color/materialColorOnSurface"
android:clickable="false"
android:focusable="false"/>
<LinearLayout
@@ -236,7 +236,7 @@
android:paddingEnd="@*android:dimen/notification_2025_margin"
android:src="@drawable/ic_notifications_alert"
android:background="@android:color/transparent"
- android:tint="@color/notification_guts_priority_contents"
+ android:tint="@androidprv:color/materialColorOnSurface"
android:clickable="false"
android:focusable="false"/>
<LinearLayout
@@ -286,7 +286,7 @@
android:id="@+id/silence_icon"
android:src="@drawable/ic_notifications_silence"
android:background="@android:color/transparent"
- android:tint="@color/notification_guts_priority_contents"
+ android:tint="@androidprv:color/materialColorOnSurface"
android:layout_gravity="center"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -346,7 +346,6 @@
android:minHeight="@dimen/notification_2025_min_tap_target_size"
android:maxWidth="200dp"
style="@style/TextAppearance.NotificationInfo.Button"
- android:textSize="@*android:dimen/notification_2025_action_text_size"
/>
<TextView
android:id="@+id/turn_off_notifications"
@@ -364,7 +363,7 @@
app:layout_constraintStart_toEndOf="@id/inline_dismiss"
app:layout_constraintBaseline_toBaselineOf="@id/inline_dismiss"
style="@style/TextAppearance.NotificationInfo.Button"
- android:textSize="@*android:dimen/notification_2025_action_text_size"/>
+ />
<TextView
android:id="@+id/done"
android:text="@string/inline_ok_button"
@@ -378,7 +377,7 @@
android:minHeight="@dimen/notification_2025_min_tap_target_size"
android:maxWidth="125dp"
style="@style/TextAppearance.NotificationInfo.Button"
- android:textSize="@*android:dimen/notification_2025_action_text_size"/>
+ />
<androidx.constraintlayout.helper.widget.Flow
android:layout_width="match_parent"
android:layout_height="wrap_content"
diff --git a/packages/SystemUI/res/layout/notification_2025_partial_conversation_info.xml b/packages/SystemUI/res/layout/notification_2025_partial_conversation_info.xml
index 4925eb6..5b33e31 100644
--- a/packages/SystemUI/res/layout/notification_2025_partial_conversation_info.xml
+++ b/packages/SystemUI/res/layout/notification_2025_partial_conversation_info.xml
@@ -145,7 +145,7 @@
android:paddingTop="8dp"
android:paddingBottom="@*android:dimen/notification_2025_margin"
style="@style/TextAppearance.NotificationInfo.Button"
- android:textSize="@*android:dimen/notification_2025_action_text_size"/>
+ />
<TextView
android:id="@+id/turn_off_notifications"
android:text="@string/inline_turn_off_notifications"
@@ -160,7 +160,7 @@
android:paddingTop="8dp"
android:paddingBottom="@*android:dimen/notification_2025_margin"
style="@style/TextAppearance.NotificationInfo.Button"
- android:textSize="@*android:dimen/notification_2025_action_text_size"/>
+ />
<TextView
android:id="@+id/done"
android:text="@string/inline_done_button"
@@ -174,7 +174,7 @@
android:paddingTop="8dp"
android:paddingBottom="@*android:dimen/notification_2025_margin"
style="@style/TextAppearance.NotificationInfo.Button"
- android:textSize="@*android:dimen/notification_2025_action_text_size"/>
+ />
</RelativeLayout>
</LinearLayout>
diff --git a/packages/SystemUI/res/layout/notification_2025_smart_action_button.xml b/packages/SystemUI/res/layout/notification_2025_smart_action_button.xml
index ed90588..e808f8e 100644
--- a/packages/SystemUI/res/layout/notification_2025_smart_action_button.xml
+++ b/packages/SystemUI/res/layout/notification_2025_smart_action_button.xml
@@ -25,9 +25,7 @@
android:paddingVertical="@dimen/smart_reply_button_padding_vertical"
android:background="@drawable/notification_2025_smart_reply_button_background"
android:gravity="center"
- android:fontFamily="google-sans-flex"
- android:textSize="@dimen/smart_reply_button_font_size"
- android:textColor="@color/smart_reply_button_text"
+ android:textAppearance="@style/TextAppearance.NotificationSmartReply"
android:paddingStart="@dimen/smart_reply_button_action_padding_left"
android:paddingEnd="@dimen/smart_reply_button_padding_horizontal"
android:drawablePadding="@dimen/smart_action_button_icon_padding"
diff --git a/packages/SystemUI/res/layout/notification_2025_smart_reply_button.xml b/packages/SystemUI/res/layout/notification_2025_smart_reply_button.xml
index 4f543e5..9de3921 100644
--- a/packages/SystemUI/res/layout/notification_2025_smart_reply_button.xml
+++ b/packages/SystemUI/res/layout/notification_2025_smart_reply_button.xml
@@ -27,9 +27,7 @@
android:paddingVertical="@dimen/smart_reply_button_padding_vertical"
android:background="@drawable/notification_2025_smart_reply_button_background"
android:gravity="center"
- android:fontFamily="google-sans-flex"
- android:textSize="@dimen/smart_reply_button_font_size"
- android:textColor="@color/smart_reply_button_text"
+ android:textAppearance="@style/TextAppearance.NotificationSmartReply"
android:paddingStart="@dimen/smart_reply_button_padding_horizontal"
android:paddingEnd="@dimen/smart_reply_button_padding_horizontal"
android:textStyle="normal"
diff --git a/packages/SystemUI/res/layout/promoted_notification_info.xml b/packages/SystemUI/res/layout/promoted_notification_info.xml
index 06e5b0f..0bee79d 100644
--- a/packages/SystemUI/res/layout/promoted_notification_info.xml
+++ b/packages/SystemUI/res/layout/promoted_notification_info.xml
@@ -168,7 +168,7 @@
android:paddingEnd="@*android:dimen/notification_2025_margin"
android:src="@drawable/ic_notifications_automatic"
android:background="@android:color/transparent"
- android:tint="@color/notification_guts_priority_contents"
+ android:tint="@androidprv:color/materialColorOnSurface"
android:clickable="false"
android:focusable="false"/>
<LinearLayout
@@ -221,7 +221,7 @@
android:paddingEnd="@*android:dimen/notification_2025_margin"
android:src="@drawable/ic_notifications_alert"
android:background="@android:color/transparent"
- android:tint="@color/notification_guts_priority_contents"
+ android:tint="@androidprv:color/materialColorOnSurface"
android:clickable="false"
android:focusable="false"/>
<LinearLayout
@@ -271,7 +271,7 @@
android:id="@+id/silence_icon"
android:src="@drawable/ic_notifications_silence"
android:background="@android:color/transparent"
- android:tint="@color/notification_guts_priority_contents"
+ android:tint="@androidprv:color/materialColorOnSurface"
android:layout_gravity="center"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
diff --git a/packages/SystemUI/res/layout/status_bar_no_notifications.xml b/packages/SystemUI/res/layout/status_bar_no_notifications.xml
index e26b855..1b153df 100644
--- a/packages/SystemUI/res/layout/status_bar_no_notifications.xml
+++ b/packages/SystemUI/res/layout/status_bar_no_notifications.xml
@@ -34,7 +34,7 @@
android:layout_height="wrap_content"
android:minHeight="64dp"
android:gravity="center"
- android:textAppearance="?android:attr/textAppearanceButton"
+ android:textAppearance="@style/TextAppearance.NotificationShadeText"
android:text="@string/empty_shade_text"/>
<TextView
android:id="@+id/no_notifications_footer"
@@ -44,7 +44,7 @@
android:gravity="center"
android:drawablePadding="8dp"
android:visibility="gone"
- android:textAppearance="?android:attr/textAppearanceButton"
+ android:textAppearance="@style/TextAppearance.NotificationShadeText"
android:text="@string/unlock_to_see_notif_text"/>
</LinearLayout>
</com.android.systemui.statusbar.notification.emptyshade.ui.view.EmptyShadeView>
diff --git a/packages/SystemUI/res/layout/volume_dialog.xml b/packages/SystemUI/res/layout/volume_dialog.xml
index e094155..154b05e 100644
--- a/packages/SystemUI/res/layout/volume_dialog.xml
+++ b/packages/SystemUI/res/layout/volume_dialog.xml
@@ -18,6 +18,7 @@
android:id="@+id/volume_dialog"
android:layout_width="wrap_content"
android:layout_height="match_parent"
+ android:layout_gravity="end"
android:alpha="0"
android:clipChildren="false"
android:minWidth="@dimen/volume_dialog_window_width">
diff --git a/packages/SystemUI/res/values-af-ldrtl/strings.xml b/packages/SystemUI/res/values-af-ldrtl/strings.xml
index 277ef9d..9856b9a 100644
--- a/packages/SystemUI/res/values-af-ldrtl/strings.xml
+++ b/packages/SystemUI/res/values-af-ldrtl/strings.xml
@@ -20,4 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="recents_quick_scrub_onboarding" msgid="2452671841151577157">"Sleep links om programme vinnig te wissel"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="6543179539438220549">"Swiep van regs bo af om kennisgewings oop te maak"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="5760503283520058116">"Swiep van bo links af om Kitsinstellings oop te maak"</string>
</resources>
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index a0d5018..3d4a6f4 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -467,6 +467,8 @@
<string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"Aktiveer mikrofoontoegang in Instellings om die mikrofoonknoppie te gebruik."</string>
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Maak Instellings oop"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Ander toestel"</string>
+ <string name="media_suggestion_disconnected_text" msgid="8777932417076437525">"Speel op <xliff:g id="DEVICE_NAME">%s</xliff:g>"</string>
+ <string name="media_suggestion_failure_text" msgid="8453022394887613774">"Kan nie koppel nie. Probeer weer."</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Wissel oorsig"</string>
<string name="zen_modes_dialog_title" msgid="8854640808100096934">"Modusse"</string>
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Klaar"</string>
@@ -582,6 +584,7 @@
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
<skip />
<string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Deel hele skerm"</string>
+ <string name="screen_share_permission_dialog_option_text_entire_screen_for_display" msgid="1545850238689169166">"Deel <xliff:g id="DISPLAY_NAME">%s</xliff:g>"</string>
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
<skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Wanneer jy jou hele skerm deel, is enigiets op jou skerm sigbaar aan <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Wees dus versigtig met dinge soos wagwoorde, betalingbesonderhede, boodskappe, foto’s, en oudio en video."</string>
@@ -593,6 +596,7 @@
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Saai jou skerm uit?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Saai een app uit"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Saai hele skerm uit"</string>
+ <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen_for_display" msgid="5524817761472941086">"Saai <xliff:g id="DISPLAY_NAME">%s</xliff:g> uit"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Wanneer jy jou hele skerm uitsaai, is enigiets op jou skerm sigbaar. Wees dus versigtig met dinge soos wagwoorde, betalingbesonderhede, boodskappe, foto’s, en oudio en video."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Wanneer jy ’n app uitsaai, is enigiets wat in die app wys of speel, sigbaar. Wees dus versigtig met dinge soos wagwoorde, betalingbesonderhede, boodskappe, foto’s, en oudio en video."</string>
<string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Saai skerm uit"</string>
@@ -673,6 +677,7 @@
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Ontsluit gehou deur TrustAgent"</string>
<string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"Toestel is gesluit; te veel stawingpogings"</string>
<string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"Toestel is gesluit\nKon nie staaf nie"</string>
+ <string name="keyguard_indication_after_watch_disconnected" msgid="6329761892437377710">"Uitgesluit deur jou horlosie"</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="accessibility_volume_settings" msgid="1458961116951564784">"Klankinstellings"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Verskaf outomaties onderskrifte vir media"</string>
@@ -799,6 +804,10 @@
<string name="enable_bluetooth_message" msgid="6740938333772779717">"Jy moet Bluetooth aanskakel om jou sleutelbord aan jou tablet te koppel."</string>
<string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Skakel aan"</string>
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Aan – gesiggegrond"</string>
+ <!-- no translation found for notification_guts_bundle_title (1345506995443305361) -->
+ <skip />
+ <!-- no translation found for notification_guts_bundle_summary (3971530802237393600) -->
+ <skip />
<string name="inline_done_button" msgid="6043094985588909584">"Klaar"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Pas toe"</string>
<string name="inline_turn_off_notifications" msgid="2653064779176881329">"Skakel af"</string>
@@ -984,6 +993,7 @@
<string name="right_icon" msgid="1103955040645237425">"Regs-ikoon"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Hou en sleep om teëls by te voeg"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Hou en sleep om teëls te herrangskik"</string>
+ <string name="select_to_rearrange_tiles" msgid="6166153534235179148">"Kies teëls om te herrangskik en die grootte van te verander"</string>
<string name="tap_to_position_tile" msgid="6282815817773342757">"Tik om teël te posisioneer"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Sleep hierheen om te verwyder"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Jy moet minstens <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> teëls hê"</string>
@@ -1003,10 +1013,12 @@
<string name="tuner_low_priority" msgid="8412666814123009820">"Wys laeprioriteit-kennisgewingikone"</string>
<string name="other" msgid="429768510980739978">"Ander"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"wissel die teël se grootte"</string>
- <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"verwyder teël"</string>
+ <!-- no translation found for accessibility_qs_edit_remove_tile_action (4811778796933958616) -->
+ <skip />
<string name="accessibility_qs_edit_toggle_placement_mode" msgid="3870429389210569610">"wissel plasingmodus"</string>
<string name="accessibility_qs_edit_toggle_selection" msgid="2201248304072372239">"wissel seleksie"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"voeg teël by die laaste posisie"</string>
+ <!-- no translation found for accessibility_qs_edit_tile_add_action (7188303979684400620) -->
+ <skip />
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Skuif teël"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Voeg teël by die verlangde posisie"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Skuif na <xliff:g id="POSITION">%1$d</xliff:g>"</string>
@@ -1093,7 +1105,8 @@
<string name="auto_saver_text" msgid="3214960308353838764">"Skakel aan wanneer battery waarskynlik sal leegloop"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Nee, dankie"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"In gebruik"</string>
- <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Programme gebruik tans jou <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps (4994455315457996796) -->
+ <skip />
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" en "</string>
<string name="ongoing_privacy_dialog_using_op" msgid="426635338010011796">"Word tans gebruik deur <xliff:g id="APPLICATION_NAME">%1$s</xliff:g>"</string>
@@ -1508,11 +1521,13 @@
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"Voeg kortpad by"</string>
<string name="shortcut_helper_delete_shortcut_button_label" msgid="3148773472696137052">"Vee kortpad uit"</string>
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navigeer met jou sleutelbord"</string>
- <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Leer kortpadsleutels"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2131074100205028210) -->
+ <skip />
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navigeer met jou raakpaneel"</string>
<string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Leer raakpaneelgebare"</string>
<string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Navigeer met jou sleutelbord en raakpaneel"</string>
- <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Leer raakpaneelgebare, kortpadsleutels en meer"</string>
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (8557098239919786046) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Gaan terug"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Gaan na tuisskerm"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Bekyk onlangse apps"</string>
@@ -1582,4 +1597,7 @@
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Alle Kitsinstellingsteëls sal na die toestel se oorspronklike instellings teruggestel word"</string>
<string name="demote_explain_text" msgid="3942301497888762295">"<xliff:g id="APPLICATION">%1$s</xliff:g> sal nie meer regstreekse opdaterings wys nie. Jy kan dit enige tyd in Instellings verander."</string>
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
+ <string name="underlay_close_button_content_description" msgid="6518481455548869894">"Maak toe"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="7666159428113013319">"Swiep van bo links af om kennisgewings oop te maak"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="3842355853737057332">"Swiep van bo regs af om Kitsinstellings oop te maak"</string>
</resources>
diff --git a/packages/SystemUI/res/values-am-ldrtl/strings.xml b/packages/SystemUI/res/values-am-ldrtl/strings.xml
index 4ab1714..0cb2468 100644
--- a/packages/SystemUI/res/values-am-ldrtl/strings.xml
+++ b/packages/SystemUI/res/values-am-ldrtl/strings.xml
@@ -20,4 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="recents_quick_scrub_onboarding" msgid="2452671841151577157">"መተግበሪያዎችን በፍጥነት ለመቀየር ወደ ግራ ይጎትቱ"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="6543179539438220549">"ማሳወቂያዎችን ለመክፈት ከላይ ወደ ቀኝ ያንሸራትቱ"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="5760503283520058116">"ፈጣን ቅንብሮችን ለመክፈት ከላይ ወደ ግራ ያንሸራትቱ"</string>
</resources>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index 7eeb263..ffb45f4 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -467,6 +467,8 @@
<string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"የማይክሮፎን አዝራርን ለመጠቀም በቅንብሮች ውስጥ የማይክሮፎን መዳረሻን ያንቁ።"</string>
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"ቅንብሮችን ክፈት"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"ሌላ መሣሪያ"</string>
+ <string name="media_suggestion_disconnected_text" msgid="8777932417076437525">"<xliff:g id="DEVICE_NAME">%s</xliff:g> ላይ አጫውት"</string>
+ <string name="media_suggestion_failure_text" msgid="8453022394887613774">"ማገናኘት አልተቻለም። እንደገና ይሞክሩ።"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"አጠቃላይ እይታን ቀያይር"</string>
<string name="zen_modes_dialog_title" msgid="8854640808100096934">"ሁነታዎች"</string>
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"ተከናውኗል"</string>
@@ -582,6 +584,7 @@
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
<skip />
<string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"መላውን ማያ ገፅ ያጋሩ"</string>
+ <string name="screen_share_permission_dialog_option_text_entire_screen_for_display" msgid="1545850238689169166">"<xliff:g id="DISPLAY_NAME">%s</xliff:g> ያጋሩ"</string>
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
<skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"እርስዎ ሙሉ ማያ ገፅዎን ሲያጋሩ በማያ ገፅዎ ላይ ያለው ማንኛውም ነገር ለ<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ይታያል። ስለዚህ እንደ የይለፍ ቃላት፣ የክፍያ ዝርዝሮች፣ መልዕክቶች፣ ፎቶዎች እና ኦዲዮ እና ቪድዮ ላሉ ነገሮች ጥንቃቄ ያድርጉ።"</string>
@@ -593,6 +596,7 @@
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"ማያ ገፅዎ cast ይደረግ?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"አንድ መተግበሪያ cast ያድርጉ"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"መላውን ማያ ገፅ cast ያድርጉ"</string>
+ <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen_for_display" msgid="5524817761472941086">"<xliff:g id="DISPLAY_NAME">%s</xliff:g>ን Cast ያድርጉ"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"እርስዎ ሙሉ ማያ ገፅዎን cast ሲያደርጉ በማያ ገፅዎ ላይ ያለው ማንኛውም ነገር ይታያል። ስለዚህ እንደ የይለፍ ቃላት፣ የክፍያ ዝርዝሮች፣ መልዕክቶች፣ ፎቶዎች እና ኦዲዮ እና ቪድዮ ላሉ ነገሮች ጥንቃቄ ያድርጉ።"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"መተግበሪያን cast ሲያደርጉ በዚያ መተግበሪያ ውስጥ የሚታይ ወይም የሚጫወት ማንኛውም ነገር ይታያል። ስለዚህ እንደ የይለፍ ቃላት፣ የክፍያ ዝርዝሮች፣ መልዕክቶች፣ ፎቶዎች እና ኦዲዮ እና ቪድዮ ላሉ ነገሮች ጥንቃቄ ያድርጉ።"</string>
<string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"ማያ ገፅ Cast አድርግ"</string>
@@ -673,6 +677,7 @@
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"በ TrustAgent እንደተከፈተ ቀርቷል"</string>
<string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"መሣሪያው ተቆልፏል፣ ከልክ በላይ ብዙ የማረጋገጫ ሙከራዎች"</string>
<string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"መሣሪያው ተቆልፏል\nማረጋገጥ አልተሳካም"</string>
+ <string name="keyguard_indication_after_watch_disconnected" msgid="6329761892437377710">"በእጅ ሰዓትዎ ተቆለፎብዎታል"</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="accessibility_volume_settings" msgid="1458961116951564784">"የድምፅ ቅንብሮች"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"ራስሰር የሥዕል መግለጫ ጽሑፍን ሚዲያ"</string>
@@ -799,6 +804,10 @@
<string name="enable_bluetooth_message" msgid="6740938333772779717">"የቁልፍ ሰሌዳዎን ከእርስዎ ጡባዊ ጋር ለማገናኘት በመጀመሪያ ብሉቱዝን ማብራት አለብዎት።"</string>
<string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"አብራ"</string>
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"በርቷል - መልክ ላይ የተመሠረተ"</string>
+ <!-- no translation found for notification_guts_bundle_title (1345506995443305361) -->
+ <skip />
+ <!-- no translation found for notification_guts_bundle_summary (3971530802237393600) -->
+ <skip />
<string name="inline_done_button" msgid="6043094985588909584">"ተከናውኗል"</string>
<string name="inline_ok_button" msgid="603075490581280343">"ተግብር"</string>
<string name="inline_turn_off_notifications" msgid="2653064779176881329">"አጥፋ"</string>
@@ -984,6 +993,7 @@
<string name="right_icon" msgid="1103955040645237425">"የቀኝ አዶ"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"ፋይሎችን ለማከል ይዘት ይጎትቱ"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"ሰድሮችን ዳግም ለማስተካከል ይያዙ እና ይጎትቱ።"</string>
+ <string name="select_to_rearrange_tiles" msgid="6166153534235179148">"እንደገና ለመደርደር እና መጠን ለመቀየር ሰቆችን ይምረጡ"</string>
<string name="tap_to_position_tile" msgid="6282815817773342757">"የሰቅን አቀማመጥ ለማስተካከል መታ ያድርጉ"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"ለማስወገድ ወደዚህ ይጎትቱ"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"ቢያንስ <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> ሰቆች ያስፈልገዎታል"</string>
@@ -1003,10 +1013,12 @@
<string name="tuner_low_priority" msgid="8412666814123009820">"አነስተኛ ቅድሚያ ያላቸው የማሳወቂያ አዶዎችን አሳይ"</string>
<string name="other" msgid="429768510980739978">"ሌላ"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"የርዕሱን መጠን ይቀያይሩ"</string>
- <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"ሰቅ አስወግድ"</string>
+ <!-- no translation found for accessibility_qs_edit_remove_tile_action (4811778796933958616) -->
+ <skip />
<string name="accessibility_qs_edit_toggle_placement_mode" msgid="3870429389210569610">"የምደባ ሁነታን ይቀያይሩ"</string>
<string name="accessibility_qs_edit_toggle_selection" msgid="2201248304072372239">"ምርጫን ይቀያይሩ"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"በመጨረሻው ቦታ ላይ ሰቅ ያክሉ"</string>
+ <!-- no translation found for accessibility_qs_edit_tile_add_action (7188303979684400620) -->
+ <skip />
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"ሰቁን ውሰድ"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"ወደተፈለገው ቦታ ሰቅ ያክሉ"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"ወደ <xliff:g id="POSITION">%1$d</xliff:g> ውሰድ"</string>
@@ -1093,7 +1105,8 @@
<string name="auto_saver_text" msgid="3214960308353838764">"ባትሪው የማለቅ ዕድሉ ከፍ ያለ ከሆነ ያብሩት"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"አይ፣ አመሰግናለሁ"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"በጥቅም ላይ"</string>
- <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"መተግበሪያዎች የእርስዎን <xliff:g id="TYPES_LIST">%s</xliff:g> እየተጠቀሙ ነው።"</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps (4994455315457996796) -->
+ <skip />
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">"፣ "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" እና "</string>
<string name="ongoing_privacy_dialog_using_op" msgid="426635338010011796">"በ<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> ሥራ ላይ እየዋለ ነው"</string>
@@ -1508,11 +1521,11 @@
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"አቋራጭ አክል"</string>
<string name="shortcut_helper_delete_shortcut_button_label" msgid="3148773472696137052">"አቋረጭ ሰርዝ"</string>
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"የቁልፍ ሰሌዳዎን በመጠቀም ያስሱ"</string>
- <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"የቁልፍ ሰሌዳ አቋራጮችን ይወቁ"</string>
+ <string name="launch_keyboard_tutorial_notification_content" msgid="2131074100205028210">"የቁልፍ ሰሌዳ አቋራጭን ይወቁ"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"የመዳሰሻ ሰሌዳዎን በመጠቀም ያስሱ"</string>
<string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"የመዳሰሻ ሰሌዳ ምልክቶችን ይወቁ"</string>
<string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"የእርስዎን የቁልፍ ሰሌዳ እና የመዳሰሻ ሰሌዳ በመጠቀም ያስሱ"</string>
- <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"የመዳሰሻ ሰሌዳ ምልክቶችን፣ የቁልፍ ሰሌዳ አቋራጮችን እና ሌሎችን ይወቁ"</string>
+ <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="8557098239919786046">"የመዳሰሻ ሰሌዳ ምልክቶችን፣ የቁልፍ ሰሌዳ አቋራጮችን እና ሌሎችን ይወቁ"</string>
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"ወደኋላ ተመለስ"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"ወደ መነሻ ሂድ"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"የቅርብ ጊዜ መተግበሪያዎችን አሳይ"</string>
@@ -1582,4 +1595,7 @@
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"ሁሉም የፈጣን ቅንብሮች ሰቆች ወደ የመሣሪያው የመጀመሪያ ቅንብሮች ዳግም ይጀምራሉ"</string>
<string name="demote_explain_text" msgid="3942301497888762295">"<xliff:g id="APPLICATION">%1$s</xliff:g> ከእንግዲህ የቀጥታ ዝማኔዎችን አያሳይም። ይህን በማንኛውም ጊዜ በቅንብሮች ውስጥ መለወጥ ይችላሉ።"</string>
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>፣ <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
+ <string name="underlay_close_button_content_description" msgid="6518481455548869894">"ዝጋ"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="7666159428113013319">"ማሳወቂያዎችን ለመክፈት ከላይ ወደ ግራ ያንሸራትቱ"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="3842355853737057332">"ፈጣን ቅንብሮችን ለመክፈት ከላይ ወደ ቀኝ ያንሸራትቱ"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ar-ldrtl/strings.xml b/packages/SystemUI/res/values-ar-ldrtl/strings.xml
index 526143c..a511988 100644
--- a/packages/SystemUI/res/values-ar-ldrtl/strings.xml
+++ b/packages/SystemUI/res/values-ar-ldrtl/strings.xml
@@ -20,4 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="recents_quick_scrub_onboarding" msgid="2452671841151577157">"اسحب لليمين للتبديل السريع بين التطبيقات."</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="6543179539438220549">"لفتح الإشعارات، عليك التمرير سريعًا من أعلى يسار الشاشة"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="5760503283520058116">"لفتح \"الإعدادات السريعة\"، عليك التمرير سريعًا من أعلى يمين الشاشة"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index db3a3c4..1af7a61 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -202,7 +202,7 @@
<string name="biometric_dialog_cancel_authentication" msgid="981316588773442637">"إلغاء المصادقة"</string>
<string name="biometric_dialog_content_view_more_options_button" msgid="2663810393874865475">"مزيد من الخيارات"</string>
<string name="biometric_dialog_use_pin" msgid="8385294115283000709">"استخدام رقم تعريف شخصي"</string>
- <string name="biometric_dialog_use_pattern" msgid="2315593393167211194">"استخدام نقش"</string>
+ <string name="biometric_dialog_use_pattern" msgid="2315593393167211194">"استخدام النقش"</string>
<string name="biometric_dialog_use_password" msgid="3445033859393474779">"استخدام كلمة المرور"</string>
<string name="biometric_dialog_wrong_pin" msgid="1878539073972762803">"رقم تعريف شخصي خاطئ"</string>
<string name="biometric_dialog_wrong_pattern" msgid="8954812279840889029">"النقش غير صحيح."</string>
@@ -467,6 +467,8 @@
<string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"لاستخدام زر الميكروفون، عليك تفعيل إمكانية الوصول إلى الميكروفون في \"الإعدادات\"."</string>
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"فتح الإعدادات"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"جهاز آخر"</string>
+ <string name="media_suggestion_disconnected_text" msgid="8777932417076437525">"تشغيل الوسائط على \"<xliff:g id="DEVICE_NAME">%s</xliff:g>\""</string>
+ <string name="media_suggestion_failure_text" msgid="8453022394887613774">"يتعذَّر الاتصال بالجهاز. يُرجى إعادة المحاولة."</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"تبديل \"النظرة العامة\""</string>
<string name="zen_modes_dialog_title" msgid="8854640808100096934">"الأوضاع"</string>
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"تم"</string>
@@ -582,6 +584,7 @@
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
<skip />
<string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"مشاركة الشاشة بأكملها"</string>
+ <string name="screen_share_permission_dialog_option_text_entire_screen_for_display" msgid="1545850238689169166">"مشاركة محتوى \"<xliff:g id="DISPLAY_NAME">%s</xliff:g>\""</string>
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
<skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"أثناء مشاركة محتوى الشاشة بالكامل، سيكون كل المحتوى المعروض على شاشتك مرئيًا لتطبيق \"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>\". لذا يُرجى توخي الحذر بشأن المعلومات، مثل كلمات المرور وتفاصيل الدفع والرسائل والصور وملفات الصوت والفيديو."</string>
@@ -593,6 +596,7 @@
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"هل تريد بث محتوى الشاشة؟"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"بث محتوى تطبيق واحد"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"بث محتوى الشاشة بالكامل"</string>
+ <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen_for_display" msgid="5524817761472941086">"بثّ محتوى \"<xliff:g id="DISPLAY_NAME">%s</xliff:g>\""</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"سيتم بث كل المحتوى المعروض على شاشتك، لذا يُرجى توخي الحذر بشأن المعلومات الظاهرة، مثل كلمات المرور وتفاصيل الدفع والرسائل والصور والمقاطع الصوتية والفيديوهات."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"سيتم بث كل المحتوى المعروض أو الذي يتم تشغيله في ذلك التطبيق، لذا يُرجى توخي الحذر بشأن المعلومات الظاهرة، مثل كلمات المرور وتفاصيل الدفع والرسائل والصور والمقاطع الصوتية والفيديوهات."</string>
<string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"بث محتوى الشاشة"</string>
@@ -673,6 +677,7 @@
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"فتح القفل باستمرار بواسطة TrustAgent"</string>
<string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"تم قفل الجهاز بسبب إجراء العديد من محاولات المصادقة"</string>
<string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"تم قفل الجهاز\nتعذّرت المصادقة"</string>
+ <string name="keyguard_indication_after_watch_disconnected" msgid="6329761892437377710">"تم قفله من خلال ساعتك"</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="accessibility_volume_settings" msgid="1458961116951564784">"إعدادات الصوت"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"شرح تلقائي للوسائط"</string>
@@ -719,7 +724,7 @@
<string name="stream_notification_unavailable" msgid="4313854556205836435">"غير متاح بسبب كتم صوت الرنين"</string>
<string name="stream_alarm_unavailable" msgid="4059817189292197839">"مستوى الصوت غير متاح بسبب تفعيل وضع \"عدم الإزعاج\""</string>
<string name="stream_media_unavailable" msgid="6823020894438959853">"مستوى الصوت غير متاح لأنّ وضع \"عدم الإزعاج\" مفعّل"</string>
- <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"غير متوفِّر لأنّ وضع \"<xliff:g id="MODE">%s</xliff:g>\" مفعَّل"</string>
+ <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"غير متاح لأنّ وضع \"<xliff:g id="MODE">%s</xliff:g>\" مفعَّل"</string>
<string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"غير متوفِّر"</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. انقر لإلغاء التجاهل."</string>
<string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. انقر للتعيين على الاهتزاز. قد يتم تجاهل خدمات \"سهولة الاستخدام\"."</string>
@@ -799,6 +804,10 @@
<string name="enable_bluetooth_message" msgid="6740938333772779717">"لتوصيل لوحة المفاتيح بالجهاز اللوحي، يلزمك تفعيل بلوتوث أولاً."</string>
<string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"تفعيل"</string>
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"تفعيل - استنادًا للوجه"</string>
+ <!-- no translation found for notification_guts_bundle_title (1345506995443305361) -->
+ <skip />
+ <!-- no translation found for notification_guts_bundle_summary (3971530802237393600) -->
+ <skip />
<string name="inline_done_button" msgid="6043094985588909584">"تمّ"</string>
<string name="inline_ok_button" msgid="603075490581280343">"تطبيق"</string>
<string name="inline_turn_off_notifications" msgid="2653064779176881329">"إيقاف"</string>
@@ -984,6 +993,7 @@
<string name="right_icon" msgid="1103955040645237425">"رمز اليمين"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"اضغط باستمرار مع السحب لإضافة المربّعات"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"اضغط باستمرار مع السحب لإعادة ترتيب الميزات"</string>
+ <string name="select_to_rearrange_tiles" msgid="6166153534235179148">"يمكنك اختيار المربّعات لإعادة ترتيبها وتغيير حجمها"</string>
<string name="tap_to_position_tile" msgid="6282815817773342757">"عليك النقر لتحديد موضع المربّع"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"اسحب هنا للإزالة"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"الحدّ الأدنى من عدد المربعات الذي تحتاج إليه هو <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g>"</string>
@@ -1003,10 +1013,12 @@
<string name="tuner_low_priority" msgid="8412666814123009820">"إظهار رموز الإشعارات ذات الأولوية المنخفضة"</string>
<string name="other" msgid="429768510980739978">"غير ذلك"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"تبديل حجم المربّع"</string>
- <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"إزالة بطاقة"</string>
+ <!-- no translation found for accessibility_qs_edit_remove_tile_action (4811778796933958616) -->
+ <skip />
<string name="accessibility_qs_edit_toggle_placement_mode" msgid="3870429389210569610">"تفعيل أو إيقاف وضع الترتيب"</string>
<string name="accessibility_qs_edit_toggle_selection" msgid="2201248304072372239">"تفعيل أو إيقاف الاختيار"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"إضافة مربّع إلى الموضع الأخير"</string>
+ <!-- no translation found for accessibility_qs_edit_tile_add_action (7188303979684400620) -->
+ <skip />
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"نقل بطاقة"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"إضافة مربّع إلى الموضع المطلوب"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"الانتقال إلى <xliff:g id="POSITION">%1$d</xliff:g>"</string>
@@ -1093,7 +1105,8 @@
<string name="auto_saver_text" msgid="3214960308353838764">"فعِّل الميزة إذا كان من المرجح نفاد شحن البطارية."</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"لا، شكرًا"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"قيد الاستخدام"</string>
- <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"تستخدم التطبيقات <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps (4994455315457996796) -->
+ <skip />
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">"، "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" و "</string>
<string name="ongoing_privacy_dialog_using_op" msgid="426635338010011796">"يجري الاستخدام الآن من قِبل \"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g>\"."</string>
@@ -1508,11 +1521,11 @@
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"إضافة اختصار"</string>
<string name="shortcut_helper_delete_shortcut_button_label" msgid="3148773472696137052">"حذف الاختصار"</string>
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"التنقّل باستخدام لوحة المفاتيح"</string>
- <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"تعرَّف على اختصارات لوحة المفاتيح"</string>
+ <string name="launch_keyboard_tutorial_notification_content" msgid="2131074100205028210">"المزيد من المعلومات حول اختصارات لوحة المفاتيح"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"التنقّل باستخدام لوحة اللمس"</string>
<string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"تعرَّف على إيماءات لوحة اللمس"</string>
<string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"التنقّل باستخدام لوحة المفاتيح ولوحة اللمس"</string>
- <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"تعرَّف على إيماءات لوحة اللمس واختصارات لوحة المفاتيح والمزيد"</string>
+ <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="8557098239919786046">"المزيد من المعلومات حول إيماءات لوحة اللمس واختصارات لوحة المفاتيح وغير ذلك"</string>
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"رجوع"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"الانتقال إلى الصفحة الرئيسية"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"عرض التطبيقات المستخدَمة مؤخرًا"</string>
@@ -1573,13 +1586,16 @@
<string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="165474092660941104">"تمّت إزالة اختصارات \"زيادة تعتيم الشاشة\""</string>
<string name="qs_edit_mode_category_connectivity" msgid="4559726936546032672">"إمكانية الاتصال"</string>
<string name="qs_edit_mode_category_accessibility" msgid="7969091385071475922">"تسهيل الاستخدام"</string>
- <string name="qs_edit_mode_category_utilities" msgid="8123080090108420095">"خدمات عامة"</string>
+ <string name="qs_edit_mode_category_utilities" msgid="8123080090108420095">"الأدوات المساعدة"</string>
<string name="qs_edit_mode_category_privacy" msgid="6577774443194551775">"الخصوصية"</string>
<string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"مقدَّمة من التطبيقات"</string>
<string name="qs_edit_mode_category_display" msgid="4749511439121053942">"العرض"</string>
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"غير معروفة"</string>
- <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"هل تريد إعادة ضبط كل المربّعات؟"</string>
+ <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"هل المطلوب ضبط كل المربّعات؟"</string>
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"ستتم إعادة ضبط جميع مربّعات \"الإعدادات السريعة\" إلى الإعدادات الأصلية للجهاز"</string>
<string name="demote_explain_text" msgid="3942301497888762295">"لن يعرض تطبيق \"<xliff:g id="APPLICATION">%1$s</xliff:g>\" التغطية المباشرة للأخبار بعد الآن. يمكنك تغيير ذلك في أي وقت من خلال \"الإعدادات\"."</string>
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"\"<xliff:g id="STREAM_NAME">%1$s</xliff:g>\"، <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
+ <string name="underlay_close_button_content_description" msgid="6518481455548869894">"إغلاق"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="7666159428113013319">"لفتح الإشعارات، عليك التمرير سريعًا من أعلى يمين الشاشة"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="3842355853737057332">"لفتح \"الإعدادات السريعة\"، عليك التمرير سريعًا من أعلى يسار الشاشة"</string>
</resources>
diff --git a/packages/SystemUI/res/values-as-ldrtl/strings.xml b/packages/SystemUI/res/values-as-ldrtl/strings.xml
index f017d0c..ee856c2 100644
--- a/packages/SystemUI/res/values-as-ldrtl/strings.xml
+++ b/packages/SystemUI/res/values-as-ldrtl/strings.xml
@@ -20,4 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="recents_quick_scrub_onboarding" msgid="2452671841151577157">"তাত্ক্ষণিকভাৱে আনটো এপ্ ব্যৱহাৰ কৰিবলৈ বাওঁফালে টানক"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="6543179539438220549">"জাননীসমূহ খুলিবলৈ একেবাৰে ওপৰৰ সোঁফালৰ পৰা ছোৱাইপ কৰক"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="5760503283520058116">"ক্ষিপ্ৰ ছেটিংসমূহ খুলিবলৈ একেবাৰে ওপৰৰ বাওঁফালৰ পৰা ছোৱাইপ কৰক"</string>
</resources>
diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml
index 04f3b2e..27abb71 100644
--- a/packages/SystemUI/res/values-as/strings.xml
+++ b/packages/SystemUI/res/values-as/strings.xml
@@ -467,6 +467,8 @@
<string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"মাইক্ৰ’ফ’নৰ বুটামটো ব্যৱহাৰ কৰিবলৈ, ছেটিঙত মাইক্ৰ’ফ’নৰ এক্সেছ সক্ষম কৰক।"</string>
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"ছেটিং খোলক"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"অন্য ডিভাইচ"</string>
+ <string name="media_suggestion_disconnected_text" msgid="8777932417076437525">"<xliff:g id="DEVICE_NAME">%s</xliff:g>ত প্লে’ কৰক"</string>
+ <string name="media_suggestion_failure_text" msgid="8453022394887613774">"সংযোগ কৰিব নোৱাৰি। পুনৰ চেষ্টা কৰক।"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"অৱলোকন ট’গল কৰক"</string>
<string name="zen_modes_dialog_title" msgid="8854640808100096934">"ম’ড"</string>
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"কৰা হ’ল"</string>
@@ -582,6 +584,7 @@
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
<skip />
<string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"গোটেই স্ক্ৰীনখন শ্বেয়াৰ কৰক"</string>
+ <string name="screen_share_permission_dialog_option_text_entire_screen_for_display" msgid="1545850238689169166">"<xliff:g id="DISPLAY_NAME">%s</xliff:g> শ্বেয়াৰ কৰক"</string>
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
<skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"আপুনি আপোনাৰ গোটেই স্ক্ৰীনখন শ্বেয়াৰ কৰি থাকোঁতে, আপোনাৰ স্ক্ৰীনত থকা যিকোনো বস্তু <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>ত দৃশ্যমান হয়। সেয়ে পাছৱৰ্ড, পৰিশোধৰ সবিশেষ, বাৰ্তা, ফট’ আৰু অডিঅ’ আৰু ভিডিঅ’ৰ ক্ষেত্ৰত সাৱধান হওক।"</string>
@@ -593,6 +596,7 @@
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"আপোনাৰ স্ক্ৰীনখন কাষ্ট কৰিবনে?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"এটা এপ্ কাষ্ট কৰক"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"গোটেই স্ক্ৰীনখন কাষ্ট কৰক"</string>
+ <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen_for_display" msgid="5524817761472941086">"<xliff:g id="DISPLAY_NAME">%s</xliff:g> কাষ্ট কৰক"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"যেতিয়া আপুনি গোটেই স্ক্ৰীনখন কাষ্ট কৰি থাকে, তেতিয়া আপোনাৰ স্ক্ৰীনত থকা যিকোনো বস্তু দৃশ্যমান হয়। গতিকে, পাছৱৰ্ড, পৰিশোধৰ সবিশেষ, বাৰ্তা, ফট’ আৰু অডিঅ’ আৰু ভিডিঅ’ৰ ক্ষেত্ৰত সাৱধান হওক।"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"যেতিয়া আপুনি কোনো এপ্ কাষ্ট কৰি থাকে, তেতিয়া সেই এপ্টোত দেখুওৱা বা প্লে’ কৰা যিকোনো বস্তু দৃশ্যমান হয়। গতিকে, পাছৱৰ্ড, পৰিশোধৰ সবিশেষ, বাৰ্তা, ফট’ আৰু অডিঅ’ আৰু ভিডিঅ’ৰ ক্ষেত্ৰত সাৱধান হওক।"</string>
<string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"স্ক্ৰীন কাষ্ট কৰক"</string>
@@ -673,6 +677,7 @@
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"TrustAgentএ আনলক কৰি ৰাখিছে"</string>
<string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"ডিভাইচ লক কৰা হৈছে, বিশ্বাসযোগ্যতা প্ৰমাণীকৰণৰ অতি বেছিসংখ্যক প্ৰয়াস"</string>
<string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"ডিভাইচ লক কৰা হৈছে\nবিশ্বাসযোগ্যতা প্ৰমাণীকৰণ কৰিব পৰা নগ’ল"</string>
+ <string name="keyguard_indication_after_watch_disconnected" msgid="6329761892437377710">"আপোনাৰ ঘড়ীটোৱে লক কৰিছে"</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="accessibility_volume_settings" msgid="1458961116951564784">"ধ্বনিৰ ছেটিং"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"স্বয়ংক্ৰিয় কেপশ্বন মিডিয়া"</string>
@@ -799,6 +804,10 @@
<string name="enable_bluetooth_message" msgid="6740938333772779717">"আপোনাৰ টেবলেটত আপোনাৰ কীব\'ৰ্ড সংযোগ কৰিবলৈ আপুনি প্ৰথমে ব্লুটুথ অন কৰিব লাগিব।"</string>
<string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"অন কৰক"</string>
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"অন আছে - মুখাৱয়ব ভিত্তিক"</string>
+ <!-- no translation found for notification_guts_bundle_title (1345506995443305361) -->
+ <skip />
+ <!-- no translation found for notification_guts_bundle_summary (3971530802237393600) -->
+ <skip />
<string name="inline_done_button" msgid="6043094985588909584">"কৰা হ’ল"</string>
<string name="inline_ok_button" msgid="603075490581280343">"প্ৰয়োগ কৰক"</string>
<string name="inline_turn_off_notifications" msgid="2653064779176881329">"অফ কৰক"</string>
@@ -984,6 +993,7 @@
<string name="right_icon" msgid="1103955040645237425">"সোঁ আইকন"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"টাইল যোগ কৰিবলৈ হেঁচি ধৰি টানি আনক"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"টাইলসমূহ পুনৰ সজাবলৈ ধৰি টানক"</string>
+ <string name="select_to_rearrange_tiles" msgid="6166153534235179148">"পুনৰ সজাবলৈ আৰু আকাৰ সলনি কৰিবলৈ টাইল বাছনি কৰক"</string>
<string name="tap_to_position_tile" msgid="6282815817773342757">"টাইল লগাবলৈ টিপক"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"আঁতৰাবৰ বাবে টানি আনি ইয়াত এৰি দিয়ক"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"আপোনাক অতিকমেও <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g>খন টাইল লাগিব"</string>
@@ -1003,10 +1013,10 @@
<string name="tuner_low_priority" msgid="8412666814123009820">"কম গুৰুত্বপূৰ্ণ জাননীৰ আইকনসমূহ দেখুৱাওক"</string>
<string name="other" msgid="429768510980739978">"অন্যান্য"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"টাইলৰ আকাৰ ট’গল কৰক"</string>
- <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"টাইল আঁতৰাবলৈ"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="4811778796933958616">"টাইল আঁতৰাওক"</string>
<string name="accessibility_qs_edit_toggle_placement_mode" msgid="3870429389210569610">"প্লে’চমেণ্ট ম’ড ট’গল কৰক"</string>
<string name="accessibility_qs_edit_toggle_selection" msgid="2201248304072372239">"বাছনি ট’গল কৰক"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"অন্তিম স্থানত টাইল যোগ দিয়ক"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="7188303979684400620">"অন্তিম স্থানত টাইল যোগ দিয়ক"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"টাইল স্থানান্তৰ কৰক"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"বিচৰা স্থানত টাইল যোগ দিয়ক"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> নম্বৰলৈ স্থানান্তৰ কৰক"</string>
@@ -1093,7 +1103,8 @@
<string name="auto_saver_text" msgid="3214960308353838764">"বেটাৰী শেষ হোৱাৰ সম্ভাৱনা থাকিলে অন কৰক"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"নালাগে, ধন্যবাদ"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"ব্যৱহাৰ হৈ আছে"</string>
- <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"এপ্লিকেশ্বনসমূহে আপোনাৰ <xliff:g id="TYPES_LIST">%s</xliff:g> ব্যৱহাৰ কৰি আছে।"</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps (4994455315457996796) -->
+ <skip />
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" আৰু "</string>
<string name="ongoing_privacy_dialog_using_op" msgid="426635338010011796">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g>এ ব্যৱহাৰ কৰি আছে"</string>
@@ -1508,11 +1519,11 @@
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"শ্বৰ্টকাট যোগ দিয়ক"</string>
<string name="shortcut_helper_delete_shortcut_button_label" msgid="3148773472696137052">"শ্বৰ্টকাট মচক"</string>
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"কীব’ৰ্ড ব্যৱহাৰ কৰি নেভিগে’ট কৰক"</string>
- <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"কীব’ৰ্ডৰ শ্বৰ্টকাটসমূহৰ বিষয়ে জানক"</string>
+ <string name="launch_keyboard_tutorial_notification_content" msgid="2131074100205028210">"কীব’ৰ্ডৰ শ্বৰ্টকাটসমূহ জানক"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"আপোনাৰ টাচ্চপেড ব্যৱহাৰ কৰি নেভিগে’ট কৰক"</string>
<string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"টাচ্চপেডৰ নিৰ্দেশসমূহ জানক"</string>
<string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"আপোনাৰ কীব’ৰ্ড আৰু টাচ্চপেড ব্যৱহাৰ কৰি নেভিগে’ট কৰক"</string>
- <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"টাচ্চপেডৰ নিৰ্দেশ, কীব’ৰ্ডৰ শ্বৰ্টকাট আৰু অধিকৰ বিষয়ে জানক"</string>
+ <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="8557098239919786046">"টাচ্চপেডত দিয়া নিৰ্দেশ, কীব’ৰ্ডৰ শ্বৰ্টকাট আৰু অধিকৰ বিষয়ে জানক"</string>
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"উভতি যাওক"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"গৃহ পৃষ্ঠালৈ যাওক"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"শেহতীয়া এপ্সমূহ চাওক"</string>
@@ -1582,4 +1593,7 @@
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"আটাইবোৰ ক্ষিপ্ৰ ছেটিঙৰ টাইল ডিভাইচৰ মূল ছেটিংছলৈ ৰিছেট হ’ব"</string>
<string name="demote_explain_text" msgid="3942301497888762295">"<xliff:g id="APPLICATION">%1$s</xliff:g>এ আৰু লাইভ আপডে’ট নেদেখুৱাব। আপুনি যিকোনো সময়তে ছেটিঙত এইটো সলনি কৰিব পাৰে।"</string>
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
+ <string name="underlay_close_button_content_description" msgid="6518481455548869894">"বন্ধ কৰক"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="7666159428113013319">"জাননীসমূহ খুলিবলৈ একেবাৰে ওপৰৰ বাওঁফালৰ পৰা ছোৱাইপ কৰক"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="3842355853737057332">"ক্ষিপ্ৰ ছেটিংসমূহ খুলিবলৈ একেবাৰে ওপৰৰ সোঁফালৰ পৰা ছোৱাইপ কৰক"</string>
</resources>
diff --git a/packages/SystemUI/res/values-az-ldrtl/strings.xml b/packages/SystemUI/res/values-az-ldrtl/strings.xml
index 13a0a20..4eb9c7f 100644
--- a/packages/SystemUI/res/values-az-ldrtl/strings.xml
+++ b/packages/SystemUI/res/values-az-ldrtl/strings.xml
@@ -20,4 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="recents_quick_scrub_onboarding" msgid="2452671841151577157">"Digər tətbiqə sürətlə keçmək üçün sola çəkin"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="6543179539438220549">"Bildirişləri açmaq üçün yuxarı sağ tərəfdən çəkin"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="5760503283520058116">"Sürətli Ayarları açmaq üçün yuxarı sol tərəfdən çəkin"</string>
</resources>
diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml
index d329dfa3..a422b6e 100644
--- a/packages/SystemUI/res/values-az/strings.xml
+++ b/packages/SystemUI/res/values-az/strings.xml
@@ -252,8 +252,7 @@
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g> üzərindən qoşuldu."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"<xliff:g id="CAST">%s</xliff:g> cihazına qoşulub."</string>
<string name="accessibility_expand_group" msgid="521237935987978624">"Qrupu genişləndirin."</string>
- <!-- no translation found for accessibility_collapse_group (1842704016768329517) -->
- <skip />
+ <string name="accessibility_collapse_group" msgid="1842704016768329517">"Qrupu yığcamlaşdırın."</string>
<string name="accessibility_add_device_to_group" msgid="5446422960697860806">"Qrupa cihaz əlavə edin."</string>
<string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"Cihazı qrupdan silin."</string>
<string name="accessibility_open_application" msgid="1749126077501259712">"Tətbiqi açın."</string>
@@ -468,6 +467,8 @@
<string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"Mikrofon düyməsini istifadə etmək üçün Ayarlarda mikrofona girişi aktiv edin."</string>
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Ayarları açın"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Digər cihaz"</string>
+ <string name="media_suggestion_disconnected_text" msgid="8777932417076437525">"Burada işə salın: <xliff:g id="DEVICE_NAME">%s</xliff:g>"</string>
+ <string name="media_suggestion_failure_text" msgid="8453022394887613774">"Qoşulmur. Yenə cəhd edin."</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"İcmala Keçin"</string>
<string name="zen_modes_dialog_title" msgid="8854640808100096934">"Rejimlər"</string>
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Hazırdır"</string>
@@ -583,6 +584,7 @@
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
<skip />
<string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Bütün ekranı paylaşın"</string>
+ <string name="screen_share_permission_dialog_option_text_entire_screen_for_display" msgid="1545850238689169166">"Paylaşın: <xliff:g id="DISPLAY_NAME">%s</xliff:g>"</string>
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
<skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Bütün ekranı paylaşdığınız zaman ekrandakı hər şey <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> üçün görünən olacaq. Parol, ödəniş məlumatı, mesaj, foto, habelə audio və video kimi məlumatlarla bağlı diqqətli olun."</string>
@@ -594,6 +596,7 @@
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Ekran yayımlansın?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Bir tətbiqi yayımlayın"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Bütün ekranı yayımlayın"</string>
+ <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen_for_display" msgid="5524817761472941086">"Yayımlayın: <xliff:g id="DISPLAY_NAME">%s</xliff:g>"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Bütün ekranı yayımladıqda ekrandakı hər şey görünür. Parol, ödəniş detalları, mesaj, foto, habelə audio və video kimi məlumatlarla bağlı diqqətli olun."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Tətbiq yayımladıqda həmin tətbiqdə göstərilən və ya işə salınan hər şey görünür. Parol, ödəniş detalları, mesaj, foto, habelə audio və video kimi məlumatlarla bağlı diqqətli olun."</string>
<string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Ekranı yayımlayın"</string>
@@ -674,6 +677,7 @@
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"TrustAgent ilə açıq saxlayın"</string>
<string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"Cihaz kilidlənib, həddindən çox identifikasiya cəhdi"</string>
<string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"Cihaz kilidləndi\nDoğrulama uğursuz oldu"</string>
+ <string name="keyguard_indication_after_watch_disconnected" msgid="6329761892437377710">"Saatınız tərəfindən kilidlənib"</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="accessibility_volume_settings" msgid="1458961116951564784">"Səs ayarları"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Avtomatik başlıq mediası"</string>
@@ -800,6 +804,10 @@
<string name="enable_bluetooth_message" msgid="6740938333772779717">"Tabletinizlə klaviaturaya bağlanmaq üçün ilk olaraq Bluetooth\'u aktivləşdirməlisiniz."</string>
<string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Aktivləşdirin"</string>
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Aktiv - Üz əsaslı"</string>
+ <!-- no translation found for notification_guts_bundle_title (1345506995443305361) -->
+ <skip />
+ <!-- no translation found for notification_guts_bundle_summary (3971530802237393600) -->
+ <skip />
<string name="inline_done_button" msgid="6043094985588909584">"Hazırdır"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Tətbiq edin"</string>
<string name="inline_turn_off_notifications" msgid="2653064779176881329">"Söndürün"</string>
@@ -985,6 +993,7 @@
<string name="right_icon" msgid="1103955040645237425">"Sağ ikona"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Mozaika əlavə etmək üçün basıb saxlayaraq çəkin"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"İkonları yenidən düzənləmək üçün saxlayaraq dartın"</string>
+ <string name="select_to_rearrange_tiles" msgid="6166153534235179148">"Yenidən nizamlamaq və ölçüləndirmək üçün mozaikləri seçin"</string>
<string name="tap_to_position_tile" msgid="6282815817773342757">"Mozaiki yerləşdirmək üçün toxunun"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Silmək üçün bura sürüşdürün"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Minimum <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> mozaika lazımdır"</string>
@@ -1004,10 +1013,12 @@
<string name="tuner_low_priority" msgid="8412666814123009820">"Aşağı prioritet bildiriş işarələrini göstərin"</string>
<string name="other" msgid="429768510980739978">"Digər"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"mozaik ölçüsünü aktiv/deaktiv edin"</string>
- <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"lövhəni silin"</string>
+ <!-- no translation found for accessibility_qs_edit_remove_tile_action (4811778796933958616) -->
+ <skip />
<string name="accessibility_qs_edit_toggle_placement_mode" msgid="3870429389210569610">"yerləşdirmə rejimini aktiv/deaktiv edin"</string>
<string name="accessibility_qs_edit_toggle_selection" msgid="2201248304072372239">"seçimi aktiv/deaktiv edin"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"son mövqeyə mozaik əlavə edin"</string>
+ <!-- no translation found for accessibility_qs_edit_tile_add_action (7188303979684400620) -->
+ <skip />
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Lövhəni köçürün"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"İstənilən mövqeyə mozaik əlavə edin"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> mövqeyinə köçürün"</string>
@@ -1094,7 +1105,8 @@
<string name="auto_saver_text" msgid="3214960308353838764">"Batareya bitmək üzrə olduqda aktiv edin"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Xeyr, təşəkkür"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"İstifadə olunur"</string>
- <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Tətbiqlər <xliff:g id="TYPES_LIST">%s</xliff:g> istifadə edir."</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps (4994455315457996796) -->
+ <skip />
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" və "</string>
<string name="ongoing_privacy_dialog_using_op" msgid="426635338010011796">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> tərəfindən istifadə edilir"</string>
@@ -1251,15 +1263,13 @@
<string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(bağlantı kəsildi)"</string>
<string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"Dəyişmək olmur. Yenidən cəhd etmək üçün toxunun."</string>
<string name="media_output_dialog_pairing_new" msgid="5098212763195577270">"Cihaz qoşun"</string>
- <!-- no translation found for media_output_dialog_button_connect_device (2819038239946643550) -->
- <skip />
+ <string name="media_output_dialog_button_connect_device" msgid="2819038239946643550">"Cihaz qoşun"</string>
<string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Naməlum tətbiq"</string>
<string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Yayımı dayandırın"</string>
<string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"Audio çıxış üçün əlçatan cihazlar."</string>
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Səs"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
- <!-- no translation found for media_output_group_title_connected_speakers (3413876972919684479) -->
- <skip />
+ <string name="media_output_group_title_connected_speakers" msgid="3413876972919684479">"Qoşulmuş dinamiklər"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Dinamiklər & Displeylər"</string>
<string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Təklif olunan Cihazlar"</string>
<string name="media_input_group_title" msgid="2057057473860783021">"Daxiletmə"</string>
@@ -1328,10 +1338,8 @@
<string name="accessibility_enter_hint" msgid="2617864063504824834">"cihaz daxil edin"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Açmaq üçün barmaq izindən istifadə edin"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Doğrulanma tələb olunur. Doğrulamaq üçün barmaq izi sensoruna toxunun."</string>
- <!-- no translation found for resume_animation (9148788411429330671) -->
- <skip />
- <!-- no translation found for pause_animation (1919705510058107504) -->
- <skip />
+ <string name="resume_animation" msgid="9148788411429330671">"Animasiyanı davam etdirin"</string>
+ <string name="pause_animation" msgid="1919705510058107504">"Animasiyanı durdurun"</string>
<string name="ongoing_call_content_description" msgid="6394763878322348560">"Davam edən zəng"</string>
<string name="ongoing_notification_extra_content_description" msgid="2098752668861351265">"Davam edir"</string>
<string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobil data"</string>
@@ -1513,11 +1521,13 @@
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"Qısayol əlavə edin"</string>
<string name="shortcut_helper_delete_shortcut_button_label" msgid="3148773472696137052">"Qısayolu silin"</string>
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Klaviaturadan istifadə edərək hərəkət edin"</string>
- <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Klaviatura qısayolları haqqında öyrənin"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2131074100205028210) -->
+ <skip />
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Taçpeddən istifadə edərək hərəkət edin"</string>
<string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Taçped jestlərini öyrənin"</string>
<string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Klaviatura və taçpeddən istifadə edərək hərəkət edin"</string>
- <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Taçped jestləri, klaviatura qısayolları və s. haqqında öyrənin"</string>
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (8557098239919786046) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Geri qayıdın"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Əsas səhifəyə keçin"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Son tətbiqlərə baxın"</string>
@@ -1587,4 +1597,7 @@
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Bütün Sürətli Ayarlar mozaiki cihazın orijinal ayarlarına sıfırlanacaq"</string>
<string name="demote_explain_text" msgid="3942301497888762295">"<xliff:g id="APPLICATION">%1$s</xliff:g> artıq Canlı Güncəlləmələri göstərməyəcək. Ayarlarda dəyişə bilərsiniz."</string>
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
+ <string name="underlay_close_button_content_description" msgid="6518481455548869894">"Bağlayın"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="7666159428113013319">"Bildirişləri açmaq üçün yuxarı sol tərəfdən çəkin"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="3842355853737057332">"Sürətli Ayarları açmaq üçün yuxarı sağ tərəfdən çəkin"</string>
</resources>
diff --git a/packages/SystemUI/res/values-b+sr+Latn-ldrtl/strings.xml b/packages/SystemUI/res/values-b+sr+Latn-ldrtl/strings.xml
index 8e5ecf9..0bc8b23 100644
--- a/packages/SystemUI/res/values-b+sr+Latn-ldrtl/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn-ldrtl/strings.xml
@@ -20,4 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="recents_quick_scrub_onboarding" msgid="2452671841151577157">"Prevucite ulevo da biste brzo promenili aplikacije"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="6543179539438220549">"Prevucite od gornjeg desnog ugla da biste otvorili obaveštenja"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="5760503283520058116">"Prevucite od gornjeg levog ugla da biste otvorili Brza podešavanja"</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 5b8fa99..5f6e83d 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -398,7 +398,7 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Snimanje ekrana"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Počnite"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Zaustavite"</string>
- <string name="qs_record_issue_label" msgid="8166290137285529059">"Evidentirajte problem"</string>
+ <string name="qs_record_issue_label" msgid="8166290137285529059">"Evidentiraj problem"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"Pokreni"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"Zaustavi"</string>
<string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Izveštaj o grešci"</string>
@@ -467,6 +467,8 @@
<string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"Da biste koristili dugme mikrofona, omogućite pristup mikrofonu u Podešavanjima."</string>
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Otvori Podešavanja"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Drugi uređaj"</string>
+ <string name="media_suggestion_disconnected_text" msgid="8777932417076437525">"Pusti na: <xliff:g id="DEVICE_NAME">%s</xliff:g>"</string>
+ <string name="media_suggestion_failure_text" msgid="8453022394887613774">"Povez. nije uspelo. Ponovite."</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Uključi/isključi pregled"</string>
<string name="zen_modes_dialog_title" msgid="8854640808100096934">"Režimi"</string>
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Gotovo"</string>
@@ -582,6 +584,7 @@
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
<skip />
<string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Deli ceo ekran"</string>
+ <string name="screen_share_permission_dialog_option_text_entire_screen_for_display" msgid="1545850238689169166">"Deli: <xliff:g id="DISPLAY_NAME">%s</xliff:g>"</string>
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
<skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Kada delite ceo ekran, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> vidi sve što je na njemu. Zato pazite na lozinke, informacije o plaćanju, poruke, slike, audio i video sadržaj."</string>
@@ -593,6 +596,7 @@
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Želite da prebacite ekran?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Prebaci jednu aplikaciju"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Prebaci ceo ekran"</string>
+ <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen_for_display" msgid="5524817761472941086">"Prebaci: <xliff:g id="DISPLAY_NAME">%s</xliff:g>"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Kada prebacujete ceo ekran, vidi se sve što je na njemu. Zato pazite na lozinke, informacije o plaćanju, poruke, slike, audio i video sadržaj."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Kada prebacujete aplikaciju, vidi se sav sadržaj koji se prikazuje ili pušta u njoj. Zato pazite na lozinke, informacije o plaćanju, poruke, slike, audio i video sadržaj."</string>
<string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Prebaci ekran"</string>
@@ -673,6 +677,7 @@
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Pouzdani agent sprečava zaključavanje"</string>
<string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"Uređaj je zaključan, previše pokušaja potvrde identiteta"</string>
<string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"Uređaj je zaključan\nPotvrda identiteta nije uspela"</string>
+ <string name="keyguard_indication_after_watch_disconnected" msgid="6329761892437377710">"Sat je onemogućio pristup"</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="accessibility_volume_settings" msgid="1458961116951564784">"Podešavanja zvuka"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Automatski titl za medije"</string>
@@ -799,6 +804,10 @@
<string name="enable_bluetooth_message" msgid="6740938333772779717">"Da biste povezali tastaturu sa tabletom, prvo morate da uključite Bluetooth."</string>
<string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Uključi"</string>
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Uključeno – na osnovu lica"</string>
+ <!-- no translation found for notification_guts_bundle_title (1345506995443305361) -->
+ <skip />
+ <!-- no translation found for notification_guts_bundle_summary (3971530802237393600) -->
+ <skip />
<string name="inline_done_button" msgid="6043094985588909584">"Gotovo"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Primeni"</string>
<string name="inline_turn_off_notifications" msgid="2653064779176881329">"Isključi"</string>
@@ -984,6 +993,7 @@
<string name="right_icon" msgid="1103955040645237425">"Desna ikona"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Zadržite i prevucite da biste dodali pločice"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Zadržite i prevucite da biste promenili raspored pločica"</string>
+ <string name="select_to_rearrange_tiles" msgid="6166153534235179148">"Izaberite pločice da biste im promenili raspored i veličinu"</string>
<string name="tap_to_position_tile" msgid="6282815817773342757">"Dodirnite da biste postavili pločicu"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Prevucite ovde da biste uklonili"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Minimalan broj pločica je <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g>"</string>
@@ -1003,10 +1013,10 @@
<string name="tuner_low_priority" msgid="8412666814123009820">"Prikaži ikone obaveštenja niskog prioriteta"</string>
<string name="other" msgid="429768510980739978">"Drugo"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"uključivanje ili isključivanje veličine pločice"</string>
- <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"uklonili pločicu"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="4811778796933958616">"Uklonite pločicu"</string>
<string name="accessibility_qs_edit_toggle_placement_mode" msgid="3870429389210569610">"uključivanje ili isključivanje režima postavljanja"</string>
<string name="accessibility_qs_edit_toggle_selection" msgid="2201248304072372239">"potvrdili ili opozvali izbor"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"dodali pločicu na poslednju poziciju"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="7188303979684400620">"Dodajte pločicu na poslednju poziciju"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Premestite pločicu"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Dodajte pločicu na željenu poziciju"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Premestite na <xliff:g id="POSITION">%1$d</xliff:g>. poziciju"</string>
@@ -1093,7 +1103,8 @@
<string name="auto_saver_text" msgid="3214960308353838764">"Uključite ako će baterija verovatno da se isprazni"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Ne, hvala"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"U upotrebi"</string>
- <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Aplikacije koriste <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps (4994455315457996796) -->
+ <skip />
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" i "</string>
<string name="ongoing_privacy_dialog_using_op" msgid="426635338010011796">"Koristi: <xliff:g id="APPLICATION_NAME">%1$s</xliff:g>"</string>
@@ -1508,11 +1519,11 @@
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"Dodajte prečicu"</string>
<string name="shortcut_helper_delete_shortcut_button_label" msgid="3148773472696137052">"Izbrišite prečicu"</string>
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Krećite se pomoću tastature"</string>
- <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Saznajte više o tasterskim prečicama"</string>
+ <string name="launch_keyboard_tutorial_notification_content" msgid="2131074100205028210">"Saznajte više o tasterskim prečicama"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Krećite se pomoću tačpeda"</string>
<string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Naučite pokrete za tačped"</string>
<string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Krećite se pomoću tastature i tačpeda"</string>
- <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Naučite pokrete za tačped, tasterske prečice i drugo"</string>
+ <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="8557098239919786046">"Naučite pokrete na tačpedu, tasterske prečice i drugo"</string>
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Nazad"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Idi na početni ekran"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Prikaži nedavno korišćene aplikacije"</string>
@@ -1582,4 +1593,7 @@
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Sve pločice Brzih podešavanja će se resetovati na prvobitna podešavanja uređaja"</string>
<string name="demote_explain_text" msgid="3942301497888762295">"<xliff:g id="APPLICATION">%1$s</xliff:g> više neće prikazivati novosti uživo. Ovo možete da promenite u svakom trenutku u Podešavanjima."</string>
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
+ <string name="underlay_close_button_content_description" msgid="6518481455548869894">"Zatvori"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="7666159428113013319">"Prevucite od gornjeg levog ugla da biste otvorili obaveštenja"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="3842355853737057332">"Prevucite od gornjeg desnog ugla da biste otvorili Brza podešavanja"</string>
</resources>
diff --git a/packages/SystemUI/res/values-be-ldrtl/strings.xml b/packages/SystemUI/res/values-be-ldrtl/strings.xml
index 30f691a5..de52905 100644
--- a/packages/SystemUI/res/values-be-ldrtl/strings.xml
+++ b/packages/SystemUI/res/values-be-ldrtl/strings.xml
@@ -20,4 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="recents_quick_scrub_onboarding" msgid="2452671841151577157">"Каб хутка пераключыцца паміж праграмамі, перацягніце ўлева"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="6543179539438220549">"Каб адкрыць апавяшчэнні, правядзіце пальцам ад верхняга правага вугла"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="5760503283520058116">"Каб адкрыць хуткія налады, правядзіце пальцам ад верхняга левага вугла"</string>
</resources>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index 61c1937..9604568 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -467,6 +467,8 @@
<string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"Каб выкарыстоўваць кнопку мікрафона, дайце доступ да мікрафона ў Наладах."</string>
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Адкрыць налады"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Іншая прылада"</string>
+ <string name="media_suggestion_disconnected_text" msgid="8777932417076437525">"Прайграць на прыладзе \"<xliff:g id="DEVICE_NAME">%s</xliff:g>\""</string>
+ <string name="media_suggestion_failure_text" msgid="8453022394887613774">"Не ўдаецца падключыцца. Паўтарыце спробу."</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Уключыць/выключыць агляд"</string>
<string name="zen_modes_dialog_title" msgid="8854640808100096934">"Рэжымы"</string>
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Гатова"</string>
@@ -582,6 +584,7 @@
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
<skip />
<string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Абагуліць увесь экран"</string>
+ <string name="screen_share_permission_dialog_option_text_entire_screen_for_display" msgid="1545850238689169166">"Абагуліць дысплэй \"<xliff:g id="DISPLAY_NAME">%s</xliff:g>\""</string>
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
<skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Калі вы абагульваеце ўвесь экран, праграма \"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>\" можа бачыць усё, што адбываецца на экране. Таму прадухіліце паказ пароляў, плацежных рэквізітаў, паведамленняў, фота, відэа і аўдыя."</string>
@@ -593,6 +596,7 @@
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Уключыць трансляцыю экрана?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Трансліраваць адну праграму"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Трансліраваць увесь экран"</string>
+ <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen_for_display" msgid="5524817761472941086">"Трансліраваць на дысплэй \"<xliff:g id="DISPLAY_NAME">%s</xliff:g>\""</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Калі вы трансліруеце ўвесь экран, бачна ўсё, што адбываецца на экране. Таму прадухіліце паказ пароляў, плацежных рэквізітаў, паведамленняў, фота, відэа і аўдыя."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Калі вы трансліруеце праграму, бачна ўсё, што паказваецца ці прайграецца ў гэтай праграме. Таму прадухіліце паказ пароляў, плацежных рэквізітаў, паведамленняў, фота, відэа і аўдыя."</string>
<string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Трансліраваць экран"</string>
@@ -673,6 +677,7 @@
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Разблакіравана з дапамогай TrustAgent"</string>
<string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"Прылада заблакіравана, бо было занадта многа спроб аўтэнтыфікацыі"</string>
<string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"Прылада заблакіравана\nАўтэнтыфікацыя не пройдзена"</string>
+ <string name="keyguard_indication_after_watch_disconnected" msgid="6329761892437377710">"Заблакіравана гадзіннікам"</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="accessibility_volume_settings" msgid="1458961116951564784">"Налады гуку"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Аўтаматычныя субцітры"</string>
@@ -799,6 +804,10 @@
<string name="enable_bluetooth_message" msgid="6740938333772779717">"Для падлучэння клавіятуры да планшэта трэба спачатку ўключыць Bluetooth."</string>
<string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Уключыць"</string>
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Уключана – З улікам паставы галавы"</string>
+ <!-- no translation found for notification_guts_bundle_title (1345506995443305361) -->
+ <skip />
+ <!-- no translation found for notification_guts_bundle_summary (3971530802237393600) -->
+ <skip />
<string name="inline_done_button" msgid="6043094985588909584">"Гатова"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Прымяніць"</string>
<string name="inline_turn_off_notifications" msgid="2653064779176881329">"Выключыць"</string>
@@ -984,6 +993,7 @@
<string name="right_icon" msgid="1103955040645237425">"Значок \"управа\""</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Перацягніце патрэбныя пліткі"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Каб змяніць парадак плітак, утрымлівайце і перацягвайце іх"</string>
+ <string name="select_to_rearrange_tiles" msgid="6166153534235179148">"Выберыце пліткі, парадак ці памер якіх трэба змяніць"</string>
<string name="tap_to_position_tile" msgid="6282815817773342757">"Націсніце, каб размясціць плітку"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Перацягніце сюды, каб выдаліць"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Мінімальная колькасць плітак: <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g>"</string>
@@ -1003,10 +1013,12 @@
<string name="tuner_low_priority" msgid="8412666814123009820">"Паказваць значкі апавяшчэнняў з нізкім прыярытэтам"</string>
<string name="other" msgid="429768510980739978">"Іншае"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"змяніць памер пліткі"</string>
- <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"выдаліць плітку"</string>
+ <!-- no translation found for accessibility_qs_edit_remove_tile_action (4811778796933958616) -->
+ <skip />
<string name="accessibility_qs_edit_toggle_placement_mode" msgid="3870429389210569610">"пераключыць рэжым размяшчэння"</string>
<string name="accessibility_qs_edit_toggle_selection" msgid="2201248304072372239">"выбраць або скасаваць выбар"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"дадаць плітку ў апошнюю пазіцыю"</string>
+ <!-- no translation found for accessibility_qs_edit_tile_add_action (7188303979684400620) -->
+ <skip />
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Перамясціць плітку"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Дадаць плітку ў патрэбную пазіцыю"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Перамясціць на пазіцыю <xliff:g id="POSITION">%1$d</xliff:g>"</string>
@@ -1093,7 +1105,8 @@
<string name="auto_saver_text" msgid="3214960308353838764">"Уключыце, калі зарад акумулятара заканчваецца"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Не, дзякуй"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"Выкарыстоўваецца"</string>
- <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Праграмы выкарыстоўваюць: <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps (4994455315457996796) -->
+ <skip />
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" і "</string>
<string name="ongoing_privacy_dialog_using_op" msgid="426635338010011796">"Выкарыстоўваецца праграмай \"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g>\""</string>
@@ -1508,11 +1521,11 @@
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"Дадаць ярлык"</string>
<string name="shortcut_helper_delete_shortcut_button_label" msgid="3148773472696137052">"Выдаліць спалучэнне клавіш"</string>
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Навігацыя з дапамогай клавіятуры"</string>
- <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Азнаёмцеся са спалучэннямі клавіш"</string>
+ <string name="launch_keyboard_tutorial_notification_content" msgid="2131074100205028210">"Азнаёмцеся са спалучэннямі клавіш"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Навігацыя з дапамогай сэнсарнай панэлі"</string>
<string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Азнаёмцеся з жэстамі для сэнсарнай панэлі"</string>
<string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Навігацыя з дапамогай клавіятуры і сэнсарнай панэлі"</string>
- <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Азнаёмцеся з жэстамі для сэнсарнай панэлі, спалучэннямі клавіш і г. д."</string>
+ <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="8557098239919786046">"Азнаёмцеся з жэстамі для сэнсарнай панэлі, спалучэннямі клавіш і г. д."</string>
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Назад"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"На галоўную старонку"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Прагляд нядаўніх праграм"</string>
@@ -1582,4 +1595,7 @@
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Усе пліткі хуткіх налад будуць скінуты да першапачатковых налад прылады"</string>
<string name="demote_explain_text" msgid="3942301497888762295">"<xliff:g id="APPLICATION">%1$s</xliff:g> больш не будзе паказваць навіны ў рэальным часе. Гэты параметр можна ў любы час змяніць у наладах."</string>
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
+ <string name="underlay_close_button_content_description" msgid="6518481455548869894">"Закрыць"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="7666159428113013319">"Каб адкрыць апавяшчэнні, правядзіце пальцам ад верхняга левага вугла"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="3842355853737057332">"Каб адкрыць хуткія налады, правядзіце пальцам ад верхняга правага вугла"</string>
</resources>
diff --git a/packages/SystemUI/res/values-bg-ldrtl/strings.xml b/packages/SystemUI/res/values-bg-ldrtl/strings.xml
index b5a92ea..17d7c6c 100644
--- a/packages/SystemUI/res/values-bg-ldrtl/strings.xml
+++ b/packages/SystemUI/res/values-bg-ldrtl/strings.xml
@@ -20,4 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="recents_quick_scrub_onboarding" msgid="2452671841151577157">"Плъзнете наляво за бързо превключване между приложенията"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="6543179539438220549">"Прекарайте пръст от горния десен ъгъл, за да отворите известията"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="5760503283520058116">"Прекарайте пръст от горния ляв ъгъл, за да отворите бързите настройки"</string>
</resources>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index 405ea55..0527b43 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -252,8 +252,7 @@
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Има връзка с <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"Установена е връзка с/ъс <xliff:g id="CAST">%s</xliff:g>."</string>
<string name="accessibility_expand_group" msgid="521237935987978624">"Разгъване на групата."</string>
- <!-- no translation found for accessibility_collapse_group (1842704016768329517) -->
- <skip />
+ <string name="accessibility_collapse_group" msgid="1842704016768329517">"Свиване на групата."</string>
<string name="accessibility_add_device_to_group" msgid="5446422960697860806">"Добавяне на устройството към групата."</string>
<string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"Премахване на устройството от групата."</string>
<string name="accessibility_open_application" msgid="1749126077501259712">"Отваряне на приложението."</string>
@@ -468,6 +467,8 @@
<string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"Активирайте достъпа до микрофона, за да използвате съответния бутон."</string>
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Към настройките"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Друго устройство"</string>
+ <string name="media_suggestion_disconnected_text" msgid="8777932417076437525">"Възпроизвеждане на <xliff:g id="DEVICE_NAME">%s</xliff:g>"</string>
+ <string name="media_suggestion_failure_text" msgid="8453022394887613774">"Няма връзка. Опитайте отново."</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Превключване на общия преглед"</string>
<string name="zen_modes_dialog_title" msgid="8854640808100096934">"Режими"</string>
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Готово"</string>
@@ -583,6 +584,7 @@
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
<skip />
<string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Споделяне на целия екран"</string>
+ <string name="screen_share_permission_dialog_option_text_entire_screen_for_display" msgid="1545850238689169166">"Споделяне на <xliff:g id="DISPLAY_NAME">%s</xliff:g>"</string>
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
<skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Когато споделяте целия си екран, всичко, което се показва на него, е видимо за <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Затова бъдете внимателни с неща като пароли, подробности за начини на плащане, съобщения, снимки, аудио и видео."</string>
@@ -594,6 +596,7 @@
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Искате ли да предавате екрана си?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Предаване на едно приложение"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Предаване на целия екран"</string>
+ <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen_for_display" msgid="5524817761472941086">"Предаване на <xliff:g id="DISPLAY_NAME">%s</xliff:g>"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Когато предавате целия екран, всичко, което се показва на него, е видимо. Затова бъдете внимателни с неща като пароли, подробности за начини на плащане, съобщения, снимки, аудио и видео."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Когато предавате дадено приложение, всичко, което се показва или възпроизвежда в него, е видимо. Затова бъдете внимателни с неща като пароли, подробности за начини на плащане, съобщения, снимки, аудио и видео."</string>
<string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Предаване на екрана"</string>
@@ -674,6 +677,7 @@
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Поддържа се отключено от надежден агент"</string>
<string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"Устройството бе заключено. Твърде много опити за удостоверяване"</string>
<string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"Устройството е заключено\nНеуспешно удостоверяване"</string>
+ <string name="keyguard_indication_after_watch_disconnected" msgid="6329761892437377710">"Отключено от часовника ви"</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="accessibility_volume_settings" msgid="1458961116951564784">"Настройки за звука"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Медия с автоматични надписи"</string>
@@ -800,6 +804,10 @@
<string name="enable_bluetooth_message" msgid="6740938333772779717">"За да свържете клавиатурата с таблета си, първо трябва да включите Bluetooth."</string>
<string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Включване"</string>
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Вкл. – въз основа на лицето"</string>
+ <!-- no translation found for notification_guts_bundle_title (1345506995443305361) -->
+ <skip />
+ <!-- no translation found for notification_guts_bundle_summary (3971530802237393600) -->
+ <skip />
<string name="inline_done_button" msgid="6043094985588909584">"Готово"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Прилагане"</string>
<string name="inline_turn_off_notifications" msgid="2653064779176881329">"Изключване"</string>
@@ -985,6 +993,7 @@
<string name="right_icon" msgid="1103955040645237425">"Дясна икона"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Задръжте и плъзнете, за да добавите плочки"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Задръжте и плъзнете, за да пренаредите плочките"</string>
+ <string name="select_to_rearrange_tiles" msgid="6166153534235179148">"Изберете панелите, които да пренаредите и преоразмерите"</string>
<string name="tap_to_position_tile" msgid="6282815817773342757">"Докоснете, за да позиционирате панела"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Преместете тук с плъзгане за премахване"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Трябва да останат поне <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> плочки"</string>
@@ -1004,10 +1013,10 @@
<string name="tuner_low_priority" msgid="8412666814123009820">"Показване на иконите за известията с нисък приоритет"</string>
<string name="other" msgid="429768510980739978">"Друго"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"превключване на размера на панела"</string>
- <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"премахване на панел"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="4811778796933958616">"Премахване на панел"</string>
<string name="accessibility_qs_edit_toggle_placement_mode" msgid="3870429389210569610">"превключване на режима за поставяне"</string>
<string name="accessibility_qs_edit_toggle_selection" msgid="2201248304072372239">"превключване на избраното"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"добавяне на панела на последната позиция"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="7188303979684400620">"Добавяне на панела на последната позиция"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Преместване на панел"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Добавяне на панела на желаната позиция"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Преместване към позиция <xliff:g id="POSITION">%1$d</xliff:g>"</string>
@@ -1094,7 +1103,8 @@
<string name="auto_saver_text" msgid="3214960308353838764">"Включване, когато е вероятно батерията да се изтощи"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Не, благодаря"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"Използва се"</string>
- <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Някои приложения използват <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps (4994455315457996796) -->
+ <skip />
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" и "</string>
<string name="ongoing_privacy_dialog_using_op" msgid="426635338010011796">"Използва се от <xliff:g id="APPLICATION_NAME">%1$s</xliff:g>"</string>
@@ -1251,15 +1261,13 @@
<string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(връзката е прекратена)"</string>
<string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"Не може да се превключи. Докоснете за нов опит."</string>
<string name="media_output_dialog_pairing_new" msgid="5098212763195577270">"Свързване на устройство"</string>
- <!-- no translation found for media_output_dialog_button_connect_device (2819038239946643550) -->
- <skip />
+ <string name="media_output_dialog_button_connect_device" msgid="2819038239946643550">"Свързване на устройството"</string>
<string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Неизвестно приложение"</string>
<string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Спиране на предаването"</string>
<string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"Налични устройства за аудиоизход."</string>
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Сила на звука"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
- <!-- no translation found for media_output_group_title_connected_speakers (3413876972919684479) -->
- <skip />
+ <string name="media_output_group_title_connected_speakers" msgid="3413876972919684479">"Свързани високоговорители"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Високоговорители и екрани"</string>
<string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Предложени устройства"</string>
<string name="media_input_group_title" msgid="2057057473860783021">"Вход"</string>
@@ -1328,10 +1336,8 @@
<string name="accessibility_enter_hint" msgid="2617864063504824834">"вход в устройството"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Използвайте отпечатък за отваряне"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Изисква се удостоверяване на самоличността. За целта докоснете сензора за отпечатъци."</string>
- <!-- no translation found for resume_animation (9148788411429330671) -->
- <skip />
- <!-- no translation found for pause_animation (1919705510058107504) -->
- <skip />
+ <string name="resume_animation" msgid="9148788411429330671">"Възобновяване на анимацията"</string>
+ <string name="pause_animation" msgid="1919705510058107504">"Поставяне на анимацията на пауза"</string>
<string name="ongoing_call_content_description" msgid="6394763878322348560">"Текущо обаждане"</string>
<string name="ongoing_notification_extra_content_description" msgid="2098752668861351265">"Текуща активност"</string>
<string name="mobile_data_settings_title" msgid="3955246641380064901">"Мобилни данни"</string>
@@ -1513,11 +1519,11 @@
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"Добавяне на пряк път"</string>
<string name="shortcut_helper_delete_shortcut_button_label" msgid="3148773472696137052">"Изтриване на прекия път"</string>
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Навигирайте посредством клавиатурата си"</string>
- <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Научете за клавишните комбинации"</string>
+ <string name="launch_keyboard_tutorial_notification_content" msgid="2131074100205028210">"Научете за клавишните комбинации"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Навигирайте посредством сензорния панел"</string>
<string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Научете за жестовете със сензорния панел"</string>
<string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Навигирайте посредством клавиатурата и сензорния панел"</string>
- <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Научете за жестовете със сензорния панел, клавишните комбинации и др."</string>
+ <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="8557098239919786046">"Научете за жестовете посредством сензорния панел, клавишните комбинации и др."</string>
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Назад"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Към началния екран"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Преглед на скорошните приложения"</string>
@@ -1587,4 +1593,7 @@
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Всички панели с бързи настройки ще бъдат нулирани до първоначалните настройки на устройството"</string>
<string name="demote_explain_text" msgid="3942301497888762295">"<xliff:g id="APPLICATION">%1$s</xliff:g> вече няма да показва актуализации на живо. Можете да промените това по всяко време от „Настройки“."</string>
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g> – <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
+ <string name="underlay_close_button_content_description" msgid="6518481455548869894">"Затваряне"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="7666159428113013319">"Прекарайте пръст от горния ляв ъгъл, за да отворите известията"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="3842355853737057332">"Прекарайте пръст от горния десен ъгъл, за да отворите бързите настройки"</string>
</resources>
diff --git a/packages/SystemUI/res/values-bn-ldrtl/strings.xml b/packages/SystemUI/res/values-bn-ldrtl/strings.xml
index 0158b29..4a0b1fe 100644
--- a/packages/SystemUI/res/values-bn-ldrtl/strings.xml
+++ b/packages/SystemUI/res/values-bn-ldrtl/strings.xml
@@ -20,4 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="recents_quick_scrub_onboarding" msgid="2452671841151577157">"একটি অ্যাপ ছেড়ে দ্রুত অন্য অ্যাপে যেতে বাঁ দিকে টেনে আনুন"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="6543179539438220549">"বিজ্ঞপ্তি খুলতে উপরের ডান দিক থেকে সোয়াইপ করুন"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="5760503283520058116">"দ্রুত সেটিংস খুলতে উপরের বাঁদিক থেকে সোয়াইপ করুন"</string>
</resources>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index 55ba0e9..2904419 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -252,8 +252,7 @@
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g>এ সংযুক্ত হয়ে আছে।"</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"<xliff:g id="CAST">%s</xliff:g> এর সাথে সংযুক্ত৷"</string>
<string name="accessibility_expand_group" msgid="521237935987978624">"গ্রুপ বড় করুন।"</string>
- <!-- no translation found for accessibility_collapse_group (1842704016768329517) -->
- <skip />
+ <string name="accessibility_collapse_group" msgid="1842704016768329517">"গ্রুপ আড়াল করুন।"</string>
<string name="accessibility_add_device_to_group" msgid="5446422960697860806">"গ্রুপে ডিভাইস যোগ করুন।"</string>
<string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"গ্রুপ থেকে ডিভাইস সরিয়ে দিন।"</string>
<string name="accessibility_open_application" msgid="1749126077501259712">"অ্যাপ্লিকেশন খুলুন।"</string>
@@ -356,7 +355,7 @@
<string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"কোনো ডিভাইস উপলব্ধ নয়"</string>
<string name="quick_settings_cast_no_network" msgid="3863016850468559522">"কোনও ওয়াই-ফাই বা ইথারনেট কানেকশন উপলভ্য নেই"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"উজ্জ্বলতা"</string>
- <string name="quick_settings_inversion_label" msgid="3501527749494755688">"কালার ইনভার্সন"</string>
+ <string name="quick_settings_inversion_label" msgid="3501527749494755688">"রঙ ইনভার্সন"</string>
<string name="quick_settings_color_correction_label" msgid="5636617913560474664">"রঙ সংশোধন"</string>
<string name="quick_settings_font_scaling_label" msgid="5289001009876936768">"ফন্ট সাইজ"</string>
<string name="quick_settings_more_user_settings" msgid="7634653308485206306">"ব্যবহারকারীদের ম্যানেজ করুন"</string>
@@ -468,6 +467,8 @@
<string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"মাইক্রোফোনের বোতাম ব্যবহার করতে, সেটিংস থেকে মাইক্রোফোনের অ্যাক্সেস চালু করুন।"</string>
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"সেটিংস খুলুন"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"অন্য ডিভাইস"</string>
+ <string name="media_suggestion_disconnected_text" msgid="8777932417076437525">"<xliff:g id="DEVICE_NAME">%s</xliff:g>-এ চালান"</string>
+ <string name="media_suggestion_failure_text" msgid="8453022394887613774">"কানেক্ট হচ্ছে না। আবার করুন।"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"\'এক নজরে\' বৈশিষ্ট্যটি চালু বা বন্ধ করুন"</string>
<string name="zen_modes_dialog_title" msgid="8854640808100096934">"মোড"</string>
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"হয়ে গেছে"</string>
@@ -583,6 +584,7 @@
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
<skip />
<string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"সম্পূর্ণ স্ক্রিন শেয়ার করুন"</string>
+ <string name="screen_share_permission_dialog_option_text_entire_screen_for_display" msgid="1545850238689169166">"<xliff:g id="DISPLAY_NAME">%s</xliff:g> শেয়ার করুন"</string>
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
<skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"আপনার সম্পূর্ণ স্ক্রিন শেয়ার করার সময়, স্ক্রিনে থাকা সব কিছু <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> দেখতে পাবে। তাই পাসওয়ার্ড, পেমেন্টের বিবরণ, মেসেজ, ফটো এবং অডিও ও ভিডিওর মতো বিষয়ের ক্ষেত্রে সতর্ক থাকুন।"</string>
@@ -594,6 +596,7 @@
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"আপনার স্ক্রিন কাস্ট করতে চান?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"একটি অ্যাপ কাস্ট করুন"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"সম্পূর্ণ স্ক্রিন কাস্ট করুন"</string>
+ <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen_for_display" msgid="5524817761472941086">"<xliff:g id="DISPLAY_NAME">%s</xliff:g> কাস্ট করুন"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"আপনি সম্পূর্ণ স্ক্রিন কাস্ট করলে, আপনার স্ক্রিনে থাকা সব কিছুই দেখা যাবে। তাই পাসওয়ার্ড, পেমেন্টের বিবরণ, মেসেজ, ফটো এবং অডিও ও ভিডিওর মতো বিষয়ে সতর্ক থাকুন।"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"আপনি কোনও অ্যাপ কাস্ট করলে, ওই অ্যাপে কিছু দেখানো বা চালানো হলে তা দেখা যাবে। তাই পাসওয়ার্ড, পেমেন্টের বিবরণ, মেসেজ, ফটো এবং অডিও ও ভিডিওর মতো বিষয়ে সতর্ক থাকুন।"</string>
<string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"স্ক্রিন কাস্ট করুন"</string>
@@ -672,8 +675,9 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"আপনার অভিভাবক এই ডিভাইস ম্যানেজ করেন। আপনার অভিভাবক আপনার ব্যবহার করা অ্যাপ, লোকেশন ও স্ক্রিন টাইমের মতো তথ্যগুলি দেখতে এবং ম্যানেজ করতে পারেন।"</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"TrustAgent দিয়ে আনলক করে রাখা হয়েছে"</string>
- <string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"ডিভাইস লক করা আছে, যাচাইকরণের জন্য অনেক বেশিবার চেষ্টা করা হয়েছে"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"ডিভাইস লক করা আছে, যাচাইকরণের জন্য অনেকবার চেষ্টা করা হয়েছে"</string>
<string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"ডিভাইস লক করা আছে\nযাচাইকরণ করা যায়নি"</string>
+ <string name="keyguard_indication_after_watch_disconnected" msgid="6329761892437377710">"আপনার স্মার্টওয়াচ থেকে লক আউট হয়ে গেছে"</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="accessibility_volume_settings" msgid="1458961116951564784">"সাউন্ড সেটিংস"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"অটোমেটিক মিডিয়া ক্যাপশন দেখুন"</string>
@@ -800,6 +804,8 @@
<string name="enable_bluetooth_message" msgid="6740938333772779717">"আপনার ট্যাবলেটের সাথে আপনার কীবোর্ড সংযুক্ত করতে, আপনাকে প্রথমে ব্লুটুথ চালু করতে হবে।"</string>
<string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"চালু করুন"</string>
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"চালু আছে - মুখের হিসেবে"</string>
+ <string name="notification_guts_bundle_title" msgid="1345506995443305361">"বিজ্ঞপ্তি অর্গানাইজার ব্যবহার করুন"</string>
+ <string name="notification_guts_bundle_summary" msgid="3971530802237393600">"<xliff:g id="APP_NAME">%1$s</xliff:g>-এর ক্ষেত্রে"</string>
<string name="inline_done_button" msgid="6043094985588909584">"হয়ে গেছে"</string>
<string name="inline_ok_button" msgid="603075490581280343">"প্রয়োগ করুন"</string>
<string name="inline_turn_off_notifications" msgid="2653064779176881329">"বন্ধ করুন"</string>
@@ -985,6 +991,7 @@
<string name="right_icon" msgid="1103955040645237425">"ডানদিকের আইকন"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"টাইল যোগ করতে ধরে থেকে টেনে আনুন"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"টাইলগুলি আবার সাজানোর জন্য ধরে থেকে টেনে আনুন"</string>
+ <string name="select_to_rearrange_tiles" msgid="6166153534235179148">"আবার সাজাতে এবং ছোট বড় করতে টাইল বেছে নিন"</string>
<string name="tap_to_position_tile" msgid="6282815817773342757">"টাইল সঠিক জায়গায় রাখার জন্য ট্যাপ করুন"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"সরানোর জন্য এখানে টেনে আনুন"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"আপনাকে কমপক্ষে <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g>টি টাইল রাখতে হবে"</string>
@@ -1004,10 +1011,10 @@
<string name="tuner_low_priority" msgid="8412666814123009820">"কম-গুরুত্বপূর্ণ বিজ্ঞপ্তির আইকন দেখুন"</string>
<string name="other" msgid="429768510980739978">"অন্যান্য"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"টাইলের সাইজ টগল করুন"</string>
- <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"টাইল সরান"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="4811778796933958616">"টাইল সরান"</string>
<string name="accessibility_qs_edit_toggle_placement_mode" msgid="3870429389210569610">"প্লেসমেন্ট মোড টগল করুন"</string>
<string name="accessibility_qs_edit_toggle_selection" msgid="2201248304072372239">"বেছে নেওয়া বিকল্প টগল করুন"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"শেষ জায়গাতে টাইল যোগ করুন"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="7188303979684400620">"শেষ জায়গাতে টাইল যোগ করুন"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"টাইল সরান"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"আপনার পছন্দের জায়গাতে টাইল যোগ করুন"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g>-এ সরান"</string>
@@ -1094,7 +1101,7 @@
<string name="auto_saver_text" msgid="3214960308353838764">"ব্যাটারির চার্জ শেষ হয়ে যাওয়ার সম্ভাবনা দেখা দিলে চালু করুন"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"না থাক"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"ব্যবহার হচ্ছে"</string>
- <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"অ্যাপ্লিকেশনগুলি আপনার <xliff:g id="TYPES_LIST">%s</xliff:g> ব্যবহার করছে।"</string>
+ <string name="ongoing_privacy_chip_content_multiple_apps" msgid="4994455315457996796">"<xliff:g id="TYPES_LIST">%s</xliff:g> ব্যবহার হচ্ছে।"</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" এবং "</string>
<string name="ongoing_privacy_dialog_using_op" msgid="426635338010011796">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> অ্যাপে ব্যবহার করা হচ্ছে"</string>
@@ -1251,15 +1258,13 @@
<string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(ডিসকানেক্ট হয়ে গেছে)"</string>
<string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"পাল্টানো যাচ্ছে না। আবার চেষ্টা করতে ট্যাপ করুন।"</string>
<string name="media_output_dialog_pairing_new" msgid="5098212763195577270">"ডিভাইস কানেক্ট করুন"</string>
- <!-- no translation found for media_output_dialog_button_connect_device (2819038239946643550) -->
- <skip />
+ <string name="media_output_dialog_button_connect_device" msgid="2819038239946643550">"ডিভাইস কানেক্ট করুন"</string>
<string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"অজানা অ্যাপ"</string>
<string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"কাস্ট করা বন্ধ করুন"</string>
<string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"অডিও আউটপুটের জন্য উপলভ্য ডিভাইস।"</string>
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"ভলিউম"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
- <!-- no translation found for media_output_group_title_connected_speakers (3413876972919684479) -->
- <skip />
+ <string name="media_output_group_title_connected_speakers" msgid="3413876972919684479">"স্পিকার কানেক্ট করা হয়েছে"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"স্পিকার ও ডিসপ্লে"</string>
<string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"সাজেস্ট করা ডিভাইস"</string>
<string name="media_input_group_title" msgid="2057057473860783021">"ইনপুট"</string>
@@ -1328,10 +1333,8 @@
<string name="accessibility_enter_hint" msgid="2617864063504824834">"ডিভাইস আনলক করুন"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"খুলতে ফিঙ্গারপ্রিন্ট ব্যবহার করুন"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"যাচাইকরণ করতে হবে। যাচাইকরণ করতে আঙুলের ছাপের সেন্সরে টাচ করুন।"</string>
- <!-- no translation found for resume_animation (9148788411429330671) -->
- <skip />
- <!-- no translation found for pause_animation (1919705510058107504) -->
- <skip />
+ <string name="resume_animation" msgid="9148788411429330671">"অ্যানিমেশন আবার চালু করুন"</string>
+ <string name="pause_animation" msgid="1919705510058107504">"অ্যানিমেশন পজ করুন"</string>
<string name="ongoing_call_content_description" msgid="6394763878322348560">"চালু থাকা কল"</string>
<string name="ongoing_notification_extra_content_description" msgid="2098752668861351265">"চলছে"</string>
<string name="mobile_data_settings_title" msgid="3955246641380064901">"মোবাইল ডেটা"</string>
@@ -1513,11 +1516,11 @@
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"শর্টকাট যোগ করুন"</string>
<string name="shortcut_helper_delete_shortcut_button_label" msgid="3148773472696137052">"শর্টকাট মুছুন"</string>
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"আপনার কীবোর্ড ব্যবহার করে নেভিগেট করুন"</string>
- <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"কীবোর্ড শর্টকাট সম্পর্কে জানুন"</string>
+ <string name="launch_keyboard_tutorial_notification_content" msgid="2131074100205028210">"কীবোর্ড শর্টকাট সম্পর্কে জানুন"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"আপনার টাচপ্যাড ব্যবহার করে নেভিগেট করুন"</string>
<string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"টাচপ্যাডের জেসচার সম্পর্কে জানুন"</string>
<string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"আপনার কীবোর্ড এবং টাচপ্যাড ব্যবহার করে নেভিগেট করুন"</string>
- <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"টাচপ্যাড জেসচার, কীবোর্ড শর্টকাট এবং আরও অনেক কিছু সম্পর্কে জানুন"</string>
+ <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="8557098239919786046">"টাচপ্যাড জেসচার, কীবোর্ড শর্টকাট এবং আরও অনেক কিছু সম্পর্কে জানুন"</string>
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"ফিরে যান"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"হোমে যান"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"সম্প্রতি ব্যবহার করা হয়েছে এমন অ্যাপ দেখুন"</string>
@@ -1587,4 +1590,7 @@
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"সব কুইক সেটিংস টাইল, ডিভাইসের আসল সেটিংসে রিসেট হয়ে যাবে"</string>
<string name="demote_explain_text" msgid="3942301497888762295">"<xliff:g id="APPLICATION">%1$s</xliff:g> আর লাইভ আপডেট দেখাবে না। যেকোনও সময় \'সেটিংস\' থেকে এটি পরিবর্তন করা যাবে।"</string>
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
+ <string name="underlay_close_button_content_description" msgid="6518481455548869894">"বন্ধ করুন"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="7666159428113013319">"বিজ্ঞপ্তি খুলতে উপরের বাঁদিক থেকে সোয়াইপ করুন"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="3842355853737057332">"দ্রুত সেটিংস খুলতে উপরের ডানদিক থেকে সোয়াইপ করুন"</string>
</resources>
diff --git a/packages/SystemUI/res/values-bs-ldrtl/strings.xml b/packages/SystemUI/res/values-bs-ldrtl/strings.xml
index 7a3948e..c88859f 100644
--- a/packages/SystemUI/res/values-bs-ldrtl/strings.xml
+++ b/packages/SystemUI/res/values-bs-ldrtl/strings.xml
@@ -20,4 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="recents_quick_scrub_onboarding" msgid="2452671841151577157">"Prevucite ulijevo za brzo prebacivanje između aplikacija"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="6543179539438220549">"Prevucite s gornjeg desnog ugla da otvorite obavještenja"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="5760503283520058116">"Prevucite s gornjeg lijevog ugla da otvorite Brze postavke"</string>
</resources>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index c0a9323..9589239 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -252,8 +252,7 @@
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Povezan na <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"Povezan na <xliff:g id="CAST">%s</xliff:g>."</string>
<string name="accessibility_expand_group" msgid="521237935987978624">"Proširivanje grupe."</string>
- <!-- no translation found for accessibility_collapse_group (1842704016768329517) -->
- <skip />
+ <string name="accessibility_collapse_group" msgid="1842704016768329517">"Sužavanje grupe."</string>
<string name="accessibility_add_device_to_group" msgid="5446422960697860806">"Dodavanje uređaja u grupu."</string>
<string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"Uklanjanje uređaja iz grupe."</string>
<string name="accessibility_open_application" msgid="1749126077501259712">"Otvaranje aplikacije."</string>
@@ -371,7 +370,7 @@
<string name="quick_settings_hotspot_secondary_label_num_devices" msgid="7536823087501239457">"{count,plural, =1{# uređaj}one{# uređaj}few{# uređaja}other{# uređaja}}"</string>
<string name="quick_settings_flashlight_label" msgid="4904634272006284185">"Svjetiljka"</string>
<string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"Kamera u upotrebi"</string>
- <string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"Prijenos podataka na mobilnoj mreži"</string>
+ <string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"Prenos podataka na mobilnoj mreži"</string>
<string name="quick_settings_cellular_detail_data_usage" msgid="6105969068871138427">"Prijenos podataka"</string>
<string name="quick_settings_cellular_detail_remaining_data" msgid="1136599216568805644">"Preostala količina podataka"</string>
<string name="quick_settings_cellular_detail_over_limit" msgid="4561921367680636235">"Prekoračeno"</string>
@@ -468,6 +467,8 @@
<string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"Da koristite dugme za mikrofon, omogućite pristup mikrofonu u Postavkama."</string>
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Otvori Postavke"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Drugi uređaj"</string>
+ <string name="media_suggestion_disconnected_text" msgid="8777932417076437525">"Reproduciraj na uređaju <xliff:g id="DEVICE_NAME">%s</xliff:g>"</string>
+ <string name="media_suggestion_failure_text" msgid="8453022394887613774">"Nije moguće povezati. Ponovite."</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Pregled uključivanja/isključivanja"</string>
<string name="zen_modes_dialog_title" msgid="8854640808100096934">"Načini rada"</string>
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Gotovo"</string>
@@ -583,6 +584,7 @@
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
<skip />
<string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Dijeli cijeli ekran"</string>
+ <string name="screen_share_permission_dialog_option_text_entire_screen_for_display" msgid="1545850238689169166">"Dijeli ekran <xliff:g id="DISPLAY_NAME">%s</xliff:g>"</string>
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
<skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Kada dijelite cijeli ekran, sve što je na ekranu će biti vidljivo aplikaciji <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Stoga budite oprezni s informacijama kao što su lozinke, podaci o plaćanju, poruke, fotografije, audio i videozapisi."</string>
@@ -594,6 +596,7 @@
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Emitirati ekran?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Emitiraj jednu aplikaciju"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Emitiraj cijeli ekran"</string>
+ <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen_for_display" msgid="5524817761472941086">"Emitiraj ekran <xliff:g id="DISPLAY_NAME">%s</xliff:g>"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Kada emitirate cijeli ekran, vidljivo je sve što je na ekranu. Stoga budite oprezni s informacijama kao što su lozinke, podaci o plaćanju, poruke, fotografije, audio i videozapisi."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Kada emitirate aplikaciju, vidljivo je sve što se prikazuje ili reproducira u toj aplikaciji. Stoga budite oprezni s informacijama kao što su lozinke, podaci o plaćanju, poruke, fotografije, audio i videozapisi."</string>
<string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Emitiraj ekran"</string>
@@ -674,6 +677,7 @@
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Pouzdani agent sprečava zaključavanje"</string>
<string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"Uređaj je zaključan zbog previše pokušaja autentifikacije"</string>
<string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"Uređaj je zaključan\nAutentifikacija nije uspjela"</string>
+ <string name="keyguard_indication_after_watch_disconnected" msgid="6329761892437377710">"Zaključano je putem sata"</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="accessibility_volume_settings" msgid="1458961116951564784">"Postavke zvuka"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Automatski titlovi za medije"</string>
@@ -720,7 +724,7 @@
<string name="stream_notification_unavailable" msgid="4313854556205836435">"Nedostupno zbog isključenog zvona"</string>
<string name="stream_alarm_unavailable" msgid="4059817189292197839">"Nedostupno jer je funkcija Ne ometaj uključena"</string>
<string name="stream_media_unavailable" msgid="6823020894438959853">"Nedostupno jer je funkcija Ne ometaj uključena"</string>
- <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Nije dostupno jer je način rada <xliff:g id="MODE">%s</xliff:g> uključen"</string>
+ <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Nedostupno jer je način rada <xliff:g id="MODE">%s</xliff:g> uključen"</string>
<string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Nedostupno"</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Dodirnite da uključite zvukove."</string>
<string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Dodirnite za postavljanje vibracije. Zvukovi usluga pristupačnosti mogu biti isključeni."</string>
@@ -800,6 +804,10 @@
<string name="enable_bluetooth_message" msgid="6740938333772779717">"Da povežete tastaturu sa tabletom, prvo morate uključiti Bluetooth."</string>
<string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Uključi"</string>
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Uključeno – na osnovu lica"</string>
+ <!-- no translation found for notification_guts_bundle_title (1345506995443305361) -->
+ <skip />
+ <!-- no translation found for notification_guts_bundle_summary (3971530802237393600) -->
+ <skip />
<string name="inline_done_button" msgid="6043094985588909584">"Gotovo"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Primijeni"</string>
<string name="inline_turn_off_notifications" msgid="2653064779176881329">"Isključi"</string>
@@ -985,6 +993,7 @@
<string name="right_icon" msgid="1103955040645237425">"Ikona desno"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Držite i prevucite da dodate polja"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Držite i prevucite da preuredite polja"</string>
+ <string name="select_to_rearrange_tiles" msgid="6166153534235179148">"Odaberite kartice da ih preuredite i promijenite im veličinu"</string>
<string name="tap_to_position_tile" msgid="6282815817773342757">"Dodirnite da postavite karticu"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Prevucite ovdje za uklanjanje"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Broj polja mora biti najmanje <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g>"</string>
@@ -1004,10 +1013,10 @@
<string name="tuner_low_priority" msgid="8412666814123009820">"Prikaži ikone obavještenja niskog prioriteta"</string>
<string name="other" msgid="429768510980739978">"Ostalo"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"uključivanje/isključivanje veličine kartice"</string>
- <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"uklanjanje kartice"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="4811778796933958616">"Ukloni pločicu"</string>
<string name="accessibility_qs_edit_toggle_placement_mode" msgid="3870429389210569610">"aktiviranje/deaktiviranje načina rada za postavljanje"</string>
<string name="accessibility_qs_edit_toggle_selection" msgid="2201248304072372239">"aktiviranje/deaktiviranje odabira"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"dodavanje kartice na posljednji položaj"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="7188303979684400620">"Dodaj pločicu na posljednji položaj"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Pomjeranje kartice"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Dodavanje kartice na željeni položaj"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Pomjeranje u položaj <xliff:g id="POSITION">%1$d</xliff:g>"</string>
@@ -1064,7 +1073,7 @@
<string name="instant_apps_message_with_help" msgid="1816952263531203932">"Aplikacija je otvorena bez prethodne instalacije. Dodirnite da saznate više."</string>
<string name="app_info" msgid="5153758994129963243">"Info. o aplikaciji"</string>
<string name="go_to_web" msgid="636673528981366511">"Idi na preglednik"</string>
- <string name="mobile_data" msgid="4564407557775397216">"Prijenos podataka"</string>
+ <string name="mobile_data" msgid="4564407557775397216">"Prenos podataka"</string>
<string name="mobile_data_text_format" msgid="6806501540022589786">"<xliff:g id="ID_1">%1$s</xliff:g> — <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="mobile_carrier_text_format" msgid="8912204177152950766">"<xliff:g id="CARRIER_NAME">%1$s</xliff:g>, <xliff:g id="MOBILE_DATA_TYPE">%2$s</xliff:g>"</string>
<string name="wifi_is_off" msgid="5389597396308001471">"WiFi je isključen"</string>
@@ -1094,7 +1103,8 @@
<string name="auto_saver_text" msgid="3214960308353838764">"Uključite ako je vjerovatno da će se baterija istrošiti"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Ne, hvala"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"U upotrebi"</string>
- <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Aplikacije koriste <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps (4994455315457996796) -->
+ <skip />
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" i "</string>
<string name="ongoing_privacy_dialog_using_op" msgid="426635338010011796">"Koristi aplikacija <xliff:g id="APPLICATION_NAME">%1$s</xliff:g>"</string>
@@ -1251,15 +1261,13 @@
<string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(veza je prekinuta)"</string>
<string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"Nije moguće prebaciti. Dodirnite da pokušate ponovo."</string>
<string name="media_output_dialog_pairing_new" msgid="5098212763195577270">"Povežite uređaj"</string>
- <!-- no translation found for media_output_dialog_button_connect_device (2819038239946643550) -->
- <skip />
+ <string name="media_output_dialog_button_connect_device" msgid="2819038239946643550">"Poveži uređaj"</string>
<string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Nepoznata aplikacija"</string>
<string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Zaustavi emitiranje"</string>
<string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"Dostupni uređaji za audio izlaz."</string>
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Jačina zvuka"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
- <!-- no translation found for media_output_group_title_connected_speakers (3413876972919684479) -->
- <skip />
+ <string name="media_output_group_title_connected_speakers" msgid="3413876972919684479">"Povezani zvučnici"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Zvučnici i ekrani"</string>
<string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Predloženi uređaji"</string>
<string name="media_input_group_title" msgid="2057057473860783021">"Ulaz"</string>
@@ -1328,13 +1336,11 @@
<string name="accessibility_enter_hint" msgid="2617864063504824834">"pristup uređaju"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Otvorite pomoću otiska prsta"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Potrebna je autentifikacija. Dodirnite senzor za otisak prsta da autentificirate."</string>
- <!-- no translation found for resume_animation (9148788411429330671) -->
- <skip />
- <!-- no translation found for pause_animation (1919705510058107504) -->
- <skip />
+ <string name="resume_animation" msgid="9148788411429330671">"Nastavljanje animacije"</string>
+ <string name="pause_animation" msgid="1919705510058107504">"Pauziranje animacije"</string>
<string name="ongoing_call_content_description" msgid="6394763878322348560">"Poziv u toku"</string>
<string name="ongoing_notification_extra_content_description" msgid="2098752668861351265">"U toku"</string>
- <string name="mobile_data_settings_title" msgid="3955246641380064901">"Prijenos podataka na mobilnoj mreži"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Prenos podataka na mobilnoj mreži"</string>
<string name="mobile_data_connection_active" msgid="944490013299018227">"Povezano"</string>
<string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Privremeno povezano"</string>
<string name="mobile_data_poor_connection" msgid="819617772268371434">"Slaba veza"</string>
@@ -1513,11 +1519,13 @@
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"Dodavanje prečice"</string>
<string name="shortcut_helper_delete_shortcut_button_label" msgid="3148773472696137052">"Brisanje prečice"</string>
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Krećite se pomoću tastature"</string>
- <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Saznajte više o prečicama tastature"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2131074100205028210) -->
+ <skip />
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Krećite se pomoću dodirne podloge"</string>
<string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Saznajte više o pokretima na dodirnoj podlozi"</string>
<string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Krećite se pomoću tastature i dodirne podloge"</string>
- <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Saznajte više o pokretima na dodirnoj podlozi, prečicama tastature i drugim opcijama"</string>
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (8557098239919786046) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Nazad"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Odlazak na početni ekran"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Prikaži nedavne aplikacije"</string>
@@ -1587,4 +1595,7 @@
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Sve kartice Brze postavke će se vratiti na originalne postavke uređaja"</string>
<string name="demote_explain_text" msgid="3942301497888762295">"<xliff:g id="APPLICATION">%1$s</xliff:g> više neće prikazivati novosti uživo. Ovo možete promijeniti bilo kada u Postavkama."</string>
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
+ <string name="underlay_close_button_content_description" msgid="6518481455548869894">"Zatvaranje"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="7666159428113013319">"Prevucite s gornjeg lijevog ugla da otvorite obavještenja"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="3842355853737057332">"Prevucite s gornjeg desnog ugla da otvorite Brze postavke"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ca-ldrtl/strings.xml b/packages/SystemUI/res/values-ca-ldrtl/strings.xml
index b0f5076..5b6febf6 100644
--- a/packages/SystemUI/res/values-ca-ldrtl/strings.xml
+++ b/packages/SystemUI/res/values-ca-ldrtl/strings.xml
@@ -20,4 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="recents_quick_scrub_onboarding" msgid="2452671841151577157">"Arrossega el dit cap a l\'esquerra per canviar ràpidament d\'aplicació"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="6543179539438220549">"Llisca des de la part superior dreta per obrir les notificacions"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="5760503283520058116">"Llisca des de la part superior esquerra per obrir la configuració ràpida"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index a957a83..a7a9e99 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -113,7 +113,7 @@
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Grava una aplicació"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="4882406311415082016">"Grava aquesta pantalla"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="4169494703993148253">"Grava %s"</string>
- <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Quan graves tota la pantalla, es grava tot el que es mostra en pantalla. Per aquest motiu, ves amb compte amb elements com les contrasenyes, les dades de pagament, els missatges, les fotos, i l\'àudio i el vídeo."</string>
+ <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Quan graves tota la pantalla, es grava tot el que es mostra en pantalla. Per aquest motiu, ves amb compte amb les contrasenyes, les dades de pagament, els missatges, les fotos, i l\'àudio i el vídeo."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Quan graves una aplicació, es grava tot el que es mostra o es reprodueix en aquesta aplicació. Per aquest motiu, ves amb compte amb les contrasenyes, les dades de pagament, els missatges, les fotos, i l\'àudio i el vídeo."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Grava la pantalla"</string>
<string name="screenrecord_app_selector_title" msgid="3854492366333954736">"Tria una aplicació per gravar"</string>
@@ -252,8 +252,7 @@
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"S\'ha connectat a <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"Està connectat amb <xliff:g id="CAST">%s</xliff:g>."</string>
<string name="accessibility_expand_group" msgid="521237935987978624">"Desplega el grup."</string>
- <!-- no translation found for accessibility_collapse_group (1842704016768329517) -->
- <skip />
+ <string name="accessibility_collapse_group" msgid="1842704016768329517">"Replega el grup."</string>
<string name="accessibility_add_device_to_group" msgid="5446422960697860806">"Afegeix el dispositiu al grup."</string>
<string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"Suprimeix el dispositiu del grup."</string>
<string name="accessibility_open_application" msgid="1749126077501259712">"Obre l\'aplicació."</string>
@@ -468,6 +467,8 @@
<string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"Per utilitzar el botó de micròfon, activa l\'accés al micròfon a Configuració."</string>
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Obre Configuració"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Un altre dispositiu"</string>
+ <string name="media_suggestion_disconnected_text" msgid="8777932417076437525">"Reprodueix a <xliff:g id="DEVICE_NAME">%s</xliff:g>"</string>
+ <string name="media_suggestion_failure_text" msgid="8453022394887613774">"No es pot connectar. Torna-hi."</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Activa o desactiva Aplicacions recents"</string>
<string name="zen_modes_dialog_title" msgid="8854640808100096934">"Modes"</string>
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Fet"</string>
@@ -583,10 +584,11 @@
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
<skip />
<string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Comparteix tota la pantalla"</string>
+ <string name="screen_share_permission_dialog_option_text_entire_screen_for_display" msgid="1545850238689169166">"Comparteix <xliff:g id="DISPLAY_NAME">%s</xliff:g>"</string>
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
<skip />
- <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Quan comparteixes tota la pantalla, qualsevol cosa que es mostra en pantalla és visible a <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Per aquest motiu, ves amb compte amb les contrasenyes, les dades de pagament, els missatges, les fotos i l\'àudio i el vídeo."</string>
- <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Quan comparteixes una aplicació, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> pot veure qualsevol cosa que s\'hi mostra o que s\'hi reprodueix. Per aquest motiu, ves amb compte amb les contrasenyes, les dades de pagament, els missatges, les fotos i l\'àudio i el vídeo."</string>
+ <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Quan comparteixes tota la pantalla, qualsevol cosa que es mostra en pantalla és visible a <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Per aquest motiu, ves amb compte amb les contrasenyes, les dades de pagament, els missatges, les fotos, i l\'àudio i el vídeo."</string>
+ <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Quan comparteixes una aplicació, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> pot veure qualsevol cosa que s\'hi mostra o que s\'hi reprodueix. Per aquest motiu, ves amb compte amb les contrasenyes, les dades de pagament, els missatges, les fotos, i l\'àudio i el vídeo."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Comparteix la pantalla"</string>
<string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> ha desactivat aquesta opció"</string>
<string name="media_projection_entry_app_permission_dialog_single_app_not_supported" msgid="4860247304058870233">"No és compatible amb l\'aplicació"</string>
@@ -594,13 +596,14 @@
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Vols emetre la pantalla?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Emet una aplicació"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Emet tota la pantalla"</string>
- <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Quan emets tota la pantalla, qualsevol cosa que s\'hi mostra és visible. Per aquest motiu, ves amb compte amb elements com les contrasenyes, les dades de pagament, els missatges, les fotos, i l\'àudio i el vídeo."</string>
- <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Quan emets una aplicació, qualsevol cosa que s\'hi mostra o reprodueix és visible. Per aquest motiu, ves amb compte amb elements com les contrasenyes, les dades de pagament, els missatges, les fotos, i l\'àudio i el vídeo."</string>
+ <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen_for_display" msgid="5524817761472941086">"Emet <xliff:g id="DISPLAY_NAME">%s</xliff:g>"</string>
+ <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Quan emets tota la pantalla, qualsevol cosa que s\'hi mostra és visible. Per aquest motiu, ves amb compte amb les contrasenyes, les dades de pagament, els missatges, les fotos, i l\'àudio i el vídeo."</string>
+ <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Quan emets una aplicació, qualsevol cosa que s\'hi mostra o reprodueix és visible. Per aquest motiu, ves amb compte amb les contrasenyes, les dades de pagament, els missatges, les fotos, i l\'àudio i el vídeo."</string>
<string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Emet la pantalla"</string>
<string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"Tria una aplicació per emetre"</string>
<string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"Vols començar a compartir?"</string>
- <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"Quan comparteixes, graves o emets contingut, Android té accés a qualsevol cosa que es vegi a la pantalla o que es reprodueixi al dispositiu. Per aquest motiu, ves amb compte amb les contrasenyes, les dades de pagament, els missatges, les fotos i l\'àudio i el vídeo."</string>
- <string name="media_projection_entry_generic_permission_dialog_warning_single_app" msgid="3454859977888159495">"Quan comparteixes, graves o emets una aplicació, Android té accés a qualsevol cosa que es mostri o que es reprodueixi en aquella aplicació. Per aquest motiu, ves amb compte amb les contrasenyes, les dades de pagament, els missatges, les fotos i l\'àudio i el vídeo."</string>
+ <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"Quan comparteixes, graves o emets contingut, Android té accés a qualsevol cosa que es vegi a la pantalla o que es reprodueixi al dispositiu. Per aquest motiu, ves amb compte amb les contrasenyes, les dades de pagament, els missatges, les fotos, i l\'àudio i el vídeo."</string>
+ <string name="media_projection_entry_generic_permission_dialog_warning_single_app" msgid="3454859977888159495">"Quan comparteixes, graves o emets una aplicació, Android té accés a qualsevol cosa que es mostri o que es reprodueixi en aquella aplicació. Per aquest motiu, ves amb compte amb les contrasenyes, les dades de pagament, els missatges, les fotos, i l\'àudio i el vídeo."</string>
<string name="media_projection_entry_generic_permission_dialog_continue" msgid="8640381403048097116">"Inicia"</string>
<string name="media_projection_entry_generic_permission_dialog_continue_single_app" msgid="5920814988611877051">"Següent"</string>
<string name="media_projection_task_switcher_text" msgid="590885489897412359">"La compartició es posa en pausa quan canvies d\'aplicació"</string>
@@ -674,6 +677,7 @@
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Desbloquejat per TrustAgent"</string>
<string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"El dispositiu s\'ha bloquejat; massa intents d\'autenticació"</string>
<string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"El dispositiu s\'ha bloquejat\nHa fallat l\'autenticació"</string>
+ <string name="keyguard_indication_after_watch_disconnected" msgid="6329761892437377710">"Bloquejat pel rellotge"</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="accessibility_volume_settings" msgid="1458961116951564784">"Configuració del so"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Subtitula el contingut multimèdia automàticament"</string>
@@ -695,7 +699,7 @@
<string name="screen_pinning_description_gestural" msgid="7246323931831232068">"Aquest element es continuarà mostrant fins que deixis de fixar-lo. Llisca cap amunt i mantén premut per deixar de fixar-lo."</string>
<string name="screen_pinning_description_accessible" msgid="7386449191953535332">"Aquest element es continuarà mostrant fins que deixis de fixar-lo. Per fer-ho, toca i mantén premut el botó Aplicacions recents."</string>
<string name="screen_pinning_description_recents_invisible_accessible" msgid="2857071808674481986">"Aquest element es continuarà mostrant fins que deixis de fixar-lo. Per fer-ho, mantén premut el botó d\'inici."</string>
- <string name="screen_pinning_exposes_personal_data" msgid="8189852022981524789">"Es pot accedir a les dades personals (com ara els contactes i el contingut dels correus electrònics)."</string>
+ <string name="screen_pinning_exposes_personal_data" msgid="8189852022981524789">"És possible que es pugui accedir a dades personals (com ara els contactes i el contingut dels correus electrònics)."</string>
<string name="screen_pinning_can_open_other_apps" msgid="7529756813231421455">"Una aplicació fixada pot obrir-ne d\'altres."</string>
<string name="screen_pinning_toast" msgid="8177286912533744328">"Per deixar de fixar aquesta aplicació, mantén premuts els botons Enrere i Aplicacions recents"</string>
<string name="screen_pinning_toast_recents_invisible" msgid="6850978077443052594">"Per deixar de fixar aquesta aplicació, mantén premuts els botons Enrere i Inici"</string>
@@ -800,6 +804,10 @@
<string name="enable_bluetooth_message" msgid="6740938333772779717">"Per connectar el teclat amb la tauleta, primer has d\'activar el Bluetooth."</string>
<string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Activa"</string>
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Activat: basat en cares"</string>
+ <!-- no translation found for notification_guts_bundle_title (1345506995443305361) -->
+ <skip />
+ <!-- no translation found for notification_guts_bundle_summary (3971530802237393600) -->
+ <skip />
<string name="inline_done_button" msgid="6043094985588909584">"Fet"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Aplica"</string>
<string name="inline_turn_off_notifications" msgid="2653064779176881329">"Desactiva"</string>
@@ -985,6 +993,7 @@
<string name="right_icon" msgid="1103955040645237425">"Icona de la dreta"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Mantén premudes les icones i arrossega-les per afegir-les"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Mantén premudes les icones i arrossega-les per reordenar-les"</string>
+ <string name="select_to_rearrange_tiles" msgid="6166153534235179148">"Selecciona les icones per reordenar-les i canviar-ne la mida"</string>
<string name="tap_to_position_tile" msgid="6282815817773342757">"Toca per col·locar la icona"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Arrossega aquí per suprimir"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Necessites com a mínim <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> mosaics"</string>
@@ -1004,10 +1013,10 @@
<string name="tuner_low_priority" msgid="8412666814123009820">"Mostra les icones de notificació amb prioritat baixa"</string>
<string name="other" msgid="429768510980739978">"Altres"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"commutar la mida de la icona"</string>
- <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"suprimir el mosaic"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="4811778796933958616">"Suprimir la icona"</string>
<string name="accessibility_qs_edit_toggle_placement_mode" msgid="3870429389210569610">"commuta el mode d\'emplaçament"</string>
<string name="accessibility_qs_edit_toggle_selection" msgid="2201248304072372239">"commuta la selecció"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"afegir una icona a la darrera posició"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="7188303979684400620">"Afegir la icona a la darrera posició"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Mou el mosaic"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Afegeix una icona a la posició que vulguis"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Mou a la posició <xliff:g id="POSITION">%1$d</xliff:g>"</string>
@@ -1055,7 +1064,7 @@
<string name="notification_channel_screenshot" msgid="7665814998932211997">"Captures de pantalla"</string>
<string name="notification_channel_instant" msgid="7556135423486752680">"Aplicacions instantànies"</string>
<string name="notification_channel_setup" msgid="7660580986090760350">"Configuració"</string>
- <string name="notification_channel_storage" msgid="2720725707628094977">"Emmagatzematge"</string>
+ <string name="notification_channel_storage" msgid="2720725707628094977">"Espai"</string>
<string name="notification_channel_hints" msgid="7703783206000346876">"Suggeriments"</string>
<string name="notification_channel_accessibility" msgid="8956203986976245820">"Accessibilitat"</string>
<string name="instant_apps" msgid="8337185853050247304">"Aplicacions instantànies"</string>
@@ -1094,7 +1103,8 @@
<string name="auto_saver_text" msgid="3214960308353838764">"Activa\'l quan sigui probable que et quedis sense bateria"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"No, gràcies"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"En ús"</string>
- <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Algunes aplicacions estan fent servir el següent: <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps (4994455315457996796) -->
+ <skip />
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" i "</string>
<string name="ongoing_privacy_dialog_using_op" msgid="426635338010011796">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> ho està utilitzant"</string>
@@ -1251,15 +1261,13 @@
<string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(desconnectat)"</string>
<string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"No es pot canviar. Torna-ho a provar."</string>
<string name="media_output_dialog_pairing_new" msgid="5098212763195577270">"Connecta un dispositiu"</string>
- <!-- no translation found for media_output_dialog_button_connect_device (2819038239946643550) -->
- <skip />
+ <string name="media_output_dialog_button_connect_device" msgid="2819038239946643550">"Connecta el dispositiu"</string>
<string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Aplicació desconeguda"</string>
<string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Atura l\'emissió"</string>
<string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"Dispositius disponibles per a la sortida d\'àudio."</string>
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Volum"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
- <!-- no translation found for media_output_group_title_connected_speakers (3413876972919684479) -->
- <skip />
+ <string name="media_output_group_title_connected_speakers" msgid="3413876972919684479">"Altaveus connectats"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Altaveus i pantalles"</string>
<string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Dispositius suggerits"</string>
<string name="media_input_group_title" msgid="2057057473860783021">"Entrada"</string>
@@ -1328,10 +1336,8 @@
<string name="accessibility_enter_hint" msgid="2617864063504824834">"accedir al dispositiu"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Utilitza l\'empremta digital per obrir"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Autenticació necessària. Toca el sensor d\'empremtes digitals per autenticar-te."</string>
- <!-- no translation found for resume_animation (9148788411429330671) -->
- <skip />
- <!-- no translation found for pause_animation (1919705510058107504) -->
- <skip />
+ <string name="resume_animation" msgid="9148788411429330671">"Reprèn l\'animació"</string>
+ <string name="pause_animation" msgid="1919705510058107504">"Posa en pausa l\'animació"</string>
<string name="ongoing_call_content_description" msgid="6394763878322348560">"Trucada en curs"</string>
<string name="ongoing_notification_extra_content_description" msgid="2098752668861351265">"En curs"</string>
<string name="mobile_data_settings_title" msgid="3955246641380064901">"Dades mòbils"</string>
@@ -1513,11 +1519,13 @@
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"Afegeix una drecera"</string>
<string name="shortcut_helper_delete_shortcut_button_label" msgid="3148773472696137052">"Suprimeix la drecera"</string>
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navega amb el teclat"</string>
- <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Aprèn les tecles de drecera"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2131074100205028210) -->
+ <skip />
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navega amb el ratolí tàctil"</string>
<string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Aprèn els gestos del ratolí tàctil"</string>
<string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Navega amb el teclat i el ratolí tàctil"</string>
- <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Aprèn els gestos del ratolí tàctil, les tecles de drecera i més"</string>
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (8557098239919786046) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Torna"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Ves a la pantalla d\'inici"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Mostra les aplicacions recents"</string>
@@ -1587,4 +1595,7 @@
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Totes les icones de configuració ràpida es restabliran a les opcions originals del dispositiu"</string>
<string name="demote_explain_text" msgid="3942301497888762295">"<xliff:g id="APPLICATION">%1$s</xliff:g> ja no mostrarà les actualitzacions en temps real. Pots canviar aquesta opció en qualsevol moment a Configuració."</string>
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
+ <string name="underlay_close_button_content_description" msgid="6518481455548869894">"Tanca"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="7666159428113013319">"Llisca des de la part superior esquerra per obrir les notificacions"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="3842355853737057332">"Llisca des de la part superior dreta per obrir la configuració ràpida"</string>
</resources>
diff --git a/packages/SystemUI/res/values-cs-ldrtl/strings.xml b/packages/SystemUI/res/values-cs-ldrtl/strings.xml
index 90f986d..ff0f605 100644
--- a/packages/SystemUI/res/values-cs-ldrtl/strings.xml
+++ b/packages/SystemUI/res/values-cs-ldrtl/strings.xml
@@ -20,4 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="recents_quick_scrub_onboarding" msgid="2452671841151577157">"Přetažením doleva rychle přepnete aplikace"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="6543179539438220549">"Přejetím z pravého horního rohu otevřete oznámení"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="5760503283520058116">"Přejetím z levého horního rohu otevřete panel Rychlé nastavení"</string>
</resources>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index efc0f5b..8f35c3d 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -111,10 +111,10 @@
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Trvalé oznámení o relaci nahrávání"</string>
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Pořídit nahrávku obrazovky?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Nahrát jednu aplikaci"</string>
- <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="4882406311415082016">"Nahrávat tuhle obrazovku"</string>
+ <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="4882406311415082016">"Nahrát tuto obrazovku"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="4169494703993148253">"Nahrávat obrazovku %s"</string>
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Při nahrávání celé obrazovky se zaznamenává veškerý obsah na obrazovce. Buďte proto opatrní, když jde o hesla, platební údaje, zprávy, fotografie, zvuk a video."</string>
- <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Při nahrávání aplikace se zaznamenává všechno, co se v dané obrazovce zobrazuje nebo přehrává. Buďte proto opatrní, když jde o hesla, platební údaje, zprávy, fotografie, zvuk a video."</string>
+ <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Při nahrávání aplikace se zaznamenává všechno, co se v dané aplikaci zobrazuje nebo přehrává. Buďte proto opatrní, když jde o hesla, platební údaje, zprávy, fotografie, zvuk a video."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Nahrát obrazovku"</string>
<string name="screenrecord_app_selector_title" msgid="3854492366333954736">"Vyberte aplikaci, ze které chcete pořídit nahrávku"</string>
<string name="screenrecord_audio_label" msgid="6183558856175159629">"Nahrávat zvuk"</string>
@@ -252,8 +252,7 @@
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Připojeno k zařízení <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"Jste připojeni k zařízení <xliff:g id="CAST">%s</xliff:g>."</string>
<string name="accessibility_expand_group" msgid="521237935987978624">"Rozbalit skupinu."</string>
- <!-- no translation found for accessibility_collapse_group (1842704016768329517) -->
- <skip />
+ <string name="accessibility_collapse_group" msgid="1842704016768329517">"Sbalit skupinu."</string>
<string name="accessibility_add_device_to_group" msgid="5446422960697860806">"Přidat zařízení do skupiny."</string>
<string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"Odstranit zařízení ze skupiny."</string>
<string name="accessibility_open_application" msgid="1749126077501259712">"Otevřít aplikaci."</string>
@@ -399,7 +398,7 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Nahrávání obrazovky"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Spustit"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Ukončit"</string>
- <string name="qs_record_issue_label" msgid="8166290137285529059">"Zaznamenat problém"</string>
+ <string name="qs_record_issue_label" msgid="8166290137285529059">"Nahrát problém"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"Spustit"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"Ukončit"</string>
<string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Zpráva o chybě"</string>
@@ -413,10 +412,10 @@
<string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Vlastní nastavení trasování"</string>
<string name="restore_default" msgid="5259420807486239755">"Obnovit výchozí"</string>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Režim jedné ruky"</string>
- <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Naslouchátka"</string>
+ <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Sluchové pomůcky"</string>
<string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktivní"</string>
<string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Odpojeno"</string>
- <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Naslouchátka"</string>
+ <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Sluchové pomůcky"</string>
<string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Spárovat nové zařízení"</string>
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Kliknutím spárujete nové zařízení"</string>
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Předvolbu nelze aktualizovat"</string>
@@ -468,6 +467,8 @@
<string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"Pokud chcete použít tlačítko mikrofonu, v nastavení udělte přístup k mikrofonu."</string>
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Otevřít nastavení"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Další zařízení"</string>
+ <string name="media_suggestion_disconnected_text" msgid="8777932417076437525">"Přehrát na: <xliff:g id="DEVICE_NAME">%s</xliff:g>"</string>
+ <string name="media_suggestion_failure_text" msgid="8453022394887613774">"Nejde se připojit. Zkuste to znovu."</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Přepnout přehled"</string>
<string name="zen_modes_dialog_title" msgid="8854640808100096934">"Režimy"</string>
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Hotovo"</string>
@@ -583,6 +584,7 @@
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
<skip />
<string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Sdílet celou obrazovku"</string>
+ <string name="screen_share_permission_dialog_option_text_entire_screen_for_display" msgid="1545850238689169166">"Sdílet: <xliff:g id="DISPLAY_NAME">%s</xliff:g>"</string>
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
<skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Při sdílení celé obrazovky vidí aplikace <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> vše, co se na obrazovce nachází nebo děje. Buďte proto opatrní, když jde o hesla, platební údaje, zprávy, fotografie, zvuk a video."</string>
@@ -591,12 +593,13 @@
<string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"Aplikace <xliff:g id="APP_NAME">%1$s</xliff:g> tuto možnost zakázala"</string>
<string name="media_projection_entry_app_permission_dialog_single_app_not_supported" msgid="4860247304058870233">"Není aplikací podporováno"</string>
<string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Vyberte aplikaci ke sdílení"</string>
- <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Odeslat obrazovku?"</string>
- <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Odeslat jednu aplikaci"</string>
+ <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Odesílat obrazovku?"</string>
+ <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Odesílat jednu aplikaci"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Odeslat celou obrazovku"</string>
+ <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen_for_display" msgid="5524817761472941086">"Odesílat: <xliff:g id="DISPLAY_NAME">%s</xliff:g>"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Při odesílání celé obrazovky je vidět vše, co se na obrazovce nachází nebo děje. Buďte proto opatrní, když jde o hesla, platební údaje, zprávy, fotografie, zvuk a video."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Při odesílání aplikace je vidět vše, co se v aplikaci nachází nebo děje. Buďte proto opatrní, když jde o hesla, platební údaje, zprávy, fotografie, zvuk a video."</string>
- <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Odeslat obrazovku"</string>
+ <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Odesílat obrazovku"</string>
<string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"Vyberte aplikaci k odesílání"</string>
<string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"Začít sdílet?"</string>
<string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"Během sdílení, nahrávání nebo odesílání má Android přístup k veškerému obsahu, který je viditelný na obrazovce nebo se přehrává v zařízení. Buďte proto opatrní, když jde o hesla, platební údaje, zprávy, fotografie, zvuk a video."</string>
@@ -674,6 +677,7 @@
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Odemknutí udržováno funkcí TrustAgent"</string>
<string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"Zařízení bylo uzamčeno, příliš mnoho pokusů o ověření"</string>
<string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"Zařízení uzamčeno\nOvěření se nezdařilo"</string>
+ <string name="keyguard_indication_after_watch_disconnected" msgid="6329761892437377710">"Zamknuto hodinkami"</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="accessibility_volume_settings" msgid="1458961116951564784">"Nastavení zvuku"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Automatické přepisy médií"</string>
@@ -800,6 +804,10 @@
<string name="enable_bluetooth_message" msgid="6740938333772779717">"Chcete-li klávesnici připojit k tabletu, nejdříve musíte zapnout Bluetooth."</string>
<string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Zapnout"</string>
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Zapnuto – podle obličeje"</string>
+ <!-- no translation found for notification_guts_bundle_title (1345506995443305361) -->
+ <skip />
+ <!-- no translation found for notification_guts_bundle_summary (3971530802237393600) -->
+ <skip />
<string name="inline_done_button" msgid="6043094985588909584">"Hotovo"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Použít"</string>
<string name="inline_turn_off_notifications" msgid="2653064779176881329">"Vypnout"</string>
@@ -985,6 +993,7 @@
<string name="right_icon" msgid="1103955040645237425">"Ikona vpravo"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Dlaždice přidáte podržením a přetažením"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Dlaždice můžete uspořádat podržením a přetažením"</string>
+ <string name="select_to_rearrange_tiles" msgid="6166153534235179148">"Vyberte dlaždice, jejichž pozici a velikost chcete změnit"</string>
<string name="tap_to_position_tile" msgid="6282815817773342757">"Klepnutím umístěte dlaždici"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Přetažením sem dlaždice odstraníte"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Potřebujete alespoň tento počet dlaždic: <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g>"</string>
@@ -1004,10 +1013,12 @@
<string name="tuner_low_priority" msgid="8412666814123009820">"Zobrazit ikony oznámení s nízkou prioritou"</string>
<string name="other" msgid="429768510980739978">"Jiné"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"přepnout velikost dlaždice"</string>
- <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"odstranit dlaždici"</string>
+ <!-- no translation found for accessibility_qs_edit_remove_tile_action (4811778796933958616) -->
+ <skip />
<string name="accessibility_qs_edit_toggle_placement_mode" msgid="3870429389210569610">"přepnout režim umístění"</string>
<string name="accessibility_qs_edit_toggle_selection" msgid="2201248304072372239">"přepnout výběr"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"přidat dlaždici na poslední pozici"</string>
+ <!-- no translation found for accessibility_qs_edit_tile_add_action (7188303979684400620) -->
+ <skip />
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Přesunout dlaždici"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Přidat dlaždici na požadované místo"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Přesunout na pozici <xliff:g id="POSITION">%1$d</xliff:g>"</string>
@@ -1094,7 +1105,8 @@
<string name="auto_saver_text" msgid="3214960308353838764">"Zapnout, když bude pravděpodobné, že se vybije baterie"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Ne, díky"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"Používá se"</string>
- <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Aplikace využívají tato oprávnění: <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps (4994455315457996796) -->
+ <skip />
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" a "</string>
<string name="ongoing_privacy_dialog_using_op" msgid="426635338010011796">"Právě používá aplikace <xliff:g id="APPLICATION_NAME">%1$s</xliff:g>"</string>
@@ -1251,15 +1263,13 @@
<string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(odpojeno)"</string>
<string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"Nelze přepnout. Klepnutím opakujte akci."</string>
<string name="media_output_dialog_pairing_new" msgid="5098212763195577270">"Připojit zařízení"</string>
- <!-- no translation found for media_output_dialog_button_connect_device (2819038239946643550) -->
- <skip />
+ <string name="media_output_dialog_button_connect_device" msgid="2819038239946643550">"Připojit zařízení"</string>
<string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Neznámá aplikace"</string>
<string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Zastavit odesílání"</string>
<string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"Dostupná zařízení pro zvukový výstup."</string>
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Hlasitost"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g> %%"</string>
- <!-- no translation found for media_output_group_title_connected_speakers (3413876972919684479) -->
- <skip />
+ <string name="media_output_group_title_connected_speakers" msgid="3413876972919684479">"Připojené reproduktory"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Reproduktory a displeje"</string>
<string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Navrhovaná zařízení"</string>
<string name="media_input_group_title" msgid="2057057473860783021">"Vstup"</string>
@@ -1328,10 +1338,8 @@
<string name="accessibility_enter_hint" msgid="2617864063504824834">"zadáte zařízení"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"K otevření použijte otisk prstu"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Je vyžadováno ověření. Dotkněte se snímače otisků prstů."</string>
- <!-- no translation found for resume_animation (9148788411429330671) -->
- <skip />
- <!-- no translation found for pause_animation (1919705510058107504) -->
- <skip />
+ <string name="resume_animation" msgid="9148788411429330671">"Znovu spustit animaci"</string>
+ <string name="pause_animation" msgid="1919705510058107504">"Pozastavit animaci"</string>
<string name="ongoing_call_content_description" msgid="6394763878322348560">"Probíhající hovor"</string>
<string name="ongoing_notification_extra_content_description" msgid="2098752668861351265">"Probíhá"</string>
<string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobilní data"</string>
@@ -1513,11 +1521,11 @@
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"Přidat zkratku"</string>
<string name="shortcut_helper_delete_shortcut_button_label" msgid="3148773472696137052">"Smazat zkratku"</string>
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navigujte pomocí klávesnice"</string>
- <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Naučte se klávesové zkratky"</string>
+ <string name="launch_keyboard_tutorial_notification_content" msgid="2131074100205028210">"Naučte se klávesové zkratky"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navigujte pomocí touchpadu"</string>
<string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Naučte se gesta touchpadu"</string>
<string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Navigujte pomocí klávesnice a touchpadu"</string>
- <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Naučte se gesta touchpadu, klávesové zkratky a další"</string>
+ <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="8557098239919786046">"Naučte se gesta touchpadu, klávesové zkratky a další"</string>
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Zpět"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Přejít na domovskou stránku"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Zobrazit nedávné aplikace"</string>
@@ -1587,4 +1595,7 @@
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Všechny dlaždice Rychlého nastavení se resetují do původní konfigurace zařízení"</string>
<string name="demote_explain_text" msgid="3942301497888762295">"Aplikace <xliff:g id="APPLICATION">%1$s</xliff:g> už nebude zobrazovat aktuality živě. Tuto volbu můžete v nastavení kdykoli změnit."</string>
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
+ <string name="underlay_close_button_content_description" msgid="6518481455548869894">"Zavřít"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="7666159428113013319">"Přejetím z levého horního rohu otevřete oznámení"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="3842355853737057332">"Přejetím z pravého horního rohu otevřete panel Rychlé nastavení"</string>
</resources>
diff --git a/packages/SystemUI/res/values-da-ldrtl/strings.xml b/packages/SystemUI/res/values-da-ldrtl/strings.xml
index 653f5c5..845b002 100644
--- a/packages/SystemUI/res/values-da-ldrtl/strings.xml
+++ b/packages/SystemUI/res/values-da-ldrtl/strings.xml
@@ -20,4 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="recents_quick_scrub_onboarding" msgid="2452671841151577157">"Træk til venstre for hurtigt at skifte app"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="6543179539438220549">"Stryg nedad fra øverste højre hjørne for at åbne notifikationer"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="5760503283520058116">"Stryg fra øverst til venstre for at åbne kvikmenuen"</string>
</resources>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index 7409b8e..6fef737 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -252,8 +252,7 @@
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Forbundet med <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"Forbundet til <xliff:g id="CAST">%s</xliff:g>."</string>
<string name="accessibility_expand_group" msgid="521237935987978624">"Udvid gruppe."</string>
- <!-- no translation found for accessibility_collapse_group (1842704016768329517) -->
- <skip />
+ <string name="accessibility_collapse_group" msgid="1842704016768329517">"Skjul gruppe."</string>
<string name="accessibility_add_device_to_group" msgid="5446422960697860806">"Føj enhed til gruppe."</string>
<string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"Fjern enhed fra gruppe."</string>
<string name="accessibility_open_application" msgid="1749126077501259712">"Åbn app."</string>
@@ -338,8 +337,8 @@
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Roter skærmen automatisk"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Lokation"</string>
<string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Pauseskærm"</string>
- <string name="quick_settings_camera_label" msgid="5612076679385269339">"Kameraadgang"</string>
- <string name="quick_settings_mic_label" msgid="8392773746295266375">"Mikrofonadgang"</string>
+ <string name="quick_settings_camera_label" msgid="5612076679385269339">"Kameraadgang"</string>
+ <string name="quick_settings_mic_label" msgid="8392773746295266375">"Mikrofonadgang"</string>
<string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Tilgængelig"</string>
<string name="quick_settings_camera_mic_blocked" msgid="4710884905006788281">"Blokeret"</string>
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"Medieenhed"</string>
@@ -357,7 +356,7 @@
<string name="quick_settings_cast_no_network" msgid="3863016850468559522">"Ingen Wi-Fi- eller Ethernet-forbindelse"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Lysstyrke"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"Ombytning af farver"</string>
- <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Farvekorrigering"</string>
+ <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Farvekorrigering"</string>
<string name="quick_settings_font_scaling_label" msgid="5289001009876936768">"Skriftstørrelse"</string>
<string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Administrer brugere"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Udfør"</string>
@@ -412,7 +411,7 @@
<string name="custom" msgid="3337456985275158299">"Tilpas"</string>
<string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Tilpas indstillinger for registrering"</string>
<string name="restore_default" msgid="5259420807486239755">"Gendan standard"</string>
- <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Enhåndstilstand"</string>
+ <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Enhåndstilstand"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Høreapparater"</string>
<string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktivt"</string>
<string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Afbrudt"</string>
@@ -468,6 +467,8 @@
<string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"Hvis du vil bruge mikrofonknappen, skal du aktivere mikrofonadgang under Indstillinger."</string>
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Åbn Indstillinger"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Anden enhed"</string>
+ <string name="media_suggestion_disconnected_text" msgid="8777932417076437525">"Afspil på <xliff:g id="DEVICE_NAME">%s</xliff:g>"</string>
+ <string name="media_suggestion_failure_text" msgid="8453022394887613774">"Ingen forbindelse. Prøv igen."</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Slå Oversigt til/fra"</string>
<string name="zen_modes_dialog_title" msgid="8854640808100096934">"Tilstande"</string>
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Udfør"</string>
@@ -583,6 +584,7 @@
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
<skip />
<string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Del hele skærmen"</string>
+ <string name="screen_share_permission_dialog_option_text_entire_screen_for_display" msgid="1545850238689169166">"Del <xliff:g id="DISPLAY_NAME">%s</xliff:g>"</string>
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
<skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Når du deler hele skærmen, er alt på din skærm synligt for <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Vær derfor forsigtig med f.eks. adgangskoder, betalingsoplysninger, beskeder, billeder, lyd og video."</string>
@@ -594,6 +596,7 @@
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Vil du caste din skærm?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Cast én app"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Cast hele skærmen"</string>
+ <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen_for_display" msgid="5524817761472941086">"Cast <xliff:g id="DISPLAY_NAME">%s</xliff:g>"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Når du caster hele din skærm, er alt på skærmen synligt. Vær derfor forsigtig med ting såsom adgangskoder, betalingsoplysninger, beskeder, billeder, lyd og video."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Når du caster en app, er alt, der vises eller afspilles i appen, synligt. Vær derfor forsigtig med ting såsom adgangskoder, betalingsoplysninger, beskeder, billeder, lyd og video."</string>
<string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Cast skærm"</string>
@@ -674,6 +677,7 @@
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Holdes oplåst af TrustAgent"</string>
<string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"Enheden er låst efter for mange forsøg på godkendelse"</string>
<string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"Enheden er låst\nGodkendelsen mislykkedes"</string>
+ <string name="keyguard_indication_after_watch_disconnected" msgid="6329761892437377710">"Låst ude af dit ur"</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="accessibility_volume_settings" msgid="1458961116951564784">"Lydindstillinger"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Undertekster til medier"</string>
@@ -800,6 +804,10 @@
<string name="enable_bluetooth_message" msgid="6740938333772779717">"Bluetooth skal være slået til, før du kan knytte dit tastatur til din tablet."</string>
<string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Slå til"</string>
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Til – ansigtsbaseret"</string>
+ <!-- no translation found for notification_guts_bundle_title (1345506995443305361) -->
+ <skip />
+ <!-- no translation found for notification_guts_bundle_summary (3971530802237393600) -->
+ <skip />
<string name="inline_done_button" msgid="6043094985588909584">"Udfør"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Anvend"</string>
<string name="inline_turn_off_notifications" msgid="2653064779176881329">"Deaktiver"</string>
@@ -953,7 +961,7 @@
<string name="accessibility_long_click_tile" msgid="210472753156768705">"Åbn indstillinger"</string>
<string name="accessibility_status_bar_headphones" msgid="1304082414912647414">"Hovedtelefoner er tilsluttet"</string>
<string name="accessibility_status_bar_headset" msgid="2699275863720926104">"Headset er forbundet"</string>
- <string name="data_saver" msgid="3484013368530820763">"Datasparefunktion"</string>
+ <string name="data_saver" msgid="3484013368530820763">"Datasparefunktion"</string>
<string name="accessibility_data_saver_on" msgid="5394743820189757731">"Datasparefunktionen er aktiveret"</string>
<string name="switch_bar_on" msgid="1770868129120096114">"Til"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Fra"</string>
@@ -985,6 +993,7 @@
<string name="right_icon" msgid="1103955040645237425">"Højre ikon"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Tilføj felter ved at holde dem nede og trække dem"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Flyt rundt på felterne ved at holde dem nede og trække"</string>
+ <string name="select_to_rearrange_tiles" msgid="6166153534235179148">"Vælg handlingsfelter for at omarrangere og ændre størrelsen"</string>
<string name="tap_to_position_tile" msgid="6282815817773342757">"Tryk for at placere handlingsfelt"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Træk herhen for at fjerne"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Du skal bruge mindst <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> felter"</string>
@@ -1004,10 +1013,12 @@
<string name="tuner_low_priority" msgid="8412666814123009820">"Vis ikoner for notifikationer med lav prioritet"</string>
<string name="other" msgid="429768510980739978">"Andet"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"ændre feltets størrelse"</string>
- <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"fjern felt"</string>
+ <!-- no translation found for accessibility_qs_edit_remove_tile_action (4811778796933958616) -->
+ <skip />
<string name="accessibility_qs_edit_toggle_placement_mode" msgid="3870429389210569610">"skifte placeringstilstand"</string>
<string name="accessibility_qs_edit_toggle_selection" msgid="2201248304072372239">"tilføje eller fjerne markering"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"føj handlingsfeltet til den sidste position"</string>
+ <!-- no translation found for accessibility_qs_edit_tile_add_action (7188303979684400620) -->
+ <skip />
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Flyt felt"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Føj handlingsfeltet til den ønskede placering"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Flyt til <xliff:g id="POSITION">%1$d</xliff:g>"</string>
@@ -1094,7 +1105,8 @@
<string name="auto_saver_text" msgid="3214960308353838764">"Aktivér, når det ser ud til, at batteriet løber tør"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Nej tak"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"I brug"</string>
- <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Apps anvender enhedens <xliff:g id="TYPES_LIST">%s</xliff:g>"</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps (4994455315457996796) -->
+ <skip />
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" og "</string>
<string name="ongoing_privacy_dialog_using_op" msgid="426635338010011796">"Anvendes i øjeblikket af <xliff:g id="APPLICATION_NAME">%1$s</xliff:g>"</string>
@@ -1251,15 +1263,13 @@
<string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(afbrudt)"</string>
<string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"Det var ikke muligt at skifte. Tryk for at prøve igen."</string>
<string name="media_output_dialog_pairing_new" msgid="5098212763195577270">"Tilknyt en enhed"</string>
- <!-- no translation found for media_output_dialog_button_connect_device (2819038239946643550) -->
- <skip />
+ <string name="media_output_dialog_button_connect_device" msgid="2819038239946643550">"Forbind enhed"</string>
<string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Ukendt app"</string>
<string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Stop med at caste"</string>
<string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"Enheder, der er tilgængelige for lydudgang."</string>
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Lydstyrke"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g> %%"</string>
- <!-- no translation found for media_output_group_title_connected_speakers (3413876972919684479) -->
- <skip />
+ <string name="media_output_group_title_connected_speakers" msgid="3413876972919684479">"Forbundne højttalere"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Højttalere og skærme"</string>
<string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Foreslåede enheder"</string>
<string name="media_input_group_title" msgid="2057057473860783021">"Input"</string>
@@ -1328,10 +1338,8 @@
<string name="accessibility_enter_hint" msgid="2617864063504824834">"få adgang til enheden"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Brug fingeraftryk for at åbne"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Godkendelse er påkrævet. Sæt fingeren på fingeraftrykssensoren for at godkende."</string>
- <!-- no translation found for resume_animation (9148788411429330671) -->
- <skip />
- <!-- no translation found for pause_animation (1919705510058107504) -->
- <skip />
+ <string name="resume_animation" msgid="9148788411429330671">"Genoptag animation"</string>
+ <string name="pause_animation" msgid="1919705510058107504">"Sæt animation på pause"</string>
<string name="ongoing_call_content_description" msgid="6394763878322348560">"Igangværende opkald"</string>
<string name="ongoing_notification_extra_content_description" msgid="2098752668861351265">"I gang"</string>
<string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobildata"</string>
@@ -1513,11 +1521,13 @@
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"Tilføj genvej"</string>
<string name="shortcut_helper_delete_shortcut_button_label" msgid="3148773472696137052">"Slet genvej"</string>
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Naviger ved hjælp af dit tastatur"</string>
- <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Se tastaturgenveje"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2131074100205028210) -->
+ <skip />
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Naviger ved hjælp af din touchplade"</string>
<string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Se bevægelser på touchpladen"</string>
<string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Naviger ved hjælp af dit tastatur og din touchplade"</string>
- <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Se bevægelser på touchpladen, tastaturgenveje m.m."</string>
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (8557098239919786046) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Gå tilbage"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Gå til startsiden"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Se seneste apps"</string>
@@ -1587,4 +1597,7 @@
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Alle handlingsfelter i kvikmenuen nulstilles til enhedens oprindelige indstillinger"</string>
<string name="demote_explain_text" msgid="3942301497888762295">"<xliff:g id="APPLICATION">%1$s</xliff:g> viser ikke længere liveopdateringer. Du kan til enhver tid ændre dette under Indstillinger."</string>
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
+ <string name="underlay_close_button_content_description" msgid="6518481455548869894">"Luk"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="7666159428113013319">"Stryg nedad fra øverste venstre hjørne for at åbne notifikationer"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="3842355853737057332">"Stryg fra øverst til højre for at åbne kvikmenuen"</string>
</resources>
diff --git a/packages/SystemUI/res/values-de-ldrtl/strings.xml b/packages/SystemUI/res/values-de-ldrtl/strings.xml
index 44c9ddc..27bb5ca 100644
--- a/packages/SystemUI/res/values-de-ldrtl/strings.xml
+++ b/packages/SystemUI/res/values-de-ldrtl/strings.xml
@@ -20,4 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="recents_quick_scrub_onboarding" msgid="2452671841151577157">"Zum schnellen Wechseln der Apps nach links ziehen"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="6543179539438220549">"Zum Öffnen von Benachrichtigungen von rechts oben nach unten wischen"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="5760503283520058116">"Zum Öffnen der Schnelleinstellungen von links oben nach unten wischen"</string>
</resources>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index 938a576..4c9db57 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -252,8 +252,7 @@
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Mit <xliff:g id="BLUETOOTH">%s</xliff:g> verbunden"</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"Verbunden mit <xliff:g id="CAST">%s</xliff:g>."</string>
<string name="accessibility_expand_group" msgid="521237935987978624">"Gruppe erweitern."</string>
- <!-- no translation found for accessibility_collapse_group (1842704016768329517) -->
- <skip />
+ <string name="accessibility_collapse_group" msgid="1842704016768329517">"Gruppe minimieren."</string>
<string name="accessibility_add_device_to_group" msgid="5446422960697860806">"Gerät zu Gruppe hinzufügen."</string>
<string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"Gerät aus Gruppe entfernen."</string>
<string name="accessibility_open_application" msgid="1749126077501259712">"Anwendung öffnen."</string>
@@ -468,6 +467,8 @@
<string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"Wenn du die Mikrofontaste verwenden möchtest, musst du den Mikrofonzugriff in den Einstellungen aktivieren."</string>
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Einstellungen öffnen"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Sonstiges Gerät"</string>
+ <string name="media_suggestion_disconnected_text" msgid="8777932417076437525">"Auf <xliff:g id="DEVICE_NAME">%s</xliff:g> wiedergeben"</string>
+ <string name="media_suggestion_failure_text" msgid="8453022394887613774">"Verbindung nicht möglich. Versuch es noch einmal."</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Übersicht ein-/ausblenden"</string>
<string name="zen_modes_dialog_title" msgid="8854640808100096934">"Modi"</string>
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Fertig"</string>
@@ -583,6 +584,7 @@
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
<skip />
<string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Gesamten Bildschirm teilen"</string>
+ <string name="screen_share_permission_dialog_option_text_entire_screen_for_display" msgid="1545850238689169166">"<xliff:g id="DISPLAY_NAME">%s</xliff:g> teilen"</string>
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
<skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Wenn du den gesamten Bildschirm teilst, ist für <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> alles auf dem Bildschirm sichtbar. Sei also vorsichtig mit Informationen wie Passwörtern, Zahlungsdetails, Nachrichten, Fotos sowie Audio- und Videoinhalten."</string>
@@ -594,6 +596,7 @@
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Bildschirm streamen?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Eine App streamen"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Gesamten Bildschirm streamen"</string>
+ <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen_for_display" msgid="5524817761472941086">"<xliff:g id="DISPLAY_NAME">%s</xliff:g> übertragen"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Wenn du den gesamten Bildschirm streamst, ist alles auf dem Bildschirm sichtbar. Sei also vorsichtig mit Informationen wie Passwörtern, Zahlungsdetails, Nachrichten, Fotos sowie Audio- und Videoinhalten."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Wenn du eine App streamst, ist alles sichtbar, was in dieser App angezeigt oder abgespielt wird. Sei also vorsichtig mit Informationen wie Passwörtern, Zahlungsdetails, Nachrichten, Fotos sowie Audio- und Videoinhalten."</string>
<string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Bildschirm streamen"</string>
@@ -674,6 +677,7 @@
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Durch TrustAgent entsperrt"</string>
<string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"Das Gerät wurde aufgrund zu vieler Authentifizierungsversuche gesperrt"</string>
<string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"Gerät gesperrt\nAuthentifizierung fehlgeschlagen"</string>
+ <string name="keyguard_indication_after_watch_disconnected" msgid="6329761892437377710">"Von deiner Smartwatch gesperrt"</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="accessibility_volume_settings" msgid="1458961116951564784">"Toneinstellungen"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Medien autom. untertiteln"</string>
@@ -720,7 +724,7 @@
<string name="stream_notification_unavailable" msgid="4313854556205836435">"Nicht verfügbar, da Klingelton stumm"</string>
<string name="stream_alarm_unavailable" msgid="4059817189292197839">"Nicht verfügbar, weil „Bitte nicht stören“ an ist"</string>
<string name="stream_media_unavailable" msgid="6823020894438959853">"Nicht verfügbar, weil „Bitte nicht stören“ an"</string>
- <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Nicht verfügbar, weil <xliff:g id="MODE">%s</xliff:g> aktiviert ist"</string>
+ <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Nicht verfügbar, weil „<xliff:g id="MODE">%s</xliff:g>“ aktiviert ist"</string>
<string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Nicht verfügbar"</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Zum Aufheben der Stummschaltung tippen."</string>
<string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Tippen, um Vibrieren festzulegen. Bedienungshilfen werden unter Umständen stummgeschaltet."</string>
@@ -800,6 +804,10 @@
<string name="enable_bluetooth_message" msgid="6740938333772779717">"Zum Verbinden von Tastatur und Tablet muss Bluetooth aktiviert sein."</string>
<string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Aktivieren"</string>
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"An – gesichtsbasiert"</string>
+ <!-- no translation found for notification_guts_bundle_title (1345506995443305361) -->
+ <skip />
+ <!-- no translation found for notification_guts_bundle_summary (3971530802237393600) -->
+ <skip />
<string name="inline_done_button" msgid="6043094985588909584">"Fertig"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Anwenden"</string>
<string name="inline_turn_off_notifications" msgid="2653064779176881329">"Deaktivieren"</string>
@@ -985,6 +993,7 @@
<string name="right_icon" msgid="1103955040645237425">"Rechtes Symbol"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Zum Hinzufügen Kachel halten und ziehen"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Zum Verschieben Kachel halten und ziehen"</string>
+ <string name="select_to_rearrange_tiles" msgid="6166153534235179148">"Kacheln zum Neuanordnen und Anpassen der Größe auswählen"</string>
<string name="tap_to_position_tile" msgid="6282815817773342757">"Zum Positionieren der Kachel tippen"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Zum Entfernen hierher ziehen"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Du brauchst mindestens <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> Kacheln"</string>
@@ -1004,10 +1013,10 @@
<string name="tuner_low_priority" msgid="8412666814123009820">"Symbole für Benachrichtigungen mit einer niedrigen Priorität anzeigen"</string>
<string name="other" msgid="429768510980739978">"Sonstiges"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"Größe der Kachel umschalten"</string>
- <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"Entfernen der Kachel"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="4811778796933958616">"Kachel entfernen"</string>
<string name="accessibility_qs_edit_toggle_placement_mode" msgid="3870429389210569610">"Platzierungsmodus ein-/ausschalten"</string>
<string name="accessibility_qs_edit_toggle_selection" msgid="2201248304072372239">"Auswahl wechseln"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"Kachel an letzter Position hinzufügen"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="7188303979684400620">"Kachel an letzter Position hinzufügen"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Kachel verschieben"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Kachel an gewünschter Position hinzufügen"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Auf Position <xliff:g id="POSITION">%1$d</xliff:g> verschieben"</string>
@@ -1094,7 +1103,8 @@
<string name="auto_saver_text" msgid="3214960308353838764">"Aktivieren, wenn der Akku wahrscheinlich nicht mehr lange hält"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Nein danke"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"In Verwendung"</string>
- <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Apps verwenden gerade Folgendes: <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps (4994455315457996796) -->
+ <skip />
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" und "</string>
<string name="ongoing_privacy_dialog_using_op" msgid="426635338010011796">"Von <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> verwendet"</string>
@@ -1251,15 +1261,13 @@
<string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(nicht verbunden)"</string>
<string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"Wechseln nicht möglich. Tippe, um es noch einmal zu versuchen."</string>
<string name="media_output_dialog_pairing_new" msgid="5098212763195577270">"Gerät verbinden"</string>
- <!-- no translation found for media_output_dialog_button_connect_device (2819038239946643550) -->
- <skip />
+ <string name="media_output_dialog_button_connect_device" msgid="2819038239946643550">"Gerät verbinden"</string>
<string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Unbekannte App"</string>
<string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Streaming beenden"</string>
<string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"Für die Audioausgabe verfügbare Geräte."</string>
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Lautstärke"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g> %%"</string>
- <!-- no translation found for media_output_group_title_connected_speakers (3413876972919684479) -->
- <skip />
+ <string name="media_output_group_title_connected_speakers" msgid="3413876972919684479">"Verbundene Lautsprecher"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Lautsprecher & Displays"</string>
<string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Vorgeschlagene Geräte"</string>
<string name="media_input_group_title" msgid="2057057473860783021">"Eingabe"</string>
@@ -1286,7 +1294,7 @@
<string name="select_conversation_title" msgid="6716364118095089519">"Unterhaltungs-Widgets"</string>
<string name="select_conversation_text" msgid="3376048251434956013">"Tippe auf eine Unterhaltung, um sie deinem Startbildschirm hinzuzufügen"</string>
<string name="no_conversations_text" msgid="5354115541282395015">"Deine letzten Unterhaltungen werden hier angezeigt"</string>
- <string name="priority_conversations" msgid="3967482288896653039">"Vorrangige Unterhaltungen"</string>
+ <string name="priority_conversations" msgid="3967482288896653039">"Priorisierte Unterhaltungen"</string>
<string name="recent_conversations" msgid="8531874684782574622">"Neueste Unterhaltungen"</string>
<string name="days_timestamp" msgid="5821854736213214331">"Vor <xliff:g id="DURATION">%1$s</xliff:g> Tagen"</string>
<string name="one_week_timestamp" msgid="4925600765473875590">"Vor 1 Woche"</string>
@@ -1328,10 +1336,8 @@
<string name="accessibility_enter_hint" msgid="2617864063504824834">"Eingeben des Geräts"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Mit Fingerabdruck öffnen"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Authentifizierung erforderlich. Tippe dazu einfach auf den Fingerabdrucksensor."</string>
- <!-- no translation found for resume_animation (9148788411429330671) -->
- <skip />
- <!-- no translation found for pause_animation (1919705510058107504) -->
- <skip />
+ <string name="resume_animation" msgid="9148788411429330671">"Animation fortsetzen"</string>
+ <string name="pause_animation" msgid="1919705510058107504">"Animation pausieren"</string>
<string name="ongoing_call_content_description" msgid="6394763878322348560">"Aktiver Anruf"</string>
<string name="ongoing_notification_extra_content_description" msgid="2098752668861351265">"Aktiv"</string>
<string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobile Daten"</string>
@@ -1430,7 +1436,7 @@
<string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Akkustand des Eingabestifts: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
<string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Schließe deinen Eingabestift an ein Ladegerät an"</string>
<string name="stylus_battery_low" msgid="7134370101603167096">"Stylus-Akkustand niedrig"</string>
- <string name="video_camera" msgid="7654002575156149298">"Videokamera"</string>
+ <string name="video_camera" msgid="7654002575156149298">"Videokamera"</string>
<string name="call_from_work_profile_title" msgid="5418253516453177114">"Telefonieren über private App nicht möglich"</string>
<string name="call_from_work_profile_text" msgid="2856337395968118274">"Deine Organisation lässt das Telefonieren nur über geschäftliche Apps zu"</string>
<string name="call_from_work_profile_action" msgid="2937701298133010724">"Zum Arbeitsprofil wechseln"</string>
@@ -1513,11 +1519,13 @@
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"Verknüpfung hinzufügen"</string>
<string name="shortcut_helper_delete_shortcut_button_label" msgid="3148773472696137052">"Verknüpfung löschen"</string>
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navigation mit der Tastatur"</string>
- <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Informationen zu Tastenkombinationen"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2131074100205028210) -->
+ <skip />
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navigation mit dem Touchpad"</string>
<string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Informationen zu Touchpad-Gesten"</string>
<string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Navigation mit Tastatur und Touchpad"</string>
- <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Informationen zu Touchpad-Gesten, Tastenkombinationen und mehr"</string>
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (8557098239919786046) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Zurück"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Zum Startbildschirm"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Letzte Apps aufrufen"</string>
@@ -1528,12 +1536,12 @@
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Zurück"</string>
<string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Wische mit drei Fingern auf dem Touchpad nach links oder rechts"</string>
<string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Sehr gut!"</string>
- <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Du hast das Tutorial für die „Zurück“-Touch-Geste abgeschlossen."</string>
+ <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Du hast das Tutorial für die Touch-Geste „Zurück“ abgeschlossen."</string>
<string name="touchpad_back_gesture_error_body" msgid="7112668207481458792">"Wenn du mit dem Touchpad zurückgehen möchtest, wische mit drei Fingern nach links oder rechts"</string>
<string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Startbildschirm"</string>
<string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Wische an einer beliebigen Stelle auf dem Touchpad mit drei Fingern nach oben"</string>
<string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Gut gemacht!"</string>
- <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Du hast den Schritt für die „Startbildschirm“-Touch-Geste abgeschlossen"</string>
+ <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Du hast das Tutorial für die Touch-Geste „Zum Startbildschirm“ abgeschlossen"</string>
<string name="touchpad_home_gesture_error_body" msgid="3810674109999513073">"Wenn du zum Startbildschirm gelangen möchtest, wische auf dem Touchpad mit drei Fingern nach oben"</string>
<string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Letzte Apps aufrufen"</string>
<string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Wische mit drei Fingern nach oben und halte das Touchpad gedrückt"</string>
@@ -1543,7 +1551,7 @@
<string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Zwischen Apps wechseln"</string>
<string name="touchpad_switch_apps_gesture_guidance" msgid="2554844933805502538">"Wische mit vier Fingern auf dem Touchpad nach rechts"</string>
<string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Gut gemacht!"</string>
- <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Du hast die Touch-Geste „Zwischen Apps wechseln\" abgeschlossen."</string>
+ <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Du hast das Tutorial für die Touch-Geste „Apps wechseln“ abgeschlossen."</string>
<string name="touchpad_switch_gesture_error_body" msgid="5895231916964677918">"Wische mit vier Fingern auf dem Touchpad nach rechts, um zwischen Apps zu wechseln"</string>
<string name="tutorial_action_key_title" msgid="8172535792469008169">"Alle Apps anzeigen"</string>
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Drücke die Aktionstaste auf deiner Tastatur"</string>
@@ -1554,7 +1562,7 @@
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Tastaturbeleuchtung"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Level %1$d von %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Smart-Home-Steuerung"</string>
- <string name="home_controls_dream_description" msgid="4644150952104035789">"Smart-Home-Steuerung als Bildschirmschoner nutzen"</string>
+ <string name="home_controls_dream_description" msgid="4644150952104035789">"Smart-Home-Steuerung als Bildschirmschoner nutzen"</string>
<string name="volume_undo_action" msgid="5815519725211877114">"Rückgängig machen"</string>
<string name="back_edu_toast_content" msgid="4530314597378982956">"Wenn du zurückgehen möchtest, wische auf dem Touchpad mit drei Fingern nach links oder rechts"</string>
<string name="home_edu_toast_content" msgid="3381071147871955415">"Wenn du den Startbildschirm aufrufen möchtest, wische auf dem Touchpad mit drei Fingern nach oben"</string>
@@ -1587,4 +1595,7 @@
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Alle Schnelleinstellungen-Kacheln werden auf die Standardeinstellungen des Geräts zurückgesetzt"</string>
<string name="demote_explain_text" msgid="3942301497888762295">"<xliff:g id="APPLICATION">%1$s</xliff:g> zeigt keine Livemeldungen mehr an. Du kannst dies jederzeit in den Einstellungen ändern."</string>
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
+ <string name="underlay_close_button_content_description" msgid="6518481455548869894">"Schließen"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="7666159428113013319">"Zum Öffnen von Benachrichtigungen von links oben nach unten wischen"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="3842355853737057332">"Zum Öffnen der Schnelleinstellungen von rechts oben nach unten wischen"</string>
</resources>
diff --git a/packages/SystemUI/res/values-el-ldrtl/strings.xml b/packages/SystemUI/res/values-el-ldrtl/strings.xml
index 811c77d..b277de0 100644
--- a/packages/SystemUI/res/values-el-ldrtl/strings.xml
+++ b/packages/SystemUI/res/values-el-ldrtl/strings.xml
@@ -20,4 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="recents_quick_scrub_onboarding" msgid="2452671841151577157">"Σύρετε αριστερά για γρήγορη εναλλαγή εφαρμογών"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="6543179539438220549">"Σύρετε από επάνω δεξιά, για να ανοίξετε τις ειδοποιήσεις"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="5760503283520058116">"Σύρετε από επάνω αριστερά, για να ανοίξετε τις Γρήγορες ρυθμίσεις"</string>
</resources>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index 5eafec5..ff9851a 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -252,8 +252,7 @@
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Συνδέθηκε στο <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"Συνδέθηκε σε <xliff:g id="CAST">%s</xliff:g>."</string>
<string name="accessibility_expand_group" msgid="521237935987978624">"Αναπτύξτε την ομάδα."</string>
- <!-- no translation found for accessibility_collapse_group (1842704016768329517) -->
- <skip />
+ <string name="accessibility_collapse_group" msgid="1842704016768329517">"Σύμπτυξη ομάδας."</string>
<string name="accessibility_add_device_to_group" msgid="5446422960697860806">"Προσθήκη συσκευής στην ομάδα."</string>
<string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"Κατάργηση συσκευής από την ομάδα."</string>
<string name="accessibility_open_application" msgid="1749126077501259712">"Ανοίξτε την εφαρμογή."</string>
@@ -468,6 +467,8 @@
<string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"Για να χρησιμοποιήσετε το κουμπί μικροφώνου, ενεργοποιήστε την πρόσβαση στο μικρόφωνο από τις Ρυθμίσεις."</string>
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Άνοιγμα Ρυθμίσεων"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Άλλη συσκευή"</string>
+ <string name="media_suggestion_disconnected_text" msgid="8777932417076437525">"Αναπαραγωγή σε <xliff:g id="DEVICE_NAME">%s</xliff:g>"</string>
+ <string name="media_suggestion_failure_text" msgid="8453022394887613774">"Αδυναμία σύνδ. Δοκιμάστε ξανά."</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Εναλλαγή επισκόπησης"</string>
<string name="zen_modes_dialog_title" msgid="8854640808100096934">"Λειτουργίες"</string>
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Τέλος"</string>
@@ -583,6 +584,7 @@
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
<skip />
<string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Κοινή χρήση ολόκληρης της οθόνης"</string>
+ <string name="screen_share_permission_dialog_option_text_entire_screen_for_display" msgid="1545850238689169166">"Κοινή χρήση <xliff:g id="DISPLAY_NAME">%s</xliff:g>"</string>
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
<skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Όταν μοιράζεστε ολόκληρη την οθόνη, οτιδήποτε εμφανίζεται στην οθόνη σας είναι ορατό στην εφαρμογή <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Επομένως, να είστε προσεκτικοί με τους κωδικούς πρόσβασης, τα στοιχεία πληρωμής, τα μηνύματα, τις φωτογραφίες, τον ήχο και το βίντεο."</string>
@@ -594,6 +596,7 @@
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Να γίνει μετάδοση της οθόνης σας;"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Μετάδοση μίας εφαρμογής"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Μετάδοση ολόκληρης της οθόνης"</string>
+ <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen_for_display" msgid="5524817761472941086">"Μετάδοση <xliff:g id="DISPLAY_NAME">%s</xliff:g>"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Όταν κάνετε μετάδοση ολόκληρης της οθόνης, όλο το περιεχόμενο της οθόνης είναι ορατό. Επομένως, να είστε προσεκτικοί με τους κωδικούς πρόσβασης, τα στοιχεία πληρωμής, τα μηνύματα, τις φωτογραφίες, τον ήχο και το βίντεο."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Όταν κάνετε μετάδοση μιας εφαρμογής, όλο το περιεχόμενο που εμφανίζεται ή αναπαράγεται στην εφαρμογή είναι ορατό. Επομένως, να είστε προσεκτικοί με τους κωδικούς πρόσβασης, τα στοιχεία πληρωμής, τα μηνύματα, τις φωτογραφίες, τον ήχο και το βίντεο."</string>
<string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Μετάδοση οθόνης"</string>
@@ -617,7 +620,7 @@
<string name="notification_section_header_incoming" msgid="850925217908095197">"Νέα"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Σίγαση"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Ειδοποιήσεις"</string>
- <string name="notification_section_header_conversations" msgid="821834744538345661">"Συζητήσεις"</string>
+ <string name="notification_section_header_conversations" msgid="821834744538345661">"Συνομιλίες"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Διαγραφή όλων των ειδοποιήσεων σε σίγαση"</string>
<string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Άνοιγμα ρυθμίσεων ειδοποιήσεων"</string>
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Οι ειδοποιήσεις τέθηκαν σε παύση από τη λειτουργία \"Μην ενοχλείτε\""</string>
@@ -674,6 +677,7 @@
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Διατήρηση ξεκλειδώματος με TrustAgent"</string>
<string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"Η συσκευή ήταν κλειδωμένη, πάρα πολλές προσπάθειες ελέγχου ταυτότητας"</string>
<string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"Η συσκευή είναι κλειδωμένη\nΟ έλεγχος ταυτότητας απέτυχε"</string>
+ <string name="keyguard_indication_after_watch_disconnected" msgid="6329761892437377710">"Κλειδώθηκε από το ρολόι σας"</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="accessibility_volume_settings" msgid="1458961116951564784">"Ρυθμίσεις ήχου"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Αυτόματοι υπότιτλοι στο μέσο"</string>
@@ -800,6 +804,10 @@
<string name="enable_bluetooth_message" msgid="6740938333772779717">"Για να συνδέσετε το πληκτρολόγιο με το tablet σας, θα πρέπει πρώτα να ενεργοποιήσετε το Bluetooth."</string>
<string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Ενεργοποίηση"</string>
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Ενεργό - Βάσει προσώπου"</string>
+ <!-- no translation found for notification_guts_bundle_title (1345506995443305361) -->
+ <skip />
+ <!-- no translation found for notification_guts_bundle_summary (3971530802237393600) -->
+ <skip />
<string name="inline_done_button" msgid="6043094985588909584">"Τέλος"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Εφαρμογή"</string>
<string name="inline_turn_off_notifications" msgid="2653064779176881329">"Απενεργοποίηση"</string>
@@ -985,6 +993,7 @@
<string name="right_icon" msgid="1103955040645237425">"Δεξιό εικονίδιο"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Κρατήστε και σύρετε για την προσθήκη πλακιδίων"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Κρατήστε και σύρετε για αναδιάταξη των πλακιδίων"</string>
+ <string name="select_to_rearrange_tiles" msgid="6166153534235179148">"Επιλέξτε πλακάκια για αναδιάταξη και αλλαγή μεγέθους"</string>
<string name="tap_to_position_tile" msgid="6282815817773342757">"Πατήστε για τοποθέτηση πλακιδίου"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Σύρετε εδώ για κατάργηση"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Χρειάζεστε τουλάχιστον <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> πλακίδια"</string>
@@ -1004,10 +1013,10 @@
<string name="tuner_low_priority" msgid="8412666814123009820">"Εμφάνιση εικονιδίων ειδοποιήσεων χαμηλής προτεραιότητας"</string>
<string name="other" msgid="429768510980739978">"Άλλο"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"εναλλαγή μεγέθους για το πλακάκι"</string>
- <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"κατάργηση πλακιδίου"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="4811778796933958616">"Κατάργηση πλακακιού"</string>
<string name="accessibility_qs_edit_toggle_placement_mode" msgid="3870429389210569610">"εναλλαγή λειτουργίας τοποθέτησης"</string>
<string name="accessibility_qs_edit_toggle_selection" msgid="2201248304072372239">"εναλλαγή επιλογής"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"προσθήκη πλακιδίου στην τελευταία θέση"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="7188303979684400620">"Προσθήκη πλακακιού στην τελευταία θέση"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Μετακίνηση πλακιδίου"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Προσθήκη πλακιδίου στην επιθυμητή θέση"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Μετακίνηση στη θέση <xliff:g id="POSITION">%1$d</xliff:g>"</string>
@@ -1094,7 +1103,8 @@
<string name="auto_saver_text" msgid="3214960308353838764">"Ενεργοποίηση όταν υπάρχει σημαντική πιθανότητα εξάντλησης της μπαταρίας"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Όχι, ευχαριστώ"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"Χρησιμοποιείται"</string>
- <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Οι εφαρμογές χρησιμοποιούν τις λειτουργίες <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps (4994455315457996796) -->
+ <skip />
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" και "</string>
<string name="ongoing_privacy_dialog_using_op" msgid="426635338010011796">"Χρησιμοποιείται από την εφαρμογή <xliff:g id="APPLICATION_NAME">%1$s</xliff:g>"</string>
@@ -1251,15 +1261,13 @@
<string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(αποσυνδέθηκε)"</string>
<string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"Δεν είναι δυνατή η εναλλαγή. Πατήστε για επανάληψη."</string>
<string name="media_output_dialog_pairing_new" msgid="5098212763195577270">"Σύνδεση συσκευής"</string>
- <!-- no translation found for media_output_dialog_button_connect_device (2819038239946643550) -->
- <skip />
+ <string name="media_output_dialog_button_connect_device" msgid="2819038239946643550">"Σύνδεση συσκευής"</string>
<string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Άγνωστη εφαρμογή"</string>
<string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Διακοπή μετάδοσης"</string>
<string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"Διαθέσιμες συσκευές για έξοδο ήχου."</string>
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Ένταση ήχου"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
- <!-- no translation found for media_output_group_title_connected_speakers (3413876972919684479) -->
- <skip />
+ <string name="media_output_group_title_connected_speakers" msgid="3413876972919684479">"Συνδεδεμένα ηχεία"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Ηχεία και οθόνες"</string>
<string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Προτεινόμενες συσκευές"</string>
<string name="media_input_group_title" msgid="2057057473860783021">"Είσοδος"</string>
@@ -1328,10 +1336,8 @@
<string name="accessibility_enter_hint" msgid="2617864063504824834">"εισαγωγή συσκευής"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Χρήση δακτυλικού αποτυπώματος για άνοιγμα"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Απαιτείται έλεγχος ταυτότητας. Αγγίξτε τον αισθητήρα δακτυλικών αποτυπωμάτων για έλεγχο ταυτότητας."</string>
- <!-- no translation found for resume_animation (9148788411429330671) -->
- <skip />
- <!-- no translation found for pause_animation (1919705510058107504) -->
- <skip />
+ <string name="resume_animation" msgid="9148788411429330671">"Συνέχιση κινούμενης εικόνας"</string>
+ <string name="pause_animation" msgid="1919705510058107504">"Παύση κινούμενης εικόνας"</string>
<string name="ongoing_call_content_description" msgid="6394763878322348560">"Κλήση σε εξέλιξη"</string>
<string name="ongoing_notification_extra_content_description" msgid="2098752668861351265">"Σε εξέλιξη"</string>
<string name="mobile_data_settings_title" msgid="3955246641380064901">"Δεδομένα κινητής τηλεφωνίας"</string>
@@ -1513,11 +1519,13 @@
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"Προσθήκη συντόμευσης"</string>
<string name="shortcut_helper_delete_shortcut_button_label" msgid="3148773472696137052">"Διαγραφή συντόμευσης"</string>
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Πλοήγηση με το πληκτρολόγιο"</string>
- <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Μάθετε συντομεύσεις πληκτρολογίου"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2131074100205028210) -->
+ <skip />
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Πλοήγηση με την επιφάνεια αφής"</string>
<string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Μάθετε κινήσεις επιφάνειας αφής"</string>
<string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Πλοήγηση με το πληκτρολόγιο και την επιφάνεια αφής"</string>
- <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Μάθετε κινήσεις επιφάνειας αφής, συντομεύσεις πληκτρολογίου και άλλα"</string>
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (8557098239919786046) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Επιστροφή"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Αρχική"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Προβολή πρόσφατων εφαρμογών"</string>
@@ -1587,4 +1595,7 @@
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Σε όλα τα πλακάκια Γρήγορων ρυθμίσεων θα γίνει επαναφορά στις αρχικές ρυθμίσεις της συσκευής"</string>
<string name="demote_explain_text" msgid="3942301497888762295">"Η εφαρμογή <xliff:g id="APPLICATION">%1$s</xliff:g> δεν θα εμφανίζει πλέον Ενημερώσεις σε πραγματικό χρόνο. Μπορείτε να αλλάξετε αυτή τη ρύθμιση ανά πάσα στιγμή από τις ρυθμίσεις."</string>
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
+ <string name="underlay_close_button_content_description" msgid="6518481455548869894">"Κλείσιμο"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="7666159428113013319">"Σύρετε από επάνω αριστερά, για να ανοίξετε τις ειδοποιήσεις"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="3842355853737057332">"Σύρετε από επάνω δεξιά, για να ανοίξετε τις Γρήγορες ρυθμίσεις"</string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rAU-ldrtl/strings.xml b/packages/SystemUI/res/values-en-rAU-ldrtl/strings.xml
index 1c11cb3..c17e64b 100644
--- a/packages/SystemUI/res/values-en-rAU-ldrtl/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU-ldrtl/strings.xml
@@ -20,4 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="recents_quick_scrub_onboarding" msgid="2452671841151577157">"Drag left to quickly switch apps"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="6543179539438220549">"Swipe from the top right to open notifications"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="5760503283520058116">"Swipe from the top left to open Quick Settings"</string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index fb3d664..c636ecf 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -252,8 +252,7 @@
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Connected to <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"Connected to <xliff:g id="CAST">%s</xliff:g>."</string>
<string name="accessibility_expand_group" msgid="521237935987978624">"Expand group."</string>
- <!-- no translation found for accessibility_collapse_group (1842704016768329517) -->
- <skip />
+ <string name="accessibility_collapse_group" msgid="1842704016768329517">"Collapse group."</string>
<string name="accessibility_add_device_to_group" msgid="5446422960697860806">"Add device to group."</string>
<string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"Remove device from group."</string>
<string name="accessibility_open_application" msgid="1749126077501259712">"Open application."</string>
@@ -468,6 +467,8 @@
<string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"To use the microphone button, enable microphone access in Settings."</string>
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Open settings"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Other device"</string>
+ <string name="media_suggestion_disconnected_text" msgid="8777932417076437525">"Play on <xliff:g id="DEVICE_NAME">%s</xliff:g>"</string>
+ <string name="media_suggestion_failure_text" msgid="8453022394887613774">"Can\'t connect. Try again."</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Toggle Overview"</string>
<string name="zen_modes_dialog_title" msgid="8854640808100096934">"Modes"</string>
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Done"</string>
@@ -583,6 +584,7 @@
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
<skip />
<string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Share entire screen"</string>
+ <string name="screen_share_permission_dialog_option_text_entire_screen_for_display" msgid="1545850238689169166">"Share <xliff:g id="DISPLAY_NAME">%s</xliff:g>"</string>
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
<skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"When you\'re sharing your entire screen, anything on your screen is visible to <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. So be careful with things like passwords, payment details, messages, photos, audio and video."</string>
@@ -594,6 +596,7 @@
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Cast your screen?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Cast one app"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Cast entire screen"</string>
+ <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen_for_display" msgid="5524817761472941086">"Cast <xliff:g id="DISPLAY_NAME">%s</xliff:g>"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"When you\'re casting your entire screen, anything on your screen is visible. So be careful with things like passwords, payment details, messages, photos, audio and video."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"When you\'re casting an app, anything shown or played in that app is visible. So be careful with things like passwords, payment details, messages, photos, audio and video."</string>
<string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Cast screen"</string>
@@ -674,6 +677,7 @@
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Kept unlocked by trust agent"</string>
<string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"Device was locked – too many authentication attempts"</string>
<string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"Device locked\nFailed authentication"</string>
+ <string name="keyguard_indication_after_watch_disconnected" msgid="6329761892437377710">"Locked out by your watch"</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="accessibility_volume_settings" msgid="1458961116951564784">"Sound settings"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Automatically caption media"</string>
@@ -800,6 +804,10 @@
<string name="enable_bluetooth_message" msgid="6740938333772779717">"To connect your keyboard with your tablet, you first have to turn on Bluetooth."</string>
<string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Turn on"</string>
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"On – Face-based"</string>
+ <!-- no translation found for notification_guts_bundle_title (1345506995443305361) -->
+ <skip />
+ <!-- no translation found for notification_guts_bundle_summary (3971530802237393600) -->
+ <skip />
<string name="inline_done_button" msgid="6043094985588909584">"Done"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Apply"</string>
<string name="inline_turn_off_notifications" msgid="2653064779176881329">"Turn off"</string>
@@ -985,6 +993,7 @@
<string name="right_icon" msgid="1103955040645237425">"Right icon"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Hold and drag to add tiles"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Hold and drag to rearrange tiles"</string>
+ <string name="select_to_rearrange_tiles" msgid="6166153534235179148">"Select tiles to rearrange and resize"</string>
<string name="tap_to_position_tile" msgid="6282815817773342757">"Tap to position tile"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Drag here to remove"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"You need at least <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> tiles"</string>
@@ -1004,10 +1013,12 @@
<string name="tuner_low_priority" msgid="8412666814123009820">"Show low-priority notification icons"</string>
<string name="other" msgid="429768510980739978">"Other"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"toggle the tile\'s size"</string>
- <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"remove tile"</string>
+ <!-- no translation found for accessibility_qs_edit_remove_tile_action (4811778796933958616) -->
+ <skip />
<string name="accessibility_qs_edit_toggle_placement_mode" msgid="3870429389210569610">"toggle placement mode"</string>
<string name="accessibility_qs_edit_toggle_selection" msgid="2201248304072372239">"toggle selection"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"add tile to the last position"</string>
+ <!-- no translation found for accessibility_qs_edit_tile_add_action (7188303979684400620) -->
+ <skip />
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Move tile"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Add tile to desired position"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Move to <xliff:g id="POSITION">%1$d</xliff:g>"</string>
@@ -1094,7 +1105,8 @@
<string name="auto_saver_text" msgid="3214960308353838764">"Turn on when battery is likely to run out"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"No, thanks"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"In use"</string>
- <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Applications are using your <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps (4994455315457996796) -->
+ <skip />
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" and "</string>
<string name="ongoing_privacy_dialog_using_op" msgid="426635338010011796">"Being used by <xliff:g id="APPLICATION_NAME">%1$s</xliff:g>"</string>
@@ -1251,15 +1263,13 @@
<string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(disconnected)"</string>
<string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"Can\'t switch. Tap to try again."</string>
<string name="media_output_dialog_pairing_new" msgid="5098212763195577270">"Connect a device"</string>
- <!-- no translation found for media_output_dialog_button_connect_device (2819038239946643550) -->
- <skip />
+ <string name="media_output_dialog_button_connect_device" msgid="2819038239946643550">"Connect device"</string>
<string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Unknown app"</string>
<string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Stop casting"</string>
<string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"Available devices for audio output."</string>
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Volume"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
- <!-- no translation found for media_output_group_title_connected_speakers (3413876972919684479) -->
- <skip />
+ <string name="media_output_group_title_connected_speakers" msgid="3413876972919684479">"Connected speakers"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Speakers and displays"</string>
<string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Suggested devices"</string>
<string name="media_input_group_title" msgid="2057057473860783021">"Input"</string>
@@ -1328,10 +1338,8 @@
<string name="accessibility_enter_hint" msgid="2617864063504824834">"enter device"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Use fingerprint to open"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Authentication required. Touch the fingerprint sensor to authenticate."</string>
- <!-- no translation found for resume_animation (9148788411429330671) -->
- <skip />
- <!-- no translation found for pause_animation (1919705510058107504) -->
- <skip />
+ <string name="resume_animation" msgid="9148788411429330671">"Resume animation"</string>
+ <string name="pause_animation" msgid="1919705510058107504">"Pause animation"</string>
<string name="ongoing_call_content_description" msgid="6394763878322348560">"Ongoing call"</string>
<string name="ongoing_notification_extra_content_description" msgid="2098752668861351265">"Ongoing"</string>
<string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobile data"</string>
@@ -1513,11 +1521,13 @@
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"Add shortcut"</string>
<string name="shortcut_helper_delete_shortcut_button_label" msgid="3148773472696137052">"Delete shortcut"</string>
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navigate using your keyboard"</string>
- <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Learn keyboards shortcuts"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2131074100205028210) -->
+ <skip />
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navigate using your touchpad"</string>
<string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Learn touchpad gestures"</string>
<string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Navigate using your keyboard and touchpad"</string>
- <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Learn touchpad gestures, keyboards shortcuts and more"</string>
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (8557098239919786046) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Go back"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Go home"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"View recent apps"</string>
@@ -1587,4 +1597,7 @@
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"All Quick Settings tiles will reset to the device\'s original settings"</string>
<string name="demote_explain_text" msgid="3942301497888762295">"<xliff:g id="APPLICATION">%1$s</xliff:g> will no longer show live updates. You can change this at any time in Settings."</string>
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
+ <string name="underlay_close_button_content_description" msgid="6518481455548869894">"Close"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="7666159428113013319">"Swipe from the top left to open notifications"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="3842355853737057332">"Swipe from the top right to open Quick Settings"</string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rCA-ldrtl/strings.xml b/packages/SystemUI/res/values-en-rCA-ldrtl/strings.xml
index 1c11cb3..c17e64b 100644
--- a/packages/SystemUI/res/values-en-rCA-ldrtl/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA-ldrtl/strings.xml
@@ -20,4 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="recents_quick_scrub_onboarding" msgid="2452671841151577157">"Drag left to quickly switch apps"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="6543179539438220549">"Swipe from the top right to open notifications"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="5760503283520058116">"Swipe from the top left to open Quick Settings"</string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index 1971118..6e21e33 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -467,6 +467,8 @@
<string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"To use the microphone button, enable microphone access in Settings."</string>
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Open Settings"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Other device"</string>
+ <string name="media_suggestion_disconnected_text" msgid="8777932417076437525">"Play on <xliff:g id="DEVICE_NAME">%s</xliff:g>"</string>
+ <string name="media_suggestion_failure_text" msgid="8453022394887613774">"Can\'t connect. Try again."</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Toggle Overview"</string>
<string name="zen_modes_dialog_title" msgid="8854640808100096934">"Modes"</string>
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Done"</string>
@@ -582,6 +584,7 @@
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
<skip />
<string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Share entire screen"</string>
+ <string name="screen_share_permission_dialog_option_text_entire_screen_for_display" msgid="1545850238689169166">"Share <xliff:g id="DISPLAY_NAME">%s</xliff:g>"</string>
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
<skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"When you’re sharing your entire screen, anything on your screen is visible to <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. So be careful with things like passwords, payment details, messages, photos, and audio and video."</string>
@@ -593,6 +596,7 @@
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Cast your screen?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Cast one app"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Cast entire screen"</string>
+ <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen_for_display" msgid="5524817761472941086">"Cast <xliff:g id="DISPLAY_NAME">%s</xliff:g>"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"When you’re casting your entire screen, anything on your screen is visible. So be careful with things like passwords, payment details, messages, photos, and audio and video."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"When you’re casting an app, anything shown or played in that app is visible. So be careful with things like passwords, payment details, messages, photos, and audio and video."</string>
<string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Cast screen"</string>
@@ -673,6 +677,7 @@
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Kept unlocked by TrustAgent"</string>
<string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"Device was locked, too many authentication attempts"</string>
<string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"Device locked\nFailed authentication"</string>
+ <string name="keyguard_indication_after_watch_disconnected" msgid="6329761892437377710">"Locked out by your watch"</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="accessibility_volume_settings" msgid="1458961116951564784">"Sound settings"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Automatically caption media"</string>
@@ -799,6 +804,8 @@
<string name="enable_bluetooth_message" msgid="6740938333772779717">"To connect your keyboard with your tablet, you first have to turn on Bluetooth."</string>
<string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Turn on"</string>
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"On - Face-based"</string>
+ <string name="notification_guts_bundle_title" msgid="1345506995443305361">"Use Notification Organizer"</string>
+ <string name="notification_guts_bundle_summary" msgid="3971530802237393600">"For <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="inline_done_button" msgid="6043094985588909584">"Done"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Apply"</string>
<string name="inline_turn_off_notifications" msgid="2653064779176881329">"Turn off"</string>
@@ -984,6 +991,7 @@
<string name="right_icon" msgid="1103955040645237425">"Right icon"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Hold and drag to add tiles"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Hold and drag to rearrange tiles"</string>
+ <string name="select_to_rearrange_tiles" msgid="6166153534235179148">"Select tiles to rearrange and resize"</string>
<string name="tap_to_position_tile" msgid="6282815817773342757">"Tap to position tile"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Drag here to remove"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"You need at least <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> tiles"</string>
@@ -1003,10 +1011,10 @@
<string name="tuner_low_priority" msgid="8412666814123009820">"Show low-priority notification icons"</string>
<string name="other" msgid="429768510980739978">"Other"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"toggle the tile\'s size"</string>
- <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"remove tile"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="4811778796933958616">"Remove tile"</string>
<string name="accessibility_qs_edit_toggle_placement_mode" msgid="3870429389210569610">"toggle placement mode"</string>
<string name="accessibility_qs_edit_toggle_selection" msgid="2201248304072372239">"toggle selection"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"add tile to the last position"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="7188303979684400620">"Add tile to the last position"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Move tile"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Add tile to desired position"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Move to <xliff:g id="POSITION">%1$d</xliff:g>"</string>
@@ -1093,7 +1101,7 @@
<string name="auto_saver_text" msgid="3214960308353838764">"Turn on when battery is likely to run out"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"No thanks"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"In use"</string>
- <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Applications are using your <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
+ <string name="ongoing_privacy_chip_content_multiple_apps" msgid="4994455315457996796">"<xliff:g id="TYPES_LIST">%s</xliff:g> in use."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" and "</string>
<string name="ongoing_privacy_dialog_using_op" msgid="426635338010011796">"Being used by <xliff:g id="APPLICATION_NAME">%1$s</xliff:g>"</string>
@@ -1508,11 +1516,11 @@
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"Add shortcut"</string>
<string name="shortcut_helper_delete_shortcut_button_label" msgid="3148773472696137052">"Delete shortcut"</string>
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navigate using your keyboard"</string>
- <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Learn keyboards shortcuts"</string>
+ <string name="launch_keyboard_tutorial_notification_content" msgid="2131074100205028210">"Learn keyboard shortcuts"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navigate using your touchpad"</string>
<string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Learn touchpad gestures"</string>
<string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Navigate using your keyboard and touchpad"</string>
- <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Learn touchpad gestures, keyboards shortcuts, and more"</string>
+ <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="8557098239919786046">"Learn touchpad gestures, keyboard shortcuts, and more"</string>
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Go back"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Go home"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"View recent apps"</string>
@@ -1582,4 +1590,7 @@
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"All Quick Settings tiles will reset to the device’s original settings"</string>
<string name="demote_explain_text" msgid="3942301497888762295">"<xliff:g id="APPLICATION">%1$s</xliff:g> will no longer show Live Updates. You can change this any time in Settings."</string>
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
+ <string name="underlay_close_button_content_description" msgid="6518481455548869894">"Close"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="7666159428113013319">"Swipe from the top left to open notifications"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="3842355853737057332">"Swipe from the top right to open Quick Settings"</string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rGB-ldrtl/strings.xml b/packages/SystemUI/res/values-en-rGB-ldrtl/strings.xml
index 1c11cb3..c17e64b 100644
--- a/packages/SystemUI/res/values-en-rGB-ldrtl/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB-ldrtl/strings.xml
@@ -20,4 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="recents_quick_scrub_onboarding" msgid="2452671841151577157">"Drag left to quickly switch apps"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="6543179539438220549">"Swipe from the top right to open notifications"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="5760503283520058116">"Swipe from the top left to open Quick Settings"</string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index fb3d664..c636ecf 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -252,8 +252,7 @@
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Connected to <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"Connected to <xliff:g id="CAST">%s</xliff:g>."</string>
<string name="accessibility_expand_group" msgid="521237935987978624">"Expand group."</string>
- <!-- no translation found for accessibility_collapse_group (1842704016768329517) -->
- <skip />
+ <string name="accessibility_collapse_group" msgid="1842704016768329517">"Collapse group."</string>
<string name="accessibility_add_device_to_group" msgid="5446422960697860806">"Add device to group."</string>
<string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"Remove device from group."</string>
<string name="accessibility_open_application" msgid="1749126077501259712">"Open application."</string>
@@ -468,6 +467,8 @@
<string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"To use the microphone button, enable microphone access in Settings."</string>
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Open settings"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Other device"</string>
+ <string name="media_suggestion_disconnected_text" msgid="8777932417076437525">"Play on <xliff:g id="DEVICE_NAME">%s</xliff:g>"</string>
+ <string name="media_suggestion_failure_text" msgid="8453022394887613774">"Can\'t connect. Try again."</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Toggle Overview"</string>
<string name="zen_modes_dialog_title" msgid="8854640808100096934">"Modes"</string>
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Done"</string>
@@ -583,6 +584,7 @@
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
<skip />
<string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Share entire screen"</string>
+ <string name="screen_share_permission_dialog_option_text_entire_screen_for_display" msgid="1545850238689169166">"Share <xliff:g id="DISPLAY_NAME">%s</xliff:g>"</string>
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
<skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"When you\'re sharing your entire screen, anything on your screen is visible to <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. So be careful with things like passwords, payment details, messages, photos, audio and video."</string>
@@ -594,6 +596,7 @@
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Cast your screen?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Cast one app"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Cast entire screen"</string>
+ <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen_for_display" msgid="5524817761472941086">"Cast <xliff:g id="DISPLAY_NAME">%s</xliff:g>"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"When you\'re casting your entire screen, anything on your screen is visible. So be careful with things like passwords, payment details, messages, photos, audio and video."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"When you\'re casting an app, anything shown or played in that app is visible. So be careful with things like passwords, payment details, messages, photos, audio and video."</string>
<string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Cast screen"</string>
@@ -674,6 +677,7 @@
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Kept unlocked by trust agent"</string>
<string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"Device was locked – too many authentication attempts"</string>
<string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"Device locked\nFailed authentication"</string>
+ <string name="keyguard_indication_after_watch_disconnected" msgid="6329761892437377710">"Locked out by your watch"</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="accessibility_volume_settings" msgid="1458961116951564784">"Sound settings"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Automatically caption media"</string>
@@ -800,6 +804,10 @@
<string name="enable_bluetooth_message" msgid="6740938333772779717">"To connect your keyboard with your tablet, you first have to turn on Bluetooth."</string>
<string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Turn on"</string>
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"On – Face-based"</string>
+ <!-- no translation found for notification_guts_bundle_title (1345506995443305361) -->
+ <skip />
+ <!-- no translation found for notification_guts_bundle_summary (3971530802237393600) -->
+ <skip />
<string name="inline_done_button" msgid="6043094985588909584">"Done"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Apply"</string>
<string name="inline_turn_off_notifications" msgid="2653064779176881329">"Turn off"</string>
@@ -985,6 +993,7 @@
<string name="right_icon" msgid="1103955040645237425">"Right icon"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Hold and drag to add tiles"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Hold and drag to rearrange tiles"</string>
+ <string name="select_to_rearrange_tiles" msgid="6166153534235179148">"Select tiles to rearrange and resize"</string>
<string name="tap_to_position_tile" msgid="6282815817773342757">"Tap to position tile"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Drag here to remove"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"You need at least <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> tiles"</string>
@@ -1004,10 +1013,12 @@
<string name="tuner_low_priority" msgid="8412666814123009820">"Show low-priority notification icons"</string>
<string name="other" msgid="429768510980739978">"Other"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"toggle the tile\'s size"</string>
- <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"remove tile"</string>
+ <!-- no translation found for accessibility_qs_edit_remove_tile_action (4811778796933958616) -->
+ <skip />
<string name="accessibility_qs_edit_toggle_placement_mode" msgid="3870429389210569610">"toggle placement mode"</string>
<string name="accessibility_qs_edit_toggle_selection" msgid="2201248304072372239">"toggle selection"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"add tile to the last position"</string>
+ <!-- no translation found for accessibility_qs_edit_tile_add_action (7188303979684400620) -->
+ <skip />
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Move tile"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Add tile to desired position"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Move to <xliff:g id="POSITION">%1$d</xliff:g>"</string>
@@ -1094,7 +1105,8 @@
<string name="auto_saver_text" msgid="3214960308353838764">"Turn on when battery is likely to run out"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"No, thanks"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"In use"</string>
- <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Applications are using your <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps (4994455315457996796) -->
+ <skip />
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" and "</string>
<string name="ongoing_privacy_dialog_using_op" msgid="426635338010011796">"Being used by <xliff:g id="APPLICATION_NAME">%1$s</xliff:g>"</string>
@@ -1251,15 +1263,13 @@
<string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(disconnected)"</string>
<string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"Can\'t switch. Tap to try again."</string>
<string name="media_output_dialog_pairing_new" msgid="5098212763195577270">"Connect a device"</string>
- <!-- no translation found for media_output_dialog_button_connect_device (2819038239946643550) -->
- <skip />
+ <string name="media_output_dialog_button_connect_device" msgid="2819038239946643550">"Connect device"</string>
<string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Unknown app"</string>
<string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Stop casting"</string>
<string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"Available devices for audio output."</string>
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Volume"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
- <!-- no translation found for media_output_group_title_connected_speakers (3413876972919684479) -->
- <skip />
+ <string name="media_output_group_title_connected_speakers" msgid="3413876972919684479">"Connected speakers"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Speakers and displays"</string>
<string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Suggested devices"</string>
<string name="media_input_group_title" msgid="2057057473860783021">"Input"</string>
@@ -1328,10 +1338,8 @@
<string name="accessibility_enter_hint" msgid="2617864063504824834">"enter device"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Use fingerprint to open"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Authentication required. Touch the fingerprint sensor to authenticate."</string>
- <!-- no translation found for resume_animation (9148788411429330671) -->
- <skip />
- <!-- no translation found for pause_animation (1919705510058107504) -->
- <skip />
+ <string name="resume_animation" msgid="9148788411429330671">"Resume animation"</string>
+ <string name="pause_animation" msgid="1919705510058107504">"Pause animation"</string>
<string name="ongoing_call_content_description" msgid="6394763878322348560">"Ongoing call"</string>
<string name="ongoing_notification_extra_content_description" msgid="2098752668861351265">"Ongoing"</string>
<string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobile data"</string>
@@ -1513,11 +1521,13 @@
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"Add shortcut"</string>
<string name="shortcut_helper_delete_shortcut_button_label" msgid="3148773472696137052">"Delete shortcut"</string>
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navigate using your keyboard"</string>
- <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Learn keyboards shortcuts"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2131074100205028210) -->
+ <skip />
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navigate using your touchpad"</string>
<string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Learn touchpad gestures"</string>
<string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Navigate using your keyboard and touchpad"</string>
- <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Learn touchpad gestures, keyboards shortcuts and more"</string>
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (8557098239919786046) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Go back"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Go home"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"View recent apps"</string>
@@ -1587,4 +1597,7 @@
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"All Quick Settings tiles will reset to the device\'s original settings"</string>
<string name="demote_explain_text" msgid="3942301497888762295">"<xliff:g id="APPLICATION">%1$s</xliff:g> will no longer show live updates. You can change this at any time in Settings."</string>
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
+ <string name="underlay_close_button_content_description" msgid="6518481455548869894">"Close"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="7666159428113013319">"Swipe from the top left to open notifications"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="3842355853737057332">"Swipe from the top right to open Quick Settings"</string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rIN-ldrtl/strings.xml b/packages/SystemUI/res/values-en-rIN-ldrtl/strings.xml
index 1c11cb3..c17e64b 100644
--- a/packages/SystemUI/res/values-en-rIN-ldrtl/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN-ldrtl/strings.xml
@@ -20,4 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="recents_quick_scrub_onboarding" msgid="2452671841151577157">"Drag left to quickly switch apps"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="6543179539438220549">"Swipe from the top right to open notifications"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="5760503283520058116">"Swipe from the top left to open Quick Settings"</string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index fb3d664..c636ecf 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -252,8 +252,7 @@
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Connected to <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"Connected to <xliff:g id="CAST">%s</xliff:g>."</string>
<string name="accessibility_expand_group" msgid="521237935987978624">"Expand group."</string>
- <!-- no translation found for accessibility_collapse_group (1842704016768329517) -->
- <skip />
+ <string name="accessibility_collapse_group" msgid="1842704016768329517">"Collapse group."</string>
<string name="accessibility_add_device_to_group" msgid="5446422960697860806">"Add device to group."</string>
<string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"Remove device from group."</string>
<string name="accessibility_open_application" msgid="1749126077501259712">"Open application."</string>
@@ -468,6 +467,8 @@
<string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"To use the microphone button, enable microphone access in Settings."</string>
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Open settings"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Other device"</string>
+ <string name="media_suggestion_disconnected_text" msgid="8777932417076437525">"Play on <xliff:g id="DEVICE_NAME">%s</xliff:g>"</string>
+ <string name="media_suggestion_failure_text" msgid="8453022394887613774">"Can\'t connect. Try again."</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Toggle Overview"</string>
<string name="zen_modes_dialog_title" msgid="8854640808100096934">"Modes"</string>
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Done"</string>
@@ -583,6 +584,7 @@
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
<skip />
<string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Share entire screen"</string>
+ <string name="screen_share_permission_dialog_option_text_entire_screen_for_display" msgid="1545850238689169166">"Share <xliff:g id="DISPLAY_NAME">%s</xliff:g>"</string>
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
<skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"When you\'re sharing your entire screen, anything on your screen is visible to <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. So be careful with things like passwords, payment details, messages, photos, audio and video."</string>
@@ -594,6 +596,7 @@
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Cast your screen?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Cast one app"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Cast entire screen"</string>
+ <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen_for_display" msgid="5524817761472941086">"Cast <xliff:g id="DISPLAY_NAME">%s</xliff:g>"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"When you\'re casting your entire screen, anything on your screen is visible. So be careful with things like passwords, payment details, messages, photos, audio and video."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"When you\'re casting an app, anything shown or played in that app is visible. So be careful with things like passwords, payment details, messages, photos, audio and video."</string>
<string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Cast screen"</string>
@@ -674,6 +677,7 @@
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Kept unlocked by trust agent"</string>
<string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"Device was locked – too many authentication attempts"</string>
<string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"Device locked\nFailed authentication"</string>
+ <string name="keyguard_indication_after_watch_disconnected" msgid="6329761892437377710">"Locked out by your watch"</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="accessibility_volume_settings" msgid="1458961116951564784">"Sound settings"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Automatically caption media"</string>
@@ -800,6 +804,10 @@
<string name="enable_bluetooth_message" msgid="6740938333772779717">"To connect your keyboard with your tablet, you first have to turn on Bluetooth."</string>
<string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Turn on"</string>
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"On – Face-based"</string>
+ <!-- no translation found for notification_guts_bundle_title (1345506995443305361) -->
+ <skip />
+ <!-- no translation found for notification_guts_bundle_summary (3971530802237393600) -->
+ <skip />
<string name="inline_done_button" msgid="6043094985588909584">"Done"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Apply"</string>
<string name="inline_turn_off_notifications" msgid="2653064779176881329">"Turn off"</string>
@@ -985,6 +993,7 @@
<string name="right_icon" msgid="1103955040645237425">"Right icon"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Hold and drag to add tiles"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Hold and drag to rearrange tiles"</string>
+ <string name="select_to_rearrange_tiles" msgid="6166153534235179148">"Select tiles to rearrange and resize"</string>
<string name="tap_to_position_tile" msgid="6282815817773342757">"Tap to position tile"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Drag here to remove"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"You need at least <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> tiles"</string>
@@ -1004,10 +1013,12 @@
<string name="tuner_low_priority" msgid="8412666814123009820">"Show low-priority notification icons"</string>
<string name="other" msgid="429768510980739978">"Other"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"toggle the tile\'s size"</string>
- <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"remove tile"</string>
+ <!-- no translation found for accessibility_qs_edit_remove_tile_action (4811778796933958616) -->
+ <skip />
<string name="accessibility_qs_edit_toggle_placement_mode" msgid="3870429389210569610">"toggle placement mode"</string>
<string name="accessibility_qs_edit_toggle_selection" msgid="2201248304072372239">"toggle selection"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"add tile to the last position"</string>
+ <!-- no translation found for accessibility_qs_edit_tile_add_action (7188303979684400620) -->
+ <skip />
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Move tile"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Add tile to desired position"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Move to <xliff:g id="POSITION">%1$d</xliff:g>"</string>
@@ -1094,7 +1105,8 @@
<string name="auto_saver_text" msgid="3214960308353838764">"Turn on when battery is likely to run out"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"No, thanks"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"In use"</string>
- <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Applications are using your <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps (4994455315457996796) -->
+ <skip />
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" and "</string>
<string name="ongoing_privacy_dialog_using_op" msgid="426635338010011796">"Being used by <xliff:g id="APPLICATION_NAME">%1$s</xliff:g>"</string>
@@ -1251,15 +1263,13 @@
<string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(disconnected)"</string>
<string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"Can\'t switch. Tap to try again."</string>
<string name="media_output_dialog_pairing_new" msgid="5098212763195577270">"Connect a device"</string>
- <!-- no translation found for media_output_dialog_button_connect_device (2819038239946643550) -->
- <skip />
+ <string name="media_output_dialog_button_connect_device" msgid="2819038239946643550">"Connect device"</string>
<string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Unknown app"</string>
<string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Stop casting"</string>
<string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"Available devices for audio output."</string>
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Volume"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
- <!-- no translation found for media_output_group_title_connected_speakers (3413876972919684479) -->
- <skip />
+ <string name="media_output_group_title_connected_speakers" msgid="3413876972919684479">"Connected speakers"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Speakers and displays"</string>
<string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Suggested devices"</string>
<string name="media_input_group_title" msgid="2057057473860783021">"Input"</string>
@@ -1328,10 +1338,8 @@
<string name="accessibility_enter_hint" msgid="2617864063504824834">"enter device"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Use fingerprint to open"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Authentication required. Touch the fingerprint sensor to authenticate."</string>
- <!-- no translation found for resume_animation (9148788411429330671) -->
- <skip />
- <!-- no translation found for pause_animation (1919705510058107504) -->
- <skip />
+ <string name="resume_animation" msgid="9148788411429330671">"Resume animation"</string>
+ <string name="pause_animation" msgid="1919705510058107504">"Pause animation"</string>
<string name="ongoing_call_content_description" msgid="6394763878322348560">"Ongoing call"</string>
<string name="ongoing_notification_extra_content_description" msgid="2098752668861351265">"Ongoing"</string>
<string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobile data"</string>
@@ -1513,11 +1521,13 @@
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"Add shortcut"</string>
<string name="shortcut_helper_delete_shortcut_button_label" msgid="3148773472696137052">"Delete shortcut"</string>
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navigate using your keyboard"</string>
- <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Learn keyboards shortcuts"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2131074100205028210) -->
+ <skip />
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navigate using your touchpad"</string>
<string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Learn touchpad gestures"</string>
<string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Navigate using your keyboard and touchpad"</string>
- <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Learn touchpad gestures, keyboards shortcuts and more"</string>
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (8557098239919786046) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Go back"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Go home"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"View recent apps"</string>
@@ -1587,4 +1597,7 @@
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"All Quick Settings tiles will reset to the device\'s original settings"</string>
<string name="demote_explain_text" msgid="3942301497888762295">"<xliff:g id="APPLICATION">%1$s</xliff:g> will no longer show live updates. You can change this at any time in Settings."</string>
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
+ <string name="underlay_close_button_content_description" msgid="6518481455548869894">"Close"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="7666159428113013319">"Swipe from the top left to open notifications"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="3842355853737057332">"Swipe from the top right to open Quick Settings"</string>
</resources>
diff --git a/packages/SystemUI/res/values-es-ldrtl/strings.xml b/packages/SystemUI/res/values-es-ldrtl/strings.xml
index c0d341d..56ff1a8 100644
--- a/packages/SystemUI/res/values-es-ldrtl/strings.xml
+++ b/packages/SystemUI/res/values-es-ldrtl/strings.xml
@@ -20,4 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="recents_quick_scrub_onboarding" msgid="2452671841151577157">"Arrastra el dedo hacia la izquierda para cambiar de aplicación rápidamente"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="6543179539438220549">"Desliza desde la parte superior derecha para abrir las notificaciones"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="5760503283520058116">"Desliza desde la parte superior izquierda para abrir los ajustes rápidos"</string>
</resources>
diff --git a/packages/SystemUI/res/values-es-rUS-ldrtl/strings.xml b/packages/SystemUI/res/values-es-rUS-ldrtl/strings.xml
index e8b25a2..b96dcec 100644
--- a/packages/SystemUI/res/values-es-rUS-ldrtl/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS-ldrtl/strings.xml
@@ -20,4 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="recents_quick_scrub_onboarding" msgid="2452671841151577157">"Arrastra a la izquierda para cambiar de app rápidamente"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="6543179539438220549">"Desliza desde la parte superior derecha para abrir las notificaciones"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="5760503283520058116">"Desliza el dedo desde la parte superior izquierda para abrir la Configuración rápida"</string>
</resources>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index 8720b06..4dcd101 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -252,8 +252,7 @@
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Conectado a <xliff:g id="BLUETOOTH">%s</xliff:g>"</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"Conectado a <xliff:g id="CAST">%s</xliff:g>."</string>
<string name="accessibility_expand_group" msgid="521237935987978624">"Expandir grupo."</string>
- <!-- no translation found for accessibility_collapse_group (1842704016768329517) -->
- <skip />
+ <string name="accessibility_collapse_group" msgid="1842704016768329517">"Contraer grupo"</string>
<string name="accessibility_add_device_to_group" msgid="5446422960697860806">"Agregar el dispositivo al grupo."</string>
<string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"Quitar el dispositivo del grupo."</string>
<string name="accessibility_open_application" msgid="1749126077501259712">"Abrir aplicación."</string>
@@ -468,6 +467,8 @@
<string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"Para habilitar el botón de micrófono, habilita su acceso en Configuración."</string>
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Abrir Configuración"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Otro dispositivo"</string>
+ <string name="media_suggestion_disconnected_text" msgid="8777932417076437525">"Reproducir en <xliff:g id="DEVICE_NAME">%s</xliff:g>"</string>
+ <string name="media_suggestion_failure_text" msgid="8453022394887613774">"No se pudo establecer conexión. Vuelve a intentarlo."</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Ocultar o mostrar Recientes"</string>
<string name="zen_modes_dialog_title" msgid="8854640808100096934">"Modos"</string>
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Listo"</string>
@@ -583,6 +584,7 @@
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
<skip />
<string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Compartir pantalla completa"</string>
+ <string name="screen_share_permission_dialog_option_text_entire_screen_for_display" msgid="1545850238689169166">"Compartir a <xliff:g id="DISPLAY_NAME">%s</xliff:g>"</string>
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
<skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Cuando compartes la pantalla completa, todo lo que se muestre es visible en <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Por lo tanto, debes tener cuidado con contraseñas, detalles de pagos, mensajes, fotos, audios y videos."</string>
@@ -594,6 +596,7 @@
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"¿Quieres transmitir la pantalla?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Transmitir una app"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Transmitir pantalla entera"</string>
+ <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen_for_display" msgid="5524817761472941086">"Transmitir a <xliff:g id="DISPLAY_NAME">%s</xliff:g>"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Cuando transmitas la pantalla entera, todo lo que se muestre es visible. Por lo tanto, debes tener cuidado con contraseñas, detalles de pagos, mensajes, fotos, audios y videos."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Cuando transmitas una app, todo lo que se muestre o reproduzca en ella será visible. Por lo tanto, debes tener cuidado con contraseñas, detalles de pagos, mensajes, fotos, audios y videos."</string>
<string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Transmitir pantalla"</string>
@@ -674,6 +677,7 @@
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"TrustAgent lo mantiene desbloqueado"</string>
<string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"Se bloqueó el dispositivo; demasiados intentos de autenticación"</string>
<string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"Dispositivo bloqueado\nFalló la autenticación"</string>
+ <string name="keyguard_indication_after_watch_disconnected" msgid="6329761892437377710">"Bloqueado por el reloj"</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="accessibility_volume_settings" msgid="1458961116951564784">"Configuración de sonido"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Muestra subtítulos automáticos"</string>
@@ -717,7 +721,7 @@
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Vibrar"</string>
<string name="volume_ringer_status_silent" msgid="3691324657849880883">"Silenciar"</string>
<string name="media_device_cast" msgid="4786241789687569892">"Transmisión"</string>
- <string name="stream_notification_unavailable" msgid="4313854556205836435">"No disponible por timbre silenciado"</string>
+ <string name="stream_notification_unavailable" msgid="4313854556205836435">"No disponible porque el timbre está silenciado"</string>
<string name="stream_alarm_unavailable" msgid="4059817189292197839">"No disponible si está activado No interrumpir"</string>
<string name="stream_media_unavailable" msgid="6823020894438959853">"No disponible si está activado No interrumpir"</string>
<string name="stream_unavailable_by_modes" msgid="3674139029490353683">"No disponible porque se activó <xliff:g id="MODE">%s</xliff:g>"</string>
@@ -800,6 +804,10 @@
<string name="enable_bluetooth_message" msgid="6740938333772779717">"Para conectar el teclado con la tablet, primero debes activar Bluetooth."</string>
<string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Activar"</string>
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Activa - En función del rostro"</string>
+ <!-- no translation found for notification_guts_bundle_title (1345506995443305361) -->
+ <skip />
+ <!-- no translation found for notification_guts_bundle_summary (3971530802237393600) -->
+ <skip />
<string name="inline_done_button" msgid="6043094985588909584">"Listo"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Aplicar"</string>
<string name="inline_turn_off_notifications" msgid="2653064779176881329">"Desactivar"</string>
@@ -985,6 +993,7 @@
<string name="right_icon" msgid="1103955040645237425">"Ícono derecho"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Mantén presionada una tarjeta y arrástrala para agregarla"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Mantén presionada una tarjeta y arrástrala para reubicarla"</string>
+ <string name="select_to_rearrange_tiles" msgid="6166153534235179148">"Selecciona las tarjetas para reordenarlas y cambiarles el tamaño"</string>
<string name="tap_to_position_tile" msgid="6282815817773342757">"Presiona para posicionar la tarjeta"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Arrastra aquí para quitar"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Necesitas al menos <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> tarjetas"</string>
@@ -1004,10 +1013,10 @@
<string name="tuner_low_priority" msgid="8412666814123009820">"Mostrar íconos de notificaciones con prioridad baja"</string>
<string name="other" msgid="429768510980739978">"Otros"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"alternar el tamaño del mosaico"</string>
- <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"quitar tarjeta"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="4811778796933958616">"Quitar tarjeta"</string>
<string name="accessibility_qs_edit_toggle_placement_mode" msgid="3870429389210569610">"activar o desactivar el modo de posición"</string>
<string name="accessibility_qs_edit_toggle_selection" msgid="2201248304072372239">"activar o desactivar la selección"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"agregar tarjeta a la última posición"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="7188303979684400620">"Agregar tarjeta a la última posición"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Mover la tarjeta"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Agregar tarjeta a la posición deseada"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Mover a <xliff:g id="POSITION">%1$d</xliff:g>"</string>
@@ -1094,7 +1103,8 @@
<string name="auto_saver_text" msgid="3214960308353838764">"Actívalo cuando la batería se esté por acabar"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"No, gracias"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"En uso"</string>
- <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Hay aplicaciones que están usando tu <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps (4994455315457996796) -->
+ <skip />
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" y "</string>
<string name="ongoing_privacy_dialog_using_op" msgid="426635338010011796">"En uso en <xliff:g id="APPLICATION_NAME">%1$s</xliff:g>"</string>
@@ -1251,15 +1261,13 @@
<string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(desconectado)"</string>
<string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"No se pudo conectar. Presiona para volver a intentarlo."</string>
<string name="media_output_dialog_pairing_new" msgid="5098212763195577270">"Conectar un dispositivo"</string>
- <!-- no translation found for media_output_dialog_button_connect_device (2819038239946643550) -->
- <skip />
+ <string name="media_output_dialog_button_connect_device" msgid="2819038239946643550">"Conectar dispositivo"</string>
<string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"App desconocida"</string>
<string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Detener transmisión"</string>
<string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"Dispositivos disponibles para salida de audio."</string>
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Volumen"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
- <!-- no translation found for media_output_group_title_connected_speakers (3413876972919684479) -->
- <skip />
+ <string name="media_output_group_title_connected_speakers" msgid="3413876972919684479">"Bocinas conectadas"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Bocinas y pantallas"</string>
<string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Dispositivos sugeridos"</string>
<string name="media_input_group_title" msgid="2057057473860783021">"Entrada"</string>
@@ -1328,10 +1336,8 @@
<string name="accessibility_enter_hint" msgid="2617864063504824834">"ingresar al dispositivo"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Usa la huella dactilar para abrir"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Se requiere de una autenticación. Toca el sensor de huellas dactilares para autenticarte."</string>
- <!-- no translation found for resume_animation (9148788411429330671) -->
- <skip />
- <!-- no translation found for pause_animation (1919705510058107504) -->
- <skip />
+ <string name="resume_animation" msgid="9148788411429330671">"Reanudar animación"</string>
+ <string name="pause_animation" msgid="1919705510058107504">"Pausar animación"</string>
<string name="ongoing_call_content_description" msgid="6394763878322348560">"Llamada en curso"</string>
<string name="ongoing_notification_extra_content_description" msgid="2098752668861351265">"En curso"</string>
<string name="mobile_data_settings_title" msgid="3955246641380064901">"Datos móviles"</string>
@@ -1513,11 +1519,11 @@
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"Agregar combinación de teclas"</string>
<string name="shortcut_helper_delete_shortcut_button_label" msgid="3148773472696137052">"Borrar combinación de teclas"</string>
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navega con el teclado"</string>
- <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Aprende combinaciones de teclas"</string>
+ <string name="launch_keyboard_tutorial_notification_content" msgid="2131074100205028210">"Más información sobre las combinaciones de teclas"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navega con el panel táctil"</string>
<string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Aprende los gestos del panel táctil"</string>
<string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Navega con el teclado y el panel táctil"</string>
- <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Aprende sobre los gestos del panel táctil, las combinaciones de teclas y mucho más"</string>
+ <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="8557098239919786046">"Más información sobre los gestos del panel táctil, las combinaciones de teclas y más"</string>
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Atrás"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Ir a la página principal"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Ver apps recientes"</string>
@@ -1587,4 +1593,7 @@
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Se restablecerán todas las tarjeta de Configuración rápida a la configuración original del dispositivo"</string>
<string name="demote_explain_text" msgid="3942301497888762295">"<xliff:g id="APPLICATION">%1$s</xliff:g> ya no mostrará novedades en tiempo real. Puedes cambiar este parámetro cuando quieras en Configuración."</string>
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
+ <string name="underlay_close_button_content_description" msgid="6518481455548869894">"Cerrar"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="7666159428113013319">"Desliza desde la parte superior izquierda para abrir las notificaciones"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="3842355853737057332">"Desliza el dedo desde la parte superior derecha para abrir la Configuración rápida"</string>
</resources>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index db50b5f..197ead3 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -252,8 +252,7 @@
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Conectado a <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"Conectado a <xliff:g id="CAST">%s</xliff:g>."</string>
<string name="accessibility_expand_group" msgid="521237935987978624">"Mostrar grupo."</string>
- <!-- no translation found for accessibility_collapse_group (1842704016768329517) -->
- <skip />
+ <string name="accessibility_collapse_group" msgid="1842704016768329517">"Ocultar grupo."</string>
<string name="accessibility_add_device_to_group" msgid="5446422960697860806">"Añade un dispositivo al grupo."</string>
<string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"Quita un dispositivo del grupo."</string>
<string name="accessibility_open_application" msgid="1749126077501259712">"Abrir aplicación."</string>
@@ -338,8 +337,8 @@
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Girar pantalla automáticamente"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Ubicación"</string>
<string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Salvapantallas"</string>
- <string name="quick_settings_camera_label" msgid="5612076679385269339">"Acceso a cámara"</string>
- <string name="quick_settings_mic_label" msgid="8392773746295266375">"Acceso a micro"</string>
+ <string name="quick_settings_camera_label" msgid="5612076679385269339">"Cámara"</string>
+ <string name="quick_settings_mic_label" msgid="8392773746295266375">"Micrófono"</string>
<string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Disponible"</string>
<string name="quick_settings_camera_mic_blocked" msgid="4710884905006788281">"Bloqueado"</string>
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"Dispositivo multimedia"</string>
@@ -356,7 +355,7 @@
<string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"No hay dispositivos disponibles"</string>
<string name="quick_settings_cast_no_network" msgid="3863016850468559522">"Sin conexión Wi-Fi ni Ethernet"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Brillo"</string>
- <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Invertir colores"</string>
+ <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Inversión de colores"</string>
<string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Corrección de color"</string>
<string name="quick_settings_font_scaling_label" msgid="5289001009876936768">"Tamaño de fuente"</string>
<string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Gestionar usuarios"</string>
@@ -468,6 +467,8 @@
<string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"Para usar el botón del micrófono, habilita el acceso al micrófono en Ajustes."</string>
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Abrir Ajustes"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Otro dispositivo"</string>
+ <string name="media_suggestion_disconnected_text" msgid="8777932417076437525">"Reproducir en <xliff:g id="DEVICE_NAME">%s</xliff:g>"</string>
+ <string name="media_suggestion_failure_text" msgid="8453022394887613774">"No se puede conectar. Inténtalo de nuevo."</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Mostrar u ocultar aplicaciones recientes"</string>
<string name="zen_modes_dialog_title" msgid="8854640808100096934">"Modos"</string>
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Hecho"</string>
@@ -583,6 +584,7 @@
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
<skip />
<string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Compartir toda la pantalla"</string>
+ <string name="screen_share_permission_dialog_option_text_entire_screen_for_display" msgid="1545850238689169166">"Compartir <xliff:g id="DISPLAY_NAME">%s</xliff:g>"</string>
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
<skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Cuando compartes toda tu pantalla, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> puede ver todo lo que hay en ella. Debes tener cuidado con elementos como contraseñas, detalles de pagos, mensajes, fotos, audio y vídeo."</string>
@@ -594,6 +596,7 @@
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"¿Enviar tu pantalla?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Enviar una aplicación"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Enviar toda la pantalla"</string>
+ <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen_for_display" msgid="5524817761472941086">"Enviar <xliff:g id="DISPLAY_NAME">%s</xliff:g>"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Cuando envías toda tu pantalla, se ve todo lo que hay en ella. Debes tener cuidado con elementos como contraseñas, detalles de pagos, mensajes, fotos, audio y vídeo."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Cuando envías una aplicación, se ve todo lo que se muestre o reproduzca en ella. Debes tener cuidado con elementos como contraseñas, detalles de pagos, mensajes, fotos, audio y vídeo."</string>
<string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Enviar pantalla"</string>
@@ -674,6 +677,7 @@
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Desbloqueado por TrustAgent"</string>
<string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"Dispositivo bloqueado; demasiados intentos de autenticación"</string>
<string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"Dispositivo bloqueado\nError de autenticación"</string>
+ <string name="keyguard_indication_after_watch_disconnected" msgid="6329761892437377710">"Bloqueado por el reloj"</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="accessibility_volume_settings" msgid="1458961116951564784">"Ajustes de sonido"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Subtitular automáticamente"</string>
@@ -800,6 +804,10 @@
<string name="enable_bluetooth_message" msgid="6740938333772779717">"Para poder conectar tu teclado a tu tablet, debes activar el Bluetooth."</string>
<string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Activar"</string>
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Activado: basado en caras"</string>
+ <!-- no translation found for notification_guts_bundle_title (1345506995443305361) -->
+ <skip />
+ <!-- no translation found for notification_guts_bundle_summary (3971530802237393600) -->
+ <skip />
<string name="inline_done_button" msgid="6043094985588909584">"Hecho"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Aplicar"</string>
<string name="inline_turn_off_notifications" msgid="2653064779176881329">"Desactivar"</string>
@@ -985,6 +993,7 @@
<string name="right_icon" msgid="1103955040645237425">"Icono a la derecha"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Mantén pulsado un recuadro y arrástralo para añadirlo"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Mantén pulsado un recuadro y arrástralo para reubicarlo"</string>
+ <string name="select_to_rearrange_tiles" msgid="6166153534235179148">"Selecciona los recuadros para reordenarlos y cambiar su tamaño"</string>
<string name="tap_to_position_tile" msgid="6282815817773342757">"Toca para colocar el recuadro"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Arrastra aquí para quitar una función"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Necesitas al menos <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> recuadros"</string>
@@ -1004,10 +1013,12 @@
<string name="tuner_low_priority" msgid="8412666814123009820">"Mostrar iconos de notificaciones con prioridad baja"</string>
<string name="other" msgid="429768510980739978">"Otros"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"mostrar el tamaño del recuadro"</string>
- <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"quitar recuadro"</string>
+ <!-- no translation found for accessibility_qs_edit_remove_tile_action (4811778796933958616) -->
+ <skip />
<string name="accessibility_qs_edit_toggle_placement_mode" msgid="3870429389210569610">"activar o desactivar el modo de posición"</string>
<string name="accessibility_qs_edit_toggle_selection" msgid="2201248304072372239">"activar o desactivar selección"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"añadir el recuadro a la última posición"</string>
+ <!-- no translation found for accessibility_qs_edit_tile_add_action (7188303979684400620) -->
+ <skip />
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Mover recuadro"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Añadir recuadro a la posición deseada"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Mover a <xliff:g id="POSITION">%1$d</xliff:g>"</string>
@@ -1055,7 +1066,7 @@
<string name="notification_channel_screenshot" msgid="7665814998932211997">"Capturas de pantalla"</string>
<string name="notification_channel_instant" msgid="7556135423486752680">"Aplicaciones Instantáneas"</string>
<string name="notification_channel_setup" msgid="7660580986090760350">"Configuración"</string>
- <string name="notification_channel_storage" msgid="2720725707628094977">"Almacenamiento"</string>
+ <string name="notification_channel_storage" msgid="2720725707628094977">"Espacio"</string>
<string name="notification_channel_hints" msgid="7703783206000346876">"Sugerencias"</string>
<string name="notification_channel_accessibility" msgid="8956203986976245820">"Accesibilidad"</string>
<string name="instant_apps" msgid="8337185853050247304">"Aplicaciones Instantáneas"</string>
@@ -1094,7 +1105,8 @@
<string name="auto_saver_text" msgid="3214960308353838764">"Activar cuando sea probable que se quede sin batería"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"No, gracias"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"En uso"</string>
- <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Hay aplicaciones que usan tu <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps (4994455315457996796) -->
+ <skip />
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" y "</string>
<string name="ongoing_privacy_dialog_using_op" msgid="426635338010011796">"Se está usando en <xliff:g id="APPLICATION_NAME">%1$s</xliff:g>"</string>
@@ -1251,15 +1263,13 @@
<string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(desconectado)"</string>
<string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"No se puede cambiar. Toca para volver a intentarlo."</string>
<string name="media_output_dialog_pairing_new" msgid="5098212763195577270">"Conectar un dispositivo"</string>
- <!-- no translation found for media_output_dialog_button_connect_device (2819038239946643550) -->
- <skip />
+ <string name="media_output_dialog_button_connect_device" msgid="2819038239946643550">"Conectar dispositivo"</string>
<string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Aplicación desconocida"</string>
<string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Dejar de enviar contenido"</string>
<string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"Dispositivos disponibles para la salida de audio."</string>
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Volumen"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g> %%"</string>
- <!-- no translation found for media_output_group_title_connected_speakers (3413876972919684479) -->
- <skip />
+ <string name="media_output_group_title_connected_speakers" msgid="3413876972919684479">"Altavoces conectados"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Altavoces y pantallas"</string>
<string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Dispositivos sugeridos"</string>
<string name="media_input_group_title" msgid="2057057473860783021">"Entrada"</string>
@@ -1328,10 +1338,8 @@
<string name="accessibility_enter_hint" msgid="2617864063504824834">"acceder al dispositivo"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Usa la huella digital para abrir"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Autenticación obligatoria. Toca el sensor de huellas digitales para autenticarte."</string>
- <!-- no translation found for resume_animation (9148788411429330671) -->
- <skip />
- <!-- no translation found for pause_animation (1919705510058107504) -->
- <skip />
+ <string name="resume_animation" msgid="9148788411429330671">"Reanudar animación"</string>
+ <string name="pause_animation" msgid="1919705510058107504">"Pausar animación"</string>
<string name="ongoing_call_content_description" msgid="6394763878322348560">"Llamada en curso"</string>
<string name="ongoing_notification_extra_content_description" msgid="2098752668861351265">"En curso"</string>
<string name="mobile_data_settings_title" msgid="3955246641380064901">"Datos móviles"</string>
@@ -1513,11 +1521,13 @@
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"Añadir combinación de teclas"</string>
<string name="shortcut_helper_delete_shortcut_button_label" msgid="3148773472696137052">"Eliminar combinación de teclas"</string>
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Desplázate con el teclado"</string>
- <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Aprende combinaciones de teclas"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2131074100205028210) -->
+ <skip />
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Desplázate con el panel táctil"</string>
<string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Aprende gestos del panel táctil"</string>
<string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Desplázate con el teclado y el panel táctil"</string>
- <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Aprende gestos del panel táctil, combinaciones de teclas y más"</string>
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (8557098239919786046) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Volver"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Ir a Inicio"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Ver aplicaciones recientes"</string>
@@ -1553,7 +1563,7 @@
<string name="tutorial_animation_content_description" msgid="2698816574982370184">"Animación del tutorial, haz clic para pausar y reanudar la reproducción."</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Retroiluminación del teclado"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Nivel %1$d de %2$d"</string>
- <string name="home_controls_dream_label" msgid="6567105701292324257">"Controles de la casa"</string>
+ <string name="home_controls_dream_label" msgid="6567105701292324257">"Controles casa"</string>
<string name="home_controls_dream_description" msgid="4644150952104035789">"Usa los controles de tu casa como salvapantallas"</string>
<string name="volume_undo_action" msgid="5815519725211877114">"Deshacer"</string>
<string name="back_edu_toast_content" msgid="4530314597378982956">"Para volver atrás, desliza hacia la izquierda o la derecha con tres dedos en el panel táctil"</string>
@@ -1583,8 +1593,11 @@
<string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Proporcionado por aplicaciones"</string>
<string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Pantalla"</string>
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Desconocido"</string>
- <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"¿Borrar todos los recuadros?"</string>
+ <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"¿Restablecer recuadros?"</string>
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Todos los recuadros de ajustes rápidos se restablecerán a los ajustes originales del dispositivo"</string>
<string name="demote_explain_text" msgid="3942301497888762295">"<xliff:g id="APPLICATION">%1$s</xliff:g> ya no mostrará novedades en directo. Puedes cambiar esta opción en cualquier momento en Ajustes."</string>
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
+ <string name="underlay_close_button_content_description" msgid="6518481455548869894">"Cerrar"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="7666159428113013319">"Desliza desde la parte superior izquierda para abrir las notificaciones"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="3842355853737057332">"Desliza desde la parte superior derecha para abrir los ajustes rápidos"</string>
</resources>
diff --git a/packages/SystemUI/res/values-et-ldrtl/strings.xml b/packages/SystemUI/res/values-et-ldrtl/strings.xml
index e3305b9..2c1c68d 100644
--- a/packages/SystemUI/res/values-et-ldrtl/strings.xml
+++ b/packages/SystemUI/res/values-et-ldrtl/strings.xml
@@ -20,4 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="recents_quick_scrub_onboarding" msgid="2452671841151577157">"Rakenduste kiiresti vahetamiseks lohistage vasakule"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="6543179539438220549">"Märguannete avamiseks pühkige paremast ülanurgast alla"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="5760503283520058116">"Kiirseadete avamiseks pühkige vasakust ülanurgast alla"</string>
</resources>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index 88c37f0..1c6aa8b 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -234,7 +234,7 @@
<string name="face_re_enroll_dialog_title" msgid="6392173708176069994">"Näoga avamise funktsiooni seadistamine"</string>
<string name="face_re_enroll_dialog_content" msgid="7353502359464038511">"Näoga avamise uuesti seadistamiseks kustutatakse teie praegune näomudel.\n\nTelefoni avamiseks oma näoga peate selle funktsiooni uuesti seadistama."</string>
<string name="face_reenroll_failure_dialog_content" msgid="7073947334397236935">"Näoga avamist ei õnnestunud seadistada. Avage seaded ja proovige uuesti."</string>
- <string name="fingerprint_dialog_touch_sensor" msgid="2817887108047658975">"Puudutage sõrmejäljeandurit"</string>
+ <string name="fingerprint_dialog_touch_sensor" msgid="2817887108047658975">"Puudutage sõrmejäljeandurit."</string>
<string name="fingerprint_dialog_authenticated_confirmation" msgid="1603899612957562862">"Jätkamiseks vajutage avamise ikooni"</string>
<string name="fingerprint_dialog_use_fingerprint_instead" msgid="5542430577183894219">"Nägu ei tuvastatud. Kasutage sõrmejälge."</string>
<!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) -->
@@ -467,6 +467,8 @@
<string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"Mikrofoninupu kasutamiseks lubage seadetes juurdepääs mikrofonile."</string>
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Ava menüü Seaded"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Muu seade"</string>
+ <string name="media_suggestion_disconnected_text" msgid="8777932417076437525">"Esita seadmes <xliff:g id="DEVICE_NAME">%s</xliff:g>"</string>
+ <string name="media_suggestion_failure_text" msgid="8453022394887613774">"Ei saa ühendada. Proovige veel."</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Lehe Ülevaade sisse- ja väljalülitamine"</string>
<string name="zen_modes_dialog_title" msgid="8854640808100096934">"Režiimid"</string>
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Valmis"</string>
@@ -582,6 +584,7 @@
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
<skip />
<string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Jaga kogu ekraani"</string>
+ <string name="screen_share_permission_dialog_option_text_entire_screen_for_display" msgid="1545850238689169166">"Üksuse <xliff:g id="DISPLAY_NAME">%s</xliff:g> jagamine"</string>
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
<skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Kogu ekraanikuva jagamisel on kogu sellel kuvatav sisu nähtav rakendusele <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Seega olge ettevaatlik selliste andmetega nagu paroolid, makseteave, sõnumid, fotod ning heli ja video."</string>
@@ -593,6 +596,7 @@
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Kas kanda ekraanikuva üle?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Ühe rakenduse ülekandmine"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Kogu ekraanikuva ülekandmine"</string>
+ <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen_for_display" msgid="5524817761472941086">"Üksuse <xliff:g id="DISPLAY_NAME">%s</xliff:g> ülekandmine"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Kogu ekraanikuva ülekandmisel on kogu sellel kuvatav sisu nähtav. Seega olge ettevaatlik näiteks paroolide, makseteabe, sõnumite, fotode ning heli ja videoga."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Rakenduse ülekandmisel on kogu rakenduses kuvatav või esitatav sisu nähtav. Seega olge ettevaatlik näiteks paroolide, makseteabe, sõnumite, fotode ning heli ja videoga."</string>
<string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Kanna üle ekraanikuva"</string>
@@ -671,8 +675,9 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Seda seadet haldab sinu vanem. Sinu vanem näeb ja saab hallata teavet, näiteks kasutatavaid rakendusi, sinu asukohta ja ekraaniaega."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Avatuna hoiab TrustAgent"</string>
- <string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"Seade lukustati, liiga palju autentimiskatseid"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"Seade lukustati, liiga palju autentimiskatseid."</string>
<string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"Seade on lukustatud\nAutentimine ebaõnnestus"</string>
+ <string name="keyguard_indication_after_watch_disconnected" msgid="6329761892437377710">"Lukustatud teie kellaga"</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="accessibility_volume_settings" msgid="1458961116951564784">"Heliseaded"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Automaatsed subtiitrid"</string>
@@ -707,7 +712,7 @@
<string name="stream_system" msgid="7663148785370565134">"Süsteem"</string>
<string name="stream_ring" msgid="7550670036738697526">"Helin"</string>
<string name="stream_music" msgid="2188224742361847580">"Meedia"</string>
- <string name="stream_alarm" msgid="16058075093011694">"Alarm"</string>
+ <string name="stream_alarm" msgid="16058075093011694">"Äratus"</string>
<string name="stream_notification" msgid="7930294049046243939">"Märguanne"</string>
<string name="stream_bluetooth_sco" msgid="6234562365528664331">"Bluetooth"</string>
<string name="stream_dtmf" msgid="7322536356554673067">"Kaks mitme tooniga sagedust"</string>
@@ -719,7 +724,7 @@
<string name="stream_notification_unavailable" msgid="4313854556205836435">"Pole saadaval, kuna helin on vaigistatud"</string>
<string name="stream_alarm_unavailable" msgid="4059817189292197839">"Pole saadaval, kuna režiim Mitte segada on sees"</string>
<string name="stream_media_unavailable" msgid="6823020894438959853">"Pole saadaval, kuna režiim Mitte segada on sees"</string>
- <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Pole saadaval, kuna režiim <xliff:g id="MODE">%s</xliff:g> on sisse lülitatud"</string>
+ <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Pole saadaval, kuna režiim <xliff:g id="MODE">%s</xliff:g> on sees"</string>
<string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Pole saadaval"</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Puudutage vaigistuse tühistamiseks."</string>
<string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Puudutage värinarežiimi määramiseks. Juurdepääsetavuse teenused võidakse vaigistada."</string>
@@ -799,6 +804,10 @@
<string name="enable_bluetooth_message" msgid="6740938333772779717">"Klaviatuuri ühendamiseks tahvelarvutiga peate esmalt Bluetoothi sisse lülitama."</string>
<string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Lülita sisse"</string>
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Sees – näopõhine"</string>
+ <!-- no translation found for notification_guts_bundle_title (1345506995443305361) -->
+ <skip />
+ <!-- no translation found for notification_guts_bundle_summary (3971530802237393600) -->
+ <skip />
<string name="inline_done_button" msgid="6043094985588909584">"Valmis"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Rakenda"</string>
<string name="inline_turn_off_notifications" msgid="2653064779176881329">"Lülita välja"</string>
@@ -984,6 +993,7 @@
<string name="right_icon" msgid="1103955040645237425">"Parem ikoon"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Paanide lisamiseks hoidke all ja lohistage"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Paanide ümberpaigutamiseks hoidke neid all ja lohistage"</string>
+ <string name="select_to_rearrange_tiles" msgid="6166153534235179148">"Valige ümberkorraldamiseks ja suuruse muutmiseks paanid"</string>
<string name="tap_to_position_tile" msgid="6282815817773342757">"Paani paigutamiseks puudutage"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Lohistage eemaldamiseks siia"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Teil on vaja vähemalt <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> paani"</string>
@@ -1003,10 +1013,12 @@
<string name="tuner_low_priority" msgid="8412666814123009820">"Kuva madala prioriteediga märguande ikoonid"</string>
<string name="other" msgid="429768510980739978">"Muu"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"muutke paani suurust"</string>
- <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"paani eemaldamiseks"</string>
+ <!-- no translation found for accessibility_qs_edit_remove_tile_action (4811778796933958616) -->
+ <skip />
<string name="accessibility_qs_edit_toggle_placement_mode" msgid="3870429389210569610">"paigutuse režiimi lülitamiseks"</string>
<string name="accessibility_qs_edit_toggle_selection" msgid="2201248304072372239">"valiku vahetamiseks"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"lisage paan viimasesse asukohta"</string>
+ <!-- no translation found for accessibility_qs_edit_tile_add_action (7188303979684400620) -->
+ <skip />
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Teisalda paan"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Lisage paan soovitud asukohta"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Teisaldamine asendisse <xliff:g id="POSITION">%1$d</xliff:g>"</string>
@@ -1093,7 +1105,8 @@
<string name="auto_saver_text" msgid="3214960308353838764">"Lülitatakse sisse, kui aku hakkab tühjaks saama"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Tänan, ei"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"Kasutusel"</string>
- <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Rakendused kasutavad järgmisi: <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps (4994455315457996796) -->
+ <skip />
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" ja "</string>
<string name="ongoing_privacy_dialog_using_op" msgid="426635338010011796">"Kasutab rakendus <xliff:g id="APPLICATION_NAME">%1$s</xliff:g>"</string>
@@ -1508,11 +1521,13 @@
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"Otsetee lisamine"</string>
<string name="shortcut_helper_delete_shortcut_button_label" msgid="3148773472696137052">"Otsetee kustutamine"</string>
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navigeerige klaviatuuri abil"</string>
- <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Õppige klaviatuuri otseteid"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2131074100205028210) -->
+ <skip />
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navigeerige puuteplaadi abil"</string>
<string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Õppige puuteplaadi liigutusi"</string>
<string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Navigeerige klaviatuuri ja puuteplaadi abil"</string>
- <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Õppige puuteplaadi liigutusi, klaviatuuri otseteid ja palju muud."</string>
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (8557098239919786046) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Mine tagasi"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Avakuvale"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Hiljutiste rakenduste vaatamine"</string>
@@ -1528,7 +1543,7 @@
<string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Avakuvale"</string>
<string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Pühkige puuteplaadil kolme sõrmega üles"</string>
<string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Väga hea!"</string>
- <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Tegite avakuvale minemise liigutuse"</string>
+ <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Tegite avakuvale minemise liigutuse."</string>
<string name="touchpad_home_gesture_error_body" msgid="3810674109999513073">"Avakuvale liikumiseks pühkige puuteplaadil kolme sõrmega üles"</string>
<string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Hiljutiste rakenduste vaatamine"</string>
<string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Pühkige üles ja hoidke kolme sõrme puuteplaadil"</string>
@@ -1579,7 +1594,10 @@
<string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Kuva"</string>
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Teadmata"</string>
<string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Kas lähtestada kõik paanid?"</string>
- <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Kõik kiirseadete paanid lähtestatakse seadme algseadetele"</string>
+ <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Kõik kiirseadete paanid lähtestatakse seadme algseadetele."</string>
<string name="demote_explain_text" msgid="3942301497888762295">"<xliff:g id="APPLICATION">%1$s</xliff:g> ei kuva enam reaalajas värskendusi. Saate seda seadete menüüs igal ajal muuta."</string>
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
+ <string name="underlay_close_button_content_description" msgid="6518481455548869894">"Sule"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="7666159428113013319">"Märguannete avamiseks pühkige vasakust ülanurgast alla"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="3842355853737057332">"Kiirseadete avamiseks pühkige paremast ülanurgast alla"</string>
</resources>
diff --git a/packages/SystemUI/res/values-eu-ldrtl/strings.xml b/packages/SystemUI/res/values-eu-ldrtl/strings.xml
index dfc6e1b..d9f3c6a 100644
--- a/packages/SystemUI/res/values-eu-ldrtl/strings.xml
+++ b/packages/SystemUI/res/values-eu-ldrtl/strings.xml
@@ -20,4 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="recents_quick_scrub_onboarding" msgid="2452671841151577157">"Arrastatu ezkerrera aplikazioa azkar aldatzeko"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="6543179539438220549">"Jakinarazpenak irekitzeko, pasatu hatza pantailaren goiko eskuineko izkinatik"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="5760503283520058116">"Ezarpen bizkorrak irekitzeko, pasatu hatza pantailaren goiko ezkerreko izkinatik"</string>
</resources>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index 74a0fe5..b46fe1d 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -252,8 +252,7 @@
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g> gailura konektatuta."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"Hona konektatuta: <xliff:g id="CAST">%s</xliff:g>."</string>
<string name="accessibility_expand_group" msgid="521237935987978624">"Zabaldu taldea."</string>
- <!-- no translation found for accessibility_collapse_group (1842704016768329517) -->
- <skip />
+ <string name="accessibility_collapse_group" msgid="1842704016768329517">"Tolestu taldea."</string>
<string name="accessibility_add_device_to_group" msgid="5446422960697860806">"Gehitu gailua taldera"</string>
<string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"Kendu gailua taldetik."</string>
<string name="accessibility_open_application" msgid="1749126077501259712">"Ireki aplikazioa."</string>
@@ -468,6 +467,8 @@
<string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"Mikrofonoaren botoia erabiltzeko, gaitu mikrofonoa erabiltzeko baimena ezarpenetan."</string>
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Ireki Ezarpenak"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Beste gailu bat"</string>
+ <string name="media_suggestion_disconnected_text" msgid="8777932417076437525">"Erreproduzitu <xliff:g id="DEVICE_NAME">%s</xliff:g> gailuan"</string>
+ <string name="media_suggestion_failure_text" msgid="8453022394887613774">"Ezin da konektatu. Saiatu berriro."</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Aldatu ikuspegi orokorra"</string>
<string name="zen_modes_dialog_title" msgid="8854640808100096934">"Moduak"</string>
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Eginda"</string>
@@ -583,6 +584,7 @@
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
<skip />
<string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Pantaila osoa partekatu"</string>
+ <string name="screen_share_permission_dialog_option_text_entire_screen_for_display" msgid="1545850238689169166">"Partekatu <xliff:g id="DISPLAY_NAME">%s</xliff:g>"</string>
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
<skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Pantaila osoa partekatzen ari zarenean, pantailan duzun guztia ikus dezake <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> aplikazioak. Beraz, kontuz ibili pasahitzekin, ordainketen xehetasunekin, mezuekin, argazkiekin, audioekin eta bideoekin, besteak beste."</string>
@@ -594,6 +596,7 @@
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Pantaila igorri nahi duzu?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Igorri aplikazio bat"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Igorri pantaila osoa"</string>
+ <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen_for_display" msgid="5524817761472941086">"Igorri <xliff:g id="DISPLAY_NAME">%s</xliff:g>"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Pantaila osoa igortzen ari zarenean, pantailan duzun guztia dago ikusgai. Beraz, kontuz ibili pasahitzekin, ordainketen xehetasunekin, mezuekin, argazkiekin, audioekin eta bideoekin, besteak beste."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Aplikazio bat igortzen ari zarenean, aplikazio horretan agertzen edo erreproduzitzen den guztia dago ikusgai. Beraz, kontuz ibili pasahitzekin, ordainketen xehetasunekin, mezuekin, argazkiekin, audioekin eta bideoekin, besteak beste."</string>
<string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Igorri pantaila"</string>
@@ -674,6 +677,8 @@
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"TrustAgent bidez desblokeatuta"</string>
<string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"Blokeatu egin da gailua. Autentifikatzeko saiakera gehiegi egin dira."</string>
<string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"Gailua blokeatuta dago\nEzin izan da autentifikatu"</string>
+ <!-- no translation found for keyguard_indication_after_watch_disconnected (6329761892437377710) -->
+ <skip />
<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="accessibility_volume_settings" msgid="1458961116951564784">"Soinuaren ezarpenak"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Ezarri azpitituluak automatikoki"</string>
@@ -800,6 +805,10 @@
<string name="enable_bluetooth_message" msgid="6740938333772779717">"Teklatua tabletara konektatzeko, Bluetooth eginbidea aktibatu behar duzu."</string>
<string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Aktibatu"</string>
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Aktibatuta: aurpegian oinarrituta"</string>
+ <!-- no translation found for notification_guts_bundle_title (1345506995443305361) -->
+ <skip />
+ <!-- no translation found for notification_guts_bundle_summary (3971530802237393600) -->
+ <skip />
<string name="inline_done_button" msgid="6043094985588909584">"Eginda"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Aplikatu"</string>
<string name="inline_turn_off_notifications" msgid="2653064779176881329">"Desaktibatu"</string>
@@ -985,6 +994,7 @@
<string name="right_icon" msgid="1103955040645237425">"Eskuineko ikonoa"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Lauzak gehitzeko, eduki itzazu sakatuta, eta arrastatu"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Lauzak antolatzeko, eduki itzazu sakatuta, eta arrastatu"</string>
+ <string name="select_to_rearrange_tiles" msgid="6166153534235179148">"Hautatu lauzak tokiz aldatzeko eta haien tamaina aldatzeko"</string>
<string name="tap_to_position_tile" msgid="6282815817773342757">"Sakatu hau lauza kokatzeko"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Kentzeko, arrastatu hona"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"<xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> lauza behar dituzu gutxienez"</string>
@@ -1004,10 +1014,12 @@
<string name="tuner_low_priority" msgid="8412666814123009820">"Erakutsi lehentasun txikiko jakinarazpenen ikonoak"</string>
<string name="other" msgid="429768510980739978">"Beste bat"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"aldatu lauzaren tamaina"</string>
- <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"kendu lauza"</string>
+ <!-- no translation found for accessibility_qs_edit_remove_tile_action (4811778796933958616) -->
+ <skip />
<string name="accessibility_qs_edit_toggle_placement_mode" msgid="3870429389210569610">"kokatze-modua aldatzeko"</string>
<string name="accessibility_qs_edit_toggle_selection" msgid="2201248304072372239">"aldatu hautapena"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"gehitu lauza azken posizioan"</string>
+ <!-- no translation found for accessibility_qs_edit_tile_add_action (7188303979684400620) -->
+ <skip />
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Mugitu lauza"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Gehitu lauza nahi duzun posizioan"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Eraman <xliff:g id="POSITION">%1$d</xliff:g>garren lekura"</string>
@@ -1094,7 +1106,8 @@
<string name="auto_saver_text" msgid="3214960308353838764">"Aktibatu aurreztailea bateria agortzeko arriskua dagoenean"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Ez, eskerrik asko"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"Erabiltzen ari da"</string>
- <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Aplikazio batzuk <xliff:g id="TYPES_LIST">%s</xliff:g> erabiltzen ari dira."</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps (4994455315457996796) -->
+ <skip />
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" eta "</string>
<string name="ongoing_privacy_dialog_using_op" msgid="426635338010011796">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> aplikazioak darabil"</string>
@@ -1251,15 +1264,13 @@
<string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(deskonektatuta)"</string>
<string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"Ezin da aldatu. Berriro saiatzeko, sakatu hau."</string>
<string name="media_output_dialog_pairing_new" msgid="5098212763195577270">"Konektatu gailu bat"</string>
- <!-- no translation found for media_output_dialog_button_connect_device (2819038239946643550) -->
- <skip />
+ <string name="media_output_dialog_button_connect_device" msgid="2819038239946643550">"Konektatu gailu bat"</string>
<string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Aplikazio ezezaguna"</string>
<string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Gelditu igorpena"</string>
<string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"Audio-irteerarako gailu erabilgarriak."</string>
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Bolumena"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"%% <xliff:g id="PERCENTAGE">%1$d</xliff:g>"</string>
- <!-- no translation found for media_output_group_title_connected_speakers (3413876972919684479) -->
- <skip />
+ <string name="media_output_group_title_connected_speakers" msgid="3413876972919684479">"Konektatutako bozgorailuak"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Bozgorailuak eta pantailak"</string>
<string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Iradokitako gailuak"</string>
<string name="media_input_group_title" msgid="2057057473860783021">"Sarrera"</string>
@@ -1328,10 +1339,8 @@
<string name="accessibility_enter_hint" msgid="2617864063504824834">"sartu gailuan"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Erabili hatz-marka irekitzeko"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Autentifikazioa behar da. Autentifikatzeko, ukitu hatz-marken sentsorea."</string>
- <!-- no translation found for resume_animation (9148788411429330671) -->
- <skip />
- <!-- no translation found for pause_animation (1919705510058107504) -->
- <skip />
+ <string name="resume_animation" msgid="9148788411429330671">"Berrekin animazioari"</string>
+ <string name="pause_animation" msgid="1919705510058107504">"Pausatu animazioa"</string>
<string name="ongoing_call_content_description" msgid="6394763878322348560">"Deia abian"</string>
<string name="ongoing_notification_extra_content_description" msgid="2098752668861351265">"Abian"</string>
<string name="mobile_data_settings_title" msgid="3955246641380064901">"Datu-konexioa"</string>
@@ -1513,11 +1522,13 @@
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"Gehitu lasterbide bat"</string>
<string name="shortcut_helper_delete_shortcut_button_label" msgid="3148773472696137052">"Ezabatu lasterbidea"</string>
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Nabigatu teklatua erabilita"</string>
- <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Ikasi lasterbideak"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2131074100205028210) -->
+ <skip />
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Nabigatu ukipen-panela erabilita"</string>
<string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Ikasi ukipen-paneleko keinuak"</string>
<string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Nabigatu teklatua eta ukipen-panela erabilita"</string>
- <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Ikasi ukipen-paneleko keinuak, lasterbideak eta abar"</string>
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (8557098239919786046) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Egin atzera"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Joan orri nagusira"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Ikusi azkenaldiko aplikazioak"</string>
@@ -1587,4 +1598,8 @@
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Gailuaren jatorrizko ezarpenak berrezarriko dira ezarpen bizkorren lauza guztietan"</string>
<string name="demote_explain_text" msgid="3942301497888762295">"Aurrerantzean, <xliff:g id="APPLICATION">%1$s</xliff:g> aplikazioak ez du erakutsiko zuzeneko informazioa. Ezarpenetan alda dezakezu aukera hori."</string>
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
+ <!-- no translation found for underlay_close_button_content_description (6518481455548869894) -->
+ <skip />
+ <string name="dual_shade_educational_tooltip_notifs" msgid="7666159428113013319">"Jakinarazpenak irekitzeko, pasatu hatza pantailaren goiko ezkerreko izkinatik"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="3842355853737057332">"Ezarpen bizkorrak irekitzeko, pasatu hatza pantailaren goiko eskuineko izkinatik"</string>
</resources>
diff --git a/packages/SystemUI/res/values-fa-ldrtl/strings.xml b/packages/SystemUI/res/values-fa-ldrtl/strings.xml
index 42fbf61..df82df7 100644
--- a/packages/SystemUI/res/values-fa-ldrtl/strings.xml
+++ b/packages/SystemUI/res/values-fa-ldrtl/strings.xml
@@ -20,4 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="recents_quick_scrub_onboarding" msgid="2452671841151577157">"برای سریع جابهجا شدن میان برنامهها، به چپ بکشید"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="6543179539438220549">"برای باز کردن «اعلانها»، از بالا سمت راست تند بکشید"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="5760503283520058116">"برای باز کردن «تنظیمات فوری»، از بالا سمت چپ تند بکشید"</string>
</resources>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index d506186..bcd38ce 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -252,8 +252,7 @@
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"به <xliff:g id="BLUETOOTH">%s</xliff:g> متصل شد."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"به <xliff:g id="CAST">%s</xliff:g> متصل شد."</string>
<string name="accessibility_expand_group" msgid="521237935987978624">"گروه را از هم باز میکند."</string>
- <!-- no translation found for accessibility_collapse_group (1842704016768329517) -->
- <skip />
+ <string name="accessibility_collapse_group" msgid="1842704016768329517">"جمع کردن گروه."</string>
<string name="accessibility_add_device_to_group" msgid="5446422960697860806">"افزودن دستگاه به گروه."</string>
<string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"حذف دستگاه از گروه."</string>
<string name="accessibility_open_application" msgid="1749126077501259712">"برنامه را باز میکند."</string>
@@ -468,6 +467,8 @@
<string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"برای استفاده از دکمه میکروفون، دسترسی به میکروفون را در «تنظیمات» فعال کنید."</string>
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"باز کردن تنظیمات"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"دستگاه دیگر"</string>
+ <string name="media_suggestion_disconnected_text" msgid="8777932417076437525">"پخش در <xliff:g id="DEVICE_NAME">%s</xliff:g>"</string>
+ <string name="media_suggestion_failure_text" msgid="8453022394887613774">"متصل نشد. دوباره امتحان کنید."</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"تغییر وضعیت نمای کلی"</string>
<string name="zen_modes_dialog_title" msgid="8854640808100096934">"حالتها"</string>
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"تمام"</string>
@@ -583,6 +584,7 @@
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
<skip />
<string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"همرسانی کردن کل صفحهنمایش"</string>
+ <string name="screen_share_permission_dialog_option_text_entire_screen_for_display" msgid="1545850238689169166">"همرسانی <xliff:g id="DISPLAY_NAME">%s</xliff:g>"</string>
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
<skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"وقتی کل صفحهنمایش را همرسانی میکنید، هر چیزی که روی صفحهنمایش شما وجود داشته باشد برای <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> قابلمشاهده خواهد بود. درنتیجه مراقب چیزهایی مثل گذرواژهها، جزئیات پرداخت، پیامها، عکسها، و صدا و تصویر باشید."</string>
@@ -594,6 +596,7 @@
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"محتوای صفحهنمایش شما پخش شود؟"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"پخش کردن محتوای یک برنامه"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"پخش کردن محتوای کل صفحه"</string>
+ <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen_for_display" msgid="5524817761472941086">"پخش محتوای <xliff:g id="DISPLAY_NAME">%s</xliff:g>"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"وقتی محتوای کل صفحهنمایش را پخش میکنید، هر چیزی که روی صفحهنمایش شما وجود دارد قابلمشاهده است. درنتیجه مراقب چیزهایی مثل گذرواژهها، جزئیات پرداخت، پیامها، عکسها، و صدا و تصویر باشید."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"وقتی محتوای برنامهای را پخش میکنید، هر چیزی که در آن برنامه پخش میشود قابلمشاهده است. درنتیجه مراقب چیزهایی مثل گذرواژهها، جزئیات پرداخت، پیامها، عکسها، و صدا و تصویر باشید."</string>
<string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"پخش محتوای صفحهنمایش"</string>
@@ -674,6 +677,7 @@
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"با TrustAgent قفل را باز نگهدارید"</string>
<string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"بهدلیل تلاشهای بیشاز حد برای اصالتسنجی، دستگاه قفل شد"</string>
<string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"دستگاه قفل شد\nاصالتسنجی ناموفق بود"</string>
+ <string name="keyguard_indication_after_watch_disconnected" msgid="6329761892437377710">"ازطریق ساعتتان قفل شده است"</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="accessibility_volume_settings" msgid="1458961116951564784">"تنظیمات صدا"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"رسانه زیرنویس خودکار"</string>
@@ -800,6 +804,10 @@
<string name="enable_bluetooth_message" msgid="6740938333772779717">"برای مرتبط کردن صفحهکلید با رایانه لوحی، ابتدا باید بلوتوث را روشن کنید."</string>
<string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"روشن کردن"</string>
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"روشن - براساس چهره"</string>
+ <!-- no translation found for notification_guts_bundle_title (1345506995443305361) -->
+ <skip />
+ <!-- no translation found for notification_guts_bundle_summary (3971530802237393600) -->
+ <skip />
<string name="inline_done_button" msgid="6043094985588909584">"تمام"</string>
<string name="inline_ok_button" msgid="603075490581280343">"اعمال"</string>
<string name="inline_turn_off_notifications" msgid="2653064779176881329">"خاموش کردن"</string>
@@ -985,6 +993,7 @@
<string name="right_icon" msgid="1103955040645237425">"نماد راست"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"برای افزودن کاشی، نگه دارید و بکشید"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"برای تغییر دادن ترتیب کاشیها، آنها را نگه دارید و بکشید"</string>
+ <string name="select_to_rearrange_tiles" msgid="6166153534235179148">"کاشیها را برای تغییر ترتیب و اندازه انتخاب کنید"</string>
<string name="tap_to_position_tile" msgid="6282815817773342757">"برای قرار دادن کاشی، تکضرب بزنید"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"برای حذف، به اینجا بکشید"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"حداقل به <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> کاشی نیاز دارید"</string>
@@ -1004,10 +1013,10 @@
<string name="tuner_low_priority" msgid="8412666814123009820">"نمایش نمادهای اعلان کماهمیت"</string>
<string name="other" msgid="429768510980739978">"موارد دیگر"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"تغییر اندازه کاشی"</string>
- <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"برداشتن کاشی"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="4811778796933958616">"برداشتن کاشی"</string>
<string name="accessibility_qs_edit_toggle_placement_mode" msgid="3870429389210569610">"روشن/خاموش کردن حالت جایگذاری"</string>
<string name="accessibility_qs_edit_toggle_selection" msgid="2201248304072372239">"تغییر وضعیت انتخاب"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"افزودن کاشی به آخرین جایگاه"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="7188303979684400620">"افزودن کاشی به آخرین جایگاه"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"انتقال کاشی"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"افزودن کاشی به جایگاه دلخواه"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"انتقال به <xliff:g id="POSITION">%1$d</xliff:g>"</string>
@@ -1094,7 +1103,8 @@
<string name="auto_saver_text" msgid="3214960308353838764">"وقتی باتری روبهاتمام است، بهینهسازی باتری را روشن کنید"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"نه متشکرم"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"استفاده شده"</string>
- <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"برنامهها از <xliff:g id="TYPES_LIST">%s</xliff:g> شما استفاده میکنند."</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps (4994455315457996796) -->
+ <skip />
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">"، "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" و "</string>
<string name="ongoing_privacy_dialog_using_op" msgid="426635338010011796">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> درحال استفاده از آن است"</string>
@@ -1170,8 +1180,8 @@
<string name="controls_providers_title" msgid="6879775889857085056">"انتخاب برنامه برای افزودن کنترلها"</string>
<string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# کنترل اضافه شد.}one{# کنترل اضافه شد.}other{# کنترل اضافه شد.}}"</string>
<string name="controls_removed" msgid="3731789252222856959">"حذف شد"</string>
- <string name="controls_panel_authorization_title" msgid="267429338785864842">"<xliff:g id="APPNAME">%s</xliff:g> افزوده شود؟"</string>
- <string name="controls_panel_authorization" msgid="7045551688535104194">"<xliff:g id="APPNAME">%s</xliff:g> میتواند انتخاب کند چه کنترلها و محتوایی اینجا نشان داده شود."</string>
+ <string name="controls_panel_authorization_title" msgid="267429338785864842">"<xliff:g id="APPNAME">%s</xliff:g> افزوده شود؟"</string>
+ <string name="controls_panel_authorization" msgid="7045551688535104194">"<xliff:g id="APPNAME">%s</xliff:g> میتواند انتخاب کند چه کنترلها و محتوایی اینجا نشان داده شود."</string>
<string name="controls_panel_remove_app_authorization" msgid="5920442084735364674">"کنترلهای <xliff:g id="APPNAME">%s</xliff:g> برداشته شود؟"</string>
<string name="accessibility_control_favorite" msgid="8694362691985545985">"به موارد دلخواه اضافه شد"</string>
<string name="accessibility_control_favorite_position" msgid="54220258048929221">"به «موارد دلخواه» اضافه شد، جایگاه <xliff:g id="NUMBER">%d</xliff:g>"</string>
@@ -1251,15 +1261,13 @@
<string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(اتصال قطع شد)"</string>
<string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"عوض نمیشود. برای تلاش مجدد تکضرب بزنید."</string>
<string name="media_output_dialog_pairing_new" msgid="5098212763195577270">"متصل کردن دستگاه"</string>
- <!-- no translation found for media_output_dialog_button_connect_device (2819038239946643550) -->
- <skip />
+ <string name="media_output_dialog_button_connect_device" msgid="2819038239946643550">"متصل کردن دستگاه"</string>
<string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"برنامه ناشناس"</string>
<string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"توقف پخش محتوا"</string>
<string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"دستگاههای دردسترس برای خروجی صوتی."</string>
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"میزان صدا"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"%%<xliff:g id="PERCENTAGE">%1$d</xliff:g>"</string>
- <!-- no translation found for media_output_group_title_connected_speakers (3413876972919684479) -->
- <skip />
+ <string name="media_output_group_title_connected_speakers" msgid="3413876972919684479">"بلندگوهای متصلشده"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"بلندگوها و نمایشگرها"</string>
<string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"دستگاههای پیشنهادی"</string>
<string name="media_input_group_title" msgid="2057057473860783021">"ورودی"</string>
@@ -1328,10 +1336,8 @@
<string name="accessibility_enter_hint" msgid="2617864063504824834">"وارد شدن به دستگاه"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"از اثر انگشت برای باز کردن قفل استفاده کنید"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"اصالتسنجی لازم است. برای اصالتسنجی، حسگر اثر انگشت را لمس کنید."</string>
- <!-- no translation found for resume_animation (9148788411429330671) -->
- <skip />
- <!-- no translation found for pause_animation (1919705510058107504) -->
- <skip />
+ <string name="resume_animation" msgid="9148788411429330671">"ازسرگیری پویانمایی"</string>
+ <string name="pause_animation" msgid="1919705510058107504">"توقف موقت پویانمایی"</string>
<string name="ongoing_call_content_description" msgid="6394763878322348560">"تماس درحال انجام"</string>
<string name="ongoing_notification_extra_content_description" msgid="2098752668861351265">"درحال انجام"</string>
<string name="mobile_data_settings_title" msgid="3955246641380064901">"داده تلفن همراه"</string>
@@ -1513,11 +1519,11 @@
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"افزودن میانبر"</string>
<string name="shortcut_helper_delete_shortcut_button_label" msgid="3148773472696137052">"حذف میانبر"</string>
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"پیمایش کردن بااستفاده از صفحهکلید"</string>
- <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"آشنایی با میانبرهای صفحهکلید"</string>
+ <string name="launch_keyboard_tutorial_notification_content" msgid="2131074100205028210">"با میانبرهای صفحهکلید آشنا شوید"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"پیمایش کردن بااستفاده از صفحه لمسی"</string>
<string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"آشنایی با اشارههای صفحه لمسی"</string>
<string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"پیمایش کردن بااستفاده از صفحهکلید و صفحه لمسی"</string>
- <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"آشنایی با اشارههای صفحه لمسی، میانبرهای صفحهکلید، و موارد دیگر"</string>
+ <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="8557098239919786046">"با اشارههای صفحه لمسی، میانبرهای صفحهکلید، و موارد دیگر آشنا شوید"</string>
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"برگشتن"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"رفتن به صفحه اصلی"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"مشاهده برنامههای اخیر"</string>
@@ -1587,4 +1593,7 @@
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"همه کاشیهای «تنظیمات فوری» به تنظیمات اصلی دستگاه بازنشانی خواهد شد"</string>
<string name="demote_explain_text" msgid="3942301497888762295">"<xliff:g id="APPLICATION">%1$s</xliff:g> دیگر «بهروزرسانیهای زنده» را نشان نخواهد داد. هروقت خواستید میتوانید آن را در «تنظیمات» تغییر دهید."</string>
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>، <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
+ <string name="underlay_close_button_content_description" msgid="6518481455548869894">"بستن"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="7666159428113013319">"برای باز کردن «اعلانها»، از بالا سمت چپ تند بکشید"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="3842355853737057332">"برای باز کردن «تنظیمات فوری»، از بالا سمت راست تند بکشید"</string>
</resources>
diff --git a/packages/SystemUI/res/values-fi-ldrtl/strings.xml b/packages/SystemUI/res/values-fi-ldrtl/strings.xml
index ed40cd1..038d549 100644
--- a/packages/SystemUI/res/values-fi-ldrtl/strings.xml
+++ b/packages/SystemUI/res/values-fi-ldrtl/strings.xml
@@ -20,4 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="recents_quick_scrub_onboarding" msgid="2452671841151577157">"Vaihda sovellusta nopeasti vetämällä vasemmalle"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="6543179539438220549">"Avaa ilmoitukset pyyhkäisemällä oikeasta yläkulmasta"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="5760503283520058116">"Avaa pika-asetukset pyyhkäisemällä vasemmasta yläreunasta"</string>
</resources>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index 9a72c7f..3a35d28 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -112,9 +112,7 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Tallennetaanko näytön toimintaa?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Tallenna yhdestä sovelluksesta"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="4882406311415082016">"Tallenna tämä näyttö"</string>
- <!-- String.format failed for translation -->
- <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (4169494703993148253) -->
- <skip />
+ <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="4169494703993148253">"Tallenna %s"</string>
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Kun tallennat koko näyttöä, kaikki näytöllä näkyvä sisältö tallennetaan. Ole siis varovainen, kun lisäät salasanoja, maksutietoja, viestejä, kuvia, audiota tai videoita."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Kun tallennat sovellusta, kaikki sovelluksessa näkyvä tai toistettu sisältö tallennetaan. Ole siis varovainen, kun lisäät salasanoja, maksutietoja, viestejä, kuvia, audiota tai videoita."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Tallenna näyttö"</string>
@@ -254,8 +252,7 @@
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Yhteys: <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"Yhdistetty kohteeseen <xliff:g id="CAST">%s</xliff:g>"</string>
<string name="accessibility_expand_group" msgid="521237935987978624">"Laajenna ryhmä."</string>
- <!-- no translation found for accessibility_collapse_group (1842704016768329517) -->
- <skip />
+ <string name="accessibility_collapse_group" msgid="1842704016768329517">"Tiivistä ryhmä."</string>
<string name="accessibility_add_device_to_group" msgid="5446422960697860806">"Lisää laite ryhmään."</string>
<string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"Poista laite ryhmästä."</string>
<string name="accessibility_open_application" msgid="1749126077501259712">"Avaa sovellus."</string>
@@ -470,6 +467,8 @@
<string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"Jos haluat käyttää mikrofonipainiketta, salli pääsy mikrofoniin asetuksista."</string>
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Avaa Asetukset"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Muu laite"</string>
+ <string name="media_suggestion_disconnected_text" msgid="8777932417076437525">"Toista tällä: <xliff:g id="DEVICE_NAME">%s</xliff:g>"</string>
+ <string name="media_suggestion_failure_text" msgid="8453022394887613774">"Ei yhteyttä. Yritä uudelleen."</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Näytä/piilota viimeisimmät"</string>
<string name="zen_modes_dialog_title" msgid="8854640808100096934">"Tilat"</string>
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Valmis"</string>
@@ -585,6 +584,7 @@
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
<skip />
<string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Jaa koko näyttö"</string>
+ <string name="screen_share_permission_dialog_option_text_entire_screen_for_display" msgid="1545850238689169166">"Jaa <xliff:g id="DISPLAY_NAME">%s</xliff:g>"</string>
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
<skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Kun jaat koko näytön, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> näkee kaiken sen sisälllön. Ole siis varovainen, kun lisäät salasanoja, maksutietoja, viestejä, kuvia, audiota tai videoita."</string>
@@ -596,6 +596,7 @@
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Striimataanko näyttö?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Striimaa yksi sovellus"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Striimaa koko näyttö"</string>
+ <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen_for_display" msgid="5524817761472941086">"Striimaa tähän: <xliff:g id="DISPLAY_NAME">%s</xliff:g>"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Kun striimaat koko näyttöä, kaikki näytön sisältö on näkyvillä. Ole siis varovainen, kun lisäät salasanoja, maksutietoja, viestejä, kuvia, audiota tai videoita."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Kun striimaat sovellusta, kaikki sovelluksessa näkyvä tai toistettu sisältö on näkyvillä. Ole siis varovainen, kun lisäät salasanoja, maksutietoja, viestejä, kuvia, audiota tai videoita."</string>
<string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Striimaa näyttö"</string>
@@ -676,6 +677,7 @@
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"TrustAgent pitää lukitusta avattuna"</string>
<string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"Laite lukittiin, liian monta todennusyritystä"</string>
<string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"Laite lukittu\nTodennus epäonnistui"</string>
+ <string name="keyguard_indication_after_watch_disconnected" msgid="6329761892437377710">"Lukittu kellolla"</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="accessibility_volume_settings" msgid="1458961116951564784">"Ääniasetukset"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Tekstitä media automaatt."</string>
@@ -802,6 +804,10 @@
<string name="enable_bluetooth_message" msgid="6740938333772779717">"Jotta voit yhdistää näppäimistön tablettiisi, sinun on ensin otettava Bluetooth käyttöön."</string>
<string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Ota käyttöön"</string>
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Päällä – kasvojen perusteella"</string>
+ <!-- no translation found for notification_guts_bundle_title (1345506995443305361) -->
+ <skip />
+ <!-- no translation found for notification_guts_bundle_summary (3971530802237393600) -->
+ <skip />
<string name="inline_done_button" msgid="6043094985588909584">"Valmis"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Käytä"</string>
<string name="inline_turn_off_notifications" msgid="2653064779176881329">"Laita pois päältä"</string>
@@ -987,6 +993,7 @@
<string name="right_icon" msgid="1103955040645237425">"Oikea kuvake"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Lisää osioita koskettamalla pitkään ja vetämällä"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Järjestele koskettamalla pitkään ja vetämällä"</string>
+ <string name="select_to_rearrange_tiles" msgid="6166153534235179148">"Valitse laatat, joita haluat siirtää ja joiden kokoa muuttaa"</string>
<string name="tap_to_position_tile" msgid="6282815817773342757">"Sijoita laatta napauttamalla"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Poista vetämällä tähän."</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"<xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> kiekkoa on vähimmäismäärä"</string>
@@ -1006,10 +1013,12 @@
<string name="tuner_low_priority" msgid="8412666814123009820">"Näytä vähemmän tärkeät ilmoituskuvakkeet"</string>
<string name="other" msgid="429768510980739978">"Muu"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"ruudun koko päälle/pois"</string>
- <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"poista kiekko"</string>
+ <!-- no translation found for accessibility_qs_edit_remove_tile_action (4811778796933958616) -->
+ <skip />
<string name="accessibility_qs_edit_toggle_placement_mode" msgid="3870429389210569610">"laittaaksesi sijoittelutilan päälle / pois päältä"</string>
<string name="accessibility_qs_edit_toggle_selection" msgid="2201248304072372239">"vaihtaaksesi valintaa"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"lisää laatta viimeiseen kohtaan"</string>
+ <!-- no translation found for accessibility_qs_edit_tile_add_action (7188303979684400620) -->
+ <skip />
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Siirrä kiekkoa"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Lisää laatta haluttuun kohtaan"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Siirrä paikkaan <xliff:g id="POSITION">%1$d</xliff:g>"</string>
@@ -1052,7 +1061,7 @@
<string name="tuner_right" msgid="8247571132790812149">"Oikea"</string>
<string name="tuner_menu" msgid="363690665924769420">"Valikko"</string>
<string name="tuner_app" msgid="6949280415826686972">"<xliff:g id="APP">%1$s</xliff:g> sovellus"</string>
- <string name="notification_channel_alerts" msgid="3385787053375150046">"Ilmoitukset"</string>
+ <string name="notification_channel_alerts" msgid="3385787053375150046">"Varoitukset"</string>
<string name="notification_channel_battery" msgid="9219995638046695106">"Akku"</string>
<string name="notification_channel_screenshot" msgid="7665814998932211997">"Kuvakaappaukset"</string>
<string name="notification_channel_instant" msgid="7556135423486752680">"Instant Apps"</string>
@@ -1096,7 +1105,8 @@
<string name="auto_saver_text" msgid="3214960308353838764">"Ota käyttöön, jos akku todennäköisesti loppuu"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Ei kiitos"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"Käytössä"</string>
- <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"<xliff:g id="TYPES_LIST">%s</xliff:g> ovat sovellusten käytössä."</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps (4994455315457996796) -->
+ <skip />
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" ja "</string>
<string name="ongoing_privacy_dialog_using_op" msgid="426635338010011796">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> käyttää tätä"</string>
@@ -1169,7 +1179,7 @@
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"vaihda"</string>
<string name="accessibility_floating_button_action_edit" msgid="1688227814600463987">"Muokkaa"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Laitehallinta"</string>
- <string name="controls_providers_title" msgid="6879775889857085056">"Valitse sovellus lisätäksesi säätimiä"</string>
+ <string name="controls_providers_title" msgid="6879775889857085056">"Valitse sovellus ja lisää ohjaimia"</string>
<string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# säädin lisätty.}other{# säädintä lisätty.}}"</string>
<string name="controls_removed" msgid="3731789252222856959">"Poistettu"</string>
<string name="controls_panel_authorization_title" msgid="267429338785864842">"Lisätäänkö <xliff:g id="APPNAME">%s</xliff:g>?"</string>
@@ -1253,15 +1263,13 @@
<string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(yhteys katkaistu)"</string>
<string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"Vaihtaminen ei onnistunut. Yritä uudelleen."</string>
<string name="media_output_dialog_pairing_new" msgid="5098212763195577270">"Yhdistä laite"</string>
- <!-- no translation found for media_output_dialog_button_connect_device (2819038239946643550) -->
- <skip />
+ <string name="media_output_dialog_button_connect_device" msgid="2819038239946643550">"Yhdistä laite"</string>
<string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Tuntematon sovellus"</string>
<string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Lopeta striimaus"</string>
<string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"Käytettävissä olevat audiolaitteet"</string>
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Äänenvoimakkuus"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g> %%"</string>
- <!-- no translation found for media_output_group_title_connected_speakers (3413876972919684479) -->
- <skip />
+ <string name="media_output_group_title_connected_speakers" msgid="3413876972919684479">"Yhdistetyt kaiuttimet"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Kaiuttimet ja näytöt"</string>
<string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Ehdotetut laitteet"</string>
<string name="media_input_group_title" msgid="2057057473860783021">"Sisääntulo"</string>
@@ -1330,10 +1338,8 @@
<string name="accessibility_enter_hint" msgid="2617864063504824834">"avataksesi laitteen"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Avaa sormenjäljellä"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Todennus vaaditaan. Todenna koskettamalla sormenjälkitunnistinta."</string>
- <!-- no translation found for resume_animation (9148788411429330671) -->
- <skip />
- <!-- no translation found for pause_animation (1919705510058107504) -->
- <skip />
+ <string name="resume_animation" msgid="9148788411429330671">"Jatka animaatiota"</string>
+ <string name="pause_animation" msgid="1919705510058107504">"Keskeytä animaatio"</string>
<string name="ongoing_call_content_description" msgid="6394763878322348560">"Käynnissä oleva puhelu"</string>
<string name="ongoing_notification_extra_content_description" msgid="2098752668861351265">"Käynnissä"</string>
<string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobiilidata"</string>
@@ -1515,11 +1521,11 @@
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"Lisää pikanäppäin"</string>
<string name="shortcut_helper_delete_shortcut_button_label" msgid="3148773472696137052">"Poista pikanäppäin"</string>
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Siirry käyttämällä näppäimistöä"</string>
- <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Opettele pikanäppäimiä"</string>
+ <string name="launch_keyboard_tutorial_notification_content" msgid="2131074100205028210">"Opettele pikanäppäimiä"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Siirry käyttämällä kosketuslevyä"</string>
<string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Opettele kosketuslevyn eleitä"</string>
<string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Siirry käyttämällä näppäimistöä ja kosketuslevyä"</string>
- <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Opettele kosketuslevyn eleitä, pikanäppäimiä ja muuta"</string>
+ <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="8557098239919786046">"Opettele esimerkiksi kosketuslevyeleitä ja pikanäppäimiä"</string>
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Takaisin"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Siirry etusivulle"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Katso viimeisimmät sovellukset"</string>
@@ -1589,4 +1595,7 @@
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Kaikki pika-asetuslaatat palautetaan laitteen alkuperäisiin asetuksiin"</string>
<string name="demote_explain_text" msgid="3942301497888762295">"<xliff:g id="APPLICATION">%1$s</xliff:g> ei enää näytä livepäivityksiä. Voit muuttaa tätä koska tahansa asetuksissa."</string>
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
+ <string name="underlay_close_button_content_description" msgid="6518481455548869894">"Sulje"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="7666159428113013319">"Avaa ilmoitukset pyyhkäisemällä vasemmasta yläkulmasta"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="3842355853737057332">"Avaa pika-asetukset pyyhkäisemällä oikeasta yläkulmasta"</string>
</resources>
diff --git a/packages/SystemUI/res/values-fr-ldrtl/strings.xml b/packages/SystemUI/res/values-fr-ldrtl/strings.xml
index 17dc521..2b449f4 100644
--- a/packages/SystemUI/res/values-fr-ldrtl/strings.xml
+++ b/packages/SystemUI/res/values-fr-ldrtl/strings.xml
@@ -20,4 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="recents_quick_scrub_onboarding" msgid="2452671841151577157">"Déplacer vers la gauche pour changer rapidement d\'application"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="6543179539438220549">"Balayez l\'écran depuis le coin supérieur droit pour ouvrir les notifications"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="5760503283520058116">"Balayez l\'écran depuis le coin supérieur gauche pour ouvrir les réglages rapides"</string>
</resources>
diff --git a/packages/SystemUI/res/values-fr-rCA-ldrtl/strings.xml b/packages/SystemUI/res/values-fr-rCA-ldrtl/strings.xml
index 9421af3..cca3f20 100644
--- a/packages/SystemUI/res/values-fr-rCA-ldrtl/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA-ldrtl/strings.xml
@@ -20,4 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="recents_quick_scrub_onboarding" msgid="2452671841151577157">"Balayez l\'écran vers la gauche pour changer rapidement d\'appli"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="6543179539438220549">"Balayer l\'écran du haut vers la droite pour ouvrir les notifications"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="5760503283520058116">"Balayez l\'écran du haut vers la gauche pour ouvrir Paramètres rapides"</string>
</resources>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index 8a06f561..b6053e4 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -113,11 +113,11 @@
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Enregistrer une appli"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="4882406311415082016">"Enregistrer cet écran"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="4169494703993148253">"Enregistrer %s"</string>
- <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Lorsque vous enregistrez l\'intégralité de votre écran, tout ce qui s\'affiche sur votre écran est enregistré. Par conséquent, soyez prudent avec les mots de passe, les détails du mode de paiement, les messages, les photos et les contenus audio et vidéo."</string>
- <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Lorsque vous enregistrez une appli, tout ce qui est affiché ou lu dans cette appli est enregistré. Par conséquent, soyez prudent avec les mots de passe, les détails du mode de paiement, les messages, les photos et les contenus audio et vidéo."</string>
+ <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Lorsque vous enregistrez l\'intégralité de votre écran, tout ce qui s\'affiche sur votre écran est enregistré. Par conséquent, soyez prudent avec les mots de passe, les détails de paiement, les messages, les photos et les contenus audio et vidéo."</string>
+ <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Lorsque vous enregistrez une appli, tout ce qui est affiché ou lu dans cette appli est enregistré. Par conséquent, soyez prudent avec les mots de passe, les détails de paiement, les messages, les photos et les contenus audio et vidéo."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Enregistrer l\'écran"</string>
<string name="screenrecord_app_selector_title" msgid="3854492366333954736">"Choisir l\'appli à enregistrer"</string>
- <string name="screenrecord_audio_label" msgid="6183558856175159629">"Enregistrer des fichiers audio"</string>
+ <string name="screenrecord_audio_label" msgid="6183558856175159629">"Enregistrer le contenu audio"</string>
<string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Audio de l\'appareil"</string>
<string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Sons de l\'appareil comme la musique, les appels et les sonneries"</string>
<string name="screenrecord_mic_label" msgid="2111264835791332350">"Microphone"</string>
@@ -252,8 +252,7 @@
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Connecté à : <xliff:g id="BLUETOOTH">%s</xliff:g>"</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"Connecté à <xliff:g id="CAST">%s</xliff:g>."</string>
<string name="accessibility_expand_group" msgid="521237935987978624">"Développer le groupe."</string>
- <!-- no translation found for accessibility_collapse_group (1842704016768329517) -->
- <skip />
+ <string name="accessibility_collapse_group" msgid="1842704016768329517">"Réduire le groupe."</string>
<string name="accessibility_add_device_to_group" msgid="5446422960697860806">"Ajouter un appareil au groupe."</string>
<string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"Retirer l\'appareil du groupe."</string>
<string name="accessibility_open_application" msgid="1749126077501259712">"Ouvrir l\'appli."</string>
@@ -338,7 +337,7 @@
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Rotation automatique de l\'écran"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Localisation"</string>
<string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Écran de veille"</string>
- <string name="quick_settings_camera_label" msgid="5612076679385269339">"Accès à la caméra"</string>
+ <string name="quick_settings_camera_label" msgid="5612076679385269339">"Accès à l\'appareil photo"</string>
<string name="quick_settings_mic_label" msgid="8392773746295266375">"Accès au micro"</string>
<string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Accessible"</string>
<string name="quick_settings_camera_mic_blocked" msgid="4710884905006788281">"Bloqué"</string>
@@ -381,13 +380,13 @@
<string name="quick_settings_work_mode_label" msgid="6440531507319809121">"Applis professionnelles"</string>
<string name="quick_settings_work_mode_paused_state" msgid="6681788236383735976">"En pause"</string>
<string name="quick_settings_night_display_label" msgid="8180030659141778180">"Éclairage nocturne"</string>
- <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Activé la nuit"</string>
+ <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Activé le soir"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Jusqu\'à l\'aube"</string>
<string name="quick_settings_night_secondary_label_on_at" msgid="3584738542293528235">"Actif à <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_secondary_label_until" msgid="1883981263191927372">"Jusqu\'à <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_ui_mode_night_label" msgid="1398928270610780470">"Thème sombre"</string>
<string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"Économiseur de pile"</string>
- <string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"Activé la nuit"</string>
+ <string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"Activé le soir"</string>
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Jusqu\'à l\'aube"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Actif à <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Jusqu\'à <xliff:g id="TIME">%s</xliff:g>"</string>
@@ -468,6 +467,8 @@
<string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"Pour utiliser le bouton du microphone, activez l\'accès au microphone dans les paramètres."</string>
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Ouvrir les paramètres"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Autre appareil"</string>
+ <string name="media_suggestion_disconnected_text" msgid="8777932417076437525">"Faire jouer sur <xliff:g id="DEVICE_NAME">%s</xliff:g>"</string>
+ <string name="media_suggestion_failure_text" msgid="8453022394887613774">"Connexion impossible. Réessayez."</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Basculer l\'aperçu"</string>
<string name="zen_modes_dialog_title" msgid="8854640808100096934">"Modes"</string>
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"OK"</string>
@@ -583,6 +584,7 @@
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
<skip />
<string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Partager l\'intégralité de l\'écran"</string>
+ <string name="screen_share_permission_dialog_option_text_entire_screen_for_display" msgid="1545850238689169166">"Partager <xliff:g id="DISPLAY_NAME">%s</xliff:g>"</string>
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
<skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Lorsque vous partagez l\'intégralité de votre écran, tout ce qui s\'y trouve est visible par <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Par conséquent, soyez prudent avec les mots de passe, les détails du mode de paiement, les messages, les photos et les contenus audio et vidéo."</string>
@@ -594,8 +596,9 @@
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Diffuser votre écran?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Diffuser une appli"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Diffuser l\'intégralité de l\'écran"</string>
- <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Lorsque vous diffusez l\'intégralité de votre écran, tout ce qui s\'y trouve est visible. Par conséquent, soyez prudent avec les mots de passe, les détails du paiement, les messages, les photos et les contenus audio et vidéo."</string>
- <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Lorsque vous diffusez une appli, tout ce qui s\'y affiche ou s\'y joue est visible. Par conséquent, soyez prudent avec les mots de passe, les détails du mode de paiement, les messages, les photos et les contenus audio et vidéo."</string>
+ <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen_for_display" msgid="5524817761472941086">"Diffuser <xliff:g id="DISPLAY_NAME">%s</xliff:g>"</string>
+ <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Lorsque vous diffusez l\'intégralité de votre écran, tout ce qui s\'y trouve est visible. Par conséquent, soyez prudent avec des renseignements tels que les mots de passe, les détails de paiement, les messages, les photos et les contenus audio et vidéo."</string>
+ <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Lorsque vous diffusez une appli, tout ce qui s\'y affiche ou s\'y joue est visible. Par conséquent, soyez prudent avec des renseignements tels que les mots de passe, les détails de paiement, les messages, les photos et les contenus audio et vidéo."</string>
<string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Diffuser l\'écran"</string>
<string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"Choisir l\'appli à diffuser"</string>
<string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"Commencer à partager?"</string>
@@ -674,6 +677,7 @@
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Maintenu déverrouillé par TrustAgent"</string>
<string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"L\'appareil a été verrouillé : trop de tentatives d\'authentification"</string>
<string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"Appareil verrouillé\nÉchec de l\'authentification"</string>
+ <string name="keyguard_indication_after_watch_disconnected" msgid="6329761892437377710">"Verrouillé par votre montre"</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="accessibility_volume_settings" msgid="1458961116951564784">"Paramètres sonores"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Sous-titrer automatiquement"</string>
@@ -715,12 +719,12 @@
<string name="stream_accessibility" msgid="3873610336741987152">"Accessibilité"</string>
<string name="volume_ringer_status_normal" msgid="1339039682222461143">"Sonnerie"</string>
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Vibration"</string>
- <string name="volume_ringer_status_silent" msgid="3691324657849880883">"Sonnerie désactivée"</string>
+ <string name="volume_ringer_status_silent" msgid="3691324657849880883">"Silence"</string>
<string name="media_device_cast" msgid="4786241789687569892">"Diffuser"</string>
- <string name="stream_notification_unavailable" msgid="4313854556205836435">"Inaccessible : sonnerie en sourdine"</string>
+ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Inaccessible; sonnerie désactivée"</string>
<string name="stream_alarm_unavailable" msgid="4059817189292197839">"Inaccessible parce que Ne pas déranger est activée"</string>
<string name="stream_media_unavailable" msgid="6823020894438959853">"Inaccessible parce que Ne pas déranger est activée"</string>
- <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Non accessible, car le mode <xliff:g id="MODE">%s</xliff:g> est activé"</string>
+ <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Non accessible; le mode <xliff:g id="MODE">%s</xliff:g> est activé"</string>
<string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Non accessible"</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Touchez pour réactiver le son."</string>
<string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Touchez pour activer les vibrations. Il est possible de couper le son des services d\'accessibilité."</string>
@@ -758,7 +762,7 @@
<string name="status_bar_ethernet" msgid="5690979758988647484">"Ethernet"</string>
<string name="status_bar_alarm" msgid="87160847643623352">"Alarme"</string>
<string name="active_mode_content_description" msgid="1627555562186515927">"Le mode <xliff:g id="MODENAME">%1$s</xliff:g> est activé"</string>
- <string name="wallet_title" msgid="5369767670735827105">"Wallet"</string>
+ <string name="wallet_title" msgid="5369767670735827105">"Portefeuille"</string>
<string name="wallet_empty_state_label" msgid="7776761245237530394">"Préparez-vous à faire des achats plus rapidement et de façon plus sûre avec votre téléphone"</string>
<string name="wallet_app_button_label" msgid="7123784239111190992">"Tout afficher"</string>
<string name="wallet_secondary_label_no_card" msgid="8488069304491125713">"Touchez pour ouvrir"</string>
@@ -766,7 +770,7 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Déverrouiller pour utiliser"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Un problème est survenu lors de la récupération de vos cartes, veuillez réessayer plus tard"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Paramètres de l\'écran de verrouillage"</string>
- <string name="qr_code_scanner_title" msgid="1938155688725760702">"Lecteur de code QR"</string>
+ <string name="qr_code_scanner_title" msgid="1938155688725760702">"Lecteur code QR"</string>
<string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Mise à jour en cours…"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Profil professionnel"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Mode Avion"</string>
@@ -800,6 +804,10 @@
<string name="enable_bluetooth_message" msgid="6740938333772779717">"Pour connecter votre clavier à votre tablette, vous devez d\'abord activer la connectivité Bluetooth."</string>
<string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Activer"</string>
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Activé : en fonction du visage"</string>
+ <!-- no translation found for notification_guts_bundle_title (1345506995443305361) -->
+ <skip />
+ <!-- no translation found for notification_guts_bundle_summary (3971530802237393600) -->
+ <skip />
<string name="inline_done_button" msgid="6043094985588909584">"Terminé"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Appliquer"</string>
<string name="inline_turn_off_notifications" msgid="2653064779176881329">"Désactiver"</string>
@@ -985,6 +993,7 @@
<string name="right_icon" msgid="1103955040645237425">"Icône droite"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Sélectionnez et faites glisser les tuiles pour les ajouter"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Faites glisser les tuiles pour les réorganiser"</string>
+ <string name="select_to_rearrange_tiles" msgid="6166153534235179148">"Sélectionner les tuiles à réorganiser et à redimensionner"</string>
<string name="tap_to_position_tile" msgid="6282815817773342757">"Toucher pour positionner la tuile"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Faites glisser les tuiles ici pour les retirer"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Vous avez besoin d\'au moins <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> tuiles"</string>
@@ -1004,10 +1013,10 @@
<string name="tuner_low_priority" msgid="8412666814123009820">"Afficher les icônes de notification de faible priorité"</string>
<string name="other" msgid="429768510980739978">"Autre"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"activer ou désactiver la taille de la tuile"</string>
- <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"retirer la tuile"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="4811778796933958616">"Retirer la tuile"</string>
<string name="accessibility_qs_edit_toggle_placement_mode" msgid="3870429389210569610">"Activer/Désactiver le mode de placement"</string>
<string name="accessibility_qs_edit_toggle_selection" msgid="2201248304072372239">"Activer/Désactiver la sélection"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"Ajouter une tuile à la dernière position"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="7188303979684400620">"Ajouter une tuile à la dernière position"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Déplacer la tuile"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Ajouter une tuile à la position désirée"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Déplacer vers <xliff:g id="POSITION">%1$d</xliff:g>"</string>
@@ -1094,7 +1103,8 @@
<string name="auto_saver_text" msgid="3214960308353838764">"Activer si la pile est susceptible de s\'épuiser totalement"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Non merci"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"En cours d\'utilisation"</string>
- <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Des applis utilisent votre <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps (4994455315457996796) -->
+ <skip />
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" et "</string>
<string name="ongoing_privacy_dialog_using_op" msgid="426635338010011796">"En cours d\'utilisation par : <xliff:g id="APPLICATION_NAME">%1$s</xliff:g>"</string>
@@ -1171,7 +1181,7 @@
<string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# commande ajoutée.}one{# commande ajoutée.}many{# de commandes ajoutées.}other{# commandes ajoutées.}}"</string>
<string name="controls_removed" msgid="3731789252222856959">"Supprimé"</string>
<string name="controls_panel_authorization_title" msgid="267429338785864842">"Ajouter <xliff:g id="APPNAME">%s</xliff:g>?"</string>
- <string name="controls_panel_authorization" msgid="7045551688535104194">"<xliff:g id="APPNAME">%s</xliff:g> peut définir les commandes et le contenu qui s\'affiche ici."</string>
+ <string name="controls_panel_authorization" msgid="7045551688535104194">"<xliff:g id="APPNAME">%s</xliff:g> peut définir les commandes et le contenu qui s\'affichent ici."</string>
<string name="controls_panel_remove_app_authorization" msgid="5920442084735364674">"Retirer les commandes pour <xliff:g id="APPNAME">%s</xliff:g>?"</string>
<string name="accessibility_control_favorite" msgid="8694362691985545985">"Ajouté aux favoris"</string>
<string name="accessibility_control_favorite_position" msgid="54220258048929221">"Ajouté aux favoris, en position <xliff:g id="NUMBER">%d</xliff:g>"</string>
@@ -1251,15 +1261,13 @@
<string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(déconnecté)"</string>
<string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"Changement impossible. Touchez pour réessayer."</string>
<string name="media_output_dialog_pairing_new" msgid="5098212763195577270">"Connecter un appareil"</string>
- <!-- no translation found for media_output_dialog_button_connect_device (2819038239946643550) -->
- <skip />
+ <string name="media_output_dialog_button_connect_device" msgid="2819038239946643550">"Connecter l\'appareil"</string>
<string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Appli inconnue"</string>
<string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Arrêter la diffusion"</string>
<string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"Appareils disponibles pour la sortie audio."</string>
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Volume"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g> %%"</string>
- <!-- no translation found for media_output_group_title_connected_speakers (3413876972919684479) -->
- <skip />
+ <string name="media_output_group_title_connected_speakers" msgid="3413876972919684479">"Haut-parleurs connectés"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Haut-parleurs et écrans"</string>
<string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Appareils suggérés"</string>
<string name="media_input_group_title" msgid="2057057473860783021">"Entrée"</string>
@@ -1328,10 +1336,8 @@
<string name="accessibility_enter_hint" msgid="2617864063504824834">"accéder à l\'appareil"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Servez-vous de votre empreinte digitale pour ouvrir"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Authentification requise. Touchez le capteur d\'empreintes digitales pour vous authentifier."</string>
- <!-- no translation found for resume_animation (9148788411429330671) -->
- <skip />
- <!-- no translation found for pause_animation (1919705510058107504) -->
- <skip />
+ <string name="resume_animation" msgid="9148788411429330671">"Reprendre l\'animation"</string>
+ <string name="pause_animation" msgid="1919705510058107504">"Interrompre l\'animation"</string>
<string name="ongoing_call_content_description" msgid="6394763878322348560">"Appel en cours"</string>
<string name="ongoing_notification_extra_content_description" msgid="2098752668861351265">"En cours"</string>
<string name="mobile_data_settings_title" msgid="3955246641380064901">"Données cellulaires"</string>
@@ -1513,11 +1519,13 @@
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"Ajouter un raccourci"</string>
<string name="shortcut_helper_delete_shortcut_button_label" msgid="3148773472696137052">"Supprimer le raccourci"</string>
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Naviguer à l\'aide de votre clavier"</string>
- <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Apprenez à utiliser les raccourcis-clavier"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2131074100205028210) -->
+ <skip />
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Naviguer à l\'aide de votre pavé tactile"</string>
<string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Apprenez à utiliser les gestes du pavé tactile"</string>
<string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Naviguer à l\'aide de votre clavier et de votre pavé tactile"</string>
- <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Apprenez les gestes du pavé tactile, les raccourcis-clavier et bien plus encore"</string>
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (8557098239919786046) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Retour"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Retour à la page d\'accueil"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Afficher les applis récentes"</string>
@@ -1528,12 +1536,12 @@
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Retour"</string>
<string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Balayez votre pavé tactile vers la gauche ou vers la droite avec trois doigts"</string>
<string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Bien!"</string>
- <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Vous avez appris le geste de retour en arrière."</string>
+ <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Vous avez appris le geste de retour."</string>
<string name="touchpad_back_gesture_error_body" msgid="7112668207481458792">"Pour revenir en arrière à l\'aide de votre pavé tactile, balayez vers la gauche ou vers la droite avec trois doigts"</string>
<string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Retour à la page d\'accueil"</string>
<string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Balayez votre pavé tactile vers le haut avec trois doigts"</string>
<string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Bon travail!"</string>
- <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Vous avez appris le geste pour revenir à l\'écran d\'accueil"</string>
+ <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Vous avez appris le geste de retour à la page d\'accueil"</string>
<string name="touchpad_home_gesture_error_body" msgid="3810674109999513073">"Pour accéder à votre écran d\'accueil, balayez votre pavé tactile vers le haut avec trois doigts"</string>
<string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Afficher les applis récentes"</string>
<string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Balayez votre pavé tactile vers le haut avec trois doigts, puis maintenez-les en place"</string>
@@ -1554,7 +1562,7 @@
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Rétroéclairage du clavier"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Niveau %1$d de %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Domotique"</string>
- <string name="home_controls_dream_description" msgid="4644150952104035789">"Accès rapide : domotique sous forme d\'Écran de veille"</string>
+ <string name="home_controls_dream_description" msgid="4644150952104035789">"Accès domotique en écran de veille"</string>
<string name="volume_undo_action" msgid="5815519725211877114">"Annuler"</string>
<string name="back_edu_toast_content" msgid="4530314597378982956">"Pour revenir en arrière, balayez l\'écran vers la gauche ou vers la droite avec trois doigts sur le pavé tactile"</string>
<string name="home_edu_toast_content" msgid="3381071147871955415">"Pour accéder à l\'écran d\'accueil, balayez l\'écran vers le haut avec trois doigts sur le pavé tactile"</string>
@@ -1584,7 +1592,10 @@
<string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Affichage"</string>
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Inconnu"</string>
<string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Réinitialiser toutes les tuiles?"</string>
- <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Toutes les tuiles des paramètres rapides seront réinitialisées aux paramètres par défaut de l\'appareil."</string>
+ <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Toutes les tuiles des paramètres rapides seront réinitialisées aux paramètres par défaut de l\'appareil"</string>
<string name="demote_explain_text" msgid="3942301497888762295">"<xliff:g id="APPLICATION">%1$s</xliff:g> n\'affichera plus les mises à jour en direct. Vous pouvez modifier cela à tout moment dans les paramètres."</string>
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
+ <string name="underlay_close_button_content_description" msgid="6518481455548869894">"Fermer"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="7666159428113013319">"Balayer l\'écran du haut vers la gauche pour ouvrir les notifications"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="3842355853737057332">"Balayez l\'écran du haut vers la droite pour ouvrir Paramètres rapides"</string>
</resources>
diff --git a/packages/SystemUI/res/values-fr-rCA/tiles_states_strings.xml b/packages/SystemUI/res/values-fr-rCA/tiles_states_strings.xml
index 6b94691..9efeb3c 100644
--- a/packages/SystemUI/res/values-fr-rCA/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/tiles_states_strings.xml
@@ -193,7 +193,7 @@
</string-array>
<string-array name="tile_states_hearing_devices">
<item msgid="1235334096484287173">"Non accessible"</item>
- <item msgid="3079622119444911877">"Désactivé"</item>
+ <item msgid="3079622119444911877">"Désactivés"</item>
<item msgid="3028994095749238254">"Activé"</item>
</string-array>
<string-array name="tile_states_notes">
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index abe8ce2..22c3853 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -252,8 +252,7 @@
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Connecté à : <xliff:g id="BLUETOOTH">%s</xliff:g>"</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"Connecté à <xliff:g id="CAST">%s</xliff:g>."</string>
<string name="accessibility_expand_group" msgid="521237935987978624">"Développer le groupe."</string>
- <!-- no translation found for accessibility_collapse_group (1842704016768329517) -->
- <skip />
+ <string name="accessibility_collapse_group" msgid="1842704016768329517">"Réduire le groupe"</string>
<string name="accessibility_add_device_to_group" msgid="5446422960697860806">"Ajouter l\'appareil au groupe."</string>
<string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"Supprimer l\'appareil du groupe."</string>
<string name="accessibility_open_application" msgid="1749126077501259712">"Ouvrir l\'application."</string>
@@ -468,6 +467,8 @@
<string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"Pour utiliser le bouton du micro, activez l\'accès au micro dans les paramètres."</string>
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Ouvrir les paramètres"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Autre appareil"</string>
+ <string name="media_suggestion_disconnected_text" msgid="8777932417076437525">"Lire sur <xliff:g id="DEVICE_NAME">%s</xliff:g>"</string>
+ <string name="media_suggestion_failure_text" msgid="8453022394887613774">"Connexion impossible. Réessayez."</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Activer/Désactiver l\'écran Récents"</string>
<string name="zen_modes_dialog_title" msgid="8854640808100096934">"Modes"</string>
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"OK"</string>
@@ -583,6 +584,7 @@
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
<skip />
<string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Partager tout l\'écran"</string>
+ <string name="screen_share_permission_dialog_option_text_entire_screen_for_display" msgid="1545850238689169166">"Partager <xliff:g id="DISPLAY_NAME">%s</xliff:g>"</string>
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
<skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Lorsque vous partagez tout votre écran, l\'ensemble de son contenu est visible par <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Faites donc attention aux éléments tels que les mots de passe, les détails du mode de paiement, les messages, les photos et les contenus audio et vidéo."</string>
@@ -594,7 +596,8 @@
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Caster votre écran ?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Caster une appli"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Caster tout l\'écran"</string>
- <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Lorsque vous castez tout votre écran, l\'ensemble de son contenu est visible. Faites donc attention aux éléments tels que les mots de passe, détails de mode de paiement, messages, photos et contenus audio et vidéo."</string>
+ <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen_for_display" msgid="5524817761472941086">"Caster <xliff:g id="DISPLAY_NAME">%s</xliff:g>"</string>
+ <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Lorsque vous castez tout l\'écran, tout ce qui y est affiché est visible. Faites donc attention aux éléments tels que les mots de passe, détails de mode de paiement, messages, photos et contenus audio et vidéo."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Lorsque vous castez une appli, tout ce qui est affiché ou lu dans celle-ci est visible. Faites donc attention aux éléments tels que les mots de passe, détails de mode de paiement, messages, photos et contenus audio et vidéo."</string>
<string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Caster l\'écran"</string>
<string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"Choisir l\'appli à caster"</string>
@@ -674,6 +677,7 @@
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Maintenu déverrouillé par TrustAgent"</string>
<string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"L\'appareil a été verrouillé, trop de tentatives d\'authentification"</string>
<string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"Appareil verrouillé\nÉchec de l\'authentification"</string>
+ <string name="keyguard_indication_after_watch_disconnected" msgid="6329761892437377710">"Verrouillé par votre montre"</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="accessibility_volume_settings" msgid="1458961116951564784">"Paramètres audio"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Sous-titrer automatiquement"</string>
@@ -730,7 +734,7 @@
<string name="volume_panel_noise_control_title" msgid="7413949943872304474">"Contrôle du bruit"</string>
<string name="volume_panel_spatial_audio_title" msgid="3367048857932040660">"Son spatial"</string>
<string name="volume_panel_spatial_audio_off" msgid="4177490084606772989">"Désactivé"</string>
- <string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Activé"</string>
+ <string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Fixe"</string>
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Suivi de la tête"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Appuyez pour changer le mode de la sonnerie"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"mode de sonnerie"</string>
@@ -800,6 +804,10 @@
<string name="enable_bluetooth_message" msgid="6740938333772779717">"Pour connecter un clavier à votre tablette, vous devez avoir activé le Bluetooth."</string>
<string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Activer"</string>
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Active - En fonction du visage"</string>
+ <!-- no translation found for notification_guts_bundle_title (1345506995443305361) -->
+ <skip />
+ <!-- no translation found for notification_guts_bundle_summary (3971530802237393600) -->
+ <skip />
<string name="inline_done_button" msgid="6043094985588909584">"OK"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Appliquer"</string>
<string name="inline_turn_off_notifications" msgid="2653064779176881329">"Désactiver"</string>
@@ -985,6 +993,7 @@
<string name="right_icon" msgid="1103955040645237425">"Icône droite"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Faites glisser les blocs pour les ajouter"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Faites glisser les blocs pour les réorganiser"</string>
+ <string name="select_to_rearrange_tiles" msgid="6166153534235179148">"Sélectionnez les blocs à réorganiser et à redimensionner"</string>
<string name="tap_to_position_tile" msgid="6282815817773342757">"Appuyez pour positionner le bloc"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Faites glisser les blocs ici pour les supprimer"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Au minimum <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> tuiles sont nécessaires"</string>
@@ -1004,10 +1013,12 @@
<string name="tuner_low_priority" msgid="8412666814123009820">"Afficher les icônes de notification à faible priorité"</string>
<string name="other" msgid="429768510980739978">"Autre"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"activer/désactiver la taille du bloc"</string>
- <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"supprimer le bloc"</string>
+ <!-- no translation found for accessibility_qs_edit_remove_tile_action (4811778796933958616) -->
+ <skip />
<string name="accessibility_qs_edit_toggle_placement_mode" msgid="3870429389210569610">"activer/désactiver le mode de positionnement"</string>
<string name="accessibility_qs_edit_toggle_selection" msgid="2201248304072372239">"activer/désactiver la sélection"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"ajouter le bloc à la dernière position"</string>
+ <!-- no translation found for accessibility_qs_edit_tile_add_action (7188303979684400620) -->
+ <skip />
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Déplacer le bloc"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Ajouter le bloc à la position souhaitée"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Déplacer vers <xliff:g id="POSITION">%1$d</xliff:g>"</string>
@@ -1094,7 +1105,8 @@
<string name="auto_saver_text" msgid="3214960308353838764">"Activer l\'économiseur de batterie si l\'autonomie restante risque d\'être insuffisante"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Non, merci"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"En cours d\'utilisation"</string>
- <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Des applications utilisent votre <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps (4994455315457996796) -->
+ <skip />
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" et "</string>
<string name="ongoing_privacy_dialog_using_op" msgid="426635338010011796">"En cours d\'utilisation par <xliff:g id="APPLICATION_NAME">%1$s</xliff:g>"</string>
@@ -1251,15 +1263,13 @@
<string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(déconnecté)"</string>
<string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"Impossible de changer. Appuyez pour réessayer."</string>
<string name="media_output_dialog_pairing_new" msgid="5098212763195577270">"Connecter un appareil"</string>
- <!-- no translation found for media_output_dialog_button_connect_device (2819038239946643550) -->
- <skip />
+ <string name="media_output_dialog_button_connect_device" msgid="2819038239946643550">"Connecter l\'appareil"</string>
<string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Appli inconnue"</string>
<string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Arrêter la diffusion"</string>
<string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"Appareils disponibles pour la sortie audio."</string>
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Volume"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g> %%"</string>
- <!-- no translation found for media_output_group_title_connected_speakers (3413876972919684479) -->
- <skip />
+ <string name="media_output_group_title_connected_speakers" msgid="3413876972919684479">"Haut-parleurs connectés"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Enceintes et écrans"</string>
<string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Appareils suggérés"</string>
<string name="media_input_group_title" msgid="2057057473860783021">"Entrée"</string>
@@ -1320,7 +1330,7 @@
<string name="person_available" msgid="2318599327472755472">"Disponible"</string>
<string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Un problème est survenu au niveau de la lecture de votre outil de mesure de batterie"</string>
<string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Appuyer pour en savoir plus"</string>
- <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Pas d\'alarme définie"</string>
+ <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Aucune alarme"</string>
<string name="accessibility_bouncer" msgid="5896923685673320070">"accéder au verrouillage de l\'écran"</string>
<string name="accessibility_side_fingerprint_indicator_label" msgid="1673807833352363712">"Appuyez sur le lecteur d\'empreinte digitale. Il s\'agit du plus petit bouton sur le côté du téléphone."</string>
<string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Lecteur d\'empreinte digitale"</string>
@@ -1328,10 +1338,8 @@
<string name="accessibility_enter_hint" msgid="2617864063504824834">"accéder à l\'appareil"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Utilisez votre empreinte pour ouvrir"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Authentification requise. Appuyez sur le lecteur d\'empreintes digitales pour vous authentifier."</string>
- <!-- no translation found for resume_animation (9148788411429330671) -->
- <skip />
- <!-- no translation found for pause_animation (1919705510058107504) -->
- <skip />
+ <string name="resume_animation" msgid="9148788411429330671">"Reprendre l\'animation"</string>
+ <string name="pause_animation" msgid="1919705510058107504">"Mettre l\'animation en pause"</string>
<string name="ongoing_call_content_description" msgid="6394763878322348560">"Appel en cours"</string>
<string name="ongoing_notification_extra_content_description" msgid="2098752668861351265">"En cours"</string>
<string name="mobile_data_settings_title" msgid="3955246641380064901">"Données mobiles"</string>
@@ -1513,11 +1521,13 @@
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"Ajouter un raccourci"</string>
<string name="shortcut_helper_delete_shortcut_button_label" msgid="3148773472696137052">"Supprimer le raccourci"</string>
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Naviguer à l\'aide du clavier"</string>
- <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Apprenez à utiliser les raccourcis clavier"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2131074100205028210) -->
+ <skip />
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Naviguer à l\'aide de votre pavé tactile"</string>
<string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Découvrez les gestes au pavé tactile"</string>
<string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Naviguer à l\'aide de votre clavier et de votre pavé tactile"</string>
- <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Découvrir les gestes du pavé tactile, les raccourcis clavier et plus encore"</string>
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (8557098239919786046) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Retour"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Retour à l\'accueil"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Afficher les applis récentes"</string>
@@ -1528,12 +1538,12 @@
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Retour"</string>
<string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Balayez vers la gauche ou la droite avec trois doigts sur le pavé tactile"</string>
<string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Bravo !"</string>
- <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Vous avez appris le geste pour revenir en arrière"</string>
+ <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Vous connaissez maintenant le geste pour revenir en arrière"</string>
<string name="touchpad_back_gesture_error_body" msgid="7112668207481458792">"Pour revenir en arrière à l\'aide du pavé tactile, balayez vers la gauche ou la droite avec trois doigts"</string>
<string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Retour à l\'accueil"</string>
<string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Balayez vers le haut avec trois doigts sur le pavé tactile"</string>
<string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Bravo !"</string>
- <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Vous avez appris le geste pour revenir à l\'écran d\'accueil"</string>
+ <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Vous connaissez maintenant le geste pour revenir à l\'écran d\'accueil"</string>
<string name="touchpad_home_gesture_error_body" msgid="3810674109999513073">"Balayez vers le haut avec trois doigts sur le pavé tactile pour accéder à l\'écran d\'accueil"</string>
<string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Afficher les applis récentes"</string>
<string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Avec trois doigts, balayez le pavé tactile vers le haut et maintenez la position"</string>
@@ -1543,7 +1553,7 @@
<string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Passer d\'une application à l\'autre"</string>
<string name="touchpad_switch_apps_gesture_guidance" msgid="2554844933805502538">"Balayez vers la droite avec quatre doigts sur le pavé tactile"</string>
<string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Bravo !"</string>
- <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Vous avez appris le geste pour passer d\'une appli à l\'autre."</string>
+ <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Vous connaissez maintenant le geste pour passer d\'une appli à l\'autre."</string>
<string name="touchpad_switch_gesture_error_body" msgid="5895231916964677918">"Balayez vers la droite avec quatre doigts sur le pavé tactile pour passer d\'une appli à une autre"</string>
<string name="tutorial_action_key_title" msgid="8172535792469008169">"Afficher toutes les applications"</string>
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Appuyez sur la touche d\'action de votre clavier"</string>
@@ -1553,8 +1563,8 @@
<string name="tutorial_animation_content_description" msgid="2698816574982370184">"Animation du tutoriel, cliquez pour mettre en pause et reprendre la lecture."</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Rétroéclairage du clavier"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Niveau %1$d sur %2$d"</string>
- <string name="home_controls_dream_label" msgid="6567105701292324257">"Contrôle de la maison"</string>
- <string name="home_controls_dream_description" msgid="4644150952104035789">"Domotique sous forme d\'économiseur d\'écran"</string>
+ <string name="home_controls_dream_label" msgid="6567105701292324257">"Contrôle maison"</string>
+ <string name="home_controls_dream_description" msgid="4644150952104035789">"Accès rapide aux commandes de la maison"</string>
<string name="volume_undo_action" msgid="5815519725211877114">"Annuler"</string>
<string name="back_edu_toast_content" msgid="4530314597378982956">"Pour retourner en arrière, balayez vers la gauche ou la droite avec trois doigts sur le pavé tactile"</string>
<string name="home_edu_toast_content" msgid="3381071147871955415">"Pour revenir à l\'accueil, balayez vers le haut avec trois doigts sur le pavé tactile"</string>
@@ -1587,4 +1597,7 @@
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Tous les blocs \"Réglages rapides\" seront réinitialisés aux paramètres d\'origine de l\'appareil"</string>
<string name="demote_explain_text" msgid="3942301497888762295">"<xliff:g id="APPLICATION">%1$s</xliff:g> n\'affichera plus les infos en direct. Vous pouvez modifier cela à tout moment dans les paramètres."</string>
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
+ <string name="underlay_close_button_content_description" msgid="6518481455548869894">"Fermer"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="7666159428113013319">"Balayez l\'écran depuis le coin supérieur gauche pour ouvrir les notifications"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="3842355853737057332">"Balayez l\'écran depuis le coin supérieur droit pour ouvrir les réglages rapides"</string>
</resources>
diff --git a/packages/SystemUI/res/values-fr/tiles_states_strings.xml b/packages/SystemUI/res/values-fr/tiles_states_strings.xml
index ea91d2f..7258b49 100644
--- a/packages/SystemUI/res/values-fr/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-fr/tiles_states_strings.xml
@@ -68,7 +68,7 @@
</string-array>
<string-array name="tile_states_rotation">
<item msgid="4578491772376121579">"Indisponible"</item>
- <item msgid="5776427577477729185">"Désactivée"</item>
+ <item msgid="5776427577477729185">"Désactivé"</item>
<item msgid="7105052717007227415">"Activé"</item>
</string-array>
<string-array name="tile_states_bt">
diff --git a/packages/SystemUI/res/values-gl-ldrtl/strings.xml b/packages/SystemUI/res/values-gl-ldrtl/strings.xml
index 593bb09..b8d7666 100644
--- a/packages/SystemUI/res/values-gl-ldrtl/strings.xml
+++ b/packages/SystemUI/res/values-gl-ldrtl/strings.xml
@@ -20,4 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="recents_quick_scrub_onboarding" msgid="2452671841151577157">"Para cambiar de aplicación, arrastra o dedo rapidamente á esquerda"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="6543179539438220549">"Para abrir as notificacións, pasa o dedo desde a parte superior dereita"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="5760503283520058116">"Para abrir Configuración rápida, pasa o dedo desde a parte superior esquerda"</string>
</resources>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index 2807831..b4c3c8a 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -252,8 +252,7 @@
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Conectado a <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"Dispositivo conectado: <xliff:g id="CAST">%s</xliff:g>."</string>
<string name="accessibility_expand_group" msgid="521237935987978624">"Despregar o grupo."</string>
- <!-- no translation found for accessibility_collapse_group (1842704016768329517) -->
- <skip />
+ <string name="accessibility_collapse_group" msgid="1842704016768329517">"Contraer o grupo."</string>
<string name="accessibility_add_device_to_group" msgid="5446422960697860806">"Engadir o dispositivo ao grupo."</string>
<string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"Quitar o dispositivo do grupo."</string>
<string name="accessibility_open_application" msgid="1749126077501259712">"Abrir a aplicación."</string>
@@ -468,6 +467,8 @@
<string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"Para usar o botón do micrófono, activa o acceso ao micrófono en Configuración."</string>
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Abrir Configuración"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Outro dispositivo"</string>
+ <string name="media_suggestion_disconnected_text" msgid="8777932417076437525">"Reproducir en <xliff:g id="DEVICE_NAME">%s</xliff:g>"</string>
+ <string name="media_suggestion_failure_text" msgid="8453022394887613774">"Sen conexión. Téntao de novo."</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Activar/desactivar Visión xeral"</string>
<string name="zen_modes_dialog_title" msgid="8854640808100096934">"Modos"</string>
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Feito"</string>
@@ -583,6 +584,7 @@
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
<skip />
<string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Compartir toda a pantalla"</string>
+ <string name="screen_share_permission_dialog_option_text_entire_screen_for_display" msgid="1545850238689169166">"Compartir <xliff:g id="DISPLAY_NAME">%s</xliff:g>"</string>
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
<skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Se compartes toda a pantalla, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> poderá ver todo o contido que apareza nela. Ten coidado con determinada información, como os contrasinais, os detalles de pago, as mensaxes e as fotos, así como co contido de audio e de vídeo."</string>
@@ -594,6 +596,7 @@
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Queres emitir a túa pantalla?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Emitir unha aplicación"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Emitir pantalla completa"</string>
+ <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen_for_display" msgid="5524817761472941086">"Emitir <xliff:g id="DISPLAY_NAME">%s</xliff:g>"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Cando emites a pantalla enteira, pódese ver todo o que haxa nela. Xa que logo, debes ter coidado con determinada información, como os contrasinais, os detalles de pago, as mensaxes e as fotos, así como co contido de audio e de vídeo."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Cando emites unha aplicación, pódese ver todo o que se mostre ou reproduza nela. Xa que logo, debes ter coidado con determinada información, como os contrasinais, os detalles de pago, as mensaxes e as fotos, así como co contido de audio e de vídeo."</string>
<string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Emitir pantalla"</string>
@@ -674,6 +677,7 @@
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Desbloqueado por un axente de confianza"</string>
<string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"Bloqueouse o dispositivo por un exceso de intentos de autenticación"</string>
<string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"Bloqueouse o dispositivo\nProduciuse un erro na autenticación"</string>
+ <string name="keyguard_indication_after_watch_disconnected" msgid="6329761892437377710">"Dispositivo bloqueado polo reloxo"</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="accessibility_volume_settings" msgid="1458961116951564784">"Configuración do son"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Crear subtítulos automáticos"</string>
@@ -720,7 +724,7 @@
<string name="stream_notification_unavailable" msgid="4313854556205836435">"Non dispoñible: o son está silenciado"</string>
<string name="stream_alarm_unavailable" msgid="4059817189292197839">"Non dispoñible: Non molestar está activado"</string>
<string name="stream_media_unavailable" msgid="6823020894438959853">"Non dispoñible: Non molestar está activado"</string>
- <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Non está dispoñible porque se activou o modo <xliff:g id="MODE">%s</xliff:g>"</string>
+ <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Non dispoñible porque se activou o modo <xliff:g id="MODE">%s</xliff:g>"</string>
<string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Non dispoñible"</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Toca para activar o son."</string>
<string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Toca para establecer a vibración. Pódense silenciar os servizos de accesibilidade."</string>
@@ -800,6 +804,10 @@
<string name="enable_bluetooth_message" msgid="6740938333772779717">"Para conectar o teu teclado coa tableta, primeiro tes que activar o Bluetooth."</string>
<string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Activar"</string>
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Activada: baseada na cara"</string>
+ <!-- no translation found for notification_guts_bundle_title (1345506995443305361) -->
+ <skip />
+ <!-- no translation found for notification_guts_bundle_summary (3971530802237393600) -->
+ <skip />
<string name="inline_done_button" msgid="6043094985588909584">"Feito"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Aplicar"</string>
<string name="inline_turn_off_notifications" msgid="2653064779176881329">"Desactivar"</string>
@@ -985,6 +993,7 @@
<string name="right_icon" msgid="1103955040645237425">"Icona á dereita"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Mantén premido e arrastra para engadir atallos"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Para reorganizar os atallos, mantenos premidos e arrástraos"</string>
+ <string name="select_to_rearrange_tiles" msgid="6166153534235179148">"Selecciona os atallos para reordenalos e cambiar o tamaño"</string>
<string name="tap_to_position_tile" msgid="6282815817773342757">"Tocar para colocar os atallos"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Arrastra o elemento ata aquí para quitalo"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Como mínimo ten que haber <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> mosaicos"</string>
@@ -1004,10 +1013,12 @@
<string name="tuner_low_priority" msgid="8412666814123009820">"Mostrar iconas das notificacións que teñan baixa prioridade"</string>
<string name="other" msgid="429768510980739978">"Outros"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"cambiar o tamaño do atallo"</string>
- <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"quitar atallo"</string>
+ <!-- no translation found for accessibility_qs_edit_remove_tile_action (4811778796933958616) -->
+ <skip />
<string name="accessibility_qs_edit_toggle_placement_mode" msgid="3870429389210569610">"activar ou desactivar o modo de posición"</string>
<string name="accessibility_qs_edit_toggle_selection" msgid="2201248304072372239">"activar ou desactivar a selección"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"engadir o atallo á última posición"</string>
+ <!-- no translation found for accessibility_qs_edit_tile_add_action (7188303979684400620) -->
+ <skip />
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Mover atallo"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Engadir o atallo á última posición"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Mover a <xliff:g id="POSITION">%1$d</xliff:g>"</string>
@@ -1015,8 +1026,8 @@
<string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Posición non válida."</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Posición <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_tile_already_added" msgid="5900071201690226752">"Atallo xa engadido"</string>
- <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Engadiuse a tarxeta"</string>
- <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Quitouse a tarxeta"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Engadiuse o atallo"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Quitouse o atallo"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor de configuración rápida."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Notificación de <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">"Abrir configuración."</string>
@@ -1094,7 +1105,8 @@
<string name="auto_saver_text" msgid="3214960308353838764">"Activa a función se prevés que a batería pode esgotarse"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Non, grazas"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"En uso"</string>
- <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Hai aplicacións que están utilizando <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps (4994455315457996796) -->
+ <skip />
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" e "</string>
<string name="ongoing_privacy_dialog_using_op" msgid="426635338010011796">"En uso por: <xliff:g id="APPLICATION_NAME">%1$s</xliff:g>"</string>
@@ -1251,15 +1263,13 @@
<string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(desconectado)"</string>
<string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"Non se puido realizar o cambio. Toca para tentalo de novo."</string>
<string name="media_output_dialog_pairing_new" msgid="5098212763195577270">"Conectar un dispositivo"</string>
- <!-- no translation found for media_output_dialog_button_connect_device (2819038239946643550) -->
- <skip />
+ <string name="media_output_dialog_button_connect_device" msgid="2819038239946643550">"Conectar dispositivo"</string>
<string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Aplicación descoñecida"</string>
<string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Deter emisión"</string>
<string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"Dispositivos dispoñibles para a saída de audio."</string>
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Volume"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g> %%"</string>
- <!-- no translation found for media_output_group_title_connected_speakers (3413876972919684479) -->
- <skip />
+ <string name="media_output_group_title_connected_speakers" msgid="3413876972919684479">"Altofalantes conectados"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Altofalantes e pantallas"</string>
<string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Dispositivos suxeridos"</string>
<string name="media_input_group_title" msgid="2057057473860783021">"Entrada"</string>
@@ -1328,10 +1338,8 @@
<string name="accessibility_enter_hint" msgid="2617864063504824834">"poñer o dispositivo"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Usa a impresión dixital para abrir"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Requírese autenticación. Para autenticarte, toca o sensor de impresión dixital."</string>
- <!-- no translation found for resume_animation (9148788411429330671) -->
- <skip />
- <!-- no translation found for pause_animation (1919705510058107504) -->
- <skip />
+ <string name="resume_animation" msgid="9148788411429330671">"Retomar a animación"</string>
+ <string name="pause_animation" msgid="1919705510058107504">"Pór a animación en pausa"</string>
<string name="ongoing_call_content_description" msgid="6394763878322348560">"Chamada en curso"</string>
<string name="ongoing_notification_extra_content_description" msgid="2098752668861351265">"En curso"</string>
<string name="mobile_data_settings_title" msgid="3955246641380064901">"Datos móbiles"</string>
@@ -1513,11 +1521,13 @@
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"Engadir un atallo"</string>
<string name="shortcut_helper_delete_shortcut_button_label" msgid="3148773472696137052">"Eliminar un atallo"</string>
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navega co teclado"</string>
- <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Aprende a usar os atallos de teclado"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2131074100205028210) -->
+ <skip />
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navega co panel táctil"</string>
<string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Aprende a usar os xestos do panel táctil"</string>
<string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Navega co teclado e o panel táctil"</string>
- <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Aprende a usar os xestos do panel táctil, atallos de teclado e moito máis"</string>
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (8557098239919786046) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Volver"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Ir ao inicio"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Consultar aplicacións recentes"</string>
@@ -1587,4 +1597,7 @@
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Restablecerase a configuración orixinal do dispositivo para todos os atallos de Configuración rápida"</string>
<string name="demote_explain_text" msgid="3942301497888762295">"<xliff:g id="APPLICATION">%1$s</xliff:g> deixará de mostrar novas en tempo real. Podes cambiar esta opción en calquera momento na configuración."</string>
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
+ <string name="underlay_close_button_content_description" msgid="6518481455548869894">"Pechar"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="7666159428113013319">"Para abrir as notificacións, pasa o dedo desde a parte superior esquerda"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="3842355853737057332">"Para abrir Configuración rápida, pasa o dedo desde a parte superior dereita"</string>
</resources>
diff --git a/packages/SystemUI/res/values-gu-ldrtl/strings.xml b/packages/SystemUI/res/values-gu-ldrtl/strings.xml
index af07568..6fe8360 100644
--- a/packages/SystemUI/res/values-gu-ldrtl/strings.xml
+++ b/packages/SystemUI/res/values-gu-ldrtl/strings.xml
@@ -20,4 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="recents_quick_scrub_onboarding" msgid="2452671841151577157">"ઍપને ઝડપથી સ્વિચ કરવા માટે ડાબે ખેંચો"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="6543179539438220549">"નોટિફિકેશન ખોલવા માટે સૌથી ઉપર જમણી બાજુએથી નીચે સ્વાઇપ કરો"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="5760503283520058116">"ઝડપી સેટિંગ ખોલવા માટે સૌથી ઉપર ડાબી બાજુએથી નીચે સ્વાઇપ કરો"</string>
</resources>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index f769ebe..d6af1c2 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -234,7 +234,7 @@
<string name="face_re_enroll_dialog_title" msgid="6392173708176069994">"ફેસ અનલૉક સુવિધાનું સેટઅપ કરો"</string>
<string name="face_re_enroll_dialog_content" msgid="7353502359464038511">"ફેસ અનલૉક સુવિધાનું ફરી સેટઅપ કરવા માટે, તમારા ચહેરાનું વર્તમાન મૉડલ ડિલીટ કરવામાં આવશે.\n\nતમારો ફોન અનલૉક કરવા તમારા ચહેરાનો ઉપયોગ કરવા માટે, તમારે આ સુવિધાનું ફરી સેટઅપ કરવું જરૂરી રહેશે."</string>
<string name="face_reenroll_failure_dialog_content" msgid="7073947334397236935">"ફેસ અનલૉક સુવિધાનું સેટઅપ કરી શક્યા નથી. ફરી પ્રયાસ કરવા માટે સેટિંગ પર જાઓ."</string>
- <string name="fingerprint_dialog_touch_sensor" msgid="2817887108047658975">"ફિંગરપ્રિન્ટના સેન્સરને સ્પર્શ કરો"</string>
+ <string name="fingerprint_dialog_touch_sensor" msgid="2817887108047658975">"ફિંગરપ્રિન્ટના સેન્સરને ટચ કરો"</string>
<string name="fingerprint_dialog_authenticated_confirmation" msgid="1603899612957562862">"ચાલુ રાખવા \'અનલૉક કરો\' આઇકન દબાવો"</string>
<string name="fingerprint_dialog_use_fingerprint_instead" msgid="5542430577183894219">"ચહેરો ઓળખાયો નથી. તેને બદલે ફિંગરપ્રિન્ટ વાપરો."</string>
<!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) -->
@@ -252,8 +252,7 @@
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g> થી કનેક્ટ થયાં."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"<xliff:g id="CAST">%s</xliff:g> થી કનેક્ટ કરેલ."</string>
<string name="accessibility_expand_group" msgid="521237935987978624">"ગ્રૂપને મોટું કરો."</string>
- <!-- no translation found for accessibility_collapse_group (1842704016768329517) -->
- <skip />
+ <string name="accessibility_collapse_group" msgid="1842704016768329517">"ગ્રૂપ નાનું કરો."</string>
<string name="accessibility_add_device_to_group" msgid="5446422960697860806">"ડિવાઇસને ગ્રૂપમાં ઉમેરો."</string>
<string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"ડિવાઇસને ગ્રૂપમાંથી કાઢી નાખો."</string>
<string name="accessibility_open_application" msgid="1749126077501259712">"ઍપ્લિકેશન ખોલો."</string>
@@ -387,7 +386,7 @@
<string name="quick_settings_secondary_label_until" msgid="1883981263191927372">"<xliff:g id="TIME">%s</xliff:g> સુધી"</string>
<string name="quick_settings_ui_mode_night_label" msgid="1398928270610780470">"ડાર્ક થીમ"</string>
<string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"બૅટરી સેવર"</string>
- <string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"સૂર્યાસ્ત વખતે"</string>
+ <string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"સૂર્યાસ્ત વખતે ચાલુ"</string>
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"સૂર્યોદય સુધી"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"<xliff:g id="TIME">%s</xliff:g> વાગ્યે ચાલુ"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g> વાગ્યા સુધી"</string>
@@ -399,7 +398,7 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"સ્ક્રીન રેકોર્ડ"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"શરૂ કરો"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"રોકો"</string>
- <string name="qs_record_issue_label" msgid="8166290137285529059">"રેકોર્ડિંગમાં સમસ્યા"</string>
+ <string name="qs_record_issue_label" msgid="8166290137285529059">"સમસ્યા રેકોર્ડ કરો"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"શરૂ કરો"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"રોકો"</string>
<string name="qs_record_issue_bug_report" msgid="8229031766918650079">"બગ રિપોર્ટ"</string>
@@ -468,6 +467,8 @@
<string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"માઇક્રોફોન બટનનો ઉપયોગ કરવા માટે, સેટિંગમાં માઇક્રોફોનનો ઍક્સેસ ચાલુ કરો."</string>
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"સેટિંગ ખોલો"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"અન્ય ડિવાઇસ"</string>
+ <string name="media_suggestion_disconnected_text" msgid="8777932417076437525">"<xliff:g id="DEVICE_NAME">%s</xliff:g> પર ચલાવો"</string>
+ <string name="media_suggestion_failure_text" msgid="8453022394887613774">"કનેક્ટ ન થાય. ફરી પ્રયાસ કરો."</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"ઝલકને ટૉગલ કરો"</string>
<string name="zen_modes_dialog_title" msgid="8854640808100096934">"મોડ"</string>
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"થઈ ગયું"</string>
@@ -583,6 +584,7 @@
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
<skip />
<string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"સંપૂર્ણ સ્ક્રીન શેર કરો"</string>
+ <string name="screen_share_permission_dialog_option_text_entire_screen_for_display" msgid="1545850238689169166">"<xliff:g id="DISPLAY_NAME">%s</xliff:g> શેર કરો"</string>
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
<skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"જ્યારે તમે તમારી સંપૂર્ણ સ્ક્રીનને શેર કરી રહ્યાં હો, ત્યારે તમારી સ્ક્રીન પરની કોઈપણ વસ્તુ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>ને દેખાય છે. તેથી પાસવર્ડ, ચુકવણીની વિગતો, મેસેજ, ફોટા અને ડિવાઇસ પર વાગી રહેલા ઑડિયો તથા વીડિયો જેવી બાબતોને લઈને સાવચેત રહો."</string>
@@ -594,6 +596,7 @@
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"તમારી સ્ક્રીનને કાસ્ટ કરીએ?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"એક ઍપને કાસ્ટ કરો"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"સંપૂર્ણ સ્ક્રીનને કાસ્ટ કરો"</string>
+ <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen_for_display" msgid="5524817761472941086">"<xliff:g id="DISPLAY_NAME">%s</xliff:g> કાસ્ટ કરો"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"જ્યારે તમે તમારી સંપૂર્ણ સ્ક્રીનને કાસ્ટ કરી રહ્યાં હો, ત્યારે તમારી સ્ક્રીન પરની કોઈપણ વસ્તુ દેખાય છે. તેથી પાસવર્ડ, ચુકવણીની વિગતો, મેસેજ, ફોટા અને ડિવાઇસ પર વાગી રહેલા ઑડિયો તથા વીડિયો જેવી બાબતોને લઈને સાવચેત રહો."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"જ્યારે તમે ઍપને કાસ્ટ કરી રહ્યાં હો, ત્યારે તે ઍપ પર બતાવવામાં કે ચલાવવામાં આવતી હોય તેવી બધી વસ્તુ દેખાય છે. તેથી પાસવર્ડ, ચુકવણીની વિગતો, મેસેજ, ફોટા અને ડિવાઇસ પર વાગી રહેલા ઑડિયો તથા વીડિયો જેવી બાબતોને લઈને સાવચેત રહો."</string>
<string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"સ્ક્રીનને કાસ્ટ કરો"</string>
@@ -674,6 +677,7 @@
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"TrustAgent દ્વારા અનલૉક રાખેલું"</string>
<string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"પ્રમાણીકરણના ઘણા પ્રયાસો કરવામાં આવ્યા, તેથી ડિવાઇસ લૉક કરવામાં આવ્યું"</string>
<string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"ડિવાઇસ લૉક કર્યું\nપ્રમાણીકરણ નિષ્ફળ થયું છે"</string>
+ <string name="keyguard_indication_after_watch_disconnected" msgid="6329761892437377710">"તમારી વૉચ દ્વારા લૉક કરાયું છે"</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="accessibility_volume_settings" msgid="1458961116951564784">"સાઉન્ડ સેટિંગ"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"મીડિયામાં કૅપ્શન ઑટોમૅટિક રીતે ઉમેરો"</string>
@@ -730,7 +734,7 @@
<string name="volume_panel_noise_control_title" msgid="7413949943872304474">"અવાજનું નિયંત્રણ"</string>
<string name="volume_panel_spatial_audio_title" msgid="3367048857932040660">"સ્પેશલ ઑડિયો"</string>
<string name="volume_panel_spatial_audio_off" msgid="4177490084606772989">"બંધ"</string>
- <string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"ફિક્સ્ડ"</string>
+ <string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"ફિક્સ કરેલું"</string>
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"હૅડ ટ્રૅકિંગ"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"રિંગર મોડ બદલવા માટે ટૅપ કરો"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"રિંગર મોડ"</string>
@@ -800,6 +804,10 @@
<string name="enable_bluetooth_message" msgid="6740938333772779717">"તમારા ટેબ્લેટ સાથે કીબોર્ડ કનેક્ટ કરવા માટે, તમારે પહેલાં બ્લૂટૂથ ચાલુ કરવાની જરૂર પડશે."</string>
<string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"ચાલુ કરો"</string>
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"ચાલુ છે - ચહેરા આધારિત રોટેશન"</string>
+ <!-- no translation found for notification_guts_bundle_title (1345506995443305361) -->
+ <skip />
+ <!-- no translation found for notification_guts_bundle_summary (3971530802237393600) -->
+ <skip />
<string name="inline_done_button" msgid="6043094985588909584">"થઈ ગયું"</string>
<string name="inline_ok_button" msgid="603075490581280343">"લાગુ કરો"</string>
<string name="inline_turn_off_notifications" msgid="2653064779176881329">"બંધ કરો"</string>
@@ -907,8 +915,7 @@
<string name="group_system_cycle_back" msgid="8194102916946802902">"તાજેતરની ઍપ પર પાછળ જાઓ"</string>
<string name="group_system_access_all_apps_search" msgid="1553588630154197469">"ઍપની સૂચિ ખોલો"</string>
<string name="group_system_access_system_settings" msgid="8731721963449070017">"સેટિંગ ખોલો"</string>
- <!-- no translation found for group_system_access_google_assistant (3769929488906534254) -->
- <skip />
+ <string name="group_system_access_google_assistant" msgid="3769929488906534254">"ડિજિટલ આસિસ્ટંટ ખોલો"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"લૉક સ્ક્રીન"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"નોંધ લો"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"એકસાથે એકથી વધુ કાર્યો કરવા"</string>
@@ -986,11 +993,12 @@
<string name="right_icon" msgid="1103955040645237425">"જમણું આઇકન"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"ટાઇલ ઉમેરવા માટે તેના પર આંગળી દબાવીને ખેંચો"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"ટાઇલને ફરીથી ગોઠવવા માટે આંગળી દબાવીને ખેંચો"</string>
+ <string name="select_to_rearrange_tiles" msgid="6166153534235179148">"ફરીથી ગોઠવવા અને કદ બદલવા માટે ટાઇલ પસંદ કરો"</string>
<string name="tap_to_position_tile" msgid="6282815817773342757">"શીર્ષકના સ્થિતિ નિર્ધારણ માટે ટૅપ કરો"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"દૂર કરવા માટે અહીં ખેંચો"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"તમને ઓછામાં ઓછી <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> ટાઇલની જરૂર છે"</string>
<string name="qs_edit" msgid="5583565172803472437">"ફેરફાર કરો"</string>
- <string name="qs_edit_tiles" msgid="2105215324060865035">"શીર્ષકોમાં ફેરફાર કરો"</string>
+ <string name="qs_edit_tiles" msgid="2105215324060865035">"ટાઇલમાં ફેરફાર કરો"</string>
<string name="tuner_time" msgid="2450785840990529997">"સમય"</string>
<string-array name="clock_options">
<item msgid="3986445361435142273">"કલાક, મિનિટ અને સેકન્ડ બતાવો"</item>
@@ -1005,10 +1013,12 @@
<string name="tuner_low_priority" msgid="8412666814123009820">"ઓછી પ્રાધાન્યતાનું નોટિફિકેશન આઇકન બતાવો"</string>
<string name="other" msgid="429768510980739978">"અન્ય"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"ટાઇલના કદને ટૉગલ કરો"</string>
- <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"ટાઇલ કાઢી નાખો"</string>
+ <!-- no translation found for accessibility_qs_edit_remove_tile_action (4811778796933958616) -->
+ <skip />
<string name="accessibility_qs_edit_toggle_placement_mode" msgid="3870429389210569610">"પ્લેસમેન્ટ મોડ ટૉગલ કરો"</string>
<string name="accessibility_qs_edit_toggle_selection" msgid="2201248304072372239">"પસંદગી ટૉગલ કરો"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"છેલ્લા સ્થાનમાં ટાઇલ ઉમેરો"</string>
+ <!-- no translation found for accessibility_qs_edit_tile_add_action (7188303979684400620) -->
+ <skip />
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"ટાઇલ ખસેડો"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"ઇચ્છિત સ્થાનમાં ટાઇલ ઉમેરો"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> પર ખસેડો"</string>
@@ -1095,7 +1105,8 @@
<string name="auto_saver_text" msgid="3214960308353838764">"જ્યારે બૅટરી સંભવિત રૂપે પૂરી થવામાં હોય ત્યારે બૅટરી સેવર ચાલુ કરો"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"ના, આભાર"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"ઉપયોગમાં છે"</string>
- <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"ઍપ્લિકેશન તમારા <xliff:g id="TYPES_LIST">%s</xliff:g>નો ઉપયોગ કરી રહી છે."</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps (4994455315457996796) -->
+ <skip />
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" અને "</string>
<string name="ongoing_privacy_dialog_using_op" msgid="426635338010011796">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> દ્વારા ઉપયોગ થઈ રહ્યો છે"</string>
@@ -1167,7 +1178,7 @@
<string name="accessibility_floating_button_action_remove_menu" msgid="6730432848162552135">"કાઢી નાખો"</string>
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"ટૉગલ કરો"</string>
<string name="accessibility_floating_button_action_edit" msgid="1688227814600463987">"ફેરફાર કરો"</string>
- <string name="quick_controls_title" msgid="6839108006171302273">"ડિવાઇસનાં નિયંત્રણો"</string>
+ <string name="quick_controls_title" msgid="6839108006171302273">"ડિવાઇસ નિયંત્રણો"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"નિયંત્રણો ઉમેરવા માટે ઍપ પસંદ કરો"</string>
<string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# નિયંત્રણ ઉમેર્યું.}one{# નિયંત્રણ ઉમેર્યું.}other{# નિયંત્રણ ઉમેર્યા.}}"</string>
<string name="controls_removed" msgid="3731789252222856959">"કાઢી નાખ્યું"</string>
@@ -1252,15 +1263,13 @@
<string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(ડિસ્કનેક્ટ કરેલું)"</string>
<string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"સ્વિચ કરી શકતા નથી. ફરી પ્રયાસ કરવા માટે ટૅપ કરો."</string>
<string name="media_output_dialog_pairing_new" msgid="5098212763195577270">"ડિવાઇસ કનેક્ટ કરો"</string>
- <!-- no translation found for media_output_dialog_button_connect_device (2819038239946643550) -->
- <skip />
+ <string name="media_output_dialog_button_connect_device" msgid="2819038239946643550">"ડિવાઇસ કનેક્ટ કરો"</string>
<string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"અજાણી ઍપ"</string>
<string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"કાસ્ટ કરવાનું રોકો"</string>
<string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"ઑડિયો આઉટપુટ માટે ઉપલબ્ધ ડિવાઇસ."</string>
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"વૉલ્યૂમ"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
- <!-- no translation found for media_output_group_title_connected_speakers (3413876972919684479) -->
- <skip />
+ <string name="media_output_group_title_connected_speakers" msgid="3413876972919684479">"કનેક્ટ કરેલા સ્પીકર"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"સ્પીકર અને ડિસ્પ્લે"</string>
<string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"સૂચવેલા ડિવાઇસ"</string>
<string name="media_input_group_title" msgid="2057057473860783021">"ઇનપુટ"</string>
@@ -1329,10 +1338,8 @@
<string name="accessibility_enter_hint" msgid="2617864063504824834">"ડિવાઇસ અનલૉક કરો"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"ખોલવા માટે ફિંગરપ્રિન્ટનો ઉપયોગ કરો"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"પ્રમાણીકરણ આવશ્યક છે. પ્રમાણિત કરવા માટે ફિંગરપ્રિન્ટ સેન્સરને ટચ કરો."</string>
- <!-- no translation found for resume_animation (9148788411429330671) -->
- <skip />
- <!-- no translation found for pause_animation (1919705510058107504) -->
- <skip />
+ <string name="resume_animation" msgid="9148788411429330671">"ઍનિમેશન ફરી શરૂ કરો"</string>
+ <string name="pause_animation" msgid="1919705510058107504">"ઍનિમેશન થોભાવો"</string>
<string name="ongoing_call_content_description" msgid="6394763878322348560">"ચાલુ કૉલ"</string>
<string name="ongoing_notification_extra_content_description" msgid="2098752668861351265">"ચાલી રહેલી પ્રવૃત્તિ"</string>
<string name="mobile_data_settings_title" msgid="3955246641380064901">"મોબાઇલ ડેટા"</string>
@@ -1514,11 +1521,13 @@
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"શૉર્ટકટ ઉમેરો"</string>
<string name="shortcut_helper_delete_shortcut_button_label" msgid="3148773472696137052">"શૉર્ટકટ ડિલીટ કરો"</string>
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"તમારા કીબોર્ડ વડે નૅવિગેટ કરો"</string>
- <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"કીબોર્ડ શૉર્ટકર્ટ જાણો"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2131074100205028210) -->
+ <skip />
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"તમારા ટચપૅડ વડે નૅવિગેટ કરો"</string>
<string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"ટચપૅડના સંકેતો વિશે જાણો"</string>
<string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"તમારા કીબોર્ડ અને ટચપૅડ વડે નૅવિગેટ કરો"</string>
- <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"ટચપૅડના સંકેતો અને કીબોર્ડના શૉર્ટકટ જેવું બીજું ઘણું જાણો"</string>
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (8557098239919786046) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"પાછા જાઓ"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"હોમ પર જાઓ"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"તાજેતરની ઍપ જુઓ"</string>
@@ -1588,4 +1597,7 @@
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"તમામ ઝડપી સેટિંગ ટાઇલને ડિવાઇસના ઑરિજિનલ સેટિંગ પર રીસેટ કરવામાં આવશે"</string>
<string name="demote_explain_text" msgid="3942301497888762295">"<xliff:g id="APPLICATION">%1$s</xliff:g> હવે લાઇવ અપડેટ બતાવશે નહીં. તમે સેટિંગમાં જઈને કોઈપણ સમયે આને બદલી શકો છો."</string>
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
+ <string name="underlay_close_button_content_description" msgid="6518481455548869894">"બંધ કરો"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="7666159428113013319">"નોટિફિકેશન ખોલવા માટે સૌથી ઉપર ડાબી બાજુએથી નીચે સ્વાઇપ કરો"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="3842355853737057332">"ઝડપી સેટિંગ ખોલવા માટે સૌથી ઉપર જમણી બાજુએથી નીચે સ્વાઇપ કરો"</string>
</resources>
diff --git a/packages/SystemUI/res/values-hi-ldrtl/strings.xml b/packages/SystemUI/res/values-hi-ldrtl/strings.xml
index b497ee6..5771b07 100644
--- a/packages/SystemUI/res/values-hi-ldrtl/strings.xml
+++ b/packages/SystemUI/res/values-hi-ldrtl/strings.xml
@@ -20,4 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="recents_quick_scrub_onboarding" msgid="2452671841151577157">"ऐप्लिकेशन को तेज़ी से बदलने के लिए उसे बाईं ओर खींचें और छोड़ें"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="6543179539438220549">"सूचनाएं देखने के लिए, सबसे ऊपर दाईं ओर से नीचे की ओर स्वाइप करें"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="5760503283520058116">"क्विक सेटिंग खोलने के लिए, स्क्रीन पर सबसे ऊपर बाईं ओर से नीचे की ओर स्वाइप करें"</string>
</resources>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index 1ba4ab2..9b3debd 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -467,6 +467,8 @@
<string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"माइक्रोफ़ोन बटन का इस्तेमाल करने के लिए, सेटिंग में जाकर माइक्रोफ़ोन का ऐक्सेस चालू करें."</string>
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"सेटिंग खोलें"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"अन्य डिवाइस"</string>
+ <string name="media_suggestion_disconnected_text" msgid="8777932417076437525">"<xliff:g id="DEVICE_NAME">%s</xliff:g> पर चलाएं"</string>
+ <string name="media_suggestion_failure_text" msgid="8453022394887613774">"कनेक्ट नहीं हुआ. फिर से आज़माएं."</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"खास जानकारी टॉगल करें"</string>
<string name="zen_modes_dialog_title" msgid="8854640808100096934">"मोड"</string>
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"हो गया"</string>
@@ -582,6 +584,7 @@
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
<skip />
<string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"पूरी स्क्रीन शेयर करें"</string>
+ <string name="screen_share_permission_dialog_option_text_entire_screen_for_display" msgid="1545850238689169166">"<xliff:g id="DISPLAY_NAME">%s</xliff:g> शेयर करें"</string>
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
<skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"जब पूरी स्क्रीन शेयर की जाती है, तो आपकी स्क्रीन पर दिख रहा पूरा कॉन्टेंट <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> पर दिखता है. इसलिए, पासवर्ड, पेमेंट के तरीके की जानकारी, मैसेज, फ़ोटो, ऑडियो, और वीडियो को लेकर सावधानी बरतें."</string>
@@ -593,8 +596,9 @@
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"क्या स्क्रीन को कास्ट करना है?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"एक ऐप्लिकेशन को कास्ट करें"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"पूरी स्क्रीन को कास्ट करें"</string>
- <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"पूरी स्क्रीन को कास्ट करने पर, उस पर दिख रहा कॉन्टेंट दूसरी स्क्रीन पर भी दिखता है. इसलिए, पासवर्ड, पेमेंट के तरीके की जानकारी, मैसेज, फ़ोटो, ऑडियो, और वीडियो को लेकर सावधानी बरतें."</string>
- <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"किसी ऐप्लिकेशन को कास्ट करने पर, उस पर दिख रहा कॉन्टेंट या चल रहा मीडिया दूसरी स्क्रीन पर भी दिखता है. इसलिए, पासवर्ड, पेमेंट के तरीके की जानकारी, मैसेज, फ़ोटो, ऑडियो, और वीडियो को लेकर सावधानी बरतें."</string>
+ <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen_for_display" msgid="5524817761472941086">"<xliff:g id="DISPLAY_NAME">%s</xliff:g> को कास्ट करें"</string>
+ <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"पूरी स्क्रीन को कास्ट करने पर, उस पर दिख रहा कॉन्टेंट दूसरी स्क्रीन पर दिखता है. इसलिए, पासवर्ड, पेमेंट के तरीके की जानकारी, मैसेज, फ़ोटो, ऑडियो, और वीडियो को लेकर सावधानी बरतें."</string>
+ <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"किसी ऐप्लिकेशन को कास्ट करने पर, उस पर दिख रहा कॉन्टेंट या चल रहा मीडिया दूसरी स्क्रीन पर दिखता है. इसलिए, पासवर्ड, पेमेंट के तरीके की जानकारी, मैसेज, फ़ोटो, ऑडियो, और वीडियो को लेकर सावधानी बरतें."</string>
<string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"स्क्रीन कास्ट करें"</string>
<string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"कास्ट करने के लिए ऐप्लिकेशन चुनें"</string>
<string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"क्या मीडिया शेयर करना है?"</string>
@@ -671,8 +675,9 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"इस डिवाइस का प्रबंधन आपके अभिभावक करते हैं. अभिभावक आपके डिवाइस से जुड़ी जानकारी देख सकते हैं. साथ ही, इसे प्रबंधित कर सकते हैं. इनमें आपके इस्तेमाल किए गए ऐप्लिकेशन, जगह की जानकारी, और डिवाइस के इस्तेमाल में बिताए गए समय जैसी जानकारी शामिल है."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"वीपीएन"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"TrustAgent की वजह से अनलॉक रखा गया है"</string>
- <string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"कई बार पुष्टि करने की कोशिशों की वजह से, डिवाइस लॉक हो गया है"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"तय सीमा से ज़्यादा बार पुष्टि करने की कोशिशों से, डिवाइस लॉक हो गया है"</string>
<string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"डिवाइस लॉक हो गया है\nपुष्टि नहीं की जा सकी"</string>
+ <string name="keyguard_indication_after_watch_disconnected" msgid="6329761892437377710">"आपकी स्मार्टवॉच ने लॉक किया"</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="accessibility_volume_settings" msgid="1458961116951564784">"साउंड सेटिंग"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"ऑडियो-वीडियो से अपने-आप कैप्शन बनना"</string>
@@ -719,7 +724,7 @@
<string name="stream_notification_unavailable" msgid="4313854556205836435">"आवाज़ नहीं आएगी, क्योंकि रिंग म्यूट है"</string>
<string name="stream_alarm_unavailable" msgid="4059817189292197839">"सुविधा बंद है, क्योंकि \'परेशान न करें\' मोड चालू है"</string>
<string name="stream_media_unavailable" msgid="6823020894438959853">"आवाज़ बंद है, क्योंकि \'परेशान न करें\' मोड चालू है"</string>
- <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"<xliff:g id="MODE">%s</xliff:g> चालू होने की वजह से यह सुविधा उपलब्ध नहीं है"</string>
+ <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"यह सुविधा बंद है, क्योंकि <xliff:g id="MODE">%s</xliff:g> मोड चालू है"</string>
<string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"उपलब्ध नहीं है"</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. अनम्यूट करने के लिए टैप करें."</string>
<string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. कंपन पर सेट करने के लिए टैप करें. सुलभता सेवाएं म्यूट हो सकती हैं."</string>
@@ -730,7 +735,7 @@
<string name="volume_panel_spatial_audio_title" msgid="3367048857932040660">"स्पेशल ऑडियो"</string>
<string name="volume_panel_spatial_audio_off" msgid="4177490084606772989">"बंद है"</string>
<string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"चालू है"</string>
- <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"हेड ट्रैकिंग चालू है"</string>
+ <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"हेड ट्रैकिंग"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"रिंगर मोड बदलने के लिए टैप करें"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"रिंगर मोड"</string>
<string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, रिंगर मोड बदलने के लिए टैप करें"</string>
@@ -747,7 +752,7 @@
<string name="volume_panel_hint_muted" msgid="1124844870181285320">"म्यूट किया गया"</string>
<string name="volume_panel_hint_vibrate" msgid="4136223145435914132">"वाइब्रेट"</string>
<string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g> इस पर चल रहा है"</string>
- <string name="media_output_title_without_playing" msgid="3825663683169305013">"ऑडियो चलेगा"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"ऑडियो इस पर चलेगा"</string>
<string name="media_output_title_ongoing_call" msgid="208426888064112006">"कॉल चालू है"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"सिस्टम यूज़र इंटरफ़ेस (यूआई) ट्यूनर"</string>
<string name="status_bar" msgid="4357390266055077437">"स्टेटस बार"</string>
@@ -799,6 +804,10 @@
<string name="enable_bluetooth_message" msgid="6740938333772779717">"अपने कीबोर्ड को अपने टैबलेट से कनेक्ट करने के लिए, आपको पहले ब्लूटूथ चालू करना होगा."</string>
<string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"चालू करें"</string>
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"चालू है - चेहरे की गतिविधि के हिसाब से कैमरे को घुमाने की सुविधा"</string>
+ <!-- no translation found for notification_guts_bundle_title (1345506995443305361) -->
+ <skip />
+ <!-- no translation found for notification_guts_bundle_summary (3971530802237393600) -->
+ <skip />
<string name="inline_done_button" msgid="6043094985588909584">"हो गया"</string>
<string name="inline_ok_button" msgid="603075490581280343">"लागू करें"</string>
<string name="inline_turn_off_notifications" msgid="2653064779176881329">"बंद करें"</string>
@@ -840,7 +849,7 @@
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"<xliff:g id="APP_NAME">%1$s</xliff:g> के लिए सूचना नियंत्रण चालू हैं"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="1561909368876911701">"<xliff:g id="APP_NAME">%1$s</xliff:g> के लिए सूचना नियंत्रण बंद हैं"</string>
<string name="notification_more_settings" msgid="4936228656989201793">"और सेटिंग"</string>
- <string name="notification_app_settings" msgid="8963648463858039377">"पसंद के मुताबिक बनाएं"</string>
+ <string name="notification_app_settings" msgid="8963648463858039377">"इनके बारे में सूचना/जानकारी पाएं"</string>
<string name="notification_conversation_bubble" msgid="2242180995373949022">"बबल दिखाएं"</string>
<string name="notification_conversation_unbubble" msgid="6908427185031099868">"बबल्स हटाएं"</string>
<string name="notification_menu_accessibility" msgid="8984166825879886773">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string>
@@ -984,6 +993,7 @@
<string name="right_icon" msgid="1103955040645237425">"दायां आइकॉन"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"टाइल जोड़ने के लिए दबाकर खींचे और छोड़ें"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"टाइल का क्रम फिर से बदलने के लिए उन्हें दबाकर रखें और खींचें"</string>
+ <string name="select_to_rearrange_tiles" msgid="6166153534235179148">"टाइल को फिर से व्यवस्थित करने और उनका साइज़ बदलने के लिए, उन्हें चुनें"</string>
<string name="tap_to_position_tile" msgid="6282815817773342757">"टाइल की जगह तय करने के लिए टैप करें"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"हटाने के लिए यहां खींचें और छोड़ें"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"आपके पास कम से कम <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> टाइलें होनी चाहिए"</string>
@@ -1003,10 +1013,10 @@
<string name="tuner_low_priority" msgid="8412666814123009820">"कम प्राथमिकता वाली सूचना के आइकॉन दिखाएं"</string>
<string name="other" msgid="429768510980739978">"अन्य"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"टाइल के साइज़ को टॉगल करने के लिए दो बार टैप करें"</string>
- <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"टाइल हटाएं"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="4811778796933958616">"टाइल हटाएं"</string>
<string name="accessibility_qs_edit_toggle_placement_mode" msgid="3870429389210569610">"प्लेसमेंट मोड को टॉगल करें"</string>
<string name="accessibility_qs_edit_toggle_selection" msgid="2201248304072372239">"चुने हुए हिस्से को टॉगल करें"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"आखिरी जगह पर टाइल जोड़ें"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="7188303979684400620">"आखिरी जगह पर टाइल जोड़ें"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"टाइल को किसी और पोज़िशन पर ले जाएं"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"अपनी पसंदीदा जगह पर टाइल जोड़ें"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"टाइल को <xliff:g id="POSITION">%1$d</xliff:g> पोज़िशन पर ले जाएं"</string>
@@ -1093,7 +1103,8 @@
<string name="auto_saver_text" msgid="3214960308353838764">"जब बैटरी खत्म होने वाली हो तब \'बैटरी सेवर\' चालू करें"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"जी नहीं, शुक्रिया"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"इस्तेमाल किया जा रहा है"</string>
- <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"ऐप्लिकेशन आपकी <xliff:g id="TYPES_LIST">%s</xliff:g> का इस्तेमाल कर रहे हैं."</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps (4994455315457996796) -->
+ <skip />
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" और "</string>
<string name="ongoing_privacy_dialog_using_op" msgid="426635338010011796">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> इस्तेमाल कर रहा है"</string>
@@ -1166,7 +1177,7 @@
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"टॉगल करें"</string>
<string name="accessibility_floating_button_action_edit" msgid="1688227814600463987">"बदलाव करें"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"डिवाइस कंट्रोल"</string>
- <string name="controls_providers_title" msgid="6879775889857085056">"कंट्रोल जोड़ने के लिए ऐप्लिकेशन चुनें"</string>
+ <string name="controls_providers_title" msgid="6879775889857085056">"वह ऐप्लिकेशन चुनें जिसमें कंट्रोल जोड़ने हैं"</string>
<string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# कंट्रोल जोड़ा गया.}one{# कंट्रोल जोड़ा गया.}other{# कंट्रोल जोड़े गए.}}"</string>
<string name="controls_removed" msgid="3731789252222856959">"हटाया गया"</string>
<string name="controls_panel_authorization_title" msgid="267429338785864842">"<xliff:g id="APPNAME">%s</xliff:g> ऐप्लिकेशन को जोड़ना है?"</string>
@@ -1508,11 +1519,13 @@
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"शॉर्टकट जोड़ें"</string>
<string name="shortcut_helper_delete_shortcut_button_label" msgid="3148773472696137052">"शॉर्टकट मिटाएं"</string>
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"कीबोर्ड का इस्तेमाल करके नेविगेट करें"</string>
- <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"कीबोर्ड शॉर्टकट के बारे में जानें"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2131074100205028210) -->
+ <skip />
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"टचपैड का इस्तेमाल करके नेविगेट करें"</string>
<string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"टचपैड से जुड़े जेस्चर के बारे में जानें"</string>
<string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"कीबोर्ड और टचपैड का इस्तेमाल करके नेविगेट करें"</string>
- <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"टचपैड से जुड़े जेस्चर, कीबोर्ड शॉर्टकट वगैरह के बारे में जानें"</string>
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (8557098239919786046) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"वापस जाएं"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"होम स्क्रीन पर जाएं"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"हाल ही में इस्तेमाल किए गए ऐप्लिकेशन देखें"</string>
@@ -1579,7 +1592,10 @@
<string name="qs_edit_mode_category_display" msgid="4749511439121053942">"डिसप्ले"</string>
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"कोई जानकारी नहीं है"</string>
<string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"क्या सभी टाइल रीसेट करनी हैं?"</string>
- <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"क्विक सेटिंग टाइल, डिवाइस की ओरिजनल सेटिंग पर रीसेट हो जाएंगी"</string>
+ <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"क्विक सेटिंग से जुड़ी सभी टाइल, डिवाइस की ओरिजनल सेटिंग पर रीसेट हो जाएंगी"</string>
<string name="demote_explain_text" msgid="3942301497888762295">"<xliff:g id="APPLICATION">%1$s</xliff:g> अब लाइव अपडेट नहीं दिखाएगा. सेटिंग में जाकर, इस विकल्प को कभी भी बदला जा सकता है."</string>
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
+ <string name="underlay_close_button_content_description" msgid="6518481455548869894">"बंद करें"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="7666159428113013319">"सूचनाएं देखने के लिए, सबसे ऊपर बाईं ओर से नीचे की ओर स्वाइप करें"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="3842355853737057332">"क्विक सेटिंग खोलने के लिए, सबसे ऊपर दाईं ओर से नीचे की ओर स्वाइप करें"</string>
</resources>
diff --git a/packages/SystemUI/res/values-hr-ldrtl/strings.xml b/packages/SystemUI/res/values-hr-ldrtl/strings.xml
index e4ec869..5710753 100644
--- a/packages/SystemUI/res/values-hr-ldrtl/strings.xml
+++ b/packages/SystemUI/res/values-hr-ldrtl/strings.xml
@@ -20,4 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="recents_quick_scrub_onboarding" msgid="2452671841151577157">"Povucite ulijevo da biste brzo promijenili aplikaciju"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="6543179539438220549">"Prijeđite prstom od gornjeg desnog ruba prema dolje da biste otvorili obavijesti"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="5760503283520058116">"Prijeđite prstom od gornjeg lijevog ruba prema dolje da biste otvorili Brze postavke"</string>
</resources>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index b8ca38e..63207e6 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -252,8 +252,7 @@
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Spojen na <xliff:g id="BLUETOOTH">%s</xliff:g> ."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"Povezani ste sa sljedećim uređajem: <xliff:g id="CAST">%s</xliff:g>."</string>
<string name="accessibility_expand_group" msgid="521237935987978624">"Proširite grupu."</string>
- <!-- no translation found for accessibility_collapse_group (1842704016768329517) -->
- <skip />
+ <string name="accessibility_collapse_group" msgid="1842704016768329517">"Sažmite grupu."</string>
<string name="accessibility_add_device_to_group" msgid="5446422960697860806">"Dodajte uređaj u grupu."</string>
<string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"Uklonite uređaj iz grupe."</string>
<string name="accessibility_open_application" msgid="1749126077501259712">"Otvorite aplikaciju."</string>
@@ -468,6 +467,8 @@
<string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"Da biste koristili gumb mikrofona, omogućite pristup mikrofonu u postavkama."</string>
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Otvori postavke"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Ostali uređaji"</string>
+ <string name="media_suggestion_disconnected_text" msgid="8777932417076437525">"Reproduciraj na uređaju <xliff:g id="DEVICE_NAME">%s</xliff:g>"</string>
+ <string name="media_suggestion_failure_text" msgid="8453022394887613774">"Povezivanje nije uspjelo. Pokušajte ponovo."</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Uključivanje/isključivanje pregleda"</string>
<string name="zen_modes_dialog_title" msgid="8854640808100096934">"Načini"</string>
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Gotovo"</string>
@@ -583,6 +584,7 @@
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
<skip />
<string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Dijeljenje cijelog zaslona"</string>
+ <string name="screen_share_permission_dialog_option_text_entire_screen_for_display" msgid="1545850238689169166">"Podijeli <xliff:g id="DISPLAY_NAME">%s</xliff:g>"</string>
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
<skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Kada dijelite cijeli zaslon, sve na zaslonu bit će vidljivo aplikaciji <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Stoga pazite na stvari kao što su zaporke, podaci o plaćanju, poruke, fotografije te audio i videozapisi."</string>
@@ -594,6 +596,7 @@
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Želite li emitirati zaslon?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Emitiranje jedne aplikacije"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Emitiranje cijelog zaslona"</string>
+ <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen_for_display" msgid="5524817761472941086">"Emitiraj <xliff:g id="DISPLAY_NAME">%s</xliff:g>"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Kada emitirate cijeli zaslon, sve na zaslonu bit će vidljivo. Stoga pazite na stvari kao što su zaporke, podaci o plaćanju, poruke, fotografije te audio i videozapisi."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Kada emitirate aplikaciju, sve što se prikazuje ili reproducira u toj aplikaciji bit će vidljivo. Stoga pazite na stvari kao što su zaporke, podaci o plaćanju, poruke, fotografije te audio i videozapisi."</string>
<string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Emitiraj zaslon"</string>
@@ -672,8 +675,9 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Ovim uređajem upravlja tvoj roditelj. Tvoj roditelj može vidjeti podatke kao što su aplikacije kojima se koristiš, lokaciju i vrijeme upotrebe te upravljati njima."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Otključano održava TrustAgent"</string>
- <string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"Uređaj je bio zaključan zbog previše pokušaja autentifikacije"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"Uređaj je zaključan zbog previše pokušaja autentifikacije"</string>
<string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"Uređaj je zaključan\nAutentifikacija nije uspjela"</string>
+ <string name="keyguard_indication_after_watch_disconnected" msgid="6329761892437377710">"Zaključano pomoću sata"</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="accessibility_volume_settings" msgid="1458961116951564784">"Postavke zvuka"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Automatski titlovi za medije"</string>
@@ -715,7 +719,7 @@
<string name="stream_accessibility" msgid="3873610336741987152">"Pristupačnost"</string>
<string name="volume_ringer_status_normal" msgid="1339039682222461143">"Zvonjenje"</string>
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Vibriranje"</string>
- <string name="volume_ringer_status_silent" msgid="3691324657849880883">"Zvuk je isključen"</string>
+ <string name="volume_ringer_status_silent" msgid="3691324657849880883">"Isključi zvuk"</string>
<string name="media_device_cast" msgid="4786241789687569892">"Emitiraj"</string>
<string name="stream_notification_unavailable" msgid="4313854556205836435">"Nedostupno jer je zvono utišano"</string>
<string name="stream_alarm_unavailable" msgid="4059817189292197839">"Nedostupno jer je uključen način Ne uznemiravaj"</string>
@@ -800,6 +804,10 @@
<string name="enable_bluetooth_message" msgid="6740938333772779717">"Da biste povezali tipkovnicu s tabletom, morate uključiti Bluetooth."</string>
<string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Uključi"</string>
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Uključeno – na temelju lica"</string>
+ <!-- no translation found for notification_guts_bundle_title (1345506995443305361) -->
+ <skip />
+ <!-- no translation found for notification_guts_bundle_summary (3971530802237393600) -->
+ <skip />
<string name="inline_done_button" msgid="6043094985588909584">"Gotovo"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Primijeni"</string>
<string name="inline_turn_off_notifications" msgid="2653064779176881329">"Isključi"</string>
@@ -985,6 +993,7 @@
<string name="right_icon" msgid="1103955040645237425">"Desna ikona"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Zadržite i povucite da biste dodali pločice"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Zadržite i povucite da biste premjestili pločice"</string>
+ <string name="select_to_rearrange_tiles" msgid="6166153534235179148">"Odaberite pločice za preuređivanje i promjenu veličine"</string>
<string name="tap_to_position_tile" msgid="6282815817773342757">"Dodirnite da biste pozicionirali pločicu"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Povucite ovdje za uklanjanje"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Potrebno je barem <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> pločica"</string>
@@ -1004,10 +1013,10 @@
<string name="tuner_low_priority" msgid="8412666814123009820">"Prikaži ikone obavijesti niskog prioriteta"</string>
<string name="other" msgid="429768510980739978">"Ostalo"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"promjenu veličine pločice"</string>
- <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"uklanjanje kartice"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="4811778796933958616">"Ukloni pločicu"</string>
<string name="accessibility_qs_edit_toggle_placement_mode" msgid="3870429389210569610">"promjenu načina pozicioniranja"</string>
<string name="accessibility_qs_edit_toggle_selection" msgid="2201248304072372239">"promjenu odabira"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"dodavanje kartice na posljednji položaj"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="7188303979684400620">"Dodaj pločicu na posljednji položaj"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Premještanje kartice"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Dodajte karticu na željeni položaj"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Premještanje u prostoriju <xliff:g id="POSITION">%1$d</xliff:g>"</string>
@@ -1094,7 +1103,8 @@
<string name="auto_saver_text" msgid="3214960308353838764">"Uključite kad bi se baterija mogla isprazniti"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Ne, hvala"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"U upotrebi"</string>
- <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Aplikacije upotrebljavaju <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps (4994455315457996796) -->
+ <skip />
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" i "</string>
<string name="ongoing_privacy_dialog_using_op" msgid="426635338010011796">"Koristi aplikacija <xliff:g id="APPLICATION_NAME">%1$s</xliff:g>"</string>
@@ -1251,15 +1261,13 @@
<string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(nije povezano)"</string>
<string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"Nije prebačeno. Dodirnite da biste pokušali ponovo."</string>
<string name="media_output_dialog_pairing_new" msgid="5098212763195577270">"Povežite uređaj"</string>
- <!-- no translation found for media_output_dialog_button_connect_device (2819038239946643550) -->
- <skip />
+ <string name="media_output_dialog_button_connect_device" msgid="2819038239946643550">"Poveži uređaj"</string>
<string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Nepoznata aplikacija"</string>
<string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Zaustavi emitiranje"</string>
<string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"Dostupni uređaji za audioizlaz."</string>
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Glasnoća"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
- <!-- no translation found for media_output_group_title_connected_speakers (3413876972919684479) -->
- <skip />
+ <string name="media_output_group_title_connected_speakers" msgid="3413876972919684479">"Povezani zvučnici"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Zvučnici i zasloni"</string>
<string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Predloženi uređaji"</string>
<string name="media_input_group_title" msgid="2057057473860783021">"Ulaz"</string>
@@ -1328,10 +1336,8 @@
<string name="accessibility_enter_hint" msgid="2617864063504824834">"pristupili uređaju"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Otvorite pomoću otiska prsta"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Potrebna je autentifikacija. Dodirnite senzor otiska prsta da biste se autentificirali."</string>
- <!-- no translation found for resume_animation (9148788411429330671) -->
- <skip />
- <!-- no translation found for pause_animation (1919705510058107504) -->
- <skip />
+ <string name="resume_animation" msgid="9148788411429330671">"Nastavi animaciju"</string>
+ <string name="pause_animation" msgid="1919705510058107504">"Pauziraj animaciju"</string>
<string name="ongoing_call_content_description" msgid="6394763878322348560">"Poziv u tijeku"</string>
<string name="ongoing_notification_extra_content_description" msgid="2098752668861351265">"U tijeku"</string>
<string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobilni podaci"</string>
@@ -1513,11 +1519,13 @@
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"Dodaj prečac"</string>
<string name="shortcut_helper_delete_shortcut_button_label" msgid="3148773472696137052">"Izbriši prečac"</string>
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Krećite se pomoću tipkovnice"</string>
- <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Saznajte više o tipkovnim prečacima"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2131074100205028210) -->
+ <skip />
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Krećite se pomoću dodirne podloge"</string>
<string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Saznajte više o pokretima za dodirnu podlogu"</string>
<string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Krećite se pomoću tipkovnice i dodirne podloge"</string>
- <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Saznajte više o pokretima za dodirnu podlogu, tipkovnim prečacima i ostalom"</string>
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (8557098239919786046) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Natrag"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Na početni zaslon"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Pregled nedavnih aplikacija"</string>
@@ -1553,7 +1561,7 @@
<string name="tutorial_animation_content_description" msgid="2698816574982370184">"Animacija u vodiču, kliknite za pauziranje i nastavak reprodukcije."</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Pozadinsko osvjetljenje tipkovnice"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Razina %1$d od %2$d"</string>
- <string name="home_controls_dream_label" msgid="6567105701292324257">"Upravljanje uređajima"</string>
+ <string name="home_controls_dream_label" msgid="6567105701292324257">"Kontrole uređaja"</string>
<string name="home_controls_dream_description" msgid="4644150952104035789">"Brzo upravljajte uređajima putem čuvara zaslona"</string>
<string name="volume_undo_action" msgid="5815519725211877114">"Poništi"</string>
<string name="back_edu_toast_content" msgid="4530314597378982956">"Za povratak trima prstima prijeđite ulijevo ili udesno na dodirnoj podlozi"</string>
@@ -1584,7 +1592,10 @@
<string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Prikaz"</string>
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Nepoznato"</string>
<string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Želite li poništiti sve pločice?"</string>
- <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Sve pločice Brze postavke vratit će se na izvorne postavke uređaja"</string>
+ <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Sve pločice za brzi odabir postavki vratit će se na izvorne postavke uređaja"</string>
<string name="demote_explain_text" msgid="3942301497888762295">"<xliff:g id="APPLICATION">%1$s</xliff:g> više neće prikazivati ažuriranja uživo. To uvijek možete promijeniti u postavkama."</string>
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
+ <string name="underlay_close_button_content_description" msgid="6518481455548869894">"Zatvori"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="7666159428113013319">"Prijeđite prstom od gornjeg lijevog ruba prema dolje da biste otvorili obavijesti"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="3842355853737057332">"Prijeđite prstom od gornjeg desnog ruba prema dolje da biste otvorili Brze postavke"</string>
</resources>
diff --git a/packages/SystemUI/res/values-hu-ldrtl/strings.xml b/packages/SystemUI/res/values-hu-ldrtl/strings.xml
index 3250211..a33a048 100644
--- a/packages/SystemUI/res/values-hu-ldrtl/strings.xml
+++ b/packages/SystemUI/res/values-hu-ldrtl/strings.xml
@@ -20,4 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="recents_quick_scrub_onboarding" msgid="2452671841151577157">"Húzza balra az ujját az alkalmazások közötti gyors váltáshoz"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="6543179539438220549">"Az értesítések megnyitásához csúsztasson lefelé a jobb felső saroktól"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="5760503283520058116">"A Gyorsbeállítások megnyitásához csúsztasson lefelé a bal felső saroktól"</string>
</resources>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index 0986569..59fc088 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -111,7 +111,7 @@
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Folyamatban lévő értesítés képernyőrögzítési munkamenethez"</string>
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Rögzíti a képernyőt?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Egyetlen alkalmazás rögzítése"</string>
- <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="4882406311415082016">"A képernyő felvétele"</string>
+ <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="4882406311415082016">"A képernyő rögzítése"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="4169494703993148253">"%s felvétele"</string>
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"A teljes képernyő rögzítése esetén a képernyőn megjelenő minden tartalom rögzítésre kerül. Ezért legyen elővigyázatos a jelszavakkal, a fizetési adatokkal, az üzenetekkel, a fotókkal, valamint a hang- és videófelvételekkel."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Alkalmazás rögzítésekor az adott alkalmazásban megjelenített vagy lejátszott minden tartalom rögzítésre kerül. Ezért legyen elővigyázatos a jelszavakkal, a fizetési adatokkal, az üzenetekkel, a fotókkal, valamint a hang- és videófelvételekkel."</string>
@@ -356,7 +356,7 @@
<string name="quick_settings_cast_no_network" msgid="3863016850468559522">"Nincs Wi‑Fi- vagy Ethernet-kapcsolat"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Fényerő"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"Színek invertálása"</string>
- <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Színjavítás"</string>
+ <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Színkorrekció"</string>
<string name="quick_settings_font_scaling_label" msgid="5289001009876936768">"Betűméret"</string>
<string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Kezelés"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Kész"</string>
@@ -467,6 +467,8 @@
<string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"Ha használni szeretné a Mikrofon gombot, engedélyezze a mikrofonhoz való hozzáférést a Beállításokban."</string>
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Beállítások megnyitása"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Más eszköz"</string>
+ <string name="media_suggestion_disconnected_text" msgid="8777932417076437525">"Lejátszás itt: <xliff:g id="DEVICE_NAME">%s</xliff:g>"</string>
+ <string name="media_suggestion_failure_text" msgid="8453022394887613774">"Nem sikerült. Próbálja újra."</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Áttekintés be- és kikapcsolása"</string>
<string name="zen_modes_dialog_title" msgid="8854640808100096934">"Módok"</string>
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Kész"</string>
@@ -582,6 +584,7 @@
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
<skip />
<string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"A teljes képernyő megosztása"</string>
+ <string name="screen_share_permission_dialog_option_text_entire_screen_for_display" msgid="1545850238689169166">"<xliff:g id="DISPLAY_NAME">%s</xliff:g> megosztása"</string>
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
<skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"A teljes képernyő megosztása esetén a képernyő teljes tartalma látható lesz a(z) <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> számára. Ezért legyen elővigyázatos a jelszavakkal, a fizetési adatokkal, az üzenetekkel, a fotókkal, valamint a hang- és videófelvételekkel."</string>
@@ -593,6 +596,7 @@
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Átküldi a képernyőt?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Egyetlen app átküldése"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Teljes képernyő átküldése"</string>
+ <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen_for_display" msgid="5524817761472941086">"<xliff:g id="DISPLAY_NAME">%s</xliff:g> átküldése"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"A teljes képernyő átküldése esetén a képernyő teljes tartalma látható. Ezért legyen elővigyázatos a jelszavakkal, a fizetési adatokkal, az üzenetekkel, a fotókkal, valamint a hang- és videófelvételekkel."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Alkalmazás átküldése közben az adott appban megjelenített vagy lejátszott minden tartalom látható. Ezért legyen elővigyázatos a jelszavakkal, a fizetési adatokkal, az üzenetekkel, a fotókkal, valamint a hang- és videófelvételekkel."</string>
<string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Képernyőtartalom átküldése"</string>
@@ -673,6 +677,7 @@
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Feloldva tartva TrustAgent által"</string>
<string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"Az eszköz túl sok hitelesítési kísérlet miatt zárolva lett."</string>
<string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"Eszköz zárolva\nSikertelen hitelesítés"</string>
+ <string name="keyguard_indication_after_watch_disconnected" msgid="6329761892437377710">"Kizárta az órája"</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="accessibility_volume_settings" msgid="1458961116951564784">"Hangbeállítások"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Automatikus feliratozás"</string>
@@ -716,10 +721,10 @@
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Rezgés"</string>
<string name="volume_ringer_status_silent" msgid="3691324657849880883">"Néma"</string>
<string name="media_device_cast" msgid="4786241789687569892">"Átküldés"</string>
- <string name="stream_notification_unavailable" msgid="4313854556205836435">"Nem lehetséges, a csörgés le van némítva"</string>
+ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Nem elérhető, mert a csörgés le van némítva"</string>
<string name="stream_alarm_unavailable" msgid="4059817189292197839">"Nem működik, mert be van kapcsolva a Ne zavarjanak"</string>
<string name="stream_media_unavailable" msgid="6823020894438959853">"Nem működik, mert be van kapcsolva a Ne zavarjanak"</string>
- <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Nem áll rendelkezésre, mert a(z) <xliff:g id="MODE">%s</xliff:g> be van kapcsolva"</string>
+ <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Nem elérhető, mert a(z) <xliff:g id="MODE">%s</xliff:g> be van kapcsolva"</string>
<string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Nem áll rendelkezésre"</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Koppintson a némítás megszüntetéséhez."</string>
<string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Koppintson a rezgés beállításához. Előfordulhat, hogy a kisegítő lehetőségek szolgáltatásai le vannak némítva."</string>
@@ -799,6 +804,10 @@
<string name="enable_bluetooth_message" msgid="6740938333772779717">"Ha a billentyűzetet csatlakoztatni szeretné táblagépéhez, először engedélyeznie kell a Bluetooth-kapcsolatot."</string>
<string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Bekapcsolás"</string>
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Be: Arcalapú"</string>
+ <!-- no translation found for notification_guts_bundle_title (1345506995443305361) -->
+ <skip />
+ <!-- no translation found for notification_guts_bundle_summary (3971530802237393600) -->
+ <skip />
<string name="inline_done_button" msgid="6043094985588909584">"Kész"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Alkalmaz"</string>
<string name="inline_turn_off_notifications" msgid="2653064779176881329">"Kikapcsolás"</string>
@@ -984,6 +993,7 @@
<string name="right_icon" msgid="1103955040645237425">"Jobb oldali ikon"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Tartsa lenyomva, és húzza a mozaikok hozzáadásához"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Tartsa lenyomva, és húzza a mozaikok átrendezéséhez"</string>
+ <string name="select_to_rearrange_tiles" msgid="6166153534235179148">"Válassza ki az átrendezni és átméretezni kívánt mozaikokat"</string>
<string name="tap_to_position_tile" msgid="6282815817773342757">"Koppintson a mozaik pozíciójának beállításához"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Húzza ide az eltávolításhoz"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Legalább <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> kártya szükséges"</string>
@@ -1003,10 +1013,10 @@
<string name="tuner_low_priority" msgid="8412666814123009820">"Alacsony prioritású értesítési ikonok mutatása"</string>
<string name="other" msgid="429768510980739978">"Egyéb"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"a mozaik méretének módosítása"</string>
- <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"mozaik eltávolításához"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="4811778796933958616">"Mozaik eltávolítása"</string>
<string name="accessibility_qs_edit_toggle_placement_mode" msgid="3870429389210569610">"elhelyezési mód be- vagy kikapcsolása"</string>
<string name="accessibility_qs_edit_toggle_selection" msgid="2201248304072372239">"kiválasztás be- vagy kikapcsolása"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"mozaik hozzáadása az utolsó pozícióhoz"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="7188303979684400620">"Mozaik hozzáadása az utolsó pozícióhoz"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Mozaik áthelyezése"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Mozaik hozzáadása a kívánt pozícióhoz"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Áthelyezés ide: <xliff:g id="POSITION">%1$d</xliff:g>"</string>
@@ -1093,7 +1103,8 @@
<string name="auto_saver_text" msgid="3214960308353838764">"Kapcsolja be, ha az akkumulátor hamarosan lemerül"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Nem"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"Használatban"</string>
- <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Több alkalmazás használja a következőket: <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps (4994455315457996796) -->
+ <skip />
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" és "</string>
<string name="ongoing_privacy_dialog_using_op" msgid="426635338010011796">"Jelenleg használja: <xliff:g id="APPLICATION_NAME">%1$s</xliff:g>"</string>
@@ -1508,11 +1519,13 @@
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"Billentyűparancs hozzáadása"</string>
<string name="shortcut_helper_delete_shortcut_button_label" msgid="3148773472696137052">"Billentyűparancs törlése"</string>
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navigáció a billentyűzet segítségével"</string>
- <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Billentyűparancsok megismerése"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2131074100205028210) -->
+ <skip />
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navigálás az érintőpaddal"</string>
<string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Érintőpad-kézmozdulatok megismerése"</string>
<string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Navigálás a billentyűzet és az érintőpad használatával"</string>
- <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Érintőpad-kézmozdulatok, billentyűparancsok és egyebek megismerése"</string>
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (8557098239919786046) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Vissza"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Ugrás a főoldalra"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Legutóbbi alkalmazások megtekintése"</string>
@@ -1523,12 +1536,12 @@
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Vissza"</string>
<string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Csúsztassa gyorsan három ujját balra vagy jobbra az érintőpadon."</string>
<string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Remek!"</string>
- <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Teljesítette a visszalépési kézmozdulatot."</string>
+ <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Megtanulta a visszalépési kézmozdulatot."</string>
<string name="touchpad_back_gesture_error_body" msgid="7112668207481458792">"Ha az érintőpadot használva szeretne visszalépni, csúsztasson gyorsan három ujjal balra vagy jobbra."</string>
<string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Ugrás a főoldalra"</string>
<string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Csúsztasson gyorsan felfelé három ujjával az érintőpadon."</string>
<string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Kiváló!"</string>
- <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Teljesítette a kezdőképernyőre lépés kézmozdulatát."</string>
+ <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Megtanulta a kezdőképernyőre lépés kézmozdulatát."</string>
<string name="touchpad_home_gesture_error_body" msgid="3810674109999513073">"A kezdőképernyőre való ugráshoz csúsztasson gyorsan három ujjal felfelé az érintőpadon."</string>
<string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Legutóbbi alkalmazások megtekintése"</string>
<string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Csúsztasson gyorsan felfelé három ujjal az érintőpadon, és tartsa rajta lenyomva az ujjait."</string>
@@ -1582,4 +1595,7 @@
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Az összes Gyorsbeállítások mozaik visszaáll az eszköz eredeti beállításaira"</string>
<string name="demote_explain_text" msgid="3942301497888762295">"A(z) <xliff:g id="APPLICATION">%1$s</xliff:g> a továbbiakban nem jelenít meg friss híreket. A Beállítások menüpontban ezt bármikor módosíthatja."</string>
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
+ <string name="underlay_close_button_content_description" msgid="6518481455548869894">"Bezárás"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="7666159428113013319">"Az értesítések megnyitásához csúsztasson lefelé a bal felső saroktól"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="3842355853737057332">"A Gyorsbeállítások megnyitásához csúsztasson lefelé a jobb felső saroktól"</string>
</resources>
diff --git a/packages/SystemUI/res/values-hy-ldrtl/strings.xml b/packages/SystemUI/res/values-hy-ldrtl/strings.xml
index dd72284..63cd33f 100644
--- a/packages/SystemUI/res/values-hy-ldrtl/strings.xml
+++ b/packages/SystemUI/res/values-hy-ldrtl/strings.xml
@@ -20,4 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="recents_quick_scrub_onboarding" msgid="2452671841151577157">"Քաշեք ձախ՝ հավելվածների միջև անցնելու համար"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="6543179539438220549">"Ծանուցումները բացելու համար սահեցրեք մատը վերևի աջ անկյունից"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="5760503283520058116">"Արագ կարգավորումները բացելու համար սահեցրեք մատը վերևի ձախ անկյունից"</string>
</resources>
diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index c88bd05..a1ba9ed 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -467,6 +467,8 @@
<string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"Խոսափողի կոճակն օգտագործելու համար կարգավորումներում թույլատրեք խոսափողի օգտագործումը։"</string>
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Բացել կարգավորումները"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Այլ սարք"</string>
+ <string name="media_suggestion_disconnected_text" msgid="8777932417076437525">"Նվագարկել «<xliff:g id="DEVICE_NAME">%s</xliff:g>» սարքում"</string>
+ <string name="media_suggestion_failure_text" msgid="8453022394887613774">"Չստացվեց միանալ։ Նորից փորձեք։"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Միացնել/անջատել համատեսքը"</string>
<string name="zen_modes_dialog_title" msgid="8854640808100096934">"Ռեժիմներ"</string>
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Պատրաստ է"</string>
@@ -582,6 +584,7 @@
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
<skip />
<string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Ցուցադրել ամբողջ էկրանը"</string>
+ <string name="screen_share_permission_dialog_option_text_entire_screen_for_display" msgid="1545850238689169166">"Կիսվել «<xliff:g id="DISPLAY_NAME">%s</xliff:g>» էկրանով"</string>
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
<skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Երբ ցուցադրում եք ամբողջ էկրանը, տեսանելի կլինի այն ամենը, ինչ ձեր էկրանին տեսանելի է <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> հավելվածին։ Ուստի ուշադիր եղեք այնպիսի բաների հետ, ինչպիսիք են գաղտնաբառերը, վճարային տվյալները, հաղորդագրությունները, լուսանկարները, աուդիո և վիդեո բովանդակությունը։"</string>
@@ -593,6 +596,7 @@
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Հեռարձակե՞լ ձեր էկրանը"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Հեռարձակել մեկ հավելված"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Հեռարձակել ամբողջ էկրանը"</string>
+ <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen_for_display" msgid="5524817761472941086">"Հեռարձակել «<xliff:g id="DISPLAY_NAME">%s</xliff:g>» էկրանը"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Երբ հեռարձակում եք ամբողջ էկրանը, տեսանելի կլինի այն ամենը, ինչ ձեր էկրանին է։ Ուստի ուշադիր եղեք այնպիսի բաների հետ, ինչպիսիք են գաղտնաբառերը, վճարային տվյալները, հաղորդագրությունները, լուսանկարները, աուդիո և վիդեո բովանդակությունը։"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Երբ դուք որևէ հավելված եք հեռարձակում, տեսանելի կլինի այն ամենը, ինչ ցուցադրվում կամ նվագարկվում է այդ հավելվածում։ Ուստի ուշադիր եղեք այնպիսի բաների հետ, ինչպիսիք են գաղտնաբառերը, վճարային տվյալները, հաղորդագրությունները, լուսանկարները, աուդիո և վիդեո բովանդակությունը։"</string>
<string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Հեռարձակել էկրանը"</string>
@@ -673,6 +677,7 @@
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Ապակողպվում է TrustAgent-ի միջոցով"</string>
<string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"Սարքը կողպվել է․ իսկորոշման չափազանց շատ փորձեր են արվել"</string>
<string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"Սարքը կողպվել է\nՉհաջողվեց իսկորոշել"</string>
+ <string name="keyguard_indication_after_watch_disconnected" msgid="6329761892437377710">"Ձեր ժամացույցն արգելափակել է մուտքը"</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="accessibility_volume_settings" msgid="1458961116951564784">"Ձայնի կարգավորումներ"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Ավտոմատ ավելացնել ենթագրեր"</string>
@@ -719,7 +724,7 @@
<string name="stream_notification_unavailable" msgid="4313854556205836435">"Հասանելի չէ, երբ զանգի ձայնն անջատված է"</string>
<string name="stream_alarm_unavailable" msgid="4059817189292197839">"Հասանելի չէ․ «Չանհանգստացնել» ռեժիմը միացված է"</string>
<string name="stream_media_unavailable" msgid="6823020894438959853">"Հասանելի չէ․ «Չանհանգստացնել» ռեժիմը միացված է"</string>
- <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Հասանելի չէ, քանի որ «<xliff:g id="MODE">%s</xliff:g>» ռեժիմը միացված է"</string>
+ <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Հասանելի չէ «<xliff:g id="MODE">%s</xliff:g>» ռեժիմում"</string>
<string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Հասանելի չէ"</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s: Հպեք՝ ձայնը միացնելու համար:"</string>
<string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s: Հպեք՝ թրթռոցը միացնելու համար: Մատչելիության ծառայությունների ձայնը կարող է անջատվել:"</string>
@@ -799,6 +804,10 @@
<string name="enable_bluetooth_message" msgid="6740938333772779717">"Ստեղնաշարը ձեր պլանշետին միացնելու համար նախ անհրաժեշտ է միացնել Bluetooth-ը:"</string>
<string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Միացնել"</string>
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Միաց․ – Դիմաճանաչման հիման վրա"</string>
+ <!-- no translation found for notification_guts_bundle_title (1345506995443305361) -->
+ <skip />
+ <!-- no translation found for notification_guts_bundle_summary (3971530802237393600) -->
+ <skip />
<string name="inline_done_button" msgid="6043094985588909584">"Փակել"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Կիրառել"</string>
<string name="inline_turn_off_notifications" msgid="2653064779176881329">"Անջատել"</string>
@@ -984,6 +993,7 @@
<string name="right_icon" msgid="1103955040645237425">"Աջ պատկերակ"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Պահեք և քաշեք՝ սալիկներ ավելացնելու համար"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Պահեք և քաշեք՝ սալիկները վերադասավորելու համար"</string>
+ <string name="select_to_rearrange_tiles" msgid="6166153534235179148">"Ընտրեք սալիկներ՝ վերադասավորելու և չափը փոխելու համար"</string>
<string name="tap_to_position_tile" msgid="6282815817773342757">"Հպեք՝ սալիկը տեղադրելու համար"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Քաշեք այստեղ՝ հեռացնելու համար"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Հարկավոր է առնվազն <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> սալիկ"</string>
@@ -1003,10 +1013,12 @@
<string name="tuner_low_priority" msgid="8412666814123009820">"Ցուցադրել ցածր առաջնահերթության ծանուցումների պատկերակները"</string>
<string name="other" msgid="429768510980739978">"Այլ"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"փոխեք սալիկի չափը"</string>
- <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"հեռացնել սալիկը"</string>
+ <!-- no translation found for accessibility_qs_edit_remove_tile_action (4811778796933958616) -->
+ <skip />
<string name="accessibility_qs_edit_toggle_placement_mode" msgid="3870429389210569610">"միացնել կամ անջատել տեղադրման ռեժիմը"</string>
<string name="accessibility_qs_edit_toggle_selection" msgid="2201248304072372239">"ընտրել կամ չեղարկել ընտրությունը"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"ավելացնել սալիկը վերջին դիրքում"</string>
+ <!-- no translation found for accessibility_qs_edit_tile_add_action (7188303979684400620) -->
+ <skip />
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Տեղափոխել սալիկը"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Ավելացնել սալիկը նախընտրած դիրքում"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Տեղափոխել դիրք <xliff:g id="POSITION">%1$d</xliff:g>"</string>
@@ -1093,7 +1105,8 @@
<string name="auto_saver_text" msgid="3214960308353838764">"Միացնել էներգախնայումը, երբ մարտկոցի լիցքը գրեթե սպառված է"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Ոչ"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"Օգտագործվում է"</string>
- <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Հավելվածներն օգտագործում են ձեր <xliff:g id="TYPES_LIST">%s</xliff:g>:"</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps (4994455315457996796) -->
+ <skip />
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" և "</string>
<string name="ongoing_privacy_dialog_using_op" msgid="426635338010011796">"Օգտագործվում է <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> հավելվածի կողմից"</string>
@@ -1166,7 +1179,7 @@
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"միացնել/անջատել"</string>
<string name="accessibility_floating_button_action_edit" msgid="1688227814600463987">"Փոփոխել"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Սարքերի կառավարման տարրեր"</string>
- <string name="controls_providers_title" msgid="6879775889857085056">"Ընտրեք հավելված` կառավարման տարրեր ավելացնելու համար"</string>
+ <string name="controls_providers_title" msgid="6879775889857085056">"Ընտրեք հավելված՝ կառավարման տարրեր ավելացնելու համար"</string>
<string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{Ավելացվեց կառավարման # տարր։}one{Ավելացվեց կառավարման # տարր։}other{Ավելացվեց կառավարման # տարր։}}"</string>
<string name="controls_removed" msgid="3731789252222856959">"Հեռացված է"</string>
<string name="controls_panel_authorization_title" msgid="267429338785864842">"Ավելացնե՞լ <xliff:g id="APPNAME">%s</xliff:g> հավելվածը"</string>
@@ -1508,11 +1521,13 @@
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"Ավելացնել դյուրանցում"</string>
<string name="shortcut_helper_delete_shortcut_button_label" msgid="3148773472696137052">"Ջնջել դյուրանցումը"</string>
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Կողմնորոշվեք ձեր ստեղնաշարի օգնությամբ"</string>
- <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Սովորեք օգտագործել ստեղնային դյուրանցումները"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2131074100205028210) -->
+ <skip />
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Կողմնորոշվեք ձեր հպահարթակի օգնությամբ"</string>
<string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Սովորեք օգտագործել հպահարթակի ժեստերը"</string>
<string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Կողմնորոշվեք ստեղնաշարի և հպահարթակի օգնությամբ"</string>
- <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Սովորեք օգտագործել հպահարթակի ժեստերը, ստեղնային դյուրանցումները և ավելին"</string>
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (8557098239919786046) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Ինչպես հետ գնալ"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Ինչպես անցնել հիմնական էկրան"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Դիտել վերջին հավելվածները"</string>
@@ -1582,4 +1597,7 @@
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Արագ կարգավորումների բոլոր սալիկները կզրոյացվեն սարքի սկզբնական կարգավորումների համաձայն։"</string>
<string name="demote_explain_text" msgid="3942301497888762295">"«<xliff:g id="APPLICATION">%1$s</xliff:g>» հավելվածի տվյալներն այլևս չեն թարմացվի իրական ժամանակում։ Այս պարամետրը ցանկացած ժամանակ կարող եք փոփոխել Կարգավորումներում։"</string>
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
+ <string name="underlay_close_button_content_description" msgid="6518481455548869894">"Փակել"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="7666159428113013319">"Ծանուցումները բացելու համար սահեցրեք մատը վերևի ձախ անկյունից"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="3842355853737057332">"Արագ կարգավորումները բացելու համար սահեցրեք մատը վերևի աջ անկյունից"</string>
</resources>
diff --git a/packages/SystemUI/res/values-in-ldrtl/strings.xml b/packages/SystemUI/res/values-in-ldrtl/strings.xml
index 0e6e4a3..ac11e93 100644
--- a/packages/SystemUI/res/values-in-ldrtl/strings.xml
+++ b/packages/SystemUI/res/values-in-ldrtl/strings.xml
@@ -20,4 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="recents_quick_scrub_onboarding" msgid="2452671841151577157">"Tarik ke kiri untuk beralih ke aplikasi dengan cepat"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="6543179539438220549">"Geser dari kanan atas untuk membuka notifikasi"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="5760503283520058116">"Geser dari kiri atas untuk membuka Setelan Cepat"</string>
</resources>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index 23ef9b5..4beba58 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -380,13 +380,13 @@
<string name="quick_settings_work_mode_label" msgid="6440531507319809121">"Aplikasi kerja"</string>
<string name="quick_settings_work_mode_paused_state" msgid="6681788236383735976">"Dijeda"</string>
<string name="quick_settings_night_display_label" msgid="8180030659141778180">"Cahaya Malam"</string>
- <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Aktif saat malam"</string>
+ <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Aktif saat senja"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Sampai pagi"</string>
<string name="quick_settings_night_secondary_label_on_at" msgid="3584738542293528235">"Aktif pada <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_secondary_label_until" msgid="1883981263191927372">"Hingga <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_ui_mode_night_label" msgid="1398928270610780470">"Tema gelap"</string>
<string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"Penghemat Baterai"</string>
- <string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"Aktif saat malam"</string>
+ <string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"Aktif saat senja"</string>
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Sampai pagi"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Aktif pada <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Sampai <xliff:g id="TIME">%s</xliff:g>"</string>
@@ -443,9 +443,9 @@
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Berhenti memblokir mikrofon perangkat?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Berhenti memblokir kamera perangkat?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Berhenti memblokir kamera dan mikrofon perangkat?"</string>
- <string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"Ini akan berhenti memblokir akses untuk semua aplikasi dan layanan yang diizinkan menggunakan mikrofon."</string>
- <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Ini akan berhenti memblokir akses untuk semua aplikasi dan layanan yang diizinkan menggunakan kamera."</string>
- <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Langkah ini akan berhenti memblokir akses untuk semua aplikasi dan layanan yang diizinkan menggunakan kamera atau mikrofon."</string>
+ <string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"Tindakan ini akan berhenti memblokir akses untuk semua aplikasi dan layanan yang diizinkan menggunakan mikrofon."</string>
+ <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Tindakan ini akan berhenti memblokir akses untuk semua aplikasi dan layanan yang diizinkan menggunakan kamera."</string>
+ <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Tindakan ini akan berhenti memblokir akses untuk semua aplikasi dan layanan yang diizinkan menggunakan kamera atau mikrofon."</string>
<string name="sensor_privacy_start_use_mic_blocked_dialog_title" msgid="2640140287496469689">"Mikrofon diblokir"</string>
<string name="sensor_privacy_start_use_camera_blocked_dialog_title" msgid="7398084286822440384">"Kamera diblokir"</string>
<string name="sensor_privacy_start_use_mic_camera_blocked_dialog_title" msgid="195236134743281973">"Mikrofon & kamera diblokir"</string>
@@ -467,6 +467,8 @@
<string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"Untuk menggunakan tombol mikrofon, aktifkan akses mikrofon di Setelan."</string>
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Buka Setelan"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Perangkat lainnya"</string>
+ <string name="media_suggestion_disconnected_text" msgid="8777932417076437525">"Putar di <xliff:g id="DEVICE_NAME">%s</xliff:g>"</string>
+ <string name="media_suggestion_failure_text" msgid="8453022394887613774">"Tidak dapat terhubung. Coba lagi."</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Aktifkan Ringkasan"</string>
<string name="zen_modes_dialog_title" msgid="8854640808100096934">"Mode"</string>
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Selesai"</string>
@@ -582,6 +584,7 @@
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
<skip />
<string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Bagikan seluruh layar"</string>
+ <string name="screen_share_permission_dialog_option_text_entire_screen_for_display" msgid="1545850238689169166">"Bagikan <xliff:g id="DISPLAY_NAME">%s</xliff:g>"</string>
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
<skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"JIka Anda membagikan seluruh layar, semua hal yang ada di layar Anda akan terlihat oleh <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Jadi, berhati-hatilah saat memasukkan sandi, detail pembayaran, pesan, foto, audio, dan video."</string>
@@ -593,6 +596,7 @@
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Transmisikan layar?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Transmisikan satu aplikasi"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Transmisikan seluruh layar"</string>
+ <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen_for_display" msgid="5524817761472941086">"Transmisikan <xliff:g id="DISPLAY_NAME">%s</xliff:g>"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"JIka Anda mentransmisikan seluruh layar, semua hal yang ada di layar Anda akan terlihat. Jadi, berhati-hatilah saat memasukkan sandi, detail pembayaran, pesan, foto, audio, dan video."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Jika Anda mentransmisikan aplikasi, semua hal yang ditampilkan atau diputar di aplikasi tersebut akan terlihat. Jadi, berhati-hatilah saat memasukkan sandi, detail pembayaran, pesan, foto, audio, dan video."</string>
<string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Transmisikan layar"</string>
@@ -673,6 +677,7 @@
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Tetap terbuka kuncinya oleh TrustAgent"</string>
<string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"Perangkat dikunci, terlalu banyak upaya autentikasi"</string>
<string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"Perangkat dikunci\nAutentikasi gagal"</string>
+ <string name="keyguard_indication_after_watch_disconnected" msgid="6329761892437377710">"Dikunci oleh smartwatch Anda"</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="accessibility_volume_settings" msgid="1458961116951564784">"Setelan suara"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Otomatis beri teks di media"</string>
@@ -716,7 +721,7 @@
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Getar"</string>
<string name="volume_ringer_status_silent" msgid="3691324657849880883">"Bisukan"</string>
<string name="media_device_cast" msgid="4786241789687569892">"Transmisi"</string>
- <string name="stream_notification_unavailable" msgid="4313854556205836435">"Tidak tersedia - Volume dering dibisukan"</string>
+ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Tidak tersedia karena dering dibisukan"</string>
<string name="stream_alarm_unavailable" msgid="4059817189292197839">"Tidak tersedia - Fitur Jangan Ganggu aktif"</string>
<string name="stream_media_unavailable" msgid="6823020894438959853">"Tidak tersedia - Fitur Jangan Ganggu aktif"</string>
<string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Tidak tersedia karena <xliff:g id="MODE">%s</xliff:g> aktif"</string>
@@ -799,6 +804,10 @@
<string name="enable_bluetooth_message" msgid="6740938333772779717">"Untuk menghubungkan keyboard dengan tablet, terlebih dahulu aktifkan Bluetooth."</string>
<string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Aktifkan"</string>
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Aktif - Berbasis deteksi wajah"</string>
+ <!-- no translation found for notification_guts_bundle_title (1345506995443305361) -->
+ <skip />
+ <!-- no translation found for notification_guts_bundle_summary (3971530802237393600) -->
+ <skip />
<string name="inline_done_button" msgid="6043094985588909584">"Selesai"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Terapkan"</string>
<string name="inline_turn_off_notifications" msgid="2653064779176881329">"Nonaktifkan"</string>
@@ -984,6 +993,7 @@
<string name="right_icon" msgid="1103955040645237425">"Ikon kanan"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Tahan dan tarik untuk menambahkan kartu"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Tahan dan tarik untuk menata ulang kartu"</string>
+ <string name="select_to_rearrange_tiles" msgid="6166153534235179148">"Pilih kartu untuk mengatur ulang dan mengubah ukuran"</string>
<string name="tap_to_position_tile" msgid="6282815817773342757">"Ketuk untuk memposisikan kartu"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Tarik ke sini untuk menghapus"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Anda membutuhkan setidaknya <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> kartu"</string>
@@ -1003,10 +1013,12 @@
<string name="tuner_low_priority" msgid="8412666814123009820">"Tampilkan ikon notifikasi prioritas rendah"</string>
<string name="other" msgid="429768510980739978">"Lainnya"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"mengubah ukuran kartu"</string>
- <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"menghapus kartu"</string>
+ <!-- no translation found for accessibility_qs_edit_remove_tile_action (4811778796933958616) -->
+ <skip />
<string name="accessibility_qs_edit_toggle_placement_mode" msgid="3870429389210569610">"aktifkan/nonaktifkan mode penempatan"</string>
<string name="accessibility_qs_edit_toggle_selection" msgid="2201248304072372239">"aktifkan/nonaktifkan pilihan"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"menambahkan kartu ke posisi terakhir"</string>
+ <!-- no translation found for accessibility_qs_edit_tile_add_action (7188303979684400620) -->
+ <skip />
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Pindahkan kartu"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Tambahkan kartu ke posisi yang diinginkan"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Pindahkan ke <xliff:g id="POSITION">%1$d</xliff:g>"</string>
@@ -1093,7 +1105,8 @@
<string name="auto_saver_text" msgid="3214960308353838764">"Aktifkan jika daya baterai kemungkinan akan habis"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Tidak, terima kasih"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"Sedang digunakan"</string>
- <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Aplikasi menggunakan <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps (4994455315457996796) -->
+ <skip />
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" dan "</string>
<string name="ongoing_privacy_dialog_using_op" msgid="426635338010011796">"Sedang digunakan oleh <xliff:g id="APPLICATION_NAME">%1$s</xliff:g>"</string>
@@ -1508,11 +1521,13 @@
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"Tambahkan pintasan"</string>
<string name="shortcut_helper_delete_shortcut_button_label" msgid="3148773472696137052">"Hapus pintasan"</string>
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Menggunakan keyboard untuk navigasi"</string>
- <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Pelajari pintasan keyboard"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2131074100205028210) -->
+ <skip />
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Menavigasi menggunakan touchpad"</string>
<string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Pelajari gestur touchpad"</string>
<string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Menavigasi menggunakan keyboard dan touchpad"</string>
- <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Pelajari gestur touchpad, pintasan keyboard, dan lainnya"</string>
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (8557098239919786046) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Kembali"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Buka layar utama"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Lihat aplikasi terbaru"</string>
@@ -1549,7 +1564,7 @@
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Lampu latar keyboard"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Tingkat %1$d dari %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Kontrol Rumah"</string>
- <string name="home_controls_dream_description" msgid="4644150952104035789">"Akses cepat kontrol rumah Anda sebagai screensaver"</string>
+ <string name="home_controls_dream_description" msgid="4644150952104035789">"Akses cepat kontrol rumah sebagai screensaver"</string>
<string name="volume_undo_action" msgid="5815519725211877114">"Urungkan"</string>
<string name="back_edu_toast_content" msgid="4530314597378982956">"Untuk kembali, geser ke kiri atau kanan menggunakan tiga jari di touchpad"</string>
<string name="home_edu_toast_content" msgid="3381071147871955415">"Untuk membuka layar utama, geser ke atas menggunakan tiga jari di touchpad"</string>
@@ -1582,4 +1597,7 @@
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Semua kartu Setelan Cepat akan direset ke setelan asli perangkat"</string>
<string name="demote_explain_text" msgid="3942301497888762295">"<xliff:g id="APPLICATION">%1$s</xliff:g> tidak akan lagi menampilkan Info Terbaru Langsung. Anda dapat mengubahnya kapan saja di Setelan."</string>
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
+ <string name="underlay_close_button_content_description" msgid="6518481455548869894">"Tutup"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="7666159428113013319">"Geser dari kiri atas untuk membuka notifikasi"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="3842355853737057332">"Geser dari kanan atas untuk membuka Setelan Cepat"</string>
</resources>
diff --git a/packages/SystemUI/res/values-is-ldrtl/strings.xml b/packages/SystemUI/res/values-is-ldrtl/strings.xml
index 971a1d0..7a413f9 100644
--- a/packages/SystemUI/res/values-is-ldrtl/strings.xml
+++ b/packages/SystemUI/res/values-is-ldrtl/strings.xml
@@ -20,4 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="recents_quick_scrub_onboarding" msgid="2452671841151577157">"Dragðu til vinstri til að skipta hratt á milli forrita"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="6543179539438220549">"Strjúktu til vinstri efst á skjánum til að opna tilkynningar"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="5760503283520058116">"Strjúktu til hægri efst á skjánum til að opna flýtistillingar"</string>
</resources>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index a309027..3a8b70c 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -252,8 +252,7 @@
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Tengt við <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"Tengt við <xliff:g id="CAST">%s</xliff:g>."</string>
<string name="accessibility_expand_group" msgid="521237935987978624">"Stækka hóp."</string>
- <!-- no translation found for accessibility_collapse_group (1842704016768329517) -->
- <skip />
+ <string name="accessibility_collapse_group" msgid="1842704016768329517">"Minnka hóp."</string>
<string name="accessibility_add_device_to_group" msgid="5446422960697860806">"Bæta tæki við hóp."</string>
<string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"Fjarlægja tæki úr hóp."</string>
<string name="accessibility_open_application" msgid="1749126077501259712">"Opna forrit."</string>
@@ -468,6 +467,8 @@
<string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"Veittu aðgang að hljóðnema í stillingunum til að nota hljóðnemahnappinn."</string>
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Opna stillingar"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Annað tæki"</string>
+ <string name="media_suggestion_disconnected_text" msgid="8777932417076437525">"Spila í <xliff:g id="DEVICE_NAME">%s</xliff:g>"</string>
+ <string name="media_suggestion_failure_text" msgid="8453022394887613774">"Ekki tókst að tengja. Reyndu aftur."</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Kveikja/slökkva á yfirliti"</string>
<string name="zen_modes_dialog_title" msgid="8854640808100096934">"Stillingar"</string>
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Lokið"</string>
@@ -583,6 +584,7 @@
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
<skip />
<string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Deila öllum skjánum"</string>
+ <string name="screen_share_permission_dialog_option_text_entire_screen_for_display" msgid="1545850238689169166">"Deila <xliff:g id="DISPLAY_NAME">%s</xliff:g>"</string>
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
<skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Þegar þú deilir öllum skjánum verður allt á skjánum sýnilegt <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Passaðu því upp á aðgangsorð, greiðsluupplýsingar, skilaboð, myndir, hljóð og myndskeið."</string>
@@ -594,6 +596,7 @@
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Varpa skjánum?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Varpa einu forriti"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Varpa öllum skjánum"</string>
+ <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen_for_display" msgid="5524817761472941086">"Varpa <xliff:g id="DISPLAY_NAME">%s</xliff:g>"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Þegar þú varpar öllum skjánum þá er allt á skjánum sýnilegt. Passaðu því upp á aðgangsorð, greiðsluupplýsingar, skilaboð, myndir, hljóð og myndskeið."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Þegar þú varpar forriti er allt sem sést eða er spilað í því forriti sýnilegt. Passaðu því upp á aðgangsorð, greiðsluupplýsingar, skilaboð, myndir, hljóð og myndskeið."</string>
<string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Varpa skjá"</string>
@@ -674,6 +677,7 @@
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Haldið opnu af TrustAgent"</string>
<string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"Tæki var læst, of margar auðkenningartilraunir"</string>
<string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"Tæki læst\nAuðkenning mistókst"</string>
+ <string name="keyguard_indication_after_watch_disconnected" msgid="6329761892437377710">"Læst af úrinu"</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="accessibility_volume_settings" msgid="1458961116951564784">"Hljóðstillingar"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Sjálfvirkir skjátextar"</string>
@@ -800,6 +804,10 @@
<string name="enable_bluetooth_message" msgid="6740938333772779717">"Til að geta tengt lyklaborðið við spjaldtölvuna þarftu fyrst að kveikja á Bluetooth."</string>
<string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Kveikja"</string>
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Kveikt – út frá andliti"</string>
+ <!-- no translation found for notification_guts_bundle_title (1345506995443305361) -->
+ <skip />
+ <!-- no translation found for notification_guts_bundle_summary (3971530802237393600) -->
+ <skip />
<string name="inline_done_button" msgid="6043094985588909584">"Lokið"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Nota"</string>
<string name="inline_turn_off_notifications" msgid="2653064779176881329">"Slökkva"</string>
@@ -985,6 +993,7 @@
<string name="right_icon" msgid="1103955040645237425">"Tákn til hægri"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Haltu inni og dragðu til að bæta við flísum"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Haltu og dragðu til að endurraða flísum"</string>
+ <string name="select_to_rearrange_tiles" msgid="6166153534235179148">"Velja reiti til að endurraða og breyta stærð"</string>
<string name="tap_to_position_tile" msgid="6282815817773342757">"Ýttu til að staðsetja flís"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Dragðu hingað til að fjarlægja"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Flísarnar mega ekki vera færri en <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g>"</string>
@@ -1004,10 +1013,12 @@
<string name="tuner_low_priority" msgid="8412666814123009820">"Sýna tákn fyrir tilkynningar með litlum forgangi"</string>
<string name="other" msgid="429768510980739978">"Annað"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"breyta stærð reitsins"</string>
- <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"fjarlægja flís"</string>
+ <!-- no translation found for accessibility_qs_edit_remove_tile_action (4811778796933958616) -->
+ <skip />
<string name="accessibility_qs_edit_toggle_placement_mode" msgid="3870429389210569610">"kveikja/slökkva á staðsetningarstillingu"</string>
<string name="accessibility_qs_edit_toggle_selection" msgid="2201248304072372239">"breyta vali"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"bæta reit við síðustu stöðu"</string>
+ <!-- no translation found for accessibility_qs_edit_tile_add_action (7188303979684400620) -->
+ <skip />
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Færa flís"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Bæta reit við óskaða stöðu"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Færa í <xliff:g id="POSITION">%1$d</xliff:g>"</string>
@@ -1094,7 +1105,8 @@
<string name="auto_saver_text" msgid="3214960308353838764">"Kveikja þegar rafhlaða er við það að klárast"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Nei, takk"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"Í notkun"</string>
- <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Forrit eru að nota <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps (4994455315457996796) -->
+ <skip />
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" og "</string>
<string name="ongoing_privacy_dialog_using_op" msgid="426635338010011796">"Í notkun af <xliff:g id="APPLICATION_NAME">%1$s</xliff:g>"</string>
@@ -1251,15 +1263,13 @@
<string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(aftengt)"</string>
<string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"Ekki er hægt að skipta. Ýttu til að reyna aftur."</string>
<string name="media_output_dialog_pairing_new" msgid="5098212763195577270">"Tengja tæki"</string>
- <!-- no translation found for media_output_dialog_button_connect_device (2819038239946643550) -->
- <skip />
+ <string name="media_output_dialog_button_connect_device" msgid="2819038239946643550">"Tengja tæki"</string>
<string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Óþekkt forrit"</string>
<string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Stöðva útsendingu"</string>
<string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"Tæki í boði fyrir hljóðúttak."</string>
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Hljóðstyrkur"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
- <!-- no translation found for media_output_group_title_connected_speakers (3413876972919684479) -->
- <skip />
+ <string name="media_output_group_title_connected_speakers" msgid="3413876972919684479">"Tengdir hátalarar"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Hátalarar og skjáir"</string>
<string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Tillögur að tækjum"</string>
<string name="media_input_group_title" msgid="2057057473860783021">"Inntak"</string>
@@ -1328,10 +1338,8 @@
<string name="accessibility_enter_hint" msgid="2617864063504824834">"opna tæki"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Opna með fingrafari"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Auðkenningar krafist. Auðkenndu með því að snerta fingrafaralesarann."</string>
- <!-- no translation found for resume_animation (9148788411429330671) -->
- <skip />
- <!-- no translation found for pause_animation (1919705510058107504) -->
- <skip />
+ <string name="resume_animation" msgid="9148788411429330671">"Halda áfram að spila hreyfimynd"</string>
+ <string name="pause_animation" msgid="1919705510058107504">"Gera hlé á hreyfimynd"</string>
<string name="ongoing_call_content_description" msgid="6394763878322348560">"Símtal í gangi"</string>
<string name="ongoing_notification_extra_content_description" msgid="2098752668861351265">"Í gangi"</string>
<string name="mobile_data_settings_title" msgid="3955246641380064901">"Farsímagögn"</string>
@@ -1513,11 +1521,13 @@
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"Bæta flýtileið við"</string>
<string name="shortcut_helper_delete_shortcut_button_label" msgid="3148773472696137052">"Eyða flýtileið"</string>
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Flettu með því að nota lyklaborðið"</string>
- <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Kynntu þér flýtilykla"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2131074100205028210) -->
+ <skip />
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Flettu með því að nota snertiflötinn"</string>
<string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Nánar um bendingar á snertifleti"</string>
<string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Flettu með því að nota lyklaborðið og snertiflötinn"</string>
- <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Kynntu þér bendingar á snertifleti, flýtilykla og fleira"</string>
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (8557098239919786046) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Til baka"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Fara á heimaskjá"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Sjá nýleg forrit"</string>
@@ -1587,4 +1597,7 @@
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Allir flýtistillingareitir munu endurstillast á upprunalegar stillingar tækisins"</string>
<string name="demote_explain_text" msgid="3942301497888762295">"<xliff:g id="APPLICATION">%1$s</xliff:g> mun ekki lengur birta uppfærslur í beinni. Þú getur breytt þessu hvenær sem er í stillingunum."</string>
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
+ <string name="underlay_close_button_content_description" msgid="6518481455548869894">"Loka"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="7666159428113013319">"Strjúktu til hægri efst á skjánum til að opna tilkynningar"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="3842355853737057332">"Strjúktu til vinstri efst á skjánum til að opna flýtistillingar"</string>
</resources>
diff --git a/packages/SystemUI/res/values-it-ldrtl/strings.xml b/packages/SystemUI/res/values-it-ldrtl/strings.xml
index b48cdd5..e56007c 100644
--- a/packages/SystemUI/res/values-it-ldrtl/strings.xml
+++ b/packages/SystemUI/res/values-it-ldrtl/strings.xml
@@ -20,4 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="recents_quick_scrub_onboarding" msgid="2452671841151577157">"Trascina verso sinistra per cambiare velocemente app"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="6543179539438220549">"Scorri dall\'angolo in alto a destra per aprire le notifiche"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="5760503283520058116">"Scorri dall\'angolo in alto a sinistra per aprire le Impostazioni rapide"</string>
</resources>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index 44e02a1..eba2fc4 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -467,6 +467,8 @@
<string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"Per usare il pulsante del microfono devi attivare l\'accesso al microfono nelle Impostazioni."</string>
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Apri Impostazioni"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Altro dispositivo"</string>
+ <string name="media_suggestion_disconnected_text" msgid="8777932417076437525">"Riproduci su <xliff:g id="DEVICE_NAME">%s</xliff:g>"</string>
+ <string name="media_suggestion_failure_text" msgid="8453022394887613774">"Impossibile connettersi. Riprova."</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Attiva/disattiva la panoramica"</string>
<string name="zen_modes_dialog_title" msgid="8854640808100096934">"Modalità"</string>
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Fine"</string>
@@ -582,6 +584,7 @@
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
<skip />
<string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Condividi schermo intero"</string>
+ <string name="screen_share_permission_dialog_option_text_entire_screen_for_display" msgid="1545850238689169166">"Condividi <xliff:g id="DISPLAY_NAME">%s</xliff:g>"</string>
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
<skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Quando condividi lo schermo intero, tutto ciò che è nella schermata è visibile a <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Presta quindi attenzione a password, dati di pagamento, messaggi, foto, audio e video."</string>
@@ -593,6 +596,7 @@
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Trasmettere lo schermo?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Trasmetti un\'app"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Trasmetti schermo intero"</string>
+ <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen_for_display" msgid="5524817761472941086">"Trasmetti <xliff:g id="DISPLAY_NAME">%s</xliff:g>"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Quando trasmetti lo schermo intero, tutto ciò che è nella schermata è visibile. Presta quindi attenzione a password, dati di pagamento, messaggi, foto, audio e video."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Quando trasmetti un\'app, tutto ciò che viene mostrato o riprodotto al suo interno è visibile. Presta quindi attenzione a password, dati di pagamento, messaggi, foto, audio e video."</string>
<string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Trasmetti schermo"</string>
@@ -673,6 +677,7 @@
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Sbloccato da TrustAgent"</string>
<string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"Dispositivo bloccato: troppi tentativi di autenticazione"</string>
<string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"Dispositivo bloccato\nAutenticazione non riuscita"</string>
+ <string name="keyguard_indication_after_watch_disconnected" msgid="6329761892437377710">"Dispositivo bloccato dallo smartwatch"</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="accessibility_volume_settings" msgid="1458961116951564784">"Impostazioni audio"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Sottotitoli automatici"</string>
@@ -799,6 +804,8 @@
<string name="enable_bluetooth_message" msgid="6740938333772779717">"Per connettere la tastiera al tablet, devi prima attivare il Bluetooth."</string>
<string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Attiva"</string>
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"On - Rotazione basata sul viso"</string>
+ <string name="notification_guts_bundle_title" msgid="1345506995443305361">"Usa Organizzatore notifiche"</string>
+ <string name="notification_guts_bundle_summary" msgid="3971530802237393600">"Per <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="inline_done_button" msgid="6043094985588909584">"Fine"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Applica"</string>
<string name="inline_turn_off_notifications" msgid="2653064779176881329">"Disattiva"</string>
@@ -984,6 +991,7 @@
<string name="right_icon" msgid="1103955040645237425">"Icona destra"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Tieni premuto e trascina per aggiungere riquadri"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Tieni premuto e trascina per riordinare i riquadri"</string>
+ <string name="select_to_rearrange_tiles" msgid="6166153534235179148">"Seleziona i riquadri per riorganizzarli e ridimensionarli"</string>
<string name="tap_to_position_tile" msgid="6282815817773342757">"Tocca per posizionare il riquadro"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Trascina qui per rimuovere"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Occorrono almeno <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> schede"</string>
@@ -1003,10 +1011,10 @@
<string name="tuner_low_priority" msgid="8412666814123009820">"Mostra icone di notifiche con priorità bassa"</string>
<string name="other" msgid="429768510980739978">"Altro"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"attivare/disattivare le dimensioni del riquadro"</string>
- <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"rimuovere il riquadro"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="4811778796933958616">"Rimuovi riquadro"</string>
<string name="accessibility_qs_edit_toggle_placement_mode" msgid="3870429389210569610">"attivare/disattivare la modalità di posizionamento"</string>
<string name="accessibility_qs_edit_toggle_selection" msgid="2201248304072372239">"attivare/disattivare la selezione"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"aggiungere il riquadro all\'ultima posizione"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="7188303979684400620">"Aggiungi riquadro all\'ultima posizione"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Sposta riquadro"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Aggiungi il riquadro alla posizione desiderata"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Sposta nella posizione <xliff:g id="POSITION">%1$d</xliff:g>"</string>
@@ -1093,7 +1101,7 @@
<string name="auto_saver_text" msgid="3214960308353838764">"Attiva questa funzionalità se è probabile che la batteria si scarichi"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"No, grazie"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"In uso"</string>
- <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Le app stanno usando <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
+ <string name="ongoing_privacy_chip_content_multiple_apps" msgid="4994455315457996796">"<xliff:g id="TYPES_LIST">%s</xliff:g> in uso."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" e "</string>
<string name="ongoing_privacy_dialog_using_op" msgid="426635338010011796">"Attualmente in uso per <xliff:g id="APPLICATION_NAME">%1$s</xliff:g>"</string>
@@ -1508,11 +1516,11 @@
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"Aggiungi scorciatoia"</string>
<string name="shortcut_helper_delete_shortcut_button_label" msgid="3148773472696137052">"Elimina scorciatoia"</string>
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Naviga usando la tastiera"</string>
- <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Informazioni sulle scorciatoie da tastiera"</string>
+ <string name="launch_keyboard_tutorial_notification_content" msgid="2131074100205028210">"Informazioni sulle scorciatoie da tastiera"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Naviga usando il touchpad"</string>
<string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Impara i gesti con il touchpad"</string>
<string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Naviga usando la tastiera e il touchpad"</string>
- <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Impara i gesti con il touchpad, le scorciatoie da tastiera e altro ancora"</string>
+ <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="8557098239919786046">"Impara i gesti con il touchpad, le scorciatoie da tastiera e altro ancora"</string>
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Indietro"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Vai alla schermata Home"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Visualizza app recenti"</string>
@@ -1582,4 +1590,7 @@
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Tutti i riquadri Impostazioni rapide verranno reimpostati sulle impostazioni originali del dispositivo"</string>
<string name="demote_explain_text" msgid="3942301497888762295">"<xliff:g id="APPLICATION">%1$s</xliff:g> non mostrerà più gli aggiornamenti in tempo reale. Puoi modificare questa opzione in qualsiasi momento in Impostazioni."</string>
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
+ <string name="underlay_close_button_content_description" msgid="6518481455548869894">"Chiudi"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="7666159428113013319">"Scorri dall\'angolo in alto a sinistra per aprire le notifiche"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="3842355853737057332">"Scorri dall\'angolo in alto a destra per aprire le Impostazioni rapide"</string>
</resources>
diff --git a/packages/SystemUI/res/values-it/tiles_states_strings.xml b/packages/SystemUI/res/values-it/tiles_states_strings.xml
index 2d3f553..65d0bab 100644
--- a/packages/SystemUI/res/values-it/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-it/tiles_states_strings.xml
@@ -193,7 +193,7 @@
</string-array>
<string-array name="tile_states_hearing_devices">
<item msgid="1235334096484287173">"Non disponibile"</item>
- <item msgid="3079622119444911877">"Disattivi"</item>
+ <item msgid="3079622119444911877">"Off"</item>
<item msgid="3028994095749238254">"Attivi"</item>
</string-array>
<string-array name="tile_states_notes">
diff --git a/packages/SystemUI/res/values-iw-ldrtl/strings.xml b/packages/SystemUI/res/values-iw-ldrtl/strings.xml
index 16a6110..ab3d894 100644
--- a/packages/SystemUI/res/values-iw-ldrtl/strings.xml
+++ b/packages/SystemUI/res/values-iw-ldrtl/strings.xml
@@ -20,4 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="recents_quick_scrub_onboarding" msgid="2452671841151577157">"יש לגרור שמאלה כדי לעבור במהירות בין אפליקציות"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="6543179539438220549">"כדי לפתוח את ההתראות, מחליקים מהפינה השמאלית העליונה"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="5760503283520058116">"כדי לפתוח את ההגדרות המהירות, מחליקים מהפינה הימנית העליונה"</string>
</resources>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index 5f3d24d..fb70db3 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -202,10 +202,10 @@
<string name="biometric_dialog_cancel_authentication" msgid="981316588773442637">"ביטול האימות"</string>
<string name="biometric_dialog_content_view_more_options_button" msgid="2663810393874865475">"אפשרויות נוספות"</string>
<string name="biometric_dialog_use_pin" msgid="8385294115283000709">"שימוש בקוד אימות"</string>
- <string name="biometric_dialog_use_pattern" msgid="2315593393167211194">"שימוש בקו ביטול נעילה"</string>
+ <string name="biometric_dialog_use_pattern" msgid="2315593393167211194">"שימוש בקו פתיחת נעילה"</string>
<string name="biometric_dialog_use_password" msgid="3445033859393474779">"שימוש בסיסמה"</string>
<string name="biometric_dialog_wrong_pin" msgid="1878539073972762803">"קוד אימות שגוי"</string>
- <string name="biometric_dialog_wrong_pattern" msgid="8954812279840889029">"קו ביטול נעילה שגוי"</string>
+ <string name="biometric_dialog_wrong_pattern" msgid="8954812279840889029">"קו פתיחת נעילה שגוי"</string>
<string name="biometric_dialog_wrong_password" msgid="69477929306843790">"סיסמה שגויה"</string>
<string name="biometric_dialog_credential_too_many_attempts" msgid="3083141271737748716">"יותר מדי ניסיונות שגויים.\nיש לנסות שוב בעוד <xliff:g id="NUMBER">%d</xliff:g> שניות."</string>
<string name="work_challenge_emergency_button_text" msgid="8946588434515599288">"חירום"</string>
@@ -252,8 +252,7 @@
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"התבצע חיבור אל <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"מחובר אל <xliff:g id="CAST">%s</xliff:g>."</string>
<string name="accessibility_expand_group" msgid="521237935987978624">"הרחבת הקבוצה."</string>
- <!-- no translation found for accessibility_collapse_group (1842704016768329517) -->
- <skip />
+ <string name="accessibility_collapse_group" msgid="1842704016768329517">"כיווץ הקבוצה"</string>
<string name="accessibility_add_device_to_group" msgid="5446422960697860806">"הוספת מכשיר לקבוצה"</string>
<string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"הסרת מכשיר מקבוצה"</string>
<string name="accessibility_open_application" msgid="1749126077501259712">"פתיחת האפליקציה."</string>
@@ -341,7 +340,7 @@
<string name="quick_settings_camera_label" msgid="5612076679385269339">"גישה למצלמה"</string>
<string name="quick_settings_mic_label" msgid="8392773746295266375">"גישה למיקרופון"</string>
<string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"יש גישה"</string>
- <string name="quick_settings_camera_mic_blocked" msgid="4710884905006788281">"הגישה חסומה"</string>
+ <string name="quick_settings_camera_mic_blocked" msgid="4710884905006788281">"חסומה"</string>
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"מכשיר מדיה"</string>
<string name="quick_settings_user_title" msgid="8673045967216204537">"משתמש"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
@@ -468,6 +467,8 @@
<string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"כדי להשתמש בכפתור המיקרופון יש להפעיל את הגישה למיקרופון ב\'הגדרות\'."</string>
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"פתיחת ההגדרות"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"מכשיר אחר"</string>
+ <string name="media_suggestion_disconnected_text" msgid="8777932417076437525">"הפעלה במכשיר <xliff:g id="DEVICE_NAME">%s</xliff:g>"</string>
+ <string name="media_suggestion_failure_text" msgid="8453022394887613774">"לא ניתן להתחבר. אפשר לנסות שוב."</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"החלפת מצב של מסכים אחרונים"</string>
<string name="zen_modes_dialog_title" msgid="8854640808100096934">"מצבים"</string>
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"סיום"</string>
@@ -583,6 +584,7 @@
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
<skip />
<string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"שיתוף כל המסך"</string>
+ <string name="screen_share_permission_dialog_option_text_entire_screen_for_display" msgid="1545850238689169166">"שיתוף של <xliff:g id="DISPLAY_NAME">%s</xliff:g>"</string>
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
<skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"כשמשתפים את כל המסך, כל מה שמופיע בו יהיה גלוי ל-<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. מומלץ להיזהר עם סיסמאות, פרטי תשלום, הודעות, תמונות, אודיו וסרטונים."</string>
@@ -594,6 +596,7 @@
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"להפעיל Cast של המסך?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"הפעלת Cast של אפליקציה אחת"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"הפעלת Cast של כל המסך"</string>
+ <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen_for_display" msgid="5524817761472941086">"הפעלת Cast של <xliff:g id="DISPLAY_NAME">%s</xliff:g>"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"כשמפעילים Cast של כל המסך, כל מה שמופיע בו יהיה גלוי לצופים. מומלץ להיזהר עם סיסמאות, פרטי תשלום, הודעות, תמונות, אודיו וסרטונים."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"כשמפעילים Cast של אפליקציה, כל מה שרואים או מפעילים בה מופיע גם לצופים. מומלץ להיזהר ולא לחשוף פרטים אישיים כמו סיסמאות, פרטי תשלום, הודעות, תמונות, אודיו וסרטונים."</string>
<string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"הפעלת Cast של המסך"</string>
@@ -674,6 +677,7 @@
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"הנעילה נמנעת על ידי סביבה מהימנה"</string>
<string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"המכשיר ננעל, יותר מדי ניסיונות אימות"</string>
<string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"המכשיר ננעל\nהאימות נכשל"</string>
+ <string name="keyguard_indication_after_watch_disconnected" msgid="6329761892437377710">"נחסם על ידי השעון"</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="accessibility_volume_settings" msgid="1458961116951564784">"הגדרות צליל"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"הוספת כתוביות באופן אוטומטי למדיה"</string>
@@ -717,9 +721,9 @@
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"רטט"</string>
<string name="volume_ringer_status_silent" msgid="3691324657849880883">"השתקה"</string>
<string name="media_device_cast" msgid="4786241789687569892">"הפעלת Cast"</string>
- <string name="stream_notification_unavailable" msgid="4313854556205836435">"לא זמין כי הצלצול מושתק"</string>
- <string name="stream_alarm_unavailable" msgid="4059817189292197839">"לא זמין כי התכונה \'נא לא להפריע\' מופעלת"</string>
- <string name="stream_media_unavailable" msgid="6823020894438959853">"לא זמין כי התכונה \'נא לא להפריע\' מופעלת"</string>
+ <string name="stream_notification_unavailable" msgid="4313854556205836435">"אי אפשר לשנות כי הצלצול מושתק"</string>
+ <string name="stream_alarm_unavailable" msgid="4059817189292197839">"אי אפשר לשנות כי הטלפון במצב \"נא לא להפריע\""</string>
+ <string name="stream_media_unavailable" msgid="6823020894438959853">"אי אפשר לשנות כי הטלפון במצב \"נא לא להפריע\""</string>
<string name="stream_unavailable_by_modes" msgid="3674139029490353683">"לא זמין כי המצב <xliff:g id="MODE">%s</xliff:g> מופעל"</string>
<string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"לא זמין"</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. יש ללחוץ כדי לבטל את ההשתקה."</string>
@@ -800,6 +804,10 @@
<string name="enable_bluetooth_message" msgid="6740938333772779717">"כדי לחבר את המקלדת לטאבלט, תחילה עליך להפעיל את ה-Bluetooth."</string>
<string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"הפעלה"</string>
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"פועל – מבוסס על זיהוי פנים"</string>
+ <!-- no translation found for notification_guts_bundle_title (1345506995443305361) -->
+ <skip />
+ <!-- no translation found for notification_guts_bundle_summary (3971530802237393600) -->
+ <skip />
<string name="inline_done_button" msgid="6043094985588909584">"סיום"</string>
<string name="inline_ok_button" msgid="603075490581280343">"אישור"</string>
<string name="inline_turn_off_notifications" msgid="2653064779176881329">"השבתה"</string>
@@ -907,8 +915,7 @@
<string name="group_system_cycle_back" msgid="8194102916946802902">"דפדוף אחורה באפליקציות האחרונות"</string>
<string name="group_system_access_all_apps_search" msgid="1553588630154197469">"פתיחה של רשימת האפליקציות"</string>
<string name="group_system_access_system_settings" msgid="8731721963449070017">"פתיחת ההגדרות"</string>
- <!-- no translation found for group_system_access_google_assistant (3769929488906534254) -->
- <skip />
+ <string name="group_system_access_google_assistant" msgid="3769929488906534254">"פתיחת העוזר האישי"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"נעילת המסך"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"כתיבת הערה"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"ריבוי משימות"</string>
@@ -984,13 +991,14 @@
<string name="right_keycode" msgid="2480715509844798438">"קוד מפתח ימני"</string>
<string name="left_icon" msgid="5036278531966897006">"סמל שמאלי"</string>
<string name="right_icon" msgid="1103955040645237425">"סמל ימני"</string>
- <string name="drag_to_add_tiles" msgid="8933270127508303672">"יש ללחוץ ולגרור כדי להוסיף לחצנים"</string>
+ <string name="drag_to_add_tiles" msgid="8933270127508303672">"יש ללחוץ ולגרור כדי להוסיף כפתורים"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"יש ללחוץ ולגרור כדי לסדר מחדש את כרטיסי המידע"</string>
+ <string name="select_to_rearrange_tiles" msgid="6166153534235179148">"אפשר לבחור כפתורים כדי לשנות את הסדר והגודל שלהם"</string>
<string name="tap_to_position_tile" msgid="6282815817773342757">"יש להקיש כדי למקם את המשבצת"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"אפשר לגרור לכאן כדי להסיר"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"יש צורך ב-<xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> אריחים לפחות"</string>
<string name="qs_edit" msgid="5583565172803472437">"עריכה"</string>
- <string name="qs_edit_tiles" msgid="2105215324060865035">"עריכת המשבצות"</string>
+ <string name="qs_edit_tiles" msgid="2105215324060865035">"עריכת הכפתורים"</string>
<string name="tuner_time" msgid="2450785840990529997">"שעה"</string>
<string-array name="clock_options">
<item msgid="3986445361435142273">"הצגת שעות, דקות ושניות"</item>
@@ -1005,10 +1013,12 @@
<string name="tuner_low_priority" msgid="8412666814123009820">"הצגת סמלי התראות בעדיפות נמוכה"</string>
<string name="other" msgid="429768510980739978">"אחר"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"שינוי גודל הלחצן"</string>
- <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"הסרת הלחצן"</string>
+ <!-- no translation found for accessibility_qs_edit_remove_tile_action (4811778796933958616) -->
+ <skip />
<string name="accessibility_qs_edit_toggle_placement_mode" msgid="3870429389210569610">"להפעיל או להשבית את מצב המיקום"</string>
<string name="accessibility_qs_edit_toggle_selection" msgid="2201248304072372239">"לבחור או לבטל את הבחירה"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"הוספת הלחצן במיקום האחרון"</string>
+ <!-- no translation found for accessibility_qs_edit_tile_add_action (7188303979684400620) -->
+ <skip />
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"העברת הלחצן"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"הוספת הלחצן במיקום הרצוי"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"העברה למיקום <xliff:g id="POSITION">%1$d</xliff:g>"</string>
@@ -1095,7 +1105,8 @@
<string name="auto_saver_text" msgid="3214960308353838764">"מומלץ להפעיל את התכונה כשיש סבירות גבוהה שהסוללה תתרוקן"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"לא תודה"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"בשימוש"</string>
- <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"אפליקציות משתמשות ב<xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps (4994455315457996796) -->
+ <skip />
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" וגם "</string>
<string name="ongoing_privacy_dialog_using_op" msgid="426635338010011796">"בשימוש על ידי <xliff:g id="APPLICATION_NAME">%1$s</xliff:g>"</string>
@@ -1252,15 +1263,13 @@
<string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(מנותק)"</string>
<string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"לא ניתן להחליף. צריך ללחוץ כדי לנסות שוב."</string>
<string name="media_output_dialog_pairing_new" msgid="5098212763195577270">"חיבור מכשיר"</string>
- <!-- no translation found for media_output_dialog_button_connect_device (2819038239946643550) -->
- <skip />
+ <string name="media_output_dialog_button_connect_device" msgid="2819038239946643550">"חיבור מכשיר"</string>
<string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"אפליקציה לא ידועה"</string>
<string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"עצירת ההעברה (casting)"</string>
<string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"מכשירים זמינים לפלט אודיו."</string>
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"עוצמת הקול"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
- <!-- no translation found for media_output_group_title_connected_speakers (3413876972919684479) -->
- <skip />
+ <string name="media_output_group_title_connected_speakers" msgid="3413876972919684479">"רמקולים מחוברים"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"רמקולים ומסכים"</string>
<string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"הצעות למכשירים"</string>
<string name="media_input_group_title" msgid="2057057473860783021">"קלט"</string>
@@ -1321,7 +1330,7 @@
<string name="person_available" msgid="2318599327472755472">"אונליין"</string>
<string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"בעיה בקריאת מדדי הסוללה"</string>
<string name="battery_state_unknown_notification_text" msgid="13720937839460899">"יש ללחוץ כדי להציג מידע נוסף"</string>
- <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"לא הוגדרה"</string>
+ <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"לא הוגדר"</string>
<string name="accessibility_bouncer" msgid="5896923685673320070">"הזנת קוד נעילת המסך"</string>
<string name="accessibility_side_fingerprint_indicator_label" msgid="1673807833352363712">"צריך לגעת בחיישן טביעות האצבע. זה הכפתור הקצר יותר בצד של הטלפון"</string>
<string name="accessibility_fingerprint_label" msgid="5255731221854153660">"חיישן טביעות אצבע"</string>
@@ -1329,10 +1338,8 @@
<string name="accessibility_enter_hint" msgid="2617864063504824834">"הזנת מכשיר"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"שימוש בטביעת אצבע כדי לפתוח"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"נדרש אימות. יש לגעת בחיישן טביעות האצבע כדי לבצע אימות."</string>
- <!-- no translation found for resume_animation (9148788411429330671) -->
- <skip />
- <!-- no translation found for pause_animation (1919705510058107504) -->
- <skip />
+ <string name="resume_animation" msgid="9148788411429330671">"הפעלת האנימציה שוב"</string>
+ <string name="pause_animation" msgid="1919705510058107504">"השהיית האנימציה"</string>
<string name="ongoing_call_content_description" msgid="6394763878322348560">"שיחה פעילה"</string>
<string name="ongoing_notification_extra_content_description" msgid="2098752668861351265">"בתהליך"</string>
<string name="mobile_data_settings_title" msgid="3955246641380064901">"חבילת גלישה"</string>
@@ -1514,11 +1521,13 @@
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"הוספת קיצור דרך"</string>
<string name="shortcut_helper_delete_shortcut_button_label" msgid="3148773472696137052">"מחיקת קיצור הדרך"</string>
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"ניווט באמצעות המקלדת"</string>
- <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"מידע על מקשי קיצור"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2131074100205028210) -->
+ <skip />
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"ניווט באמצעות לוח המגע"</string>
<string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"מידע על התנועות בלוח המגע"</string>
<string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"ניווט עם המקלדת ולוח המגע"</string>
- <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"כאן אפשר לקרוא איך מפעילים תנועות בלוח המגע, מקשי קיצור ועוד"</string>
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (8557098239919786046) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"חזרה"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"חזרה לדף הבית"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"הצגת האפליקציות האחרונות"</string>
@@ -1544,7 +1553,7 @@
<string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"מעבר בין אפליקציות"</string>
<string name="touchpad_switch_apps_gesture_guidance" msgid="2554844933805502538">"מחליקים ימינה עם ארבע אצבעות על לוח המגע"</string>
<string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"מעולה!"</string>
- <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"השלמת את תנועת המעבר בין האפליקציות."</string>
+ <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"הבנת איך עושים את תנועת המעבר בין אפליקציות"</string>
<string name="touchpad_switch_gesture_error_body" msgid="5895231916964677918">"כדי לעבור בין אפליקציות, מחליקים ימינה עם ארבע אצבעות על לוח המגע"</string>
<string name="tutorial_action_key_title" msgid="8172535792469008169">"צפייה בכל האפליקציות"</string>
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"צריך להקיש על מקש הפעולה במקלדת"</string>
@@ -1584,8 +1593,11 @@
<string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"מסופקים על ידי אפליקציות"</string>
<string name="qs_edit_mode_category_display" msgid="4749511439121053942">"מסך"</string>
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"לא ידוע"</string>
- <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"לאפס את כל הלחצנים?"</string>
- <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"כל הלחצנים ב\'הגדרות מהירות\' יאופסו להגדרות המקוריות של המכשיר"</string>
+ <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"לאפס את כל הכפתורים?"</string>
+ <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"כל הכפתורים בהגדרות המהירות יאופסו להגדרות המקוריות של המכשיר"</string>
<string name="demote_explain_text" msgid="3942301497888762295">"לא יוצגו יותר עדכונים בזמן אמת מהאפליקציה <xliff:g id="APPLICATION">%1$s</xliff:g>. תמיד אפשר לשנות את זה בהגדרות."</string>
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
+ <string name="underlay_close_button_content_description" msgid="6518481455548869894">"סגירה"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="7666159428113013319">"כדי לפתוח את ההתראות, מחליקים מהפינה הימנית העליונה"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="3842355853737057332">"כדי לפתוח את ההגדרות המהירות, מחליקים מהפינה השמאלית העליונה"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ja-ldrtl/strings.xml b/packages/SystemUI/res/values-ja-ldrtl/strings.xml
index 9749b8c..e83508d 100644
--- a/packages/SystemUI/res/values-ja-ldrtl/strings.xml
+++ b/packages/SystemUI/res/values-ja-ldrtl/strings.xml
@@ -20,4 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="recents_quick_scrub_onboarding" msgid="2452671841151577157">"左にドラッグするとアプリを素早く切り替えることができます"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="6543179539438220549">"右上から下にスワイプして通知を開きます"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="5760503283520058116">"左上から下にスワイプしてクイック設定を開きます"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index a6e3660..2238c45 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -111,7 +111,7 @@
<string name="screenrecord_channel_description" msgid="4147077128486138351">"画面の録画セッション中の通知"</string>
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"画面を録画しますか?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"1 つのアプリを録画"</string>
- <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="4882406311415082016">"この画面の録画"</string>
+ <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="4882406311415082016">"この画面を録画"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="4169494703993148253">"%s の録画"</string>
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"画面全体を録画すると、画面に表示されるものがすべて録画されます。パスワード、お支払い情報、メッセージ、写真、音声、動画などの情報にご注意ください。"</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"アプリを録画すると、そのアプリで表示または実行される内容がすべて録画されます。パスワード、お支払い情報、メッセージ、写真、音声、動画などの情報にご注意ください。"</string>
@@ -125,7 +125,7 @@
<string name="screenrecord_continue" msgid="4055347133700593164">"開始"</string>
<string name="screenrecord_ongoing_screen_only" msgid="4459670242451527727">"画面を録画しています"</string>
<string name="screenrecord_ongoing_screen_and_audio" msgid="5351133763125180920">"画面と音声を記録しています"</string>
- <string name="screenrecord_taps_label" msgid="1595690528298857649">"画面上のタップも記録する"</string>
+ <string name="screenrecord_taps_label" msgid="1595690528298857649">"画面上でのタップ"</string>
<string name="screenrecord_stop_label" msgid="72699670052087989">"停止"</string>
<string name="screenrecord_share_label" msgid="5025590804030086930">"共有"</string>
<string name="screenrecord_save_title" msgid="1886652605520893850">"画面の録画を保存しました"</string>
@@ -316,7 +316,7 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"保存済み"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"接続を解除"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"有効化"</string>
- <string name="turn_on_bluetooth_auto_tomorrow" msgid="3345758139235739006">"日付が変わったら自動的に ON にする"</string>
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="3345758139235739006">"翌日に自動で ON にする"</string>
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Quick Share や「デバイスを探す」などの機能は Bluetooth を使用します"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"明日の朝に Bluetooth が ON になります"</string>
<string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"音声を共有"</string>
@@ -398,7 +398,7 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"スクリーン レコード"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"開始"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"停止"</string>
- <string name="qs_record_issue_label" msgid="8166290137285529059">"録音に関する問題"</string>
+ <string name="qs_record_issue_label" msgid="8166290137285529059">"問題を録画"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"開始"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"停止"</string>
<string name="qs_record_issue_bug_report" msgid="8229031766918650079">"バグレポート"</string>
@@ -467,6 +467,8 @@
<string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"マイクボタンを使用するには、[設定] でマイクへのアクセスを有効にしてください。"</string>
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"設定を開く"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"その他のデバイス"</string>
+ <string name="media_suggestion_disconnected_text" msgid="8777932417076437525">"<xliff:g id="DEVICE_NAME">%s</xliff:g> で再生"</string>
+ <string name="media_suggestion_failure_text" msgid="8453022394887613774">"接続できません。もう一度お試しください。"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"概要を切り替え"</string>
<string name="zen_modes_dialog_title" msgid="8854640808100096934">"モード"</string>
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"完了"</string>
@@ -582,6 +584,7 @@
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
<skip />
<string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"画面全体を共有"</string>
+ <string name="screen_share_permission_dialog_option_text_entire_screen_for_display" msgid="1545850238689169166">"<xliff:g id="DISPLAY_NAME">%s</xliff:g> を共有"</string>
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
<skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"画面全体を共有すると、画面に表示される内容が <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> にすべて公開されます。パスワード、お支払い情報、メッセージ、写真、音声、動画などの情報にご注意ください。"</string>
@@ -593,9 +596,10 @@
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"画面をキャストしますか?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"1 つのアプリをキャスト"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"画面全体をキャスト"</string>
+ <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen_for_display" msgid="5524817761472941086">"<xliff:g id="DISPLAY_NAME">%s</xliff:g> をキャスト"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"画面全体をキャストすると、画面に表示される内容がすべて公開されます。パスワード、お支払い情報、メッセージ、写真、音声、動画などの情報にご注意ください。"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"アプリをキャストすると、そのアプリで表示または再生される内容がすべて公開されます。パスワード、お支払い情報、メッセージ、写真、音声、動画などの情報にご注意ください。"</string>
- <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"画面のキャスト"</string>
+ <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"画面をキャスト"</string>
<string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"キャストするアプリを選択"</string>
<string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"共有を開始しますか?"</string>
<string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"共有、録画、キャスト中は、画面に表示される内容やデバイスで再生される内容に Android がアクセスできるため、パスワード、お支払い情報、メッセージ、写真、音声、動画などの情報にご注意ください。"</string>
@@ -673,9 +677,10 @@
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"信頼エージェントがロック解除を管理"</string>
<string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"認証の試行回数が上限に達したため、デバイスがロックされました"</string>
<string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"デバイスがロックされました\n認証に失敗しました"</string>
+ <string name="keyguard_indication_after_watch_disconnected" msgid="6329761892437377710">"スマートウォッチでロックされました"</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="accessibility_volume_settings" msgid="1458961116951564784">"音声の設定"</string>
- <string name="volume_odi_captions_tip" msgid="8825655463280990941">"メディアの自動字幕起こし"</string>
+ <string name="volume_odi_captions_tip" msgid="8825655463280990941">"メディアの字幕を自動で作成します"</string>
<string name="accessibility_volume_close_odi_captions_tip" msgid="8924753283621160480">"字幕のヒントを閉じる"</string>
<string name="volume_odi_captions_content_description" msgid="4172765742046013630">"字幕のオーバーレイ"</string>
<string name="volume_odi_captions_hint_enable" msgid="2073091194012843195">"有効にする"</string>
@@ -799,6 +804,8 @@
<string name="enable_bluetooth_message" msgid="6740938333772779717">"タブレットでキーボードに接続するには、最初にBluetoothをONにする必要があります。"</string>
<string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"ONにする"</string>
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"ON - 顔ベース"</string>
+ <string name="notification_guts_bundle_title" msgid="1345506995443305361">"Notification Organizer を使用する"</string>
+ <string name="notification_guts_bundle_summary" msgid="3971530802237393600">"対象: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="inline_done_button" msgid="6043094985588909584">"完了"</string>
<string name="inline_ok_button" msgid="603075490581280343">"適用"</string>
<string name="inline_turn_off_notifications" msgid="2653064779176881329">"OFF にする"</string>
@@ -984,6 +991,7 @@
<string name="right_icon" msgid="1103955040645237425">"右アイコン"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"タイルを追加するには長押ししてドラッグ"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"タイルを並べ替えるには長押ししてドラッグ"</string>
+ <string name="select_to_rearrange_tiles" msgid="6166153534235179148">"並べ替えとサイズ変更するタイルを選択"</string>
<string name="tap_to_position_tile" msgid="6282815817773342757">"タップしてタイルを配置"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"削除するにはここにドラッグ"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"タイルは <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> 個以上必要です"</string>
@@ -1003,10 +1011,10 @@
<string name="tuner_low_priority" msgid="8412666814123009820">"優先度の低い通知アイコンを表示"</string>
<string name="other" msgid="429768510980739978">"その他"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"タイルのサイズを切り替えます"</string>
- <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"タイルを削除"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="4811778796933958616">"タイルを削除する"</string>
<string name="accessibility_qs_edit_toggle_placement_mode" msgid="3870429389210569610">"配置モードを切り替える"</string>
<string name="accessibility_qs_edit_toggle_selection" msgid="2201248304072372239">"選択を切り替える"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"タイルを最後の位置に追加する"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="7188303979684400620">"タイルを最後の位置に追加する"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"タイルを移動"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"タイルを目的の位置に追加する"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> に移動"</string>
@@ -1093,7 +1101,7 @@
<string name="auto_saver_text" msgid="3214960308353838764">"電池切れになる可能性が高くなると有効になります"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"いいえ"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"使用中"</string>
- <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"アプリは<xliff:g id="TYPES_LIST">%s</xliff:g>を使用しています。"</string>
+ <string name="ongoing_privacy_chip_content_multiple_apps" msgid="4994455315457996796">"<xliff:g id="TYPES_LIST">%s</xliff:g>は使用中です。"</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">"、 "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" 、 "</string>
<string name="ongoing_privacy_dialog_using_op" msgid="426635338010011796">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> が使用中"</string>
@@ -1166,7 +1174,7 @@
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"切り替え"</string>
<string name="accessibility_floating_button_action_edit" msgid="1688227814600463987">"編集"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"デバイス コントロール"</string>
- <string name="controls_providers_title" msgid="6879775889857085056">"コントロールを追加するアプリの選択"</string>
+ <string name="controls_providers_title" msgid="6879775889857085056">"コントロールを追加するアプリを選択"</string>
<string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# 件のコントロールを追加しました。}other{# 件のコントロールを追加しました。}}"</string>
<string name="controls_removed" msgid="3731789252222856959">"削除済み"</string>
<string name="controls_panel_authorization_title" msgid="267429338785864842">"<xliff:g id="APPNAME">%s</xliff:g> を追加しますか?"</string>
@@ -1508,11 +1516,11 @@
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"ショートカットを追加"</string>
<string name="shortcut_helper_delete_shortcut_button_label" msgid="3148773472696137052">"ショートカットを削除"</string>
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"キーボードを使用して移動する"</string>
- <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"キーボード ショートカットの詳細"</string>
+ <string name="launch_keyboard_tutorial_notification_content" msgid="2131074100205028210">"キーボード ショートカットについて"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"タッチパッドを使用して移動する"</string>
<string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"タッチパッド操作の詳細"</string>
<string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"キーボードとタッチパッドを使用して移動する"</string>
- <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"タッチパッド操作やキーボード ショートカットなどの詳細"</string>
+ <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="8557098239919786046">"タッチパッド ジェスチャーやキーボード ショートカットなどについて"</string>
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"戻る"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"ホームに移動"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"最近使ったアプリを表示する"</string>
@@ -1582,4 +1590,7 @@
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"すべてのクイック設定タイルがデバイスの元の設定にリセットされます"</string>
<string name="demote_explain_text" msgid="3942301497888762295">"<xliff:g id="APPLICATION">%1$s</xliff:g> のライブ情報は表示されなくなります。これは [設定] でいつでも変更できます。"</string>
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>、<xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
+ <string name="underlay_close_button_content_description" msgid="6518481455548869894">"閉じる"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="7666159428113013319">"左上から下にスワイプして通知を開きます"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="3842355853737057332">"右上から下にスワイプしてクイック設定を開きます"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ka-ldrtl/strings.xml b/packages/SystemUI/res/values-ka-ldrtl/strings.xml
index 25e8041..68fe081 100644
--- a/packages/SystemUI/res/values-ka-ldrtl/strings.xml
+++ b/packages/SystemUI/res/values-ka-ldrtl/strings.xml
@@ -20,4 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="recents_quick_scrub_onboarding" msgid="2452671841151577157">"აპების სწრაფად გადასართავად ჩავლებით გადაიტანეთ მარცხნივ"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="6543179539438220549">"შეტყობინებების გასახსნელად ჩამოფურცლეთ ზედა მარჯვენა მხარედან"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="5760503283520058116">"სწრაფი პარამეტრების გასახსნელად ჩამოფურცლეთ ზედა მარცხენა მხარედან"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml
index 4f31da2..49a422f 100644
--- a/packages/SystemUI/res/values-ka/strings.xml
+++ b/packages/SystemUI/res/values-ka/strings.xml
@@ -467,6 +467,8 @@
<string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"მიკროფონის ღილაკის გამოსაყენებლად, ჩართეთ მიკროფონზე წვდომა პარამეტრებიდან."</string>
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"პარამეტრების გახსნა"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"სხვა მოწყობილობა"</string>
+ <string name="media_suggestion_disconnected_text" msgid="8777932417076437525">"დაკვრა <xliff:g id="DEVICE_NAME">%s</xliff:g>-ზე"</string>
+ <string name="media_suggestion_failure_text" msgid="8453022394887613774">"დაკავშირება შეუძლებელია. ცადეთ ხელახლა."</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"მიმოხილვის გადართვა"</string>
<string name="zen_modes_dialog_title" msgid="8854640808100096934">"რეჟიმები"</string>
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"მზადაა"</string>
@@ -582,6 +584,7 @@
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
<skip />
<string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"მთლიანი ეკრანის გაზიარება"</string>
+ <string name="screen_share_permission_dialog_option_text_entire_screen_for_display" msgid="1545850238689169166">"<xliff:g id="DISPLAY_NAME">%s</xliff:g>-ის გაზიარება"</string>
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
<skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"მთლიანი ეკრანის გაზიარებისას <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ხედავს ყველაფერს, რაც თქვენს ეკრანზეა. ამიტომ სიფრთხილე გამოიჩინეთ ისეთ ინფორმაციასთან, როგორიცაა პაროლები, გადახდის დეტალები, შეტყობინებები, ფოტოები, აუდიო და ვიდეო."</string>
@@ -593,6 +596,7 @@
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"გსურთ თქვენი ეკრანის ტრანსლირება?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"ერთი აპის ტრანსლირება"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"მთლიანი ეკრანის ტრანსლირება"</string>
+ <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen_for_display" msgid="5524817761472941086">"<xliff:g id="DISPLAY_NAME">%s</xliff:g>-ის ტრანსლირება"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"თქვენი მთლიანი ეკრანის ტრანსლირების დროს, რაც თქვენს ეკრანზეა ყველაფერი ჩანს. ამიტომ იყავით ფრთხილად ისეთ ინფორმაციასთან, როგორიცაა პაროლები, გადახდის დეტალები, შეტყობინებები, ფოტოები, აუდიო და ვიდეო."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"აპის ტრანსლირების დროს ყველაფერი ჩანს, რაც იკვრება ან გამოსახულია აპში. ამიტომ იყავით ფრთხილად ისეთ ინფორმაციასთან, როგორიცაა პაროლები, გადახდის დეტალები, შეტყობინებები, ფოტოები, აუდიო და ვიდეო."</string>
<string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"ეკრანის ტრანსლირება"</string>
@@ -673,6 +677,7 @@
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"განბლოკილია TrustAgent-ის მიერ"</string>
<string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"მოწყობილობა ჩაიკეტა, დაფიქსირდა ავტორიზაციის მეტისმეტად ბევრი მცდელობა"</string>
<string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"მოწყობილობა ჩაკეტილია\nავტორიზაცია ვერ შესრულდა"</string>
+ <string name="keyguard_indication_after_watch_disconnected" msgid="6329761892437377710">"დაბლოკილია თქვენი საათის მიერ"</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="accessibility_volume_settings" msgid="1458961116951564784">"ხმის პარამეტრები"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"მედიის ავტომ. სუბტიტრირება"</string>
@@ -799,6 +804,10 @@
<string name="enable_bluetooth_message" msgid="6740938333772779717">"კლავიატურის ტაბლეტთან დასაკავშირებლად, ჯერ უნდა ჩართოთ Bluetooth."</string>
<string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"ჩართვა"</string>
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"ჩართული — სახის მიხედვით"</string>
+ <!-- no translation found for notification_guts_bundle_title (1345506995443305361) -->
+ <skip />
+ <!-- no translation found for notification_guts_bundle_summary (3971530802237393600) -->
+ <skip />
<string name="inline_done_button" msgid="6043094985588909584">"მზადაა"</string>
<string name="inline_ok_button" msgid="603075490581280343">"მისადაგება"</string>
<string name="inline_turn_off_notifications" msgid="2653064779176881329">"გამორთვა"</string>
@@ -984,6 +993,7 @@
<string name="right_icon" msgid="1103955040645237425">"მარჯვენა ხატულა"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"ჩავლებით გადაიტანეთ ბლოკების დასამატებლად"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"ფილების გადაწყობა შეგიძლიათ მათი ჩავლებით გადატანით"</string>
+ <string name="select_to_rearrange_tiles" msgid="6166153534235179148">"აირჩიეთ მოზაიკის ფილები გადასაწყობად და ზომის შესაცვლელად"</string>
<string name="tap_to_position_tile" msgid="6282815817773342757">"შეეხეთ პარამეტრების ფილის პოზიციის შესაცვლელად"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"ამოსაშლელად, ჩავლებით გადმოიტანეთ აქ"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"თქვენ გჭირდებათ მოზაიკის <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> ფილა მაინც"</string>
@@ -1003,10 +1013,10 @@
<string name="tuner_low_priority" msgid="8412666814123009820">"დაბალი პრიორიტეტის მქონე შეტყობინებების ხატულების ჩვენება"</string>
<string name="other" msgid="429768510980739978">"სხვა"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"მოზაიკის ფილის ზომის გადასართავად"</string>
- <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"მოზაიკის ფილის წაშლა"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="4811778796933958616">"მოზაიკის ფილის ამოშლა"</string>
<string name="accessibility_qs_edit_toggle_placement_mode" msgid="3870429389210569610">"განთავსების რეჟიმზე გადართვა"</string>
<string name="accessibility_qs_edit_toggle_selection" msgid="2201248304072372239">"არჩევანის გადართვა"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"მოზაიკის ფილის ბოლო პოზიციაზე დამატება"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="7188303979684400620">"მოზაიკის ფილის ბოლო პოზიციაზე დამატება"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"მოზაიკის გადატანა"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"მოზაიკის ფილის სასურველ პოზიციაზე დამატება"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"გადატანა <xliff:g id="POSITION">%1$d</xliff:g>-ზე"</string>
@@ -1093,7 +1103,8 @@
<string name="auto_saver_text" msgid="3214960308353838764">"ჩაირთოს, როცა ბატარეა დაცლის პირას არის"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"არა, გმადლობთ"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"გამოიყენება"</string>
- <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"აპლიკაციების მიერ გამოიყენება თქვენი <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps (4994455315457996796) -->
+ <skip />
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" და "</string>
<string name="ongoing_privacy_dialog_using_op" msgid="426635338010011796">"ამჟამად იყენებს <xliff:g id="APPLICATION_NAME">%1$s</xliff:g>"</string>
@@ -1508,11 +1519,11 @@
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"მალსახმობის დამატება"</string>
<string name="shortcut_helper_delete_shortcut_button_label" msgid="3148773472696137052">"მალსახმობის წაშლა"</string>
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"ნავიგაცია კლავიატურის გამოყენებით"</string>
- <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"კლავიატურის მალსახმობების სწავლა"</string>
+ <string name="launch_keyboard_tutorial_notification_content" msgid="2131074100205028210">"კლავიატურის მალსახმობების სწავლა"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"ნავიგაცია სენსორული პანელის გამოყენებით"</string>
<string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"სენსორული პანელის ჟესტების სწავლა"</string>
<string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"ნავიგაცია კლავიატურის და სენსორული პანელის გამოყენებით"</string>
- <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"სენსორული პანელის ჟესტების, კლავიატურის მალსახმობების და სხვა ფუნქციების სწავლა"</string>
+ <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="8557098239919786046">"სენსორული ჟესტების, კლავიატურის მალსახმობების და სხვა ფუნქციების სწავლა"</string>
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"უკან დაბრუნება"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"მთავარ ეკრანზე გადასვლა"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"ბოლო აპების ნახვა"</string>
@@ -1582,4 +1593,7 @@
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"სწრაფი პარამეტრების ყველა ფილა გადაყენდება მოწყობილობის ორიგინალ პარამეტრებზე"</string>
<string name="demote_explain_text" msgid="3942301497888762295">"<xliff:g id="APPLICATION">%1$s</xliff:g> აღარ აჩვენებს განახლებებს რეალურ დროში. ამის შეცვლა პარამეტრებიდან ნებისმიერ დროს შეგიძლიათ."</string>
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
+ <string name="underlay_close_button_content_description" msgid="6518481455548869894">"დახურვა"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="7666159428113013319">"შეტყობინებების გასახსნელად ჩამოფურცლეთ ზედა მარცხენა მხარედან"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="3842355853737057332">"სწრაფი პარამეტრების გასახსნელად ჩამოფურცლეთ ზედა მარჯვენა მხარედან"</string>
</resources>
diff --git a/packages/SystemUI/res/values-kk-ldrtl/strings.xml b/packages/SystemUI/res/values-kk-ldrtl/strings.xml
index 80781ab..3649432 100644
--- a/packages/SystemUI/res/values-kk-ldrtl/strings.xml
+++ b/packages/SystemUI/res/values-kk-ldrtl/strings.xml
@@ -20,4 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="recents_quick_scrub_onboarding" msgid="2452671841151577157">"Қолданбалар арасында жылдам ауысу үшін солға қарай сүйреңіз"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="6543179539438220549">"Хабарландыруларды ашу үшін жоғарғы оң жақ бұрыштан сырғытыңыз"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="5760503283520058116">"Жылдам параметрлерді ашу үшін жоғарғы сол жақ бұрыштан сырғытыңыз"</string>
</resources>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index 197c60f..d73f148 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -70,7 +70,7 @@
<string name="usb_contaminant_message" msgid="7730476585174719805">"Құрылғыңызға сұйықтық немесе қоқыс кіріп кетпеуі үшін, USB порты өшірілген және ешқандай керек-жарақты анықтамайды.\n\nUSB портын қайта пайдалануға болатын кезде хабарландыру аласыз."</string>
<string name="usb_port_enabled" msgid="531823867664717018">"Зарядтағыштар мен аксессуарларды анықтау үшін USB порты қосылды."</string>
<string name="usb_disable_contaminant_detection" msgid="3827082183595978641">"USB қосу"</string>
- <string name="learn_more" msgid="4690632085667273811">"Толығырақ"</string>
+ <string name="learn_more" msgid="4690632085667273811">"Толық ақпарат"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Скриншот"</string>
<string name="global_action_smart_lock_disabled" msgid="6286551337177954859">"Құлыпты ашық ұстау функциясы өшірілді."</string>
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"сурет жіберілді"</string>
@@ -252,8 +252,7 @@
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g> қосылған."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"<xliff:g id="CAST">%s</xliff:g> трансляциясына қосылды."</string>
<string name="accessibility_expand_group" msgid="521237935987978624">"Топты жайыңыз."</string>
- <!-- no translation found for accessibility_collapse_group (1842704016768329517) -->
- <skip />
+ <string name="accessibility_collapse_group" msgid="1842704016768329517">"Топты жию."</string>
<string name="accessibility_add_device_to_group" msgid="5446422960697860806">"Құрылғыны топқа қосады."</string>
<string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"Құрылғыны топтан өшіреді."</string>
<string name="accessibility_open_application" msgid="1749126077501259712">"Қолданбаны ашыңыз."</string>
@@ -468,6 +467,8 @@
<string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"Микрофон түймесін пайдалану үшін параметрлерден микрофон пайдалану рұқсатын қосыңыз."</string>
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Параметрлерді ашу"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Басқа құрылғы"</string>
+ <string name="media_suggestion_disconnected_text" msgid="8777932417076437525">"<xliff:g id="DEVICE_NAME">%s</xliff:g> құрылғысында ойнату"</string>
+ <string name="media_suggestion_failure_text" msgid="8453022394887613774">"Қосылмады. Қайталап көріңіз."</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Шолуды қосу/өшіру"</string>
<string name="zen_modes_dialog_title" msgid="8854640808100096934">"Режимдер"</string>
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Дайын"</string>
@@ -583,6 +584,7 @@
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
<skip />
<string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Бүкіл экранды бөлісу"</string>
+ <string name="screen_share_permission_dialog_option_text_entire_screen_for_display" msgid="1545850238689169166">"<xliff:g id="DISPLAY_NAME">%s</xliff:g> бөлісу"</string>
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
<skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Бүкіл экранды бөліскен кезде, ондағы барлық контент <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> қолданбасында көрсетіледі. Сондықтан құпия сөздерді, төлем туралы мәліметті, хабарларды, фотосуреттерді және аудио мен бейнені ашқанда сақ болыңыз."</string>
@@ -594,7 +596,8 @@
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Экранды трансляциялау керек пе?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Бір қолданба экранын трансляциялау"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Бүкіл экранды трансляциялау"</string>
- <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Бүкіл экранды трансляциялаған кезде экранда барлық нәрсе көрсетіледі. Сондықтан құпия сөздерге, төлем туралы мәліметке, хабарларға, фотосуреттерге, аудиоконтент пен бейнелерге сақ болыңыз."</string>
+ <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen_for_display" msgid="5524817761472941086">"<xliff:g id="DISPLAY_NAME">%s</xliff:g> трансляциялау"</string>
+ <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Бүкіл экранды трансляциялаған кезде, экрандағы барлық нәрсе көрсетіледі. Сондықтан құпия сөздерге, төлем туралы мәліметке, хабарларға, фотосуреттерге, аудиоконтент пен бейнелерге сақ болыңыз."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Қолданба экранын трансляциялаған кезде, қолданбадағы барлық контент көрсетіледі. Сондықтан құпия сөздерге, төлем туралы мәліметке, хабарларға, фотосуреттерге, аудиоконтент пен бейнелерге сақ болыңыз."</string>
<string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Экранды трансляциялау"</string>
<string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"Трансляциялайтын қолданба экранын таңдау"</string>
@@ -674,6 +677,7 @@
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"TrustAgent арқылы құлпы ашылды."</string>
<string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"Құрылғы құлыпталды, тым көп аутентификациялау талпынысы жасалды."</string>
<string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"Құрылғы құлыпталды\nАутентификация орындалмады"</string>
+ <string name="keyguard_indication_after_watch_disconnected" msgid="6329761892437377710">"Сағат арқылы құлыпталды"</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="accessibility_volume_settings" msgid="1458961116951564784">"Дыбыс параметрлері"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Автоматты субтитр қосу"</string>
@@ -717,7 +721,7 @@
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Діріл"</string>
<string name="volume_ringer_status_silent" msgid="3691324657849880883">"Дыбысын өшіру"</string>
<string name="media_device_cast" msgid="4786241789687569892">"Трансляциялау"</string>
- <string name="stream_notification_unavailable" msgid="4313854556205836435">"Қолжетімді емес, шылдырлату өшірулі."</string>
+ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Телефонның дыбысы өшіп тұрғанда қолжетімсіз."</string>
<string name="stream_alarm_unavailable" msgid="4059817189292197839">"Мазаламау режимі қосылғандықтан өзгерту мүмкін емес."</string>
<string name="stream_media_unavailable" msgid="6823020894438959853">"Мазаламау режимі қосылғандықтан өзгерту мүмкін емес."</string>
<string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Қолжетімді емес, себебі <xliff:g id="MODE">%s</xliff:g> режимі қосулы."</string>
@@ -800,6 +804,10 @@
<string name="enable_bluetooth_message" msgid="6740938333772779717">"Пернетақтаны планшетке қосу үшін алдымен Bluetooth функциясын қосу керек."</string>
<string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Қосу"</string>
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Қосулы – бет негізінде"</string>
+ <!-- no translation found for notification_guts_bundle_title (1345506995443305361) -->
+ <skip />
+ <!-- no translation found for notification_guts_bundle_summary (3971530802237393600) -->
+ <skip />
<string name="inline_done_button" msgid="6043094985588909584">"Дайын"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Қолдану"</string>
<string name="inline_turn_off_notifications" msgid="2653064779176881329">"Өшіру"</string>
@@ -958,7 +966,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"Қосулы"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Өшірулі"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"Қолжетімді емес"</string>
- <string name="accessibility_tile_disabled_by_policy_action_description" msgid="6958422730461646926">"толығырақ"</string>
+ <string name="accessibility_tile_disabled_by_policy_action_description" msgid="6958422730461646926">"Толық ақпарат"</string>
<string name="nav_bar" msgid="4642708685386136807">"Шарлау тақтасы"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Формат"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Қосымша сол жақ түйме түрі"</string>
@@ -985,6 +993,7 @@
<string name="right_icon" msgid="1103955040645237425">"Оң жақ белгіше"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Қажетті элементтерді сүйреп әкеліп қойыңыз"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Элементтердің ретін өзгерту үшін оларды басып тұрып сүйреңіз"</string>
+ <string name="select_to_rearrange_tiles" msgid="6166153534235179148">"Ретін және өлшемін өзгерту қажет бөлшектерді таңдаңыз"</string>
<string name="tap_to_position_tile" msgid="6282815817773342757">"Бөлшекті орналастыру үшін түрту"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Керексіздерін осы жерге сүйреңіз"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Кемінде <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> бөлшек қажет."</string>
@@ -1004,10 +1013,12 @@
<string name="tuner_low_priority" msgid="8412666814123009820">"Маңызды емес хабарландыру белгішелерін көрсету"</string>
<string name="other" msgid="429768510980739978">"Басқа"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"бөлшек өлшемін ауыстыру"</string>
- <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"бөлшекті өшіру"</string>
+ <!-- no translation found for accessibility_qs_edit_remove_tile_action (4811778796933958616) -->
+ <skip />
<string name="accessibility_qs_edit_toggle_placement_mode" msgid="3870429389210569610">"орналастыру режимін ауыстыру"</string>
<string name="accessibility_qs_edit_toggle_selection" msgid="2201248304072372239">"таңдауды ауыстыру"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"соңғы позицияға бөлшек қосу"</string>
+ <!-- no translation found for accessibility_qs_edit_tile_add_action (7188303979684400620) -->
+ <skip />
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Бөлшекті жылжыту"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Қалаған позицияға бөлшек қосу"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> орнына жылжыту"</string>
@@ -1094,7 +1105,8 @@
<string name="auto_saver_text" msgid="3214960308353838764">"Батареяның заряды бітуге жақындағанда қосыңыз."</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Жоқ, рақмет"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"Қолданыста"</string>
- <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Қолданбаларда <xliff:g id="TYPES_LIST">%s</xliff:g> пайдаланылуда."</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps (4994455315457996796) -->
+ <skip />
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" және "</string>
<string name="ongoing_privacy_dialog_using_op" msgid="426635338010011796">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> пайдаланып жатыр."</string>
@@ -1251,15 +1263,13 @@
<string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(ажыратулы)"</string>
<string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"Ауысу мүмкін емес. Әрекетті қайталау үшін түртіңіз."</string>
<string name="media_output_dialog_pairing_new" msgid="5098212763195577270">"Құрылғы жалғау"</string>
- <!-- no translation found for media_output_dialog_button_connect_device (2819038239946643550) -->
- <skip />
+ <string name="media_output_dialog_button_connect_device" msgid="2819038239946643550">"Құрылғыны жалғау"</string>
<string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Белгісіз қолданба"</string>
<string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Трансляцияны тоқтату"</string>
<string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"Аудио шығыс үшін қолжетімді құрылғылар бар."</string>
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Дыбыс деңгейі"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
- <!-- no translation found for media_output_group_title_connected_speakers (3413876972919684479) -->
- <skip />
+ <string name="media_output_group_title_connected_speakers" msgid="3413876972919684479">"Жалғанған динамиктер"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Динамиктер мен дисплейлер"</string>
<string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Ұсынылған құрылғылар"</string>
<string name="media_input_group_title" msgid="2057057473860783021">"Енгізу"</string>
@@ -1328,10 +1338,8 @@
<string name="accessibility_enter_hint" msgid="2617864063504824834">"құрылғыны енгізу"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Ашу үшін саусақ ізін пайдаланыңыз."</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Аутентификациядан өту қажет. Ол үшін саусақ ізін оқу сканерін түртіңіз."</string>
- <!-- no translation found for resume_animation (9148788411429330671) -->
- <skip />
- <!-- no translation found for pause_animation (1919705510058107504) -->
- <skip />
+ <string name="resume_animation" msgid="9148788411429330671">"Анимацияны жалғастыру"</string>
+ <string name="pause_animation" msgid="1919705510058107504">"Анимацияны кідірту"</string>
<string name="ongoing_call_content_description" msgid="6394763878322348560">"Ағымдағы қоңырау"</string>
<string name="ongoing_notification_extra_content_description" msgid="2098752668861351265">"Ағымдағы"</string>
<string name="mobile_data_settings_title" msgid="3955246641380064901">"Мобильдік интернет"</string>
@@ -1513,11 +1521,13 @@
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"Жылдам пәрмен қосу"</string>
<string name="shortcut_helper_delete_shortcut_button_label" msgid="3148773472696137052">"Жылдам пәрменді жою"</string>
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Пернетақтамен жұмыс істеңіз"</string>
- <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Перне тіркесімдерін үйреніңіз."</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2131074100205028210) -->
+ <skip />
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Сенсорлық тақтамен жұмыс істеңіз"</string>
<string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Сенсорлық тақта қимылдарын үйреніңіз."</string>
<string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Пернетақтамен және сенсорлық тақтамен жұмыс істеңіз"</string>
- <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Сенсорлық тақта қимылдарын, перне тіркесімдерін және т.б. үйреніңіз."</string>
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (8557098239919786046) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Артқа"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Негізгі бетке өту"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Соңғы қолданбаларды көру"</string>
@@ -1587,4 +1597,7 @@
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Барлық \"Жылдам параметрлер\" бөлшегі құрылғының бастапқы параметрлеріне қайтарылады."</string>
<string name="demote_explain_text" msgid="3942301497888762295">"<xliff:g id="APPLICATION">%1$s</xliff:g> бұдан былай нақты уақыттағы жаңартуларды көрсетпейді. Мұны кез келген уақытта \"Параметрлер\" бөлімінен өзгертуге болады."</string>
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
+ <string name="underlay_close_button_content_description" msgid="6518481455548869894">"Жабу"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="7666159428113013319">"Хабарландыруларды ашу үшін жоғарғы сол жақ бұрыштан сырғытыңыз"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="3842355853737057332">"Жылдам параметрлерді ашу үшін жоғарғы оң жақ бұрыштан сырғытыңыз"</string>
</resources>
diff --git a/packages/SystemUI/res/values-km-ldrtl/strings.xml b/packages/SystemUI/res/values-km-ldrtl/strings.xml
index a27f841..3b80aa1 100644
--- a/packages/SystemUI/res/values-km-ldrtl/strings.xml
+++ b/packages/SystemUI/res/values-km-ldrtl/strings.xml
@@ -20,4 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="recents_quick_scrub_onboarding" msgid="2452671841151577157">"អូសទៅឆ្វេង ដើម្បីប្ដូរកម្មវិធីបានរហ័ស"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="6543179539438220549">"អូសពីខាងស្ដាំផ្នែកខាងលើដើម្បីបើកការជូនដំណឹង"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="5760503283520058116">"អូសពីខាងឆ្វេងផ្នែកខាងលើដើម្បីបើកការកំណត់រហ័ស"</string>
</resources>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index 7e4a6aeb..e0e80f4 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -467,6 +467,8 @@
<string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"ដើម្បីប្រើប្រាស់ប៊ូតុងមីក្រូហ្វូន សូមបើកសិទ្ធិចូលប្រើប្រាស់មីក្រូហ្វូននៅក្នុងការកំណត់។"</string>
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"បើកការកំណត់"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"ឧបករណ៍ផ្សេងទៀត"</string>
+ <string name="media_suggestion_disconnected_text" msgid="8777932417076437525">"ចាក់នៅលើ <xliff:g id="DEVICE_NAME">%s</xliff:g>"</string>
+ <string name="media_suggestion_failure_text" msgid="8453022394887613774">"មិនអាចភ្ជាប់បានទេ។ សូមព្យាយាមម្ដងទៀត។"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"បិទ/បើកទិដ្ឋភាពរួម"</string>
<string name="zen_modes_dialog_title" msgid="8854640808100096934">"មុខងារ"</string>
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"រួចរាល់"</string>
@@ -582,6 +584,7 @@
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
<skip />
<string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"បង្ហាញអេក្រង់ទាំងមូល"</string>
+ <string name="screen_share_permission_dialog_option_text_entire_screen_for_display" msgid="1545850238689169166">"ចែករំលែក <xliff:g id="DISPLAY_NAME">%s</xliff:g>"</string>
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
<skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"នៅពេលអ្នកបង្ហាញអេក្រង់ទាំងមូលរបស់អ្នក <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> មើលឃើញអ្វីគ្រប់យ៉ាងនៅលើអេក្រង់របស់អ្នក។ ដូច្នេះ សូមប្រុងប្រយ័ត្នចំពោះអ្វីៗដូចជា ពាក្យសម្ងាត់ ព័ត៌មានលម្អិតអំពីការទូទាត់ប្រាក់ សារ រូបថត ព្រមទាំងសំឡេង និងវីដេអូ។"</string>
@@ -593,6 +596,7 @@
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"បញ្ជូនអេក្រង់របស់អ្នកឬ?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"បញ្ជូនកម្មវិធីមួយ"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"បញ្ជូនអេក្រង់ទាំងមូល"</string>
+ <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen_for_display" msgid="5524817761472941086">"បញ្ជូន <xliff:g id="DISPLAY_NAME">%s</xliff:g>"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"នៅពេលអ្នកបញ្ជូនអេក្រង់ទាំងមូលរបស់អ្នក អ្នកផ្សេងមើលឃើញអ្វីគ្រប់យ៉ាងនៅលើអេក្រង់របស់អ្នក។ ដូច្នេះ សូមប្រុងប្រយ័ត្នចំពោះអ្វីៗដូចជា ពាក្យសម្ងាត់ ព័ត៌មានលម្អិតអំពីការទូទាត់ប្រាក់ សារ រូបថត ព្រមទាំងសំឡេង និងវីដេអូ។"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"នៅពេលអ្នកបញ្ជូនកម្មវិធីណាមួយ អ្នកផ្សេងមើលឃើញអ្វីគ្រប់យ៉ាងដែលបង្ហាញ ឬចាក់ក្នុងកម្មវិធីនោះ។ ដូច្នេះ សូមប្រុងប្រយ័ត្នចំពោះអ្វីៗដូចជា ពាក្យសម្ងាត់ ព័ត៌មានលម្អិតអំពីការទូទាត់ប្រាក់ សារ រូបថត ព្រមទាំងសំឡេង និងវីដេអូ។"</string>
<string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"បញ្ជូនអេក្រង់"</string>
@@ -673,6 +677,7 @@
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"បានដោះសោដោយភ្នាក់ងារទុកចិត្ត"</string>
<string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"ឧបករណ៍ត្រូវបានចាក់សោ ដោយសារមានការព្យាយាមផ្ទៀងផ្ទាត់ច្រើនដងពេក"</string>
<string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"បានចាក់សោឧបករណ៍\nការផ្ទៀងផ្ទាត់មិនបានសម្រេច"</string>
+ <string name="keyguard_indication_after_watch_disconnected" msgid="6329761892437377710">"ចាក់សោដោយនាឡិការបស់អ្នក"</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="accessibility_volume_settings" msgid="1458961116951564784">"ការកំណត់សំឡេង"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"ដាក់អក្សររត់លើមេឌៀដោយស្វ័យប្រវត្តិ"</string>
@@ -799,6 +804,10 @@
<string name="enable_bluetooth_message" msgid="6740938333772779717">"ដើម្បីភ្ជាប់ក្តារចុចរបស់អ្នកជាមួយនឹងថេប្លេតរបស់អ្នក អ្នកត្រូវតែបើកប៊្លូធូសជាមុនសិន។"</string>
<string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"បើក"</string>
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"បើក - ផ្អែកលើមុខ"</string>
+ <!-- no translation found for notification_guts_bundle_title (1345506995443305361) -->
+ <skip />
+ <!-- no translation found for notification_guts_bundle_summary (3971530802237393600) -->
+ <skip />
<string name="inline_done_button" msgid="6043094985588909584">"រួចរាល់"</string>
<string name="inline_ok_button" msgid="603075490581280343">"ប្រើ"</string>
<string name="inline_turn_off_notifications" msgid="2653064779176881329">"បិទ"</string>
@@ -984,6 +993,7 @@
<string name="right_icon" msgid="1103955040645237425">"រូបតំណាងខាងស្ដាំ"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"ចុចឱ្យជាប់ រួចអូសដើម្បីបញ្ចូលប្រអប់"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"ចុចឱ្យជាប់ រួចអូសដើម្បីរៀបចំប្រអប់ឡើងវិញ"</string>
+ <string name="select_to_rearrange_tiles" msgid="6166153534235179148">"ជ្រើសរើសប្រអប់ ដើម្បីតម្រៀបឡើងវិញ និងប្ដូរទំហំ"</string>
<string name="tap_to_position_tile" msgid="6282815817773342757">"ចុចដើម្បីកំណត់ទីតាំងប្រអប់"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"អូសទីនេះដើម្បីយកចេញ"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"អ្នកត្រូវការប្រអប់យ៉ាងតិច <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g>"</string>
@@ -1003,10 +1013,10 @@
<string name="tuner_low_priority" msgid="8412666814123009820">"បង្ហាញរូបការជូនដំណឹងដែលមានអាទិភាពទាប"</string>
<string name="other" msgid="429768510980739978">"ផ្សេងៗ"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"បិទ/បើកទំហំរបស់ប្រអប់"</string>
- <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"ដកប្រអប់ចេញ"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="4811778796933958616">"ដកប្រអប់ចេញ"</string>
<string name="accessibility_qs_edit_toggle_placement_mode" msgid="3870429389210569610">"បិទ/បើកមុខងារដាក់បង្ហាញ"</string>
<string name="accessibility_qs_edit_toggle_selection" msgid="2201248304072372239">"បិទ/បើកការជ្រើសរើស"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"បញ្ចូលប្រអប់ទៅទីតាំងចុងក្រោយ"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="7188303979684400620">"បញ្ចូលប្រអប់ទៅទីតាំងចុងក្រោយ"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"ផ្លាស់ទីប្រអប់"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"បញ្ចូលប្រអប់ទៅទីតាំងដែលចង់បាន"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"ផ្លាស់ទីទៅ <xliff:g id="POSITION">%1$d</xliff:g>"</string>
@@ -1093,7 +1103,8 @@
<string name="auto_saver_text" msgid="3214960308353838764">"បើកនៅពេលថ្មទំនងជាអស់"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"ទេ អរគុណ"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"កំពុងប្រើ"</string>
- <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"កម្មវិធីកំពុងប្រើ <xliff:g id="TYPES_LIST">%s</xliff:g> របស់អ្នក។"</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps (4994455315457996796) -->
+ <skip />
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" និង "</string>
<string name="ongoing_privacy_dialog_using_op" msgid="426635338010011796">"កំពុងប្រើដោយ <xliff:g id="APPLICATION_NAME">%1$s</xliff:g>"</string>
@@ -1508,11 +1519,11 @@
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"បញ្ចូលផ្លូវកាត់"</string>
<string name="shortcut_helper_delete_shortcut_button_label" msgid="3148773472696137052">"លុបផ្លូវកាត់"</string>
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"រុករកដោយប្រើក្ដារចុចរបស់អ្នក"</string>
- <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"ស្វែងយល់អំពីផ្លូវកាត់ក្ដារចុច"</string>
+ <string name="launch_keyboard_tutorial_notification_content" msgid="2131074100205028210">"ស្វែងយល់អំពីផ្លូវកាត់ក្ដារចុច"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"រុករកដោយប្រើផ្ទាំងប៉ះរបស់អ្នក"</string>
<string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"ស្វែងយល់អំពីចលនាផ្ទាំងប៉ះ"</string>
<string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"រុករកដោយប្រើក្ដារចុច និងផ្ទាំងប៉ះរបស់អ្នក"</string>
- <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"ស្វែងយល់អំពីចលនាផ្ទាំងប៉ះ ផ្លូវកាត់ក្ដារចុច និងអ្វីៗជាច្រើនទៀត"</string>
+ <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="8557098239919786046">"ស្វែងយល់អំពីចលនាផ្ទាំងប៉ះ ផ្លូវកាត់ក្ដារចុច និងអ្វីៗជាច្រើនទៀត"</string>
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"ថយក្រោយ"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"ទៅទំព័រដើម"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"មើលកម្មវិធីថ្មីៗ"</string>
@@ -1582,4 +1593,7 @@
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"ប្រអប់ការកំណត់រហ័សទាំងអស់នឹងកំណត់ឡើងវិញទៅការកំណត់ដើមរបស់ឧបករណ៍"</string>
<string name="demote_explain_text" msgid="3942301497888762295">"<xliff:g id="APPLICATION">%1$s</xliff:g> នឹងលែងបង្ហាញព័ត៌មានបន្តផ្ទាល់ទៀតហើយ។ អ្នកអាចប្ដូរលក្ខណៈនេះបានគ្រប់ពេលនៅក្នុងការកំណត់។"</string>
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
+ <string name="underlay_close_button_content_description" msgid="6518481455548869894">"បិទ"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="7666159428113013319">"អូសពីខាងឆ្វេងផ្នែកខាងលើដើម្បីបើកការជូនដំណឹង"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="3842355853737057332">"អូសពីខាងស្ដាំផ្នែកខាងលើដើម្បីបើកការកំណត់រហ័ស"</string>
</resources>
diff --git a/packages/SystemUI/res/values-kn-ldrtl/strings.xml b/packages/SystemUI/res/values-kn-ldrtl/strings.xml
index 9587dae..7d105bf 100644
--- a/packages/SystemUI/res/values-kn-ldrtl/strings.xml
+++ b/packages/SystemUI/res/values-kn-ldrtl/strings.xml
@@ -20,4 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="recents_quick_scrub_onboarding" msgid="2452671841151577157">"ಅಪ್ಲಿಕೇಶನ್ಗಳನ್ನು ಬದಲಿಸಲು ತ್ವರಿತವಾಗಿ ಎಡಕ್ಕೆ ಡ್ರ್ಯಾಗ್ ಮಾಡಿ"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="6543179539438220549">"ನೋಟಿಫಿಕೇಶನ್ಗಳನ್ನು ತೆರೆಯಲು ಮೇಲಿನ ಬಲದಿಂದ ಸ್ವೈಪ್ ಮಾಡಿ"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="5760503283520058116">"ತ್ವರಿತ ಸೆಟ್ಟಿಂಗ್ಗಳನ್ನು ತೆರೆಯಲು ಮೇಲಿನಿಂದ ಎಡಕ್ಕೆ ಸ್ವೈಪ್ ಮಾಡಿ"</string>
</resources>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index 93580ac..1dc410c 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -467,6 +467,8 @@
<string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"ಮೈಕ್ರೊಫೋನ್ ಬಟನ್ ಅನ್ನು ಬಳಸಲು, ಸೆಟ್ಟಿಂಗ್ಗಳಲ್ಲಿ ಮೈಕ್ರೊಫೋನ್ ಆ್ಯಕ್ಸೆಸ್ ಅನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಿ."</string>
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"ಸೆಟ್ಟಿಂಗ್ಗಳನ್ನು ತೆರೆಯಿರಿ"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"ಅನ್ಯ ಸಾಧನ"</string>
+ <string name="media_suggestion_disconnected_text" msgid="8777932417076437525">"<xliff:g id="DEVICE_NAME">%s</xliff:g> ನಲ್ಲಿ ಪ್ಲೇ ಮಾಡಿ"</string>
+ <string name="media_suggestion_failure_text" msgid="8453022394887613774">"ಕನೆಕ್ಟ್ ಮಾಡಲು ಸಾಧ್ಯವಾಗುತ್ತಿಲ್ಲ. ಪುನಃ ಪ್ರಯತ್ನಿಸಿ."</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"ಟಾಗಲ್ ನ ಅವಲೋಕನ"</string>
<string name="zen_modes_dialog_title" msgid="8854640808100096934">"ಮೋಡ್ಗಳು"</string>
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"ಮುಗಿದಿದೆ"</string>
@@ -582,6 +584,7 @@
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
<skip />
<string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"ಸಂಪೂರ್ಣ ಸ್ಕ್ರೀನ್ ಅನ್ನು ಹಂಚಿಕೊಳ್ಳಿ"</string>
+ <string name="screen_share_permission_dialog_option_text_entire_screen_for_display" msgid="1545850238689169166">"<xliff:g id="DISPLAY_NAME">%s</xliff:g> ಹಂಚಿಕೊಳ್ಳಿ"</string>
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
<skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"ನಿಮ್ಮ ಸಂಪೂರ್ಣ ಸ್ಕ್ರೀನ್ ಅನ್ನು ನೀವು ಹಂಚಿಕೊಳ್ಳುತ್ತಿರುವಾಗ, ನಿಮ್ಮ ಸ್ಕ್ರೀನ್ನಲ್ಲಿರುವ ಏನಾದರೂ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ಗೆ ಗೋಚರಿಸುತ್ತದೆ. ಆದ್ದರಿಂದ ಪಾಸ್ವರ್ಡ್ಗಳು, ಪಾವತಿ ವಿವರಗಳು, ಸಂದೇಶಗಳು, ಫೋಟೋಗಳು ಮತ್ತು ಆಡಿಯೋ ಮತ್ತು ವೀಡಿಯೊದಂತಹ ವಿಷಯಗಳ ಬಗ್ಗೆ ಜಾಗರೂಕರಾಗಿರಿ."</string>
@@ -593,6 +596,7 @@
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"ನಿಮ್ಮ ಸ್ಕ್ರೀನ್ ಅನ್ನು ಬಿತ್ತರಿಸಬೇಕೇ?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"ಒಂದು ಆ್ಯಪ್ ಅನ್ನು ಬಿತ್ತರಿಸಿ"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"ಸಂಪೂರ್ಣ ಸ್ಕ್ರೀನ್ ಅನ್ನು ಕ್ಯಾಸ್ಟ್ ಮಾಡಿ"</string>
+ <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen_for_display" msgid="5524817761472941086">"<xliff:g id="DISPLAY_NAME">%s</xliff:g> ಬಿತ್ತರಿಸು"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"ನಿಮ್ಮ ಸಂಪೂರ್ಣ ಸ್ಕ್ರೀನ್ ಅನ್ನು ನೀವು ಕ್ಯಾಸ್ಟ್ ಮಾಡುವಾಗ, ನಿಮ್ಮ ಸ್ಕ್ರೀನ್ ಮೇಲೆ ಏನಾದರೂ ಗೋಚರಿಸುತ್ತದೆ. ಆದ್ದರಿಂದ ಪಾಸ್ವರ್ಡ್ಗಳು, ಪಾವತಿ ವಿವರಗಳು, ಸಂದೇಶಗಳು, ಫೋಟೋಗಳು ಹಾಗೂ ಆಡಿಯೊ ಮತ್ತು ವೀಡಿಯೊದಂತಹ ವಿಷಯಗಳ ಕುರಿತು ಜಾಗರೂಕರಾಗಿರಿ."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"ನೀವು ಆ್ಯಪ್ ಅನ್ನು ಬಿತ್ತರಿಸುತ್ತಿರುವಾಗ, ಆ ಆ್ಯಪ್ನಲ್ಲಿ ತೋರಿಸಿರುವ ಅಥವಾ ಪ್ಲೇ ಮಾಡಿರುವುದು ಗೋಚರಿಸುತ್ತದೆ. ಆದ್ದರಿಂದ ಪಾಸ್ವರ್ಡ್ಗಳು, ಪಾವತಿ ವಿವರಗಳು, ಸಂದೇಶಗಳು, ಫೋಟೋಗಳು ಹಾಗೂ ಆಡಿಯೊ ಮತ್ತು ವೀಡಿಯೊದಂತಹ ವಿಷಯಗಳ ಕುರಿತು ಜಾಗರೂಕರಾಗಿರಿ."</string>
<string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"ಸ್ಕ್ರೀನ್ ಅನ್ನು ಬಿತ್ತರಿಸಿ"</string>
@@ -673,6 +677,7 @@
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"TrustAgent ನಿಂದ ಅನ್ಲಾಕ್ ಮಾಡಲಾಗಿದೆ"</string>
<string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"ಹಲವಾರು ದೃಢೀಕರಣ ಪ್ರಯತ್ನಗಳನ್ನು ಮಾಡಿರುವ ಕಾರಣ ಸಾಧನವನ್ನು ಲಾಕ್ ಮಾಡಲಾಗಿದೆ"</string>
<string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"ಸಾಧನವನ್ನು ಲಾಕ್ ಮಾಡಲಾಗಿದೆ\nದೃಢೀಕರಣ ವಿಫಲವಾಗಿದೆ"</string>
+ <string name="keyguard_indication_after_watch_disconnected" msgid="6329761892437377710">"ನಿಮ್ಮ ವಾಚ್ನಿಂದ ಲಾಕ್ ಮಾಡಲಾಗಿದೆ"</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="accessibility_volume_settings" msgid="1458961116951564784">"ಸೌಂಡ್ ಸೆಟ್ಟಿಂಗ್ಗಳು"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"ಸ್ವಯಂಚಾಲಿತ ಶೀರ್ಷಿಕೆ ಮಾಧ್ಯಮ"</string>
@@ -799,6 +804,10 @@
<string name="enable_bluetooth_message" msgid="6740938333772779717">"ನಿಮ್ಮ ಕೀಬೋರ್ಡ್ ಅನ್ನು ಟ್ಯಾಬ್ಲೆಟ್ಗೆ ಸಂಪರ್ಕಿಸಲು, ನೀವು ಮೊದಲು ಬ್ಲೂಟೂತ್ ಆನ್ ಮಾಡಬೇಕಾಗುತ್ತದೆ."</string>
<string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"ಆನ್ ಮಾಡಿ"</string>
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"ಆನ್ ಆಗಿದೆ - ಮುಖ-ಆಧಾರಿತ"</string>
+ <!-- no translation found for notification_guts_bundle_title (1345506995443305361) -->
+ <skip />
+ <!-- no translation found for notification_guts_bundle_summary (3971530802237393600) -->
+ <skip />
<string name="inline_done_button" msgid="6043094985588909584">"ಪೂರ್ಣಗೊಂಡಿದೆ"</string>
<string name="inline_ok_button" msgid="603075490581280343">"ಅನ್ವಯಿಸಿ"</string>
<string name="inline_turn_off_notifications" msgid="2653064779176881329">"ಆಫ್ ಮಾಡಿ"</string>
@@ -984,6 +993,7 @@
<string name="right_icon" msgid="1103955040645237425">"ಬಲ ಐಕಾನ್"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"ಟೈಲ್ಗಳನ್ನು ಸೇರಿಸಲು ಹೋಲ್ಡ್ ಮಾಡಿ ಮತ್ತು ಡ್ರ್ಯಾಗ್ ಮಾಡಿ"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"ಟೈಲ್ಗಳನ್ನು ಮರುಹೊಂದಿಸಲು ಹೋಲ್ಡ್ ಮಾಡಿ ಮತ್ತು ಡ್ರ್ಯಾಗ್ ಮಾಡಿ"</string>
+ <string name="select_to_rearrange_tiles" msgid="6166153534235179148">"ಮರುಹೊಂದಿಸಲು ಮತ್ತು ಮರುಗಾತ್ರಗೊಳಿಸಲು ಟೈಲ್ಗಳನ್ನು ಆಯ್ಕೆಮಾಡಿ"</string>
<string name="tap_to_position_tile" msgid="6282815817773342757">"ಟೈಲ್ನ ಸ್ಥಾನ ನಿಯೋಜನೆ ಮಾಡಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"ತೆಗೆದುಹಾಕಲು ಇಲ್ಲಿ ಡ್ರ್ಯಾಗ್ ಮಾಡಿ"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"ನಿಮಗೆ ಕನಿಷ್ಠ <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> ಟೈಲ್ಗಳ ಅಗತ್ಯವಿದೆ"</string>
@@ -1003,10 +1013,10 @@
<string name="tuner_low_priority" msgid="8412666814123009820">"ಕಡಿಮೆ-ಆದ್ಯತೆ ಸೂಚನೆಯ ಐಕಾನ್ಗಳನ್ನು ತೋರಿಸಿ"</string>
<string name="other" msgid="429768510980739978">"ಇತರ"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"ಟೈಲ್ ನ ಗಾತ್ರವನ್ನು ಟಾಗಲ್ ಮಾಡಿ"</string>
- <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"ಟೈಲ್ ಅನ್ನು ತೆಗೆದುಹಾಕಿ"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="4811778796933958616">"ಟೈಲ್ ಅನ್ನು ತೆಗೆದುಹಾಕಿ"</string>
<string name="accessibility_qs_edit_toggle_placement_mode" msgid="3870429389210569610">"ಪ್ಲೇಸ್ಮೆಂಟ್ ಮೋಡ್ ಅನ್ನು ಟಾಗಲ್ ಮಾಡಿ"</string>
<string name="accessibility_qs_edit_toggle_selection" msgid="2201248304072372239">"ಟಾಗಲ್ ಆಯ್ಕೆ"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"ಕೊನೆಯ ಸ್ಥಾನಕ್ಕೆ ಟೈಲ್ ಅನ್ನು ಸೇರಿಸಿ"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="7188303979684400620">"ಕೊನೆಯ ಸ್ಥಾನಕ್ಕೆ ಟೈಲ್ ಅನ್ನು ಸೇರಿಸಿ"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"ಟೈಲ್ ಸರಿಸಿ"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"ಬಯಸಿದ ಸ್ಥಾನಕ್ಕೆ ಟೈಲ್ ಅನ್ನು ಸೇರಿಸಿ"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"ಇಲ್ಲಿಗೆ ಸರಿಸಿ <xliff:g id="POSITION">%1$d</xliff:g>"</string>
@@ -1093,7 +1103,8 @@
<string name="auto_saver_text" msgid="3214960308353838764">"ಬ್ಯಾಟರಿ ಖಾಲಿಯಾಗುವ ಸಾಧ್ಯತೆ ಇದ್ದಾಗ ಆನ್ ಮಾಡಿ"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"ಬೇಡ"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"ಬಳಕೆಯಲ್ಲಿದೆ"</string>
- <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"ನಿಮ್ಮ <xliff:g id="TYPES_LIST">%s</xliff:g> ಅನ್ನು ಆ್ಯಪ್ಗಳು ಬಳಸುತ್ತಿವೆ."</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps (4994455315457996796) -->
+ <skip />
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" ಮತ್ತು "</string>
<string name="ongoing_privacy_dialog_using_op" msgid="426635338010011796">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> ಇದನ್ನು ಬಳಸುತ್ತಿದೆ"</string>
@@ -1508,11 +1519,11 @@
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"ಶಾರ್ಟ್ಕಟ್ ಸೇರಿಸಿ"</string>
<string name="shortcut_helper_delete_shortcut_button_label" msgid="3148773472696137052">"ಶಾರ್ಟ್ಕಟ್ ಅಳಿಸಿ"</string>
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"ನಿಮ್ಮ ಕೀಬೋರ್ಡ್ ಬಳಸಿ ನ್ಯಾವಿಗೇಟ್ ಮಾಡಿ"</string>
- <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"ಕೀಬೋರ್ಡ್ ಶಾರ್ಟ್ಕಟ್ಗಳನ್ನು ಕಲಿಯಿರಿ"</string>
+ <string name="launch_keyboard_tutorial_notification_content" msgid="2131074100205028210">"ಕೀಬೋರ್ಡ್ ಶಾರ್ಟ್ಕಟ್ಗಳನ್ನು ತಿಳಿದುಕೊಳ್ಳಿ"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"ನಿಮ್ಮ ಟಚ್ಪ್ಯಾಡ್ ಬಳಸಿ ನ್ಯಾವಿಗೇಟ್ ಮಾಡಿ"</string>
<string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"ಟಚ್ಪ್ಯಾಡ್ ಜೆಸ್ಚರ್ಗಳನ್ನು ಕಲಿಯಿರಿ"</string>
<string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"ನಿಮ್ಮ ಕೀಬೋರ್ಡ್ ಮತ್ತು ಟಚ್ಪ್ಯಾಡ್ ಬಳಸಿ ನ್ಯಾವಿಗೇಟ್ ಮಾಡಿ"</string>
- <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"ಟಚ್ಪ್ಯಾಡ್ ಗೆಸ್ಚರ್ಗಳು, ಕೀಬೋರ್ಡ್ಗಳ ಶಾರ್ಟ್ಕಟ್ಗಳು ಮತ್ತು ಹೆಚ್ಚಿನದನ್ನು ತಿಳಿಯಿರಿ"</string>
+ <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="8557098239919786046">"ಟಚ್ಪ್ಯಾಡ್ ಜೆಶ್ಚರ್ಗಳು, ಕೀಬೋರ್ಡ್ ಶಾರ್ಟ್ಕಟ್ಗಳು ಮತ್ತು ಇತ್ಯಾದಿಯನ್ನು ತಿಳಿದುಕೊಳ್ಳಿ"</string>
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"ಹಿಂದಿರುಗಿ"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"ಮುಖಪುಟಕ್ಕೆ ಹೋಗಿ"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"ಇತ್ತೀಚಿನ ಆ್ಯಪ್ಗಳನ್ನು ವೀಕ್ಷಿಸಿ"</string>
@@ -1582,4 +1593,7 @@
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"ಎಲ್ಲಾ ತ್ವರಿತ ಸೆಟ್ಟಿಂಗ್ಗಳ ಟೈಲ್ಗಳನ್ನು ಸಾಧನದ ಮೂಲ ಸೆಟ್ಟಿಂಗ್ಗಳಿಗೆ ರೀಸೆಟ್ ಮಾಡಲಾಗುತ್ತದೆ"</string>
<string name="demote_explain_text" msgid="3942301497888762295">"<xliff:g id="APPLICATION">%1$s</xliff:g> ಇನ್ನು ಮುಂದೆ ಲೈವ್ ಅಪ್ಡೇಟ್ಗಳನ್ನು ತೋರಿಸುವುದಿಲ್ಲ. ನೀವು ಇದನ್ನು ಯಾವಾಗ ಬೇಕಾದರೂ ಸೆಟ್ಟಿಂಗ್ಗಳಲ್ಲಿ ಬದಲಾಯಿಸಬಹುದು."</string>
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
+ <string name="underlay_close_button_content_description" msgid="6518481455548869894">"ಮುಚ್ಚಿರಿ"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="7666159428113013319">"ನೋಟಿಫಿಕೇಶನ್ಗಳನ್ನು ತೆರೆಯಲು ಮೇಲಿನ ಎಡದಿಂದ ಸ್ವೈಪ್ ಮಾಡಿ"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="3842355853737057332">"ತ್ವರಿತ ಸೆಟ್ಟಿಂಗ್ಗಳನ್ನು ತೆರೆಯಲು ಮೇಲಿನ ಬಲದಿಂದ ಸ್ವೈಪ್ ಮಾಡಿ"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ko-ldrtl/strings.xml b/packages/SystemUI/res/values-ko-ldrtl/strings.xml
index 634664f..15369ab 100644
--- a/packages/SystemUI/res/values-ko-ldrtl/strings.xml
+++ b/packages/SystemUI/res/values-ko-ldrtl/strings.xml
@@ -20,4 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="recents_quick_scrub_onboarding" msgid="2452671841151577157">"앱을 빠르게 전환하려면 왼쪽으로 드래그"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="6543179539438220549">"알림을 열려면 오른쪽 상단에서 스와이프하세요."</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="5760503283520058116">"빠른 설정을 열려면 왼쪽 상단에서 스와이프하세요."</string>
</resources>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index db7d208..0b8ac9c 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -467,6 +467,8 @@
<string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"마이크 버튼을 사용하려면 설정에서 마이크 액세스를 사용 설정하세요."</string>
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"설정 열기"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"기타 기기"</string>
+ <string name="media_suggestion_disconnected_text" msgid="8777932417076437525">"<xliff:g id="DEVICE_NAME">%s</xliff:g>에서 재생"</string>
+ <string name="media_suggestion_failure_text" msgid="8453022394887613774">"연결할 수 없습니다. 다시 시도해 보세요."</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"최근 사용 버튼 전환"</string>
<string name="zen_modes_dialog_title" msgid="8854640808100096934">"모드"</string>
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"완료"</string>
@@ -582,6 +584,7 @@
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
<skip />
<string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"전체 화면 공유"</string>
+ <string name="screen_share_permission_dialog_option_text_entire_screen_for_display" msgid="1545850238689169166">"<xliff:g id="DISPLAY_NAME">%s</xliff:g> 공유"</string>
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
<skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"전체 화면을 공유하면 화면에 있는 모든 항목이 <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>에 표시됩니다. 따라서 비밀번호, 결제 세부정보, 메시지, 사진, 오디오 및 동영상 등이 노출되지 않도록 주의하세요."</string>
@@ -593,6 +596,7 @@
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"화면을 전송하시겠습니까?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"앱 1개 전송"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"전체 화면 전송"</string>
+ <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen_for_display" msgid="5524817761472941086">"<xliff:g id="DISPLAY_NAME">%s</xliff:g> 전송"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"전체 화면을 전송하면 화면에 있는 모든 항목을 볼 수 있게 됩니다. 따라서 비밀번호, 결제 세부정보, 메시지, 사진, 오디오 및 동영상 등이 노출되지 않도록 주의하세요."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"앱을 전송하면 해당 앱에 표시되거나 재생되는 모든 항목을 볼 수 있게 됩니다. 비밀번호, 결제 세부정보, 메시지, 사진, 오디오 및 동영상 등이 노출되지 않도록 주의하세요."</string>
<string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"화면 전송"</string>
@@ -673,6 +677,7 @@
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"TrustAgent가 잠금 해제함"</string>
<string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"인증 시도 횟수가 너무 많아 기기가 잠겼습니다."</string>
<string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"기기 잠김\n인증에 실패함"</string>
+ <string name="keyguard_indication_after_watch_disconnected" msgid="6329761892437377710">"워치에서 잠금"</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="accessibility_volume_settings" msgid="1458961116951564784">"소리 설정"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"미디어 자막 자동 생성"</string>
@@ -799,6 +804,10 @@
<string name="enable_bluetooth_message" msgid="6740938333772779717">"키보드를 태블릿에 연결하려면 먼저 블루투스를 켜야 합니다."</string>
<string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"사용"</string>
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"켜짐 - 얼굴 기준"</string>
+ <!-- no translation found for notification_guts_bundle_title (1345506995443305361) -->
+ <skip />
+ <!-- no translation found for notification_guts_bundle_summary (3971530802237393600) -->
+ <skip />
<string name="inline_done_button" msgid="6043094985588909584">"완료"</string>
<string name="inline_ok_button" msgid="603075490581280343">"적용"</string>
<string name="inline_turn_off_notifications" msgid="2653064779176881329">"사용 중지"</string>
@@ -984,6 +993,7 @@
<string name="right_icon" msgid="1103955040645237425">"오른쪽 아이콘"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"길게 터치하고 드래그하여 타일 추가"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"길게 터치하고 드래그하여 타일 재정렬"</string>
+ <string name="select_to_rearrange_tiles" msgid="6166153534235179148">"타일을 선택하여 재정렬하고 크기를 조절합니다"</string>
<string name="tap_to_position_tile" msgid="6282815817773342757">"탭하여 타일 위치 지정"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"여기로 드래그하여 삭제"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"<xliff:g id="MIN_NUM_TILES">%1$d</xliff:g>개 이상의 타일이 필요합니다."</string>
@@ -1003,10 +1013,12 @@
<string name="tuner_low_priority" msgid="8412666814123009820">"우선순위가 낮은 알림 아이콘 표시"</string>
<string name="other" msgid="429768510980739978">"기타"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"타일 크기 전환"</string>
- <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"타일 삭제"</string>
+ <!-- no translation found for accessibility_qs_edit_remove_tile_action (4811778796933958616) -->
+ <skip />
<string name="accessibility_qs_edit_toggle_placement_mode" msgid="3870429389210569610">"배치 모드 전환"</string>
<string name="accessibility_qs_edit_toggle_selection" msgid="2201248304072372239">"선택 전환"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"마지막 위치에 타일 추가"</string>
+ <!-- no translation found for accessibility_qs_edit_tile_add_action (7188303979684400620) -->
+ <skip />
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"타일 이동"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"원하는 위치에 타일 추가"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> 위치로 이동"</string>
@@ -1093,7 +1105,8 @@
<string name="auto_saver_text" msgid="3214960308353838764">"배터리가 소진될 것 같으면 사용 설정"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"사용 안함"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"사용 중"</string>
- <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"애플리케이션이 <xliff:g id="TYPES_LIST">%s</xliff:g>을(를) 사용 중입니다."</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps (4994455315457996796) -->
+ <skip />
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" 및 "</string>
<string name="ongoing_privacy_dialog_using_op" msgid="426635338010011796">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g>에서 사용 중"</string>
@@ -1508,11 +1521,13 @@
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"단축키 추가"</string>
<string name="shortcut_helper_delete_shortcut_button_label" msgid="3148773472696137052">"단축키 삭제"</string>
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"키보드를 사용하여 이동"</string>
- <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"단축키에 관해 알아보세요."</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2131074100205028210) -->
+ <skip />
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"터치패드를 사용하여 이동"</string>
<string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"터치패드 동작을 알아보세요."</string>
<string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"키보드와 터치패드를 사용하여 이동"</string>
- <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"터치패드 동작, 단축키 등 알아보기"</string>
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (8557098239919786046) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"뒤로 이동"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"홈으로 이동"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"최근 앱 보기"</string>
@@ -1582,4 +1597,7 @@
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"모든 빠른 설정 타일이 기기의 원래 설정으로 재설정됩니다."</string>
<string name="demote_explain_text" msgid="3942301497888762295">"<xliff:g id="APPLICATION">%1$s</xliff:g>에서 더 이상 실시간 소식을 표시하지 않습니다. 설정에서 언제든지 변경할 수 있습니다."</string>
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
+ <string name="underlay_close_button_content_description" msgid="6518481455548869894">"닫기"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="7666159428113013319">"알림을 열려면 왼쪽 상단에서 스와이프하세요."</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="3842355853737057332">"빠른 설정을 열려면 오른쪽 상단에서 스와이프하세요."</string>
</resources>
diff --git a/packages/SystemUI/res/values-ky-ldrtl/strings.xml b/packages/SystemUI/res/values-ky-ldrtl/strings.xml
index 2bc0fe4..a32acf5 100644
--- a/packages/SystemUI/res/values-ky-ldrtl/strings.xml
+++ b/packages/SystemUI/res/values-ky-ldrtl/strings.xml
@@ -20,4 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="recents_quick_scrub_onboarding" msgid="2452671841151577157">"Колдонмолорду тез которуштуруу үчүн, солго сүйрөңүз"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="6543179539438220549">"Билдирмелерди ачуу үчүн жогорку оң бурчтан ылдый сүрүп коюңуз"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="5760503283520058116">"Ыкчам параметрлерди ачуу үчүн жогорку сол бурчтан ылдый сүрүп коюңуз"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index ede5569..58e39db 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -252,8 +252,7 @@
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g> менен туташкан."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"<xliff:g id="CAST">%s</xliff:g> менен туташты."</string>
<string name="accessibility_expand_group" msgid="521237935987978624">"Топту жайып көрсөтүү."</string>
- <!-- no translation found for accessibility_collapse_group (1842704016768329517) -->
- <skip />
+ <string name="accessibility_collapse_group" msgid="1842704016768329517">"Топту жыйыштырыңыз."</string>
<string name="accessibility_add_device_to_group" msgid="5446422960697860806">"Түзмөктү топко кошуңуз."</string>
<string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"Түзмөктү топтон алып салыңыз."</string>
<string name="accessibility_open_application" msgid="1749126077501259712">"Колдонмону ачуу."</string>
@@ -468,6 +467,8 @@
<string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"Микрофон баскычын колдонуу үчүн Параметрлерден микрофонду колдонууну иштетиңиз."</string>
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Параметрлерди ачуу"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Башка түзмөк"</string>
+ <string name="media_suggestion_disconnected_text" msgid="8777932417076437525">"<xliff:g id="DEVICE_NAME">%s</xliff:g> түзмөгүндө ойнотуу"</string>
+ <string name="media_suggestion_failure_text" msgid="8453022394887613774">"Туташпай жатат. Кайталаңыз."</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Назар режимин өчүрүү/күйгүзүү"</string>
<string name="zen_modes_dialog_title" msgid="8854640808100096934">"Режимдер"</string>
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Бүттү"</string>
@@ -583,6 +584,7 @@
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
<skip />
<string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Толук экранды бөлүшүү"</string>
+ <string name="screen_share_permission_dialog_option_text_entire_screen_for_display" msgid="1545850238689169166">"Бөлүшүү: <xliff:g id="DISPLAY_NAME">%s</xliff:g>"</string>
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
<skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Бүтүндөй экранды бөлүшкөндө андагы бардык нерселер <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> колдонмосуна көрүнөт. Андыктан сырсөздөрдү, төлөмдүн чоо-жайын, билдирүүлөрдү, сүрөттөрдү, аудио жана видеону көрсөтүп албаңыз."</string>
@@ -594,6 +596,7 @@
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Экранды башка түзмөккө чыгарасызбы?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Бир колдонмону чыгаруу"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Бүтүндөй экранды чыгаруу"</string>
+ <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen_for_display" msgid="5524817761472941086">"Тышкы экранга чыгаруу: <xliff:g id="DISPLAY_NAME">%s</xliff:g>"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Бүтүндөй экранды тышкы экранга чыгарганда, андагы нерселердин баары көрүнөт. Андыктан сырсөздөр, төлөмдүн чоо-жайы, билдирүүлөр, сүрөттөр, аудио жана видео сыяктуу нерселер менен этият болуңуз."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Колдонмону тышкы экранга чыгарганда, андагы нерселердин баары көрүнөт. Андыктан сырсөздөр, төлөмдүн чоо-жайы, билдирүүлөр, сүрөттөр, аудио жана видео сыяктуу нерселер менен этият болуңуз."</string>
<string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Тышкы экранга чыгаруу"</string>
@@ -674,6 +677,7 @@
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Ишеним агенти кулпусун ачты"</string>
<string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"Аутентификациядан өтө албай койгонуңуздан улам, түзмөк кулпуланды"</string>
<string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"Түзмөк кулпуланды\nАутентификациядан өткөн жоксуз"</string>
+ <string name="keyguard_indication_after_watch_disconnected" msgid="6329761892437377710">"Саатыңыз аркылуу кулпуланды"</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="accessibility_volume_settings" msgid="1458961116951564784">"Добуштун параметрлери"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Автоматтык коштомо жазуулар"</string>
@@ -800,6 +804,10 @@
<string name="enable_bluetooth_message" msgid="6740938333772779717">"Баскычтобуңузду планшетиңизге туташтыруу үчүн, адегенде Bluetooth\'ту күйгүзүшүңүз керек."</string>
<string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Күйгүзүү"</string>
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Күйүк – Жүздүн негизинде"</string>
+ <!-- no translation found for notification_guts_bundle_title (1345506995443305361) -->
+ <skip />
+ <!-- no translation found for notification_guts_bundle_summary (3971530802237393600) -->
+ <skip />
<string name="inline_done_button" msgid="6043094985588909584">"Бүттү"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Колдонуу"</string>
<string name="inline_turn_off_notifications" msgid="2653064779176881329">"Өчүрүү"</string>
@@ -985,6 +993,7 @@
<string name="right_icon" msgid="1103955040645237425">"¨Оңго¨ сүрөтчөсү"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Керектүү элементтерди сүйрөп келиңиз"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Иретин өзгөртүү үчүн кармап туруп, сүйрөңүз"</string>
+ <string name="select_to_rearrange_tiles" msgid="6166153534235179148">"Иреттештирүү жана өлчөмүн өзгөртүү үчүн карталарды тандаңыз"</string>
<string name="tap_to_position_tile" msgid="6282815817773342757">"Картаны жайгаштыруу үчүн басыңыз"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Өчүрүү үчүн бул жерге сүйрөңүз"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Сизге жок дегенде <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> мозаика керек"</string>
@@ -1004,10 +1013,12 @@
<string name="tuner_low_priority" msgid="8412666814123009820">"Анча маанилүү эмес билдирменин сүрөтчөлөрүн көрсөтүү"</string>
<string name="other" msgid="429768510980739978">"Башка"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"плитканын өлчөмүн которуштуруу"</string>
- <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"ыкчам баскычты өчүрүү"</string>
+ <!-- no translation found for accessibility_qs_edit_remove_tile_action (4811778796933958616) -->
+ <skip />
<string name="accessibility_qs_edit_toggle_placement_mode" msgid="3870429389210569610">"жайгаштыруу режимин өчүрүү/күйгүзүү"</string>
<string name="accessibility_qs_edit_toggle_selection" msgid="2201248304072372239">"тандоону өчүрүү/күйгүзүү"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"аягына карта кошуу"</string>
+ <!-- no translation found for accessibility_qs_edit_tile_add_action (7188303979684400620) -->
+ <skip />
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Ыкчам баскычты жылдыруу"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Керектүү жерге карта кошуу"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Төмөнкүгө жылдыруу: <xliff:g id="POSITION">%1$d</xliff:g>"</string>
@@ -1094,7 +1105,8 @@
<string name="auto_saver_text" msgid="3214960308353838764">"Батареянын кубаты түгөнүп калганда, күйгүзүлсүн"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Жок, рахмат"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"Колдонулууда"</string>
- <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Колдонмолор төмөнкүлөрдү пайдаланып жатышат: <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps (4994455315457996796) -->
+ <skip />
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" жана "</string>
<string name="ongoing_privacy_dialog_using_op" msgid="426635338010011796">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> колдонмосунда иштеп жатат"</string>
@@ -1251,15 +1263,13 @@
<string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(ажыратылды)"</string>
<string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"Которулбай жатат. Кайталоо үчүн басыңыз."</string>
<string name="media_output_dialog_pairing_new" msgid="5098212763195577270">"Түзмөктү туташтыруу"</string>
- <!-- no translation found for media_output_dialog_button_connect_device (2819038239946643550) -->
- <skip />
+ <string name="media_output_dialog_button_connect_device" msgid="2819038239946643550">"Түзмөктү туташтыруу"</string>
<string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Белгисиз колдонмо"</string>
<string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Тышкы экранга чыгарууну токтотуу"</string>
<string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"Аудио чыгаруу үчүн жеткиликтүү түзмөктөр."</string>
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Үндүн катуулугу"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
- <!-- no translation found for media_output_group_title_connected_speakers (3413876972919684479) -->
- <skip />
+ <string name="media_output_group_title_connected_speakers" msgid="3413876972919684479">"Туташкан динамиктер"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Динамиктер жана дисплейлер"</string>
<string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Сунушталган түзмөктөр"</string>
<string name="media_input_group_title" msgid="2057057473860783021">"Киргизүү"</string>
@@ -1328,10 +1338,8 @@
<string name="accessibility_enter_hint" msgid="2617864063504824834">"түзмөккө кирүү"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Манжаңыздын изи менен ачыңыз"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Аныктыкты текшерүү талап кылынат. Аныктыгын текшерүү үчүн манжа изинин сенсоруна тийип коюңуз."</string>
- <!-- no translation found for resume_animation (9148788411429330671) -->
- <skip />
- <!-- no translation found for pause_animation (1919705510058107504) -->
- <skip />
+ <string name="resume_animation" msgid="9148788411429330671">"Анимацияны улантуу"</string>
+ <string name="pause_animation" msgid="1919705510058107504">"Анимацияны тындыруу"</string>
<string name="ongoing_call_content_description" msgid="6394763878322348560">"Учурдагы чалуу"</string>
<string name="ongoing_notification_extra_content_description" msgid="2098752668861351265">"Учурдагы"</string>
<string name="mobile_data_settings_title" msgid="3955246641380064901">"Мобилдик трафик"</string>
@@ -1513,11 +1521,13 @@
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"Ыкчам баскыч кошуу"</string>
<string name="shortcut_helper_delete_shortcut_button_label" msgid="3148773472696137052">"Ыкчам баскычты өчүрүү"</string>
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Керектүү нерселерге баскычтоп аркылуу өтүү"</string>
- <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Ыкчам баскычтар тууралуу билип алыңыз"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2131074100205028210) -->
+ <skip />
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Керектүү жерге сенсордук такта аркылуу өтөсүз"</string>
<string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Сенсордук тактадагы жаңсоолорду үйрөнүп алыңыз"</string>
<string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Керектүү нерселерге баскычтоп жана сенсордук такта аркылуу өтүү"</string>
- <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Сенсордук тактадагы жаңсоолор, ыкчам баскычтар жана башкалар жөнүндө билип алыңыз"</string>
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (8557098239919786046) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Артка кайтуу"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Башкы бетке өтүү"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Акыркы колдонмолорду көрүү"</string>
@@ -1587,4 +1597,7 @@
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Бардык ыкчам параметрлер түзмөктүн баштапкы маанилерине кайтарылат"</string>
<string name="demote_explain_text" msgid="3942301497888762295">"<xliff:g id="APPLICATION">%1$s</xliff:g> мындан ары учурдагы жаңылыктарды көрсөтпөйт. Муну параметрлерден каалаган убакта өзгөртө аласыз."</string>
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
+ <string name="underlay_close_button_content_description" msgid="6518481455548869894">"Жабуу"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="7666159428113013319">"Билдирмелерди ачуу үчүн жогорку сол бурчтан ылдый сүрүп коюңуз"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="3842355853737057332">"Ыкчам параметрлерди ачуу үчүн жогорку оң бурчтан ылдый сүрүп коюңуз"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ldrtl/strings.xml b/packages/SystemUI/res/values-ldrtl/strings.xml
index a65bd4a..50cab70 100644
--- a/packages/SystemUI/res/values-ldrtl/strings.xml
+++ b/packages/SystemUI/res/values-ldrtl/strings.xml
@@ -24,7 +24,7 @@
Content of user education tooltip shown to teach the user that they can swipe down from the top
right edge of the display to expand the notification shade panel.
-->
- <string name="dual_shade_educational_tooltip_notifs">Swipe from the top right to open Notifications</string>
+ <string name="dual_shade_educational_tooltip_notifs">Swipe from the top right to open notifications</string>
<!--
Content of user education tooltip shown to teach the user that they can swipe down from the top
diff --git a/packages/SystemUI/res/values-lo-ldrtl/strings.xml b/packages/SystemUI/res/values-lo-ldrtl/strings.xml
index 17b5b01..e32160a 100644
--- a/packages/SystemUI/res/values-lo-ldrtl/strings.xml
+++ b/packages/SystemUI/res/values-lo-ldrtl/strings.xml
@@ -20,4 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="recents_quick_scrub_onboarding" msgid="2452671841151577157">"ລາກໄປຊ້າຍເພື່ອສະຫຼັບແອັບຢ່າງວ່ອງໄວ"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="6543179539438220549">"ປັດຈາກເທິງສຸດເບື້ອງຂວາເພື່ອເປີດການແຈ້ງເຕືອນ"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="5760503283520058116">"ປັດຈາກທາງຊ້າຍເທິງສຸດເພື່ອເປີດການຕັ້ງຄ່າດ່ວນ"</string>
</resources>
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index fbabeb0..7fad591 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -467,6 +467,8 @@
<string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"ເພື່ອໃຊ້ປຸ່ມໄມໂຄຣໂຟນ, ໃຫ້ເປີດການນຳໃຊ້ສິດເຂົ້າເຖິງໄມໂຄຣໂຟນໃນການຕັ້ງຄ່າກ່ອນ."</string>
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"ເປີດການຕັ້ງຄ່າ"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"ອຸປະກອນອື່ນໆ"</string>
+ <string name="media_suggestion_disconnected_text" msgid="8777932417076437525">"ຫຼິ້ນຢູ່ <xliff:g id="DEVICE_NAME">%s</xliff:g>"</string>
+ <string name="media_suggestion_failure_text" msgid="8453022394887613774">"ເຊື່ອມຕໍ່ບໍ່ໄດ້. ກະລຸນາລອງໃໝ່."</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"ສະຫຼັບພາບຮວມ"</string>
<string name="zen_modes_dialog_title" msgid="8854640808100096934">"ໂໝດ"</string>
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"ແລ້ວໆ"</string>
@@ -582,6 +584,7 @@
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
<skip />
<string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"ແບ່ງປັນໜ້າຈໍທັງໝົດ"</string>
+ <string name="screen_share_permission_dialog_option_text_entire_screen_for_display" msgid="1545850238689169166">"ແບ່ງປັນ <xliff:g id="DISPLAY_NAME">%s</xliff:g>"</string>
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
<skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"ເມື່ອທ່ານແບ່ງປັນໜ້າຈໍທັງໝົດຂອງທ່ານ, ຄົນອື່ນຈະເບິ່ງເຫັນທຸກຢ່າງທີ່ຢູ່ໜ້າຈໍຂອງທ່ານໃນ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. ດັ່ງນັ້ນ, ໃຫ້ລະມັດລະວັງສິ່ງຕ່າງໆ ເຊັ່ນ: ລະຫັດຜ່ານ, ລາຍລະອຽດການຈ່າຍເງິນ, ຂໍ້ຄວາມ, ຮູບພາບ, ພ້ອມທັງສຽງ ແລະ ວິດີໂອ."</string>
@@ -593,6 +596,7 @@
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"ສົ່ງສັນຍານໜ້າຈໍຂອງທ່ານບໍ?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"ສົ່ງສັນຍານແອັບ 1 ລາຍການ"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"ສົ່ງສັນຍານໜ້າຈໍທັງໝົດ"</string>
+ <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen_for_display" msgid="5524817761472941086">"ສົ່ງສັນຍານ <xliff:g id="DISPLAY_NAME">%s</xliff:g>"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"ເມື່ອທ່ານສົ່ງສັນຍານໜ້າຈໍທັງໝົດຂອງທ່ານ, ຄົນອື່ນຈະເບິ່ງເຫັນທຸກຢ່າງທີ່ຢູ່ໜ້າຈໍຂອງທ່ານ. ດັ່ງນັ້ນ, ໃຫ້ລະມັດລະວັງສິ່ງຕ່າງໆ ເຊັ່ນ: ລະຫັດຜ່ານ, ລາຍລະອຽດການຈ່າຍເງິນ, ຂໍ້ຄວາມ, ຮູບພາບ, ພ້ອມທັງສຽງ ແລະ ວິດີໂອ."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"ເມື່ອທ່ານສົ່ງສັນຍານແອັບ, ຄົນອື່ນຈະເບິ່ງເຫັນທຸກຢ່າງທີ່ສະແດງ ຫຼື ຫຼິ້ນໃນແອັບ. ດັ່ງນັ້ນ, ໃຫ້ລະມັດລະວັງສິ່ງຕ່າງໆ ເຊັ່ນ: ລະຫັດຜ່ານ, ລາຍລະອຽດການຈ່າຍເງິນ, ຂໍ້ຄວາມ, ຮູບພາບ, ພ້ອມທັງສຽງ ແລະ ວິດີໂອ."</string>
<string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"ສົ່ງສັນຍານໜ້າຈໍ"</string>
@@ -673,6 +677,7 @@
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"ປັອດລັອກປະໄວ້ໂດຍ TrustAgent"</string>
<string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"ອຸປະກອນຖືກລັອກແລ້ວ, ພະຍາຍາມເຮັດການພິສູດຢືນຢັນຫຼາຍເທື່ອເກີນໄປ"</string>
<string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"ລັອກອຸປະກອນແລ້ວ\nການພິສູດຢືນຢັນບໍ່ສຳເລັດ"</string>
+ <string name="keyguard_indication_after_watch_disconnected" msgid="6329761892437377710">"ຖືກລັອກໂດຍໂມງຂອງທ່ານ"</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="accessibility_volume_settings" msgid="1458961116951564784">"ການຕັ້ງຄ່າສຽງ"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"ສ້າງຄຳບັນຍາຍມີເດຍໂດຍອັດຕະໂນມັດ"</string>
@@ -799,6 +804,8 @@
<string name="enable_bluetooth_message" msgid="6740938333772779717">"ເພື່ອເຊື່ອມຕໍ່ແປ້ນພິມຂອງທ່ານກັບແທັບເລັດຂອງທ່ານ, ກ່ອນອື່ນໝົດທ່ານຕ້ອງເປີດ Bluetooth."</string>
<string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"ເປີດ"</string>
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"ເປີດ - ອ້າງອີງໃບໜ້າ"</string>
+ <string name="notification_guts_bundle_title" msgid="1345506995443305361">"ໃຊ້ຕົວຈັດລະບຽບການແຈ້ງເຕືອນ"</string>
+ <string name="notification_guts_bundle_summary" msgid="3971530802237393600">"ສຳລັບ <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="inline_done_button" msgid="6043094985588909584">"ແລ້ວໆ"</string>
<string name="inline_ok_button" msgid="603075490581280343">"ນຳໃຊ້"</string>
<string name="inline_turn_off_notifications" msgid="2653064779176881329">"ປິດ"</string>
@@ -984,6 +991,7 @@
<string name="right_icon" msgid="1103955040645237425">"ໄອຄອນຂວາ"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"ກົດຄ້າງໄວ້ແລ້ວລາກເພື່ອເພີ່ມຊ່ອງ"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"ກົດຄ້າງໄວ້ແລ້ວລາກເພື່ອຈັດຮຽງໃໝ່"</string>
+ <string name="select_to_rearrange_tiles" msgid="6166153534235179148">"ເລືອກແຜ່ນເພື່ອຈັດຮຽງໃໝ່ ແລະ ປັບຂະໜາດ"</string>
<string name="tap_to_position_tile" msgid="6282815817773342757">"ແຕະເພື່ອວາງຕຳແໜ່ງແຜ່ນ"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"ລາກມາບ່ອນນີ້ເພື່ອລຶບອອກ"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"ທ່ານຍຕ້ອງໃຊ້ຢ່າງໜ້ອຍ <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> ຊ່ອງ"</string>
@@ -1003,10 +1011,12 @@
<string name="tuner_low_priority" msgid="8412666814123009820">"ສະແດງໄອຄອນການແຈ້ງເຕືອນຄວາມສຳຄັນຕ່ຳ"</string>
<string name="other" msgid="429768510980739978">"ອື່ນໆ"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"ສະຫຼັບຂະໜາດຂອງແຜ່ນ"</string>
- <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"ລຶບແຜ່ນອອກ"</string>
+ <!-- no translation found for accessibility_qs_edit_remove_tile_action (4811778796933958616) -->
+ <skip />
<string name="accessibility_qs_edit_toggle_placement_mode" msgid="3870429389210569610">"ສະຫຼັບໂໝດການຈັດວາງ"</string>
<string name="accessibility_qs_edit_toggle_selection" msgid="2201248304072372239">"ສະຫຼັບການເລືອກ"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"ເພີ່ມແຜ່ນໃສ່ຕຳແໜ່ງສຸດທ້າຍ"</string>
+ <!-- no translation found for accessibility_qs_edit_tile_add_action (7188303979684400620) -->
+ <skip />
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"ຍ້າຍແຜ່ນ"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"ເພີ່ມແຜ່ນໃສ່ຕຳແໜ່ງທີ່ຕ້ອງການ"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"ຍ້າຍໄປ <xliff:g id="POSITION">%1$d</xliff:g>"</string>
@@ -1093,7 +1103,7 @@
<string name="auto_saver_text" msgid="3214960308353838764">"ເປີດໃຊ້ເມື່ອແບັດເຕີຣີໜ້າຈະໃກ້ໝົດ"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"ບໍ່, ຂອບໃຈ"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"ກຳລັງນຳໃຊ້ຢູ່"</string>
- <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"ແອັບພລິເຄຊັນກຳລັງໃຊ້ <xliff:g id="TYPES_LIST">%s</xliff:g> ຂອງທ່ານ."</string>
+ <string name="ongoing_privacy_chip_content_multiple_apps" msgid="4994455315457996796">"<xliff:g id="TYPES_LIST">%s</xliff:g> ກຳລັງນຳໃຊ້ຢູ່."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" ແລະ "</string>
<string name="ongoing_privacy_dialog_using_op" msgid="426635338010011796">"ກຳລັງໃຊ້ໂດຍ <xliff:g id="APPLICATION_NAME">%1$s</xliff:g>"</string>
@@ -1508,11 +1518,13 @@
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"ເພີ່ມທາງລັດ"</string>
<string name="shortcut_helper_delete_shortcut_button_label" msgid="3148773472696137052">"ລຶບທາງລັດ"</string>
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"ນຳທາງໂດຍໃຊ້ແປ້ນພິມຂອງທ່ານ"</string>
- <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"ສຶກສາຄີລັດ"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2131074100205028210) -->
+ <skip />
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"ນຳທາງໂດຍໃຊ້ແຜ່ນສຳຜັດຂອງທ່ານ"</string>
<string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"ສຶກສາທ່າທາງຂອງແຜ່ນສຳຜັດ"</string>
<string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"ນຳທາງໂດຍໃຊ້ແປ້ນພິມ ແລະ ແຜ່ນສຳຜັດຂອງທ່ານ"</string>
- <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"ສຶກສາທ່າທາງຂອງແຜ່ນສຳຜັດ, ຄີລັດ ແລະ ອື່ນໆ"</string>
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (8557098239919786046) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"ກັບຄືນ"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"ໄປຫາໜ້າຫຼັກ"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"ເບິ່ງແອັບຫຼ້າສຸດ"</string>
@@ -1582,4 +1594,7 @@
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"ແຜ່ນການຕັ້ງຄ່າດ່ວນທັງໝົດຈະຣີເຊັດເປັນການຕັ້ງຄ່າແບບເກົ່າຂອງອຸປະກອນ"</string>
<string name="demote_explain_text" msgid="3942301497888762295">"<xliff:g id="APPLICATION">%1$s</xliff:g> ຈະບໍ່ສະແດງຂໍ້ມູນອັບເດດສົດອີກຕໍ່ໄປ. ທ່ານສາມາດປ່ຽນການຕັ້ງຄ່ານີ້ໄດ້ທຸກເວລາໃນການຕັ້ງຄ່າ."</string>
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
+ <string name="underlay_close_button_content_description" msgid="6518481455548869894">"ປິດ"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="7666159428113013319">"ປັດຈາກເທິງສຸດເບື້ອງຊ້າຍເພື່ອເປີດການແຈ້ງເຕືອນ"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="3842355853737057332">"ປັດຈາກທາງຂວາເທິງສຸດເພື່ອເປີດການຕັ້ງຄ່າດ່ວນ"</string>
</resources>
diff --git a/packages/SystemUI/res/values-lt-ldrtl/strings.xml b/packages/SystemUI/res/values-lt-ldrtl/strings.xml
index 612a11c..d2f576b 100644
--- a/packages/SystemUI/res/values-lt-ldrtl/strings.xml
+++ b/packages/SystemUI/res/values-lt-ldrtl/strings.xml
@@ -20,4 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="recents_quick_scrub_onboarding" msgid="2452671841151577157">"Vilkite į kairę, kad greitai perjungtumėte programas"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="6543179539438220549">"Perbraukite nuo viršaus dešinėje, kad atidarytumėte pranešimus"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="5760503283520058116">"Perbraukite iš viršaus kairėje, kad atidarytumėte sparčiuosius nustatymus"</string>
</resources>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index c5c9dfa..6269041 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -252,8 +252,7 @@
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Prisijungta prie „<xliff:g id="BLUETOOTH">%s</xliff:g>“."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"Prisijungta prie <xliff:g id="CAST">%s</xliff:g>."</string>
<string name="accessibility_expand_group" msgid="521237935987978624">"Išskleisti grupę."</string>
- <!-- no translation found for accessibility_collapse_group (1842704016768329517) -->
- <skip />
+ <string name="accessibility_collapse_group" msgid="1842704016768329517">"Sutraukti grupę."</string>
<string name="accessibility_add_device_to_group" msgid="5446422960697860806">"Pridėti įrenginį prie grupės."</string>
<string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"Pašalinti įrenginį iš grupės."</string>
<string name="accessibility_open_application" msgid="1749126077501259712">"Atidaryti programą."</string>
@@ -468,6 +467,8 @@
<string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"Norėdami naudotis mikrofono mygtuku, nustatymuose įjunkite prieigą prie mikrofono."</string>
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Atidaryti nustatymus"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Kitas įrenginys"</string>
+ <string name="media_suggestion_disconnected_text" msgid="8777932417076437525">"Paleisti per „<xliff:g id="DEVICE_NAME">%s</xliff:g>“"</string>
+ <string name="media_suggestion_failure_text" msgid="8453022394887613774">"Neprisijungta. Dar kartą."</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Perjungti apžvalgą"</string>
<string name="zen_modes_dialog_title" msgid="8854640808100096934">"Režimai"</string>
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Atlikta"</string>
@@ -583,6 +584,7 @@
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
<skip />
<string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Bendrinti visą ekraną"</string>
+ <string name="screen_share_permission_dialog_option_text_entire_screen_for_display" msgid="1545850238689169166">"Bendrinti „<xliff:g id="DISPLAY_NAME">%s</xliff:g>“"</string>
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
<skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Kai bendrinate visą ekraną, „<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>“ matomas visas ekrano turinys. Todėl būkite atsargūs naudodami slaptažodžius, išsamią mokėjimo metodo informaciją, pranešimus, nuotraukas ir garso bei vaizdo įrašus."</string>
@@ -594,6 +596,7 @@
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Perduoti ekraną?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Perduoti vieną programą"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Perduoti visą ekraną"</string>
+ <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen_for_display" msgid="5524817761472941086">"Perduoti „<xliff:g id="DISPLAY_NAME">%s</xliff:g>“"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Kai perduodate visą ekraną, matomas visas ekrano turinys. Todėl būkite atsargūs naudodami slaptažodžius, išsamią mokėjimo metodo informaciją, pranešimus, nuotraukas ir garso bei vaizdo įrašus."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Kai perduodate programą, matomas visas toje programoje rodomas ar leidžiamas turinys. Todėl būkite atsargūs naudodami slaptažodžius, išsamią mokėjimo metodo informaciją, pranešimus, nuotraukas ir garso bei vaizdo įrašus."</string>
<string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Perduoti ekraną"</string>
@@ -674,6 +677,7 @@
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Atrakinta taikant „TrustAgent“"</string>
<string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"Įrenginys užrakintas, nes atlikta per daug bandymų autentifikuoti"</string>
<string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"Įrenginys užrakintas\nAutentifikuoti nepavyko"</string>
+ <string name="keyguard_indication_after_watch_disconnected" msgid="6329761892437377710">"Užrakinta laikrodžiu"</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="accessibility_volume_settings" msgid="1458961116951564784">"Garso nustatymai"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Taikyti aut. medij. subtitr."</string>
@@ -800,6 +804,8 @@
<string name="enable_bluetooth_message" msgid="6740938333772779717">"Kad galėtumėte prijungti klaviatūrą prie planšetinio kompiuterio, pirmiausia turite įjungti „Bluetooth“."</string>
<string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Įjungti"</string>
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Įjungta – pagal veidą"</string>
+ <string name="notification_guts_bundle_title" msgid="1345506995443305361">"Pranešimų tvarkyklės naudojimas"</string>
+ <string name="notification_guts_bundle_summary" msgid="3971530802237393600">"Skirta „<xliff:g id="APP_NAME">%1$s</xliff:g>“"</string>
<string name="inline_done_button" msgid="6043094985588909584">"Atlikta"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Taikyti"</string>
<string name="inline_turn_off_notifications" msgid="2653064779176881329">"Išjungti"</string>
@@ -985,6 +991,7 @@
<string name="right_icon" msgid="1103955040645237425">"Piktograma dešinėje"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Jei norite pridėti išklotinių, laikykite nuspaudę ir vilkite"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Norėdami pertvarkyti išklot., laikykite nuspaudę ir vilkite"</string>
+ <string name="select_to_rearrange_tiles" msgid="6166153534235179148">"Pasirinkite išklotines – pertvarkykite ir keiskite dydį"</string>
<string name="tap_to_position_tile" msgid="6282815817773342757">"Palieskite, kad nustatytumėte išklotinės elemento vietą"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Vilkite čia, jei norite pašalinti"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Turi būti bent <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> išklotinės elem."</string>
@@ -1004,10 +1011,10 @@
<string name="tuner_low_priority" msgid="8412666814123009820">"Rodyti mažo prioriteto pranešimų piktogramas"</string>
<string name="other" msgid="429768510980739978">"Kita"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"perjungti išklotinės elemento dydį"</string>
- <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"pašalintumėte išklotinės elementą"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="4811778796933958616">"Pašalinti išklotinės elementą"</string>
<string name="accessibility_qs_edit_toggle_placement_mode" msgid="3870429389210569610">"perjungti paskirties vietos režimą"</string>
<string name="accessibility_qs_edit_toggle_selection" msgid="2201248304072372239">"perjungti pasirinkimą"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"pridėti išklotinės elementą paskutinėje pozicijoje"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="7188303979684400620">"Pridėti išklotinės elementą paskutinėje pozicijoje"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Perkelti išklotinės elementą"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Pridėti išklotinės elementą norimoje pozicijoje"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Perkelkite į <xliff:g id="POSITION">%1$d</xliff:g> poziciją"</string>
@@ -1094,7 +1101,7 @@
<string name="auto_saver_text" msgid="3214960308353838764">"Įjunkite, jei akumuliatorius gali greitai išsekti"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Ne, ačiū"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"Naudojama"</string>
- <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Programos naudoja: <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
+ <string name="ongoing_privacy_chip_content_multiple_apps" msgid="4994455315457996796">"Naudojama <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" ir "</string>
<string name="ongoing_privacy_dialog_using_op" msgid="426635338010011796">"Naudoja <xliff:g id="APPLICATION_NAME">%1$s</xliff:g>"</string>
@@ -1251,15 +1258,13 @@
<string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(atjungta)"</string>
<string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"Nepavyko perjungti. Bandykite vėl palietę."</string>
<string name="media_output_dialog_pairing_new" msgid="5098212763195577270">"Įrenginio prijungimas"</string>
- <!-- no translation found for media_output_dialog_button_connect_device (2819038239946643550) -->
- <skip />
+ <string name="media_output_dialog_button_connect_device" msgid="2819038239946643550">"Prijungti įrenginį"</string>
<string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Nežinoma programa"</string>
<string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Sustabdyti perdavimą"</string>
<string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"Pasiekiami garso išvesties įrenginiai."</string>
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Garsumas"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g> %%"</string>
- <!-- no translation found for media_output_group_title_connected_speakers (3413876972919684479) -->
- <skip />
+ <string name="media_output_group_title_connected_speakers" msgid="3413876972919684479">"Prijungti garsiakalbiai"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Garsiakalbiai ir ekranai"</string>
<string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Siūlomi įrenginiai"</string>
<string name="media_input_group_title" msgid="2057057473860783021">"Įvestis"</string>
@@ -1328,10 +1333,8 @@
<string name="accessibility_enter_hint" msgid="2617864063504824834">"pasiektumėte įrenginį"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Naudokite kontrolinį kodą, kad atidarytumėte"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Reikia nustatyti tapatybę. Nustatykite tapatybę palietę kontrolinio kodo jutiklį."</string>
- <!-- no translation found for resume_animation (9148788411429330671) -->
- <skip />
- <!-- no translation found for pause_animation (1919705510058107504) -->
- <skip />
+ <string name="resume_animation" msgid="9148788411429330671">"Tęsti animaciją"</string>
+ <string name="pause_animation" msgid="1919705510058107504">"Pristabdyti animaciją"</string>
<string name="ongoing_call_content_description" msgid="6394763878322348560">"Vykstantis skambutis"</string>
<string name="ongoing_notification_extra_content_description" msgid="2098752668861351265">"Vyksta"</string>
<string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobiliojo ryšio duomenys"</string>
@@ -1513,11 +1516,11 @@
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"Pridėti spartųjį klavišą"</string>
<string name="shortcut_helper_delete_shortcut_button_label" msgid="3148773472696137052">"Ištrinti spartųjį klavišą"</string>
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Naršykite naudodamiesi klaviatūra"</string>
- <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Sužinokite apie sparčiuosius klavišus"</string>
+ <string name="launch_keyboard_tutorial_notification_content" msgid="2131074100205028210">"Sužinokite sparčiuosius klavišus"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Naršykite naudodamiesi jutikline dalimi"</string>
<string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Peržvelkite jutiklinės dalies gestus"</string>
<string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Naršykite naudodamiesi klaviatūra ir jutikline dalimi"</string>
- <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Sužinokite jutiklinės dalies gestus, sparčiuosius klavišus ir kt."</string>
+ <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="8557098239919786046">"Sužinokite gestus jutiklinėje dalyje, sparčiuosius klavišus ir kt."</string>
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Grįžti"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Eikite į pagrindinį puslapį"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Peržiūrėti naujausias programas"</string>
@@ -1587,4 +1590,7 @@
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Visi sparčiųjų nustatymų išklotinės elementai bus iš naujo nustatyti į pradinius įrenginio nustatymus"</string>
<string name="demote_explain_text" msgid="3942301497888762295">"„<xliff:g id="APPLICATION">%1$s</xliff:g>“ neberodys tiesioginių naujienų. Bet kada galite tai pakeisti Nustatymų skiltyje."</string>
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
+ <string name="underlay_close_button_content_description" msgid="6518481455548869894">"Uždaryti"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="7666159428113013319">"Perbraukite nuo viršaus kairėje, kad atidarytumėte pranešimus"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="3842355853737057332">"Perbraukite iš viršaus dešinėje, kad atidarytumėte sparčiuosius nustatymus"</string>
</resources>
diff --git a/packages/SystemUI/res/values-lv-ldrtl/strings.xml b/packages/SystemUI/res/values-lv-ldrtl/strings.xml
index 84633f8..c994ecf 100644
--- a/packages/SystemUI/res/values-lv-ldrtl/strings.xml
+++ b/packages/SystemUI/res/values-lv-ldrtl/strings.xml
@@ -20,4 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="recents_quick_scrub_onboarding" msgid="2452671841151577157">"Lai ātri pārslēgtu lietotnes, velciet pa kreisi"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="6543179539438220549">"Velciet no augšējā labā stūra, lai atvērtu paziņojumu paneli."</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="5760503283520058116">"Velciet no augšējā kreisā stūra, lai atvērtu ātro iestatījumu izvēlni."</string>
</resources>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index cb81a0a..fe0f9bf 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -247,13 +247,12 @@
<string name="accessibility_bluetooth_device_settings_gear" msgid="3314916468105272540">"Lai konfigurētu ierīces informāciju, noklikšķiniet"</string>
<string name="accessibility_bluetooth_device_settings_gear_with_name" msgid="114373701123165491">"<xliff:g id="DEVICE_NAME">%s</xliff:g>. Konfigurējiet ierīces informāciju."</string>
<string name="accessibility_bluetooth_device_settings_see_all" msgid="5260390270128256620">"Skatiet visas ierīces."</string>
- <string name="accessibility_bluetooth_device_settings_pair_new_device" msgid="7988547106800504256">"Savienojiet pārī jaunu ierīci."</string>
+ <string name="accessibility_bluetooth_device_settings_pair_new_device" msgid="7988547106800504256">"Savienot pārī ar jaunu ierīci"</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Akumulatora uzlādes līmenis procentos nav zināms."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Ir izveidots savienojum ar <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"Savienots ar ierīci <xliff:g id="CAST">%s</xliff:g>."</string>
<string name="accessibility_expand_group" msgid="521237935987978624">"Izvērst grupu."</string>
- <!-- no translation found for accessibility_collapse_group (1842704016768329517) -->
- <skip />
+ <string name="accessibility_collapse_group" msgid="1842704016768329517">"Sakļaut grupu."</string>
<string name="accessibility_add_device_to_group" msgid="5446422960697860806">"Pievienot ierīci grupai."</string>
<string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"Noņemt ierīci no grupas."</string>
<string name="accessibility_open_application" msgid="1749126077501259712">"Atvērt lietojumprogrammu."</string>
@@ -468,6 +467,8 @@
<string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"Lai varētu izmantot mikrofona pogu, iespējojiet piekļuvi mikrofonam sadaļā Iestatījumi."</string>
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Atvērt iestatījumus"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Cita ierīce"</string>
+ <string name="media_suggestion_disconnected_text" msgid="8777932417076437525">"Atskaņot šeit: <xliff:g id="DEVICE_NAME">%s</xliff:g>"</string>
+ <string name="media_suggestion_failure_text" msgid="8453022394887613774">"Nevar savienot. Mēģiniet vēl."</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Pārskata pārslēgšana"</string>
<string name="zen_modes_dialog_title" msgid="8854640808100096934">"Režīmi"</string>
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Gatavs"</string>
@@ -583,6 +584,7 @@
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
<skip />
<string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Kopīgot visu ekrānu"</string>
+ <string name="screen_share_permission_dialog_option_text_entire_screen_for_display" msgid="1545850238689169166">"Kopīgot šo displeju: <xliff:g id="DISPLAY_NAME">%s</xliff:g>"</string>
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
<skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Kopīgojot visu ekrānu, lietotnei <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ir pieejams viss ekrāna saturs. Tāpēc piesardzīgi apejieties ar parolēm, maksājumu informāciju, ziņojumiem, fotoattēliem un audio un video saturu."</string>
@@ -594,6 +596,7 @@
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Vai apraidīt ekrānu?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Apraidīt vienu lietotni"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Apraidīt visu ekrānu"</string>
+ <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen_for_display" msgid="5524817761472941086">"Apraidīt no šī displeja: <xliff:g id="DISPLAY_NAME">%s</xliff:g>"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Apraidot visu ekrānu, ir redzams viss ekrāna saturs. Tāpēc piesardzīgi apejieties ar parolēm, maksājumu informāciju, ziņojumiem, fotoattēliem un audio un video saturu."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Apraidot lietotni, ir redzams viss attiecīgajā lietotnē rādītais vai atskaņotais. Tāpēc piesardzīgi apejieties ar parolēm, maksājumu informāciju, ziņojumiem, fotoattēliem un audio un video saturu."</string>
<string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Apraidīt ekrānu"</string>
@@ -674,6 +677,7 @@
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Bloķēšanu liedzis TrustAgent"</string>
<string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"Ierīce tika bloķēta; pārāk daudz autentifikācijas mēģinājumu."</string>
<string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"Ierīce bloķēta\nAutentifikācija neizdevās"</string>
+ <string name="keyguard_indication_after_watch_disconnected" msgid="6329761892437377710">"Piekļuve bloķēta, izmantojot pulksteni"</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="accessibility_volume_settings" msgid="1458961116951564784">"Skaņas iestatījumi"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Autom. paraksti multividei"</string>
@@ -800,6 +804,10 @@
<string name="enable_bluetooth_message" msgid="6740938333772779717">"Lai pievienotu tastatūru planšetdatoram, vispirms ir jāieslēdz Bluetooth savienojums."</string>
<string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Ieslēgt"</string>
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Ieslēgta — ar sejas noteikšanu"</string>
+ <!-- no translation found for notification_guts_bundle_title (1345506995443305361) -->
+ <skip />
+ <!-- no translation found for notification_guts_bundle_summary (3971530802237393600) -->
+ <skip />
<string name="inline_done_button" msgid="6043094985588909584">"Gatavs"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Lietot"</string>
<string name="inline_turn_off_notifications" msgid="2653064779176881329">"Izslēgt"</string>
@@ -985,6 +993,7 @@
<string name="right_icon" msgid="1103955040645237425">"Ikona labajā pusē"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Lai pievienotu elementus, turiet un velciet tos"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Lai pārkārtotu elementus, turiet un velciet tos"</string>
+ <string name="select_to_rearrange_tiles" msgid="6166153534235179148">"Atlasiet elementus, ko pārkārtot un kam mainīt lielumu"</string>
<string name="tap_to_position_tile" msgid="6282815817773342757">"Pieskarieties, lai novietotu elementu"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Lai noņemtu vienumus, velciet tos šeit."</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Nepieciešami vismaz <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> elementi"</string>
@@ -1004,10 +1013,12 @@
<string name="tuner_low_priority" msgid="8412666814123009820">"Rādīt zemas prioritātes paziņojumu ikonas"</string>
<string name="other" msgid="429768510980739978">"Citi"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"pārslēgt elementa lielumu"</string>
- <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"noņemt elementu"</string>
+ <!-- no translation found for accessibility_qs_edit_remove_tile_action (4811778796933958616) -->
+ <skip />
<string name="accessibility_qs_edit_toggle_placement_mode" msgid="3870429389210569610">"pārslēgt izvietojuma režīmu"</string>
<string name="accessibility_qs_edit_toggle_selection" msgid="2201248304072372239">"pārslēgt atlasi"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"pievienotu elementu pēdējā pozīcijā"</string>
+ <!-- no translation found for accessibility_qs_edit_tile_add_action (7188303979684400620) -->
+ <skip />
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Pārvietot elementu"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Pievienot elementu vēlamajā pozīcijā"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Pārvietot uz pozīciju numur <xliff:g id="POSITION">%1$d</xliff:g>"</string>
@@ -1094,7 +1105,8 @@
<string name="auto_saver_text" msgid="3214960308353838764">"Ieslēgt, ja akumulators var izlādēties"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Nē, paldies"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"Pašlaik izmanto"</string>
- <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Lietojumprogrammas izmanto šādas funkcijas: <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps (4994455315457996796) -->
+ <skip />
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" un "</string>
<string name="ongoing_privacy_dialog_using_op" msgid="426635338010011796">"To izmanto lietotne <xliff:g id="APPLICATION_NAME">%1$s</xliff:g>"</string>
@@ -1251,15 +1263,13 @@
<string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(savienojums pārtraukts)"</string>
<string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"Nevar pārslēgt. Pieskarieties, lai mēģinātu vēlreiz."</string>
<string name="media_output_dialog_pairing_new" msgid="5098212763195577270">"Savienot ar ierīci"</string>
- <!-- no translation found for media_output_dialog_button_connect_device (2819038239946643550) -->
- <skip />
+ <string name="media_output_dialog_button_connect_device" msgid="2819038239946643550">"Pievienot ierīci"</string>
<string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Nezināma lietotne"</string>
<string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Apturēt apraidi"</string>
<string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"Audio izvadei pieejamās ierīces."</string>
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Skaļums"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
- <!-- no translation found for media_output_group_title_connected_speakers (3413876972919684479) -->
- <skip />
+ <string name="media_output_group_title_connected_speakers" msgid="3413876972919684479">"Pievienotie skaļruņi"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Skaļruņi un displeji"</string>
<string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Ieteiktās ierīces"</string>
<string name="media_input_group_title" msgid="2057057473860783021">"Ievade"</string>
@@ -1328,10 +1338,8 @@
<string name="accessibility_enter_hint" msgid="2617864063504824834">"izmantotu ierīci"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Atvēršanai izmantojiet pirksta nospiedumu"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Nepieciešama autentifikācija. Pieskarieties pirksta nospieduma sensoram, lai veiktu autentificēšanu."</string>
- <!-- no translation found for resume_animation (9148788411429330671) -->
- <skip />
- <!-- no translation found for pause_animation (1919705510058107504) -->
- <skip />
+ <string name="resume_animation" msgid="9148788411429330671">"Atsākt animāciju"</string>
+ <string name="pause_animation" msgid="1919705510058107504">"Pārtraukt animāciju"</string>
<string name="ongoing_call_content_description" msgid="6394763878322348560">"Notiekošs zvans"</string>
<string name="ongoing_notification_extra_content_description" msgid="2098752668861351265">"Aktīva darbība"</string>
<string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobilie dati"</string>
@@ -1513,11 +1521,13 @@
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"Pievienot saīsni"</string>
<string name="shortcut_helper_delete_shortcut_button_label" msgid="3148773472696137052">"Dzēst saīsni"</string>
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Pārvietošanās, izmantojot tastatūru"</string>
- <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Uzziniet par īsinājumtaustiņiem."</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2131074100205028210) -->
+ <skip />
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Pārvietošanās, izmantojot skārienpaliktni"</string>
<string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Apgūstiet skārienpaliktņa žestus."</string>
<string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Pārvietošanās, izmantojot tastatūru un skārienpaliktni"</string>
- <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Uzziniet par skārienpaliktņa žestiem, īsinājumtaustiņiem un citām iespējām."</string>
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (8557098239919786046) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Atpakaļ"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Pāriet uz sākuma ekrānu"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Skatīt nesen izmantotās lietotnes"</string>
@@ -1587,4 +1597,7 @@
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Visiem ātro iestatījumu elementiem tiks atiestatīti sākotnējie iestatījumi"</string>
<string name="demote_explain_text" msgid="3942301497888762295">"<xliff:g id="APPLICATION">%1$s</xliff:g> vairs nerādīs reāllaika jaunumus. Iestatījumos varat jebkurā laikā to mainīt."</string>
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
+ <string name="underlay_close_button_content_description" msgid="6518481455548869894">"Aizvērt"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="7666159428113013319">"Velciet no augšējā kreisā stūra, lai atvērtu paziņojumu paneli."</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="3842355853737057332">"Velciet no augšējā labā stūra, lai atvērtu ātro iestatījumu izvēlni."</string>
</resources>
diff --git a/packages/SystemUI/res/values-mk-ldrtl/strings.xml b/packages/SystemUI/res/values-mk-ldrtl/strings.xml
index 4529470..515aac1 100644
--- a/packages/SystemUI/res/values-mk-ldrtl/strings.xml
+++ b/packages/SystemUI/res/values-mk-ldrtl/strings.xml
@@ -20,4 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="recents_quick_scrub_onboarding" msgid="2452671841151577157">"Повлечете налево за брзо префрлање меѓу апликациите"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="6543179539438220549">"Повлечете од горниот десен агол за да ги отворите известувањата"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="5760503283520058116">"Повлечете од горниот лев агол за да ги отворите „Брзите поставки“"</string>
</resources>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index f0aef63..601fce4 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -110,11 +110,11 @@
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Се обработува снимка од екран"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Тековно известување за сесија за снимање на екранот"</string>
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Да се снима екранот?"</string>
- <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Снимање една апликација"</string>
+ <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Снимај една апликација"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="4882406311415082016">"Снимај го екранов"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="4169494703993148253">"Снимај го екранот %s"</string>
- <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Додека го снимате целиот екран, сѐ што е прикажано на екранот се снима. Затоа, бидете внимателни со лозинките, деталите за плаќање, пораките, фотографиите и аудиото и видеото."</string>
- <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Додека снимате апликација, може да се сними сѐ што се прикажува или пушта во таа апликација. Затоа, бидете внимателни со лозинките, деталите за плаќање, пораките, фотографиите и аудиото и видеото."</string>
+ <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"При снимањето на целиот екран, се снима сѐ што се прикажува на екранот. Затоа, бидете внимателни со лозинките, податоците за плаќање, пораките, фотографиите, аудиото и видеото."</string>
+ <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"При снимањето на апликацијата, се снима сѐ што се прикажува или пушта во таа апликација. Затоа, бидете внимателни со лозинките, податоците за плаќање, пораките, фотографиите, аудиото и видеото."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Снимај го екранот"</string>
<string name="screenrecord_app_selector_title" msgid="3854492366333954736">"Изберете апликација за снимање"</string>
<string name="screenrecord_audio_label" msgid="6183558856175159629">"Снимај аудио"</string>
@@ -252,8 +252,7 @@
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Поврзано со <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"Поврзано со <xliff:g id="CAST">%s</xliff:g>."</string>
<string name="accessibility_expand_group" msgid="521237935987978624">"Проширете ја групата."</string>
- <!-- no translation found for accessibility_collapse_group (1842704016768329517) -->
- <skip />
+ <string name="accessibility_collapse_group" msgid="1842704016768329517">"Собери ја групата."</string>
<string name="accessibility_add_device_to_group" msgid="5446422960697860806">"Додај го уредот во групата."</string>
<string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"Отстрани го уредот од групата."</string>
<string name="accessibility_open_application" msgid="1749126077501259712">"Отворете ја апликацијата."</string>
@@ -468,6 +467,8 @@
<string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"За да го користите копчето за микрофон, овозможете пристап до микрофонот во „Поставки“."</string>
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Отворете „Поставки“"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Друг уред"</string>
+ <string name="media_suggestion_disconnected_text" msgid="8777932417076437525">"Пуштај на <xliff:g id="DEVICE_NAME">%s</xliff:g>"</string>
+ <string name="media_suggestion_failure_text" msgid="8453022394887613774">"Не се поврза. Обидете се пак."</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Вклучи/исклучи преглед"</string>
<string name="zen_modes_dialog_title" msgid="8854640808100096934">"Режими"</string>
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Готово"</string>
@@ -583,6 +584,7 @@
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
<skip />
<string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Споделете го целиот екран"</string>
+ <string name="screen_share_permission_dialog_option_text_entire_screen_for_display" msgid="1545850238689169166">"Сподели <xliff:g id="DISPLAY_NAME">%s</xliff:g>"</string>
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
<skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Додека го споделувате целиот екран, сè на екранот е видливо за <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Затоа, бидете внимателни со работи како лозинки, детали за плаќање, пораки, фотографии и аудио и видео."</string>
@@ -594,6 +596,7 @@
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Да се емитува вашиот екран?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Емитувајте една апликација"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Емитувајте го целиот екран"</string>
+ <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen_for_display" msgid="5524817761472941086">"Емитувај <xliff:g id="DISPLAY_NAME">%s</xliff:g>"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Додека го емитувате целиот екран, може да се види сè што е на екранот. Затоа, бидете внимателни со работи како лозинки, детали за плаќање, пораки, фотографии и аудио и видео."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Додека емитувате апликација, може да се види сѐ што се прикажува или пушта во таа апликација. Затоа, бидете внимателни со работи како лозинки, детали за плаќање, пораки, фотографии и аудио и видео."</string>
<string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Емитувај го екранот"</string>
@@ -674,6 +677,7 @@
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Се одржува отклучен од TrustAgent"</string>
<string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"Уредот се заклучи, премногу обиди за автентикација"</string>
<string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"Уредот е заклучен\nАвтентикацијата не успеа"</string>
+ <string name="keyguard_indication_after_watch_disconnected" msgid="6329761892437377710">"Заклучено од часовникот"</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="accessibility_volume_settings" msgid="1458961116951564784">"Поставки за звукот"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Автоматски титлови"</string>
@@ -717,10 +721,10 @@
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Вибрации"</string>
<string name="volume_ringer_status_silent" msgid="3691324657849880883">"Исклучи звук"</string>
<string name="media_device_cast" msgid="4786241789687569892">"Емитување"</string>
- <string name="stream_notification_unavailable" msgid="4313854556205836435">"Недостапно бидејќи звукот е исклучен"</string>
+ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Недостапно бидејќи звукот на ѕвонење е исклучен"</string>
<string name="stream_alarm_unavailable" msgid="4059817189292197839">"Недостапно бидејќи е вклучено „Не вознемирувај“"</string>
<string name="stream_media_unavailable" msgid="6823020894438959853">"Недостапно бидејќи е вклучено „Не вознемирувај“"</string>
- <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Недостапно бидејќи има вклучено <xliff:g id="MODE">%s</xliff:g>"</string>
+ <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Недостапно бидејќи е вклучен „<xliff:g id="MODE">%s</xliff:g>“"</string>
<string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Недостапно"</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Допрете за да вклучите звук."</string>
<string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Допрете за да поставите на вибрации. Можеби ќе се исклучи звукот на услугите за достапност."</string>
@@ -748,7 +752,7 @@
<string name="volume_panel_hint_muted" msgid="1124844870181285320">"придушено"</string>
<string name="volume_panel_hint_vibrate" msgid="4136223145435914132">"вибрации"</string>
<string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g>: пуштено на"</string>
- <string name="media_output_title_without_playing" msgid="3825663683169305013">"Аудиото ќе се пушти на"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Аудиото ќе се пушта на"</string>
<string name="media_output_title_ongoing_call" msgid="208426888064112006">"Повик во тек"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"Адаптер на УИ на системот"</string>
<string name="status_bar" msgid="4357390266055077437">"Статусна лента"</string>
@@ -800,6 +804,10 @@
<string name="enable_bluetooth_message" msgid="6740938333772779717">"За да ја поврзете тастатурата со таблетот, најпрво треба да вклучите Bluetooth."</string>
<string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Вклучи"</string>
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Вклучено - според лице"</string>
+ <!-- no translation found for notification_guts_bundle_title (1345506995443305361) -->
+ <skip />
+ <!-- no translation found for notification_guts_bundle_summary (3971530802237393600) -->
+ <skip />
<string name="inline_done_button" msgid="6043094985588909584">"Готово"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Примени"</string>
<string name="inline_turn_off_notifications" msgid="2653064779176881329">"Исклучи"</string>
@@ -985,6 +993,7 @@
<string name="right_icon" msgid="1103955040645237425">"Десна икона"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Задржете и влечете за да додадете плочки"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Задржете и влечете за да ги преуредите плочките"</string>
+ <string name="select_to_rearrange_tiles" msgid="6166153534235179148">"Изберете плочки за преуредување и променување на големината"</string>
<string name="tap_to_position_tile" msgid="6282815817773342757">"Допрете за да ја поставите плочката"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Повлечете тука за да се отстрани"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Потребни ви се најмалку <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> плочки"</string>
@@ -1004,10 +1013,10 @@
<string name="tuner_low_priority" msgid="8412666814123009820">"Прикажувај икони за известувања со низок приоритет"</string>
<string name="other" msgid="429768510980739978">"Друго"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"вклучување/исклучување на големината на плочката"</string>
- <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"отстранување на плочката"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="4811778796933958616">"Отстранете ја плочката"</string>
<string name="accessibility_qs_edit_toggle_placement_mode" msgid="3870429389210569610">"вклучување/исклучување режим на пласирање"</string>
<string name="accessibility_qs_edit_toggle_selection" msgid="2201248304072372239">"вклучување/исклучување на изборот"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"додајте плочка на последната позиција"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="7188303979684400620">"Додајте ја плочката на последната позиција"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Преместување на плочката"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Додајте плочка на саканата позиција"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Преместување на <xliff:g id="POSITION">%1$d</xliff:g>"</string>
@@ -1094,7 +1103,8 @@
<string name="auto_saver_text" msgid="3214960308353838764">"Вклучи ако е веројатно дека батеријата ќе се испразни"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Не, фала"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"Во употреба"</string>
- <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Апликациите користат <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps (4994455315457996796) -->
+ <skip />
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" и "</string>
<string name="ongoing_privacy_dialog_using_op" msgid="426635338010011796">"Се користи од <xliff:g id="APPLICATION_NAME">%1$s</xliff:g>"</string>
@@ -1171,7 +1181,7 @@
<string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{Додадена е # контрола.}one{Додадени се # контрола.}other{Додадени се # контроли.}}"</string>
<string name="controls_removed" msgid="3731789252222856959">"Отстранета"</string>
<string name="controls_panel_authorization_title" msgid="267429338785864842">"Да се додаде <xliff:g id="APPNAME">%s</xliff:g>?"</string>
- <string name="controls_panel_authorization" msgid="7045551688535104194">"<xliff:g id="APPNAME">%s</xliff:g> може да избере кои контроли и содржини се прикажуваат овде."</string>
+ <string name="controls_panel_authorization" msgid="7045551688535104194">"<xliff:g id="APPNAME">%s</xliff:g> може да избира кои контроли и содржини ќе се прикажуваат овде."</string>
<string name="controls_panel_remove_app_authorization" msgid="5920442084735364674">"Да се отстранат контролите за <xliff:g id="APPNAME">%s</xliff:g>?"</string>
<string name="accessibility_control_favorite" msgid="8694362691985545985">"Омилена"</string>
<string name="accessibility_control_favorite_position" msgid="54220258048929221">"Омилена, позиција <xliff:g id="NUMBER">%d</xliff:g>"</string>
@@ -1251,15 +1261,13 @@
<string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(врската е прекината)"</string>
<string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"Не се префрла. Допрете и обидете се пак."</string>
<string name="media_output_dialog_pairing_new" msgid="5098212763195577270">"Поврзете уред"</string>
- <!-- no translation found for media_output_dialog_button_connect_device (2819038239946643550) -->
- <skip />
+ <string name="media_output_dialog_button_connect_device" msgid="2819038239946643550">"Поврзи го уредот"</string>
<string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Непозната апликација"</string>
<string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Сопри со емитување"</string>
<string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"Достапни уреди за аудиоизлез."</string>
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Јачина на звук"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g> %%"</string>
- <!-- no translation found for media_output_group_title_connected_speakers (3413876972919684479) -->
- <skip />
+ <string name="media_output_group_title_connected_speakers" msgid="3413876972919684479">"Поврзани звучници"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Звучници и екрани"</string>
<string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Предложени уреди"</string>
<string name="media_input_group_title" msgid="2057057473860783021">"Влез"</string>
@@ -1328,10 +1336,8 @@
<string name="accessibility_enter_hint" msgid="2617864063504824834">"внесете уред"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Користете отпечаток за да се отвори"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Потребна е проверка. Допрете го сензорот за отпечаток за да автентицирате."</string>
- <!-- no translation found for resume_animation (9148788411429330671) -->
- <skip />
- <!-- no translation found for pause_animation (1919705510058107504) -->
- <skip />
+ <string name="resume_animation" msgid="9148788411429330671">"Отпаузирај ја анимацијата"</string>
+ <string name="pause_animation" msgid="1919705510058107504">"Паузирај ја анимацијата"</string>
<string name="ongoing_call_content_description" msgid="6394763878322348560">"Тековен повик"</string>
<string name="ongoing_notification_extra_content_description" msgid="2098752668861351265">"Во тек"</string>
<string name="mobile_data_settings_title" msgid="3955246641380064901">"Мобилен интернет"</string>
@@ -1513,15 +1519,17 @@
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"Додај кратенка"</string>
<string name="shortcut_helper_delete_shortcut_button_label" msgid="3148773472696137052">"Избриши кратенка"</string>
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Движете се со користење на тастатурата"</string>
- <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Научете ги кратенките од тастатурата"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2131074100205028210) -->
+ <skip />
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Движете се со користење на допирната подлога"</string>
<string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Научете движења за допирната подлога"</string>
<string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Движете се со користење на тастатурата и допирната подлога"</string>
- <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Научете движења за допирната подлога, кратенки од тастатурата и друго"</string>
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (8557098239919786046) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Врати се назад"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Оди на почетниот екран"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Прикажи ги неодамнешните апликации"</string>
- <string name="touchpad_tutorial_switch_apps_gesture_button" msgid="7768255095423767779">"Сменете ги апликациите"</string>
+ <string name="touchpad_tutorial_switch_apps_gesture_button" msgid="7768255095423767779">"Префрлање помеѓу апликации"</string>
<string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Готово"</string>
<string name="touchpad_tutorial_next_button" msgid="9169718126626806688">"Следно"</string>
<string name="gesture_error_title" msgid="469064941635578511">"Обидете се повторно!"</string>
@@ -1533,14 +1541,14 @@
<string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Одете на почетниот екран"</string>
<string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Повлечете нагоре со три прсти на допирната подлога"</string>
<string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Одлично!"</string>
- <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Го завршивте движењето за враќање на почетниот екран"</string>
+ <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Го научивте движењето за враќање на почетниот екран"</string>
<string name="touchpad_home_gesture_error_body" msgid="3810674109999513073">"Повлечете нагоре со три прста на допирната подлога за да одите на почетниот екран"</string>
<string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Прикажи ги неодамнешните апликации"</string>
<string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Повлечете нагоре и задржете со три прста на допирната подлога"</string>
<string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Одлично!"</string>
- <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Го завршивте движењето за прегледување на неодамнешните апликации."</string>
+ <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Го научивте движењето за прикажување на неодамнешните апликации."</string>
<string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"Повлечете нагоре со три прста на допирната подлога и задржете за да ги видите неодамнешните апликации"</string>
- <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Сменете ги апликациите"</string>
+ <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Префрлање помеѓу апликации"</string>
<string name="touchpad_switch_apps_gesture_guidance" msgid="2554844933805502538">"Повлечете надесно со четири прста на допирната подлога"</string>
<string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Одлично!"</string>
<string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Го завршивте движењето за менување апликации."</string>
@@ -1548,7 +1556,7 @@
<string name="tutorial_action_key_title" msgid="8172535792469008169">"Прегледајте ги сите апликации"</string>
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Притиснете го копчето за дејство на тастатурата"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Браво!"</string>
- <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Го завршивте движењето за прегледување на сите апликации"</string>
+ <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Го научивте движењето за прикажување на сите апликации"</string>
<string name="touchpad_action_key_error_body" msgid="8685502040091860903">"Притиснете го копчето за дејство на тастатурата за да ги видите сите апликации"</string>
<string name="tutorial_animation_content_description" msgid="2698816574982370184">"Анимација за упатство, кликнете за да го паузирате и да го продолжите пуштањето."</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Осветлување на тастатура"</string>
@@ -1587,4 +1595,7 @@
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Сите плочки на „Брзи поставки“ ќе се ресетираат на првичните поставки на уредот"</string>
<string name="demote_explain_text" msgid="3942301497888762295">"<xliff:g id="APPLICATION">%1$s</xliff:g> веќе нема да прикажува новости во живо. Ова може да го промените во „Поставки“ кога сакате."</string>
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
+ <string name="underlay_close_button_content_description" msgid="6518481455548869894">"Затвори"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="7666159428113013319">"Повлечете од горниот лев агол за да ги отворите известувањата"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="3842355853737057332">"Повлечете од горниот десен агол за да ги отворите „Брзите поставки“"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ml-ldrtl/strings.xml b/packages/SystemUI/res/values-ml-ldrtl/strings.xml
index 451b447..2f60b79 100644
--- a/packages/SystemUI/res/values-ml-ldrtl/strings.xml
+++ b/packages/SystemUI/res/values-ml-ldrtl/strings.xml
@@ -20,4 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="recents_quick_scrub_onboarding" msgid="2452671841151577157">"ആപ്പുകൾ പെട്ടെന്ന് മാറ്റാൻ ഇടത്തോട്ട് വലിച്ചിടുക"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="6543179539438220549">"അറിയിപ്പുകൾ തുറക്കാൻ, മുകളിൽ വലതുവശത്ത് നിന്ന് സ്വൈപ്പ് ചെയ്യുക"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="5760503283520058116">"ദ്രുത ക്രമീകരണം തുറക്കാൻ, മുകളിൽ ഇടതുവശത്ത് നിന്ന് സ്വൈപ്പ് ചെയ്യുക"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index ac9a8ec..674c32e 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -467,6 +467,8 @@
<string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"മൈക്രോഫോൺ ബട്ടൺ ഉപയോഗിക്കുന്നതിന്, ക്രമീകരണത്തിൽ മൈക്രോഫോൺ ആക്സസ് പ്രവർത്തനക്ഷമമാക്കുക."</string>
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"ക്രമീകരണം തുറക്കുക"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"മറ്റ് ഉപകരണം"</string>
+ <string name="media_suggestion_disconnected_text" msgid="8777932417076437525">"<xliff:g id="DEVICE_NAME">%s</xliff:g> എന്നതിൽ പ്ലേ ചെയ്യുക"</string>
+ <string name="media_suggestion_failure_text" msgid="8453022394887613774">"കണക്റ്റ് ചെയ്യാനാകുന്നില്ല. വീണ്ടും ശ്രമിക്കൂ."</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"അവലോകനം മാറ്റുക"</string>
<string name="zen_modes_dialog_title" msgid="8854640808100096934">"മോഡുകൾ"</string>
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"ശരി"</string>
@@ -582,6 +584,7 @@
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
<skip />
<string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"സ്ക്രീൻ പൂർണമായും പങ്കിടുക"</string>
+ <string name="screen_share_permission_dialog_option_text_entire_screen_for_display" msgid="1545850238689169166">"<xliff:g id="DISPLAY_NAME">%s</xliff:g> പങ്കിടുക"</string>
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
<skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"നിങ്ങളുടെ സ്ക്രീൻ മുഴുവനായി പങ്കിടുമ്പോൾ, സ്ക്രീനിലെ എല്ലാ കാര്യങ്ങളും <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> എന്നതിന് ദൃശ്യമാകും. അതിനാൽ പാസ്വേഡുകൾ, പേയ്മെന്റ് വിശദാംശങ്ങൾ, സന്ദേശങ്ങൾ, ഫോട്ടോകൾ, ഓഡിയോ, വീഡിയോ എന്നിവ പോലുള്ള കാര്യങ്ങളിൽ ശ്രദ്ധ പുലർത്തുക."</string>
@@ -593,6 +596,7 @@
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"നിങ്ങളുടെ സ്ക്രീൻ കാസ്റ്റ് ചെയ്യണോ?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"ഒരു ആപ്പ് കാസ്റ്റ് ചെയ്യുക"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"മുഴുവൻ സ്ക്രീനും കാസ്റ്റ് ചെയ്യുക"</string>
+ <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen_for_display" msgid="5524817761472941086">"<xliff:g id="DISPLAY_NAME">%s</xliff:g> കാസ്റ്റ് ചെയ്യുക"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"നിങ്ങളുടെ മുഴുവൻ സ്ക്രീനും കാസ്റ്റ് ചെയ്യുമ്പോൾ, സ്ക്രീനിലെ എല്ലാ കാര്യങ്ങളും ദൃശ്യമാകും. അതിനാൽ പാസ്വേഡുകൾ, പേയ്മെന്റ് വിശദാംശങ്ങൾ, സന്ദേശങ്ങൾ, ഫോട്ടോകൾ, ഓഡിയോ, വീഡിയോ എന്നിവ പോലുള്ള കാര്യങ്ങളിൽ ശ്രദ്ധിക്കുക."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"നിങ്ങൾ ഒരു ആപ്പ് കാസ്റ്റ് ചെയ്യുമ്പോൾ, ആ ആപ്പിൽ കാണിക്കുന്നതോ പ്ലേ ചെയ്യുന്നതോ ആയ എല്ലാ കാര്യങ്ങളും ദൃശ്യമാകും. അതിനാൽ പാസ്വേഡുകൾ, പേയ്മെന്റ് വിശദാംശങ്ങൾ, സന്ദേശങ്ങൾ, ഫോട്ടോകൾ, ഓഡിയോ, വീഡിയോ എന്നിവ പോലുള്ള കാര്യങ്ങളിൽ ശ്രദ്ധിക്കുക."</string>
<string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"സ്ക്രീൻ കാസ്റ്റ് ചെയ്യുക"</string>
@@ -673,6 +677,7 @@
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"TrustAgent ഉപയോഗിച്ച് അൺലോക്ക് ചെയ്തത്"</string>
<string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"ഉപകരണം ലോക്ക് ചെയ്തു, നിരവധി തവണ പരിശോധിച്ചുറപ്പിക്കാൻ ശ്രമിച്ചു"</string>
<string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"ഉപകരണം ലോക്ക് ചെയ്തു\nപരിശോധിച്ചുറപ്പിക്കൽ പരാജയപ്പെട്ടു"</string>
+ <string name="keyguard_indication_after_watch_disconnected" msgid="6329761892437377710">"നിങ്ങളുടെ വാച്ച് ഉപയോഗിച്ച് ലോക്ക് ചെയ്തിരിക്കുന്നു"</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="accessibility_volume_settings" msgid="1458961116951564784">"ശബ്ദ ക്രമീകരണം"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"മീഡിയയ്ക്ക് സ്വയമേവ ക്യാപ്ഷൻ"</string>
@@ -799,6 +804,10 @@
<string name="enable_bluetooth_message" msgid="6740938333772779717">"നിങ്ങളുടെ ടാബ്ലെറ്റുമായി കീബോർഡ് കണക്റ്റുചെയ്യുന്നതിന്, ആദ്യം Bluetooth ഓണാക്കേണ്ടതുണ്ട്."</string>
<string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"ഓണാക്കുക"</string>
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"ഓണാണ് - ഫേസ് ബേസ്ഡ്"</string>
+ <!-- no translation found for notification_guts_bundle_title (1345506995443305361) -->
+ <skip />
+ <!-- no translation found for notification_guts_bundle_summary (3971530802237393600) -->
+ <skip />
<string name="inline_done_button" msgid="6043094985588909584">"പൂർത്തിയായി"</string>
<string name="inline_ok_button" msgid="603075490581280343">"ബാധകമാക്കുക"</string>
<string name="inline_turn_off_notifications" msgid="2653064779176881329">"ഓഫാക്കുക"</string>
@@ -952,7 +961,7 @@
<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>
+ <string name="data_saver" msgid="3484013368530820763">"ഡാറ്റാ സേവർ"</string>
<string name="accessibility_data_saver_on" msgid="5394743820189757731">"ഡാറ്റാ സേവർ ഓണാണ്"</string>
<string name="switch_bar_on" msgid="1770868129120096114">"ഓൺ"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"ഓഫ്"</string>
@@ -984,6 +993,7 @@
<string name="right_icon" msgid="1103955040645237425">"വലതുവശത്തെ ചിഹ്നം"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"ടൈലുകൾ ചേർക്കാൻ അമർത്തിപ്പിടിച്ച് വലിച്ചിടുക"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"ടൈലുകൾ പുനഃക്രമീകരിക്കാൻ അമർത്തിപ്പിടിച്ച് വലിച്ചിടുക"</string>
+ <string name="select_to_rearrange_tiles" msgid="6166153534235179148">"പുനഃക്രമീകരിക്കാനും വലുപ്പം മാറ്റാനും ടൈലുകൾ തിരഞ്ഞെടുക്കുക"</string>
<string name="tap_to_position_tile" msgid="6282815817773342757">"ടൈൽ പൊസിഷൻ ചെയ്യാൻ ടാപ്പ് ചെയ്യുക"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"നീക്കംചെയ്യുന്നതിന് ഇവിടെ വലിച്ചിടുക"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"നിങ്ങൾക്ക് ചുരുങ്ങിയത് <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> ടൈലുകളെങ്കിലും വേണം"</string>
@@ -1003,10 +1013,10 @@
<string name="tuner_low_priority" msgid="8412666814123009820">"പ്രാധാന്യം കുറഞ്ഞ അറിയിപ്പ് ചിഹ്നങ്ങൾ"</string>
<string name="other" msgid="429768510980739978">"മറ്റുള്ളവ"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"ടൈലിന്റെ വലുപ്പം മാറ്റുക"</string>
- <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"ടൈൽ നീക്കം ചെയ്യുക"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="4811778796933958616">"ടൈൽ നീക്കം ചെയ്യുക"</string>
<string name="accessibility_qs_edit_toggle_placement_mode" msgid="3870429389210569610">"പ്ലേസ്മെന്റ് മോഡ് ടോഗിൾ ചെയ്യുക"</string>
<string name="accessibility_qs_edit_toggle_selection" msgid="2201248304072372239">"തിരഞ്ഞെടുത്തത് ടോഗിൾ ചെയ്യുക"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"അവസാന ഭാഗത്ത് ടൈൽ ചേർക്കുക"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="7188303979684400620">"അവസാന ഭാഗത്ത് ടൈൽ ചേർക്കുക"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"ടൈൽ നീക്കുക"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"ഇഷ്ടമുള്ള ഭാഗത്ത് ടൈൽ ചേർക്കുക"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> എന്നതിലേക്ക് നീക്കുക"</string>
@@ -1093,7 +1103,8 @@
<string name="auto_saver_text" msgid="3214960308353838764">"ബാറ്ററി ചാർജ് തീരാൻ സാധ്യതയുണ്ടെങ്കിൽ ഓണാക്കുക"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"വേണ്ട"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"ഉപയോഗത്തിലാണ്"</string>
- <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"ആപ്പുകൾ നിങ്ങളുടെ <xliff:g id="TYPES_LIST">%s</xliff:g> ഉപയോഗിക്കുന്നു."</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps (4994455315457996796) -->
+ <skip />
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" കൂടാതെ "</string>
<string name="ongoing_privacy_dialog_using_op" msgid="426635338010011796">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> ഉപയോഗിക്കുന്നു"</string>
@@ -1508,11 +1519,11 @@
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"കുറുക്കുവഴി ചേർക്കുക"</string>
<string name="shortcut_helper_delete_shortcut_button_label" msgid="3148773472696137052">"കുറുക്കുവഴി ഇല്ലാതാക്കുക"</string>
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"നിങ്ങളുടെ കീബോർഡ് ഉപയോഗിച്ച് നാവിഗേറ്റ് ചെയ്യുക"</string>
- <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"കീബോർഡ് കുറുക്കുവഴികൾ മനസ്സിലാക്കുക"</string>
+ <string name="launch_keyboard_tutorial_notification_content" msgid="2131074100205028210">"കീബോർഡ് കുറുക്കുവഴികൾ മനസ്സിലാക്കുക"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"നിങ്ങളുടെ ടച്ച്പാഡ് ഉപയോഗിച്ച് നാവിഗേറ്റ് ചെയ്യുക"</string>
<string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"ടച്ച്പാഡ് ജെസ്ച്ചറുകൾ മനസ്സിലാക്കുക"</string>
<string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"നിങ്ങളുടെ കീപാഡ്, ടച്ച്പാഡ് എന്നിവ ഉപയോഗിച്ച് നാവിഗേറ്റ് ചെയ്യുക"</string>
- <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"ടച്ച്പാഡ് ജെസ്ച്ചറുകൾ, കീബോർഡ് കുറുക്കുവഴികൾ എന്നിവയും മറ്റും മനസ്സിലാക്കുക"</string>
+ <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="8557098239919786046">"ടച്ച്പാഡ് ജെസ്ച്ചറുകൾ, കീബോർഡ് കുറുക്കുവഴികൾ എന്നിവയും മറ്റും മനസ്സിലാക്കുക"</string>
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"മടങ്ങുക"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"ഹോമിലേക്ക് പോകുക"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"അടുത്തിടെയുള്ള ആപ്പുകൾ കാണുക"</string>
@@ -1582,4 +1593,7 @@
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"എല്ലാ ദ്രുത ക്രമീകരണ ടൈലുകളും ഉപകരണത്തിന്റെ ഒറിജിനൽ ക്രമീകരണത്തിലേക്ക് റീസെറ്റ് ചെയ്യും"</string>
<string name="demote_explain_text" msgid="3942301497888762295">"<xliff:g id="APPLICATION">%1$s</xliff:g> ഇനി തത്സമയ അപ്ഡേറ്റുകൾ കാണിക്കില്ല. ക്രമീകരണത്തിൽ ഏതുസമയത്തും നിങ്ങൾക്ക് ഇത് മാറ്റാം."</string>
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
+ <string name="underlay_close_button_content_description" msgid="6518481455548869894">"അടയ്ക്കുക"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="7666159428113013319">"അറിയിപ്പുകൾ തുറക്കാൻ, മുകളിൽ ഇടതുവശത്ത് നിന്ന് സ്വൈപ്പ് ചെയ്യുക"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="3842355853737057332">"ദ്രുത ക്രമീകരണം തുറക്കാൻ, മുകളിൽ വലതുവശത്ത് നിന്ന് സ്വൈപ്പ് ചെയ്യുക"</string>
</resources>
diff --git a/packages/SystemUI/res/values-mn-ldrtl/strings.xml b/packages/SystemUI/res/values-mn-ldrtl/strings.xml
index cabc4ab..bb68c58 100644
--- a/packages/SystemUI/res/values-mn-ldrtl/strings.xml
+++ b/packages/SystemUI/res/values-mn-ldrtl/strings.xml
@@ -20,4 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="recents_quick_scrub_onboarding" msgid="2452671841151577157">"Аппыг хурдан сэлгэхийн тулд зүүн тийш чирэх"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="6543179539438220549">"Мэдэгдлийг нээхийн тулд баруун дээд талаас шударна уу"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="5760503283520058116">"Шуурхай тохиргоог нээхийн тулд зүүн дээд талаас шударна уу"</string>
</resources>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index 98fae76..5cd977a 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -467,6 +467,8 @@
<string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"Микрофоны товчлуурыг ашиглахын тулд \"Тохиргоо\" хэсэгт микрофоны хандалтыг идэвхжүүлнэ үү."</string>
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Тохиргоог нээх"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Бусад төхөөрөмж"</string>
+ <string name="media_suggestion_disconnected_text" msgid="8777932417076437525">"<xliff:g id="DEVICE_NAME">%s</xliff:g> дээр тоглуулах"</string>
+ <string name="media_suggestion_failure_text" msgid="8453022394887613774">"Холбогдох боломжгүй байна. Дахин оролдоно уу."</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Тоймыг асаах/унтраах"</string>
<string name="zen_modes_dialog_title" msgid="8854640808100096934">"Горим"</string>
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Болсон"</string>
@@ -582,6 +584,7 @@
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
<skip />
<string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Дэлгэцийг бүтнээр нь хуваалцах"</string>
+ <string name="screen_share_permission_dialog_option_text_entire_screen_for_display" msgid="1545850238689169166">"<xliff:g id="DISPLAY_NAME">%s</xliff:g>-г хуваалцах"</string>
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
<skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Таныг дэлгэцээ бүхэлд нь хуваалцаж байхад дэлгэц дээр тань байгаа аливаа зүйл <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>-д харагдана. Тиймээс нууц үг, төлбөрийн дэлгэрэнгүй, мессеж, зураг, аудио, видео зэрэг зүйлд болгоомжтой хандаарай."</string>
@@ -593,6 +596,7 @@
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Дэлгэцээ дамжуулах уу?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Нэг апп дамжуулах"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Дэлгэцийг бүхэлд нь дамжуулах"</string>
+ <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen_for_display" msgid="5524817761472941086">"<xliff:g id="DISPLAY_NAME">%s</xliff:g>-г дамжуулах"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Та дэлгэцээ бүхэлд нь дамжуулах үед дэлгэц дээр тань байгаа аливаа зүйл харагдана. Тиймээс нууц үг, төлбөрийн дэлгэрэнгүй, мессеж, зураг, аудио, видео зэрэг зүйлд болгоомжтой хандаарай."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Та апп дамжуулах үед тухайн аппад харуулж эсвэл тоглуулж буй аливаа зүйл харагдана. Тиймээс нууц үг, төлбөрийн дэлгэрэнгүй, мессеж, зураг, аудио, видео зэрэг зүйлд болгоомжтой хандаарай."</string>
<string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Дэлгэц дамжуулах"</string>
@@ -673,6 +677,7 @@
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"TrustAgent-р түгжээгүй байлгасан"</string>
<string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"Төхөөрөмжийг түгжсэн. Хэт олон удаа баталгаажуулах оролдлого хийсэн"</string>
<string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"Төхөөрөмжийг түгжсэн\nБаталгаажуулж чадсангүй"</string>
+ <string name="keyguard_indication_after_watch_disconnected" msgid="6329761892437377710">"Таны цагнаас түгжсэн"</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="accessibility_volume_settings" msgid="1458961116951564784">"Дууны тохиргоо"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Медиад автоматаар тайлбар нэмэх"</string>
@@ -799,6 +804,10 @@
<string name="enable_bluetooth_message" msgid="6740938333772779717">"Компьютерийн гараа таблетад холбохын тулд эхлээд Bluetooth-г асаана уу."</string>
<string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Асаах"</string>
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Асаалттай - Царайнд суурилсан"</string>
+ <!-- no translation found for notification_guts_bundle_title (1345506995443305361) -->
+ <skip />
+ <!-- no translation found for notification_guts_bundle_summary (3971530802237393600) -->
+ <skip />
<string name="inline_done_button" msgid="6043094985588909584">"Болсон"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Ашиглах"</string>
<string name="inline_turn_off_notifications" msgid="2653064779176881329">"Унтраах"</string>
@@ -984,6 +993,7 @@
<string name="right_icon" msgid="1103955040645237425">"Баруун дүрс тэмдэг"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Хавтан нэмэхийн тулд дараад чирэх"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Хавтангуудыг дахин цэгцлэхийн тулд дараад чирнэ үү"</string>
+ <string name="select_to_rearrange_tiles" msgid="6166153534235179148">"Дахин эмхлэх, хэмжээг нь өөрчлөх хавтангуудыг сонгох"</string>
<string name="tap_to_position_tile" msgid="6282815817773342757">"Хавтанг байрлуулахын тулд товших"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Устгахын тулд энд зөөнө үү"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Танд хамгийн багадаа <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> хавтан шаардлагатай"</string>
@@ -1003,10 +1013,12 @@
<string name="tuner_low_priority" msgid="8412666814123009820">"Бага ач холбогдолтой мэдэгдлийн дүрс тэмдгийг харуулах"</string>
<string name="other" msgid="429768510980739978">"Бусад"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"хавтангийн хэмжээг асаах/унтраах"</string>
- <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"хавтанг хасна уу"</string>
+ <!-- no translation found for accessibility_qs_edit_remove_tile_action (4811778796933958616) -->
+ <skip />
<string name="accessibility_qs_edit_toggle_placement_mode" msgid="3870429389210569610">"байрлуулах горимыг асаах/унтраах"</string>
<string name="accessibility_qs_edit_toggle_selection" msgid="2201248304072372239">"сонголтыг асаах/унтраах"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"хавтанг сүүлийн байрлалд нэмэх"</string>
+ <!-- no translation found for accessibility_qs_edit_tile_add_action (7188303979684400620) -->
+ <skip />
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Хавтанг зөөх"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Хавтанг хүссэн байрлалд нэмэх"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> руу зөөнө үү"</string>
@@ -1093,7 +1105,8 @@
<string name="auto_saver_text" msgid="3214960308353838764">"Батарей дуусах гэж байгаа үед асаана уу"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Үгүй, баярлалаа"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"Ашиглаж байгаа"</string>
- <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Аппууд таны <xliff:g id="TYPES_LIST">%s</xliff:g>-г ашиглаж байна."</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps (4994455315457996796) -->
+ <skip />
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" болон "</string>
<string name="ongoing_privacy_dialog_using_op" msgid="426635338010011796">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> ашиглаж байна"</string>
@@ -1508,11 +1521,11 @@
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"Товчлол нэмэх"</string>
<string name="shortcut_helper_delete_shortcut_button_label" msgid="3148773472696137052">"Товчлол устгах"</string>
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Гараа ашиглан шилжих"</string>
- <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Товчлуурын шууд холбоосыг мэдэж аваарай"</string>
+ <string name="launch_keyboard_tutorial_notification_content" msgid="2131074100205028210">"Товчлуурын шууд холбоосыг мэдэж аваарай"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Мэдрэгч самбараа ашиглан шилжээрэй"</string>
<string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Мэдрэгч самбарын зангааг мэдэж аваарай"</string>
<string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Гар эсвэл мэдрэгч самбараа ашиглан шилжээрэй"</string>
- <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Мэдрэгч самбарын зангаа, товчлуурын шууд холбоос болон бусад зүйлийг мэдэж аваарай"</string>
+ <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="8557098239919786046">"Мэдрэгч самбарын зангаа, товчлуурын шууд холбоос болон бусад зүйлийг мэдэж аваарай"</string>
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Буцах"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Нүүр хуудас руу очих"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Саяхны аппуудыг харах"</string>
@@ -1582,4 +1595,7 @@
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Шуурхай тохиргооны бүх хавтан төхөөрөмжийн эх тохиргоо руу шинэчлэгдэнэ"</string>
<string name="demote_explain_text" msgid="3942301497888762295">"<xliff:g id="APPLICATION">%1$s</xliff:g> цаашид Шууд шинэчлэлтийг харуулахгүй. Та хүссэн үедээ үүнийг Тохиргооноос өөрчлөх боломжтой."</string>
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
+ <string name="underlay_close_button_content_description" msgid="6518481455548869894">"Хаах"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="7666159428113013319">"Мэдэгдлийг нээхийн тулд зүүн дээд талаас шударна уу"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="3842355853737057332">"Шуурхай тохиргоог нээхийн тулд баруун дээд талаас шударна уу"</string>
</resources>
diff --git a/packages/SystemUI/res/values-mr-ldrtl/strings.xml b/packages/SystemUI/res/values-mr-ldrtl/strings.xml
index 059f05f..b48d0d2 100644
--- a/packages/SystemUI/res/values-mr-ldrtl/strings.xml
+++ b/packages/SystemUI/res/values-mr-ldrtl/strings.xml
@@ -20,4 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="recents_quick_scrub_onboarding" msgid="2452671841151577157">"अॅप्स त्वरित स्विच करण्यासाठी डावीकडे ड्रॅग करा"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="6543179539438220549">"नोटिफिकेशन उघडण्यासाठी सर्वात वरती उजवीकडून स्वाइप करा"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="5760503283520058116">"क्विक सेटिंग्ज उघडण्यासाठी सर्वात वरती उजवीकडून स्वाइप करा"</string>
</resources>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index 7aaf29c..72e3847 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -334,7 +334,7 @@
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"सुरू करत आहे…"</string>
<string name="quick_settings_brightness_unable_adjust_msg" msgid="4124028416057617517">"ब्राइटनेस टॉप ॲपद्वारे नियंत्रित केला जात असल्यामुळे ॲडजस्ट करू शकत नाही"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"ऑटो-रोटेट"</string>
- <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"ऑटो-रोटेट स्क्रीन"</string>
+ <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"ऑटोमॅटिक रोटेट स्क्रीन"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"स्थान"</string>
<string name="quick_settings_screensaver_label" msgid="1495003469366524120">"स्क्रीन सेव्हर"</string>
<string name="quick_settings_camera_label" msgid="5612076679385269339">"कॅमेराचा अॅक्सेस"</string>
@@ -467,6 +467,8 @@
<string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"मायक्रोफोन बटण वापरण्यासाठी, सेटिंग्जमधून मायक्रोफोन अॅक्सेस सुरू करा."</string>
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"सेटिंग्ज उघडा"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"इतर डिव्हाइस"</string>
+ <string name="media_suggestion_disconnected_text" msgid="8777932417076437525">"<xliff:g id="DEVICE_NAME">%s</xliff:g> वर प्ले करा"</string>
+ <string name="media_suggestion_failure_text" msgid="8453022394887613774">"कनेक्ट करू शकत नाही. परत प्रयत्न करा."</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"अवलोकन टॉगल करा."</string>
<string name="zen_modes_dialog_title" msgid="8854640808100096934">"मोड"</string>
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"पूर्ण झाले"</string>
@@ -474,7 +476,7 @@
<string name="zen_mode_on" msgid="9085304934016242591">"सुरू आहे"</string>
<string name="zen_mode_on_with_details" msgid="7416143430557895497">"सुरू • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="zen_mode_off" msgid="1736604456618147306">"बंद आहे"</string>
- <string name="zen_mode_set_up" msgid="8231201163894922821">"सेट केलेले नाही"</string>
+ <string name="zen_mode_set_up" msgid="8231201163894922821">"सेट केलेला नाही"</string>
<string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"सेटिंग्जमध्ये व्यवस्थापित करा"</string>
<string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{कोणतेही अॅक्टिव्ह मोड नाहीत}=1{{mode} अॅक्टिव्ह आहे}other{# मोड अॅक्टिव्ह आहेत}}"</string>
<string name="zen_priority_introduction" msgid="3159291973383796646">"अलार्म, रिमाइंडर, इव्हेंट आणि तुम्ही निश्चित केलेल्या कॉलर व्यतिरिक्त तुम्हाला कोणत्याही आवाज आणि कंपनांचा व्यत्त्यय आणला जाणार नाही. तरीही तुम्ही प्ले करायचे ठरवलेले कोणतेही संगीत, व्हिडिओ आणि गेमचे आवाज ऐकू शकतात."</string>
@@ -582,6 +584,7 @@
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
<skip />
<string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"संपूर्ण स्क्रीन शेअर करा"</string>
+ <string name="screen_share_permission_dialog_option_text_entire_screen_for_display" msgid="1545850238689169166">"<xliff:g id="DISPLAY_NAME">%s</xliff:g> शेअर करा"</string>
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
<skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"तुम्ही तुमची संपूर्ण स्क्रीन कास्ट करता, तेव्हा तुमच्या स्क्रीनवरील कोणत्याही गोष्टी <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> साठी दृश्यमान असतात. त्यामुळे पासवर्ड, पेमेंट तपशील, मेसेज, फोटो आणि ऑडिओ व व्हिडिओ यांसारख्या गोष्टींबाबत सावधगिरी बाळगा."</string>
@@ -593,6 +596,7 @@
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"तुमची स्क्रीन कास्ट करायची आहे का?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"एक अॅप कास्ट करा"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"संपूर्ण स्क्रीन कास्ट करा"</string>
+ <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen_for_display" msgid="5524817761472941086">"<xliff:g id="DISPLAY_NAME">%s</xliff:g> कास्ट करा"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"तुम्ही तुमची संपूर्ण स्क्रीन कास्ट करता, तेव्हा तुमच्या स्क्रीनवरील सर्व गोष्टी दृश्यमान असतात. त्यामुळे पासवर्ड, पेमेंट तपशील, मेसेज, फोटो आणि ऑडिओ व व्हिडिओ यांसारख्या गोष्टींबाबत सावधगिरी बाळगा."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"तुम्ही अॅप कास्ट करता, तेव्हा त्या अॅपमध्ये दाखवलेल्या किंवा प्ले होणाऱ्या सर्व गोष्टी दृश्यमान असतात. त्यामुळे पासवर्ड, पेमेंट तपशील, मेसेज, फोटो आणि ऑडिओ व व्हिडिओ यांसारख्या गोष्टींबाबत सावधगिरी बाळगा."</string>
<string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"स्क्रीन कास्ट करा"</string>
@@ -673,6 +677,7 @@
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"TrustAgent ने अनलॉक ठेवले"</string>
<string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"डिव्हाइस लॉक केले होते, ऑथेंटिकेशनचे खूप वेळा प्रयत्न केले गेले"</string>
<string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"डिव्हाइस लॉक केले आहे\nऑथेंटिकेशन यशस्वी झाले नाही"</string>
+ <string name="keyguard_indication_after_watch_disconnected" msgid="6329761892437377710">"तुमच्या वॉचद्वारे लॉक झाले आहे"</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="accessibility_volume_settings" msgid="1458961116951564784">"आवाज सेटिंग्ज"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"मीडियाला आपोआप सबटायटल द्या"</string>
@@ -708,7 +713,7 @@
<string name="stream_ring" msgid="7550670036738697526">"रिंग"</string>
<string name="stream_music" msgid="2188224742361847580">"मीडिया"</string>
<string name="stream_alarm" msgid="16058075093011694">"अलार्म"</string>
- <string name="stream_notification" msgid="7930294049046243939">"सूचना"</string>
+ <string name="stream_notification" msgid="7930294049046243939">"नोटिफिकेशन"</string>
<string name="stream_bluetooth_sco" msgid="6234562365528664331">"ब्लूटूथ"</string>
<string name="stream_dtmf" msgid="7322536356554673067">"दुहेरी एकाधिक टोन वारंंवारता"</string>
<string name="stream_accessibility" msgid="3873610336741987152">"अॅक्सेसिबिलिटी"</string>
@@ -728,7 +733,7 @@
<string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. म्यूट करण्यासाठी टॅप करा."</string>
<string name="volume_panel_noise_control_title" msgid="7413949943872304474">"नॉइझ कंट्रोल"</string>
<string name="volume_panel_spatial_audio_title" msgid="3367048857932040660">"स्पेशियल ऑडिओ"</string>
- <string name="volume_panel_spatial_audio_off" msgid="4177490084606772989">"बंद करा"</string>
+ <string name="volume_panel_spatial_audio_off" msgid="4177490084606772989">"बंद आहे"</string>
<string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"निश्चित केला आहे"</string>
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"हेड ट्रॅकिंग"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"रिंगर मोड बदलण्यासाठी टॅप करा"</string>
@@ -799,6 +804,10 @@
<string name="enable_bluetooth_message" msgid="6740938333772779717">"तुमचा कीबोर्ड तुमच्या टॅबलेटसह कनेक्ट करण्यासाठी, तुम्ही प्रथम ब्लूटूथ सुरू करणे आवश्यक आहे."</string>
<string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"सुरू करा"</string>
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"सुरू - चेहऱ्यावर आधारित"</string>
+ <!-- no translation found for notification_guts_bundle_title (1345506995443305361) -->
+ <skip />
+ <!-- no translation found for notification_guts_bundle_summary (3971530802237393600) -->
+ <skip />
<string name="inline_done_button" msgid="6043094985588909584">"पूर्ण झाले"</string>
<string name="inline_ok_button" msgid="603075490581280343">"लागू करा"</string>
<string name="inline_turn_off_notifications" msgid="2653064779176881329">"बंद करा"</string>
@@ -984,6 +993,7 @@
<string name="right_icon" msgid="1103955040645237425">"उजवे आयकन"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"टाइल जोडण्यासाठी धरून ठेवून ड्रॅग करा"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"टाइलची पुनर्रचना करण्यासाठी धरून ठेवून ड्रॅग करा"</string>
+ <string name="select_to_rearrange_tiles" msgid="6166153534235179148">"पुन्हा संगतवार लावण्यासाठी आणि आकार बदलण्यासाठी टाइल निवडा"</string>
<string name="tap_to_position_tile" msgid="6282815817773342757">"टाइलचे स्थान निर्धारित करण्यासाठी टॅप करा"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"काढण्यासाठी येथे ड्रॅग करा"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"तुम्हाला किमान <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> टाइलची गरज आहे"</string>
@@ -1003,10 +1013,10 @@
<string name="tuner_low_priority" msgid="8412666814123009820">"कमी प्राधान्य सूचना आयकन दर्शवा"</string>
<string name="other" msgid="429768510980739978">"अन्य"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"टाइलचा आकार टॉगल करा"</string>
- <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"टाइल काढून टाका"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="4811778796933958616">"टाइल काढून टाका"</string>
<string name="accessibility_qs_edit_toggle_placement_mode" msgid="3870429389210569610">"प्लेसमेंट मोड टॉगल करा"</string>
<string name="accessibility_qs_edit_toggle_selection" msgid="2201248304072372239">"निवड टॉगल करा"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"टाइल शेवटच्या स्थानावर जोडा"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="7188303979684400620">"शेवटच्या स्थानावर टाइल जोडा"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"टाइल हलवा"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"हव्या असलेल्या स्थानावर टाइल जोडा"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> यावर हलवा"</string>
@@ -1049,7 +1059,7 @@
<string name="tuner_right" msgid="8247571132790812149">"उजवा"</string>
<string name="tuner_menu" msgid="363690665924769420">"मेनू"</string>
<string name="tuner_app" msgid="6949280415826686972">"<xliff:g id="APP">%1$s</xliff:g> अॅप"</string>
- <string name="notification_channel_alerts" msgid="3385787053375150046">"सूचना"</string>
+ <string name="notification_channel_alerts" msgid="3385787053375150046">"अलर्ट"</string>
<string name="notification_channel_battery" msgid="9219995638046695106">"बॅटरी"</string>
<string name="notification_channel_screenshot" msgid="7665814998932211997">"स्क्रीनशॉट"</string>
<string name="notification_channel_instant" msgid="7556135423486752680">"Instant Apps"</string>
@@ -1093,7 +1103,8 @@
<string name="auto_saver_text" msgid="3214960308353838764">"बॅटरी संपण्याची शक्यता असल्यास सुरू करा"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"नाही नको"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"वापरात आहे"</string>
- <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"ॲप्लिकेशन्स तुमचे <xliff:g id="TYPES_LIST">%s</xliff:g> वापरत आहे."</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps (4994455315457996796) -->
+ <skip />
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" आणि "</string>
<string name="ongoing_privacy_dialog_using_op" msgid="426635338010011796">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> द्वारे वापरले जात आहे"</string>
@@ -1333,7 +1344,7 @@
<string name="mobile_data_connection_active" msgid="944490013299018227">"कनेक्ट केले आहे"</string>
<string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"तात्पुरते कनेक्ट केलेले"</string>
<string name="mobile_data_poor_connection" msgid="819617772268371434">"खराब कनेक्शन"</string>
- <string name="mobile_data_off_summary" msgid="3663995422004150567">"मोबाइल डेटा ऑटो-कनेक्ट होणार नाही"</string>
+ <string name="mobile_data_off_summary" msgid="3663995422004150567">"मोबाइल डेटा आपोआप कनेक्ट होणार नाही"</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"कोणतेही कनेक्शन नाही"</string>
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"इतर कोणतेही नेटवर्क उपलब्ध नाहीत"</string>
<string name="all_network_unavailable" msgid="4112774339909373349">"कोणतेही नेटवर्क उपलब्ध नाही"</string>
@@ -1508,11 +1519,13 @@
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"शॉर्टकट जोडा"</string>
<string name="shortcut_helper_delete_shortcut_button_label" msgid="3148773472696137052">"शॉर्टकट हटवा"</string>
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"तुमचा कीबोर्ड वापरून नेव्हिगेट करा"</string>
- <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"कीबोर्ड शॉर्टकट जाणून घ्या"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2131074100205028210) -->
+ <skip />
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"तुमचा टचपॅड वापरून नेव्हिगेट करा"</string>
<string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"टचपॅड जेश्चर जाणून घ्या"</string>
<string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"तुमचा कीबोर्ड आणि टचपॅड वापरून नेव्हिगेट करा"</string>
- <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"टचपॅड जेश्चर, कीबोर्ड शॉर्टकट आणि आणखी बरेच काही जाणून घ्या"</string>
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (8557098239919786046) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"मागे जा"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"होमवर जा"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"अलीकडील अॅप्स पहा"</string>
@@ -1582,4 +1595,7 @@
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"सर्व क्विक सेटिंग्ज टाइल डिव्हाइसच्या मूळ सेटिंग्जवर रीसेट केल्या जातील"</string>
<string name="demote_explain_text" msgid="3942301497888762295">"<xliff:g id="APPLICATION">%1$s</xliff:g> आता लाइव्ह अपडेट दाखवणार नाही. तुम्ही सेटिंग्ज मध्ये हे कधीही बदलू शकता."</string>
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
+ <string name="underlay_close_button_content_description" msgid="6518481455548869894">"बंद करा"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="7666159428113013319">"नोटिफिकेशन उघडण्यासाठी सर्वात वरती डावीकडून स्वाइप करा"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="3842355853737057332">"क्विक सेटिंग्ज उघडण्यासाठी सर्वात वरती डावीकडून स्वाइप करा"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ms-ldrtl/strings.xml b/packages/SystemUI/res/values-ms-ldrtl/strings.xml
index a49a780..e749c9a 100644
--- a/packages/SystemUI/res/values-ms-ldrtl/strings.xml
+++ b/packages/SystemUI/res/values-ms-ldrtl/strings.xml
@@ -20,4 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="recents_quick_scrub_onboarding" msgid="2452671841151577157">"Seret ke kiri untuk beralih apl dengan pantas"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="6543179539438220549">"Leret daripada bahagian atas sebelah kanan untuk membuka pemberitahuan"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="5760503283520058116">"Leret daripada bahagian atas sebelah kiri untuk membuka Tetapan Pantas"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index 97c8253..9a2328e 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -467,6 +467,8 @@
<string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"Untuk menggunakan butang mikrofon, dayakan akses mikrofon dalam Tetapan."</string>
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Buka Tetapan"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Peranti lain"</string>
+ <string name="media_suggestion_disconnected_text" msgid="8777932417076437525">"Mainkan pada <xliff:g id="DEVICE_NAME">%s</xliff:g>"</string>
+ <string name="media_suggestion_failure_text" msgid="8453022394887613774">"Tidak dapat menyambung. Cuba lagi."</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Togol Ikhtisar"</string>
<string name="zen_modes_dialog_title" msgid="8854640808100096934">"Mod"</string>
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Selesai"</string>
@@ -582,6 +584,7 @@
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
<skip />
<string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Kongsi seluruh skrin"</string>
+ <string name="screen_share_permission_dialog_option_text_entire_screen_for_display" msgid="1545850238689169166">"Kongsi <xliff:g id="DISPLAY_NAME">%s</xliff:g>"</string>
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
<skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Apabila anda berkongsi seluruh skrin anda, apa-apa sahaja kandungan pada skrin anda boleh dilihat oleh <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Oleh hal yang demikian, berhati-hati dengan perkara seperti kata laluan, butiran pembayaran, mesej, foto dan audio serta video."</string>
@@ -593,6 +596,7 @@
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Hantar skrin anda?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Hantar satu apl"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Hantar keseluruhan skrin"</string>
+ <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen_for_display" msgid="5524817761472941086">"Hantar <xliff:g id="DISPLAY_NAME">%s</xliff:g>"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Apabila anda menghantar seluruh skrin anda, apa-apa sahaja kandungan pada skrin anda boleh dilihat. Oleh hal yang demikian, berhati-hati dengan perkara seperti kata laluan, butiran pembayaran, mesej, foto dan audio serta video."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Apabila anda menghantar apl, apa-apa sahaja kandungan yang dipaparkan atau dimainkan pada apl itu boleh dilihat. Oleh hal yang demikian, berhati-hati dengan perkara seperti kata laluan, butiran pembayaran, mesej, foto dan audio serta video."</string>
<string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Hantar skrin"</string>
@@ -673,6 +677,7 @@
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Dibiarkan tidak berkunci oleh TrustAgent"</string>
<string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"Peranti telah dikunci, terlalu banyak percubaan pengesahan"</string>
<string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"Peranti dikunci\nPengesahan gagal"</string>
+ <string name="keyguard_indication_after_watch_disconnected" msgid="6329761892437377710">"Dikunci oleh jam tangan anda"</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="accessibility_volume_settings" msgid="1458961116951564784">"Tetapan bunyi"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Sari kata media automatik"</string>
@@ -799,6 +804,8 @@
<string name="enable_bluetooth_message" msgid="6740938333772779717">"Untuk menyambungkan papan kekunci anda dengan tablet, anda perlu menghidupkan Bluetooth terlebih dahulu."</string>
<string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Hidupkan"</string>
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Hidup - Berasaskan wajah"</string>
+ <string name="notification_guts_bundle_title" msgid="1345506995443305361">"Gunakan Pengelola Pemberitahuan"</string>
+ <string name="notification_guts_bundle_summary" msgid="3971530802237393600">"Untuk <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="inline_done_button" msgid="6043094985588909584">"Selesai"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Guna"</string>
<string name="inline_turn_off_notifications" msgid="2653064779176881329">"Matikan"</string>
@@ -984,6 +991,7 @@
<string name="right_icon" msgid="1103955040645237425">"Ikon kanan"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Tahan dan seret untuk menambahkan jubin"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Tahan dan seret untuk mengatur semula jubin"</string>
+ <string name="select_to_rearrange_tiles" msgid="6166153534235179148">"Pilih jubin untuk menyusun semula dan mengubah saiz"</string>
<string name="tap_to_position_tile" msgid="6282815817773342757">"Ketik untuk meletakkan kedudukan jubin"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Seret ke sini untuk mengalih keluar"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Anda memerlukan sekurang-kurangnya <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> jubin"</string>
@@ -1003,10 +1011,10 @@
<string name="tuner_low_priority" msgid="8412666814123009820">"Tunjukkan ikon pemberitahuan keutamaan rendah"</string>
<string name="other" msgid="429768510980739978">"Lain-lain"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"togol saiz jubin"</string>
- <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"alih keluar jubin"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="4811778796933958616">"Alih keluar jubin"</string>
<string name="accessibility_qs_edit_toggle_placement_mode" msgid="3870429389210569610">"mod peletakan togol"</string>
<string name="accessibility_qs_edit_toggle_selection" msgid="2201248304072372239">"togol pilihan"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"tambahkan jubin pada kedudukan terakhir"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="7188303979684400620">"Tambahkan jubin pada kedudukan terakhir"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Alihkan jubin"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Tambahkan jubin pada kedudukan yang dikehendaki"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Alih ke <xliff:g id="POSITION">%1$d</xliff:g>"</string>
@@ -1093,7 +1101,7 @@
<string name="auto_saver_text" msgid="3214960308353838764">"Hidupkan apabila bateri berkemungkinan habis"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Tidak perlu"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"Sedang digunakan"</string>
- <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Aplikasi sedang menggunakan <xliff:g id="TYPES_LIST">%s</xliff:g> anda."</string>
+ <string name="ongoing_privacy_chip_content_multiple_apps" msgid="4994455315457996796">"<xliff:g id="TYPES_LIST">%s</xliff:g> sedang digunakan."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" dan "</string>
<string name="ongoing_privacy_dialog_using_op" msgid="426635338010011796">"Sedang digunakan oleh <xliff:g id="APPLICATION_NAME">%1$s</xliff:g>"</string>
@@ -1508,11 +1516,11 @@
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"Tambahkan pintasan"</string>
<string name="shortcut_helper_delete_shortcut_button_label" msgid="3148773472696137052">"Padamkan pintasan"</string>
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navigasi menggunakan papan kekunci"</string>
- <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Ketahui pintasan papan kekunci"</string>
+ <string name="launch_keyboard_tutorial_notification_content" msgid="2131074100205028210">"Ketahui pintasan papan kekunci"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navigasi menggunakan pad sentuh anda"</string>
<string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Ketahui gerak isyarat pad sentuh"</string>
<string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Navigasi menggunakan papan kekunci dan pad sentuh"</string>
- <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Ketahui gerak isyarat pad sentuh, pintasan papan kekunci dan pelbagai lagi"</string>
+ <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="8557098239919786046">"Ketahui gerak isyarat pad sentuh, pintasan papan kekunci dan pelbagai lagi"</string>
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Kembali"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Akses laman utama"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Lihat apl terbaharu"</string>
@@ -1582,4 +1590,7 @@
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Semua jubin Tetapan Pantas akan ditetapkan semula kepada tetapan asal peranti"</string>
<string name="demote_explain_text" msgid="3942301497888762295">"<xliff:g id="APPLICATION">%1$s</xliff:g> tidak akan memaparkan Kemaskinian Langsung lagi. Anda boleh menukar tetapan ini pada bila-bila masa dalam Tetapan."</string>
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
+ <string name="underlay_close_button_content_description" msgid="6518481455548869894">"Tutup"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="7666159428113013319">"Leret daripada bahagian atas sebelah kiri untuk membuka pemberitahuan"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="3842355853737057332">"Leret daripada bahagian atas sebelah kanan untuk membuka Tetapan Pantas"</string>
</resources>
diff --git a/packages/SystemUI/res/values-my-ldrtl/strings.xml b/packages/SystemUI/res/values-my-ldrtl/strings.xml
index 4ca2bf5..4fad3bb 100644
--- a/packages/SystemUI/res/values-my-ldrtl/strings.xml
+++ b/packages/SystemUI/res/values-my-ldrtl/strings.xml
@@ -20,4 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="recents_quick_scrub_onboarding" msgid="2452671841151577157">"အက်ပ်များ အမြန်ပြောင်းရန် ဘယ်ဘက်သို့ ဖိဆွဲပါ"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="6543179539438220549">"အကြောင်းကြားချက်များ ဖွင့်ရန် ညာဘက်ထိပ်မှ ပွတ်ဆွဲပါ"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="5760503283520058116">"‘အမြန်ဆက်တင်များ’ ဖွင့်ရန်အတွက် ဘယ်ဘက်ထိပ်မှ ပွတ်ဆွဲပါ"</string>
</resources>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index 8c6788d..b6b07e5 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -70,7 +70,7 @@
<string name="usb_contaminant_message" msgid="7730476585174719805">"USB ပို့တ်ကို ပိတ်၍ သင့်ကိရိယာသို့ အရည် သို့မဟုတ် အမှိုက်စများ မဝင်စေရန် ကာကွယ်ပါ၊ မည်သည့် အပိုပစ္စည်းကိုမျှ အာရုံခံသိရှိနိုင်တော့မည် မဟုတ်ပါ။\n\nUSB ပို့တ်ကို ပြန်အသုံးပြုနိုင်သည့်အခါ သင့်ကိုအကြောင်းကြားပါမည်။"</string>
<string name="usb_port_enabled" msgid="531823867664717018">"အားသွင်းကိရိယာနှင့် ဆက်စပ်ပစ္စည်းများ သိရှိရန် USB ပို့တ် ဖွင့်ထားသည်"</string>
<string name="usb_disable_contaminant_detection" msgid="3827082183595978641">"USB ကို ဖွင့်ရန်"</string>
- <string name="learn_more" msgid="4690632085667273811">"ပိုမိုလေ့လာရန်"</string>
+ <string name="learn_more" msgid="4690632085667273811">"ပိုလေ့လာရန်"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"ဖန်သားပြင်ဓာတ်ပုံ"</string>
<string name="global_action_smart_lock_disabled" msgid="6286551337177954859">"‘လော့ခ်ဖွင့်ချိန်တိုးခြင်း’ ပိတ်ထားသည်"</string>
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"ပုံပို့ထားသည်"</string>
@@ -334,7 +334,7 @@
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"ဖွင့်နေသည်…"</string>
<string name="quick_settings_brightness_unable_adjust_msg" msgid="4124028416057617517">"အပေါ်အက်ပ်မှ ထိန်းချုပ်ထားသောကြောင့် တောက်ပမှုကို ချိန်ညှိ၍ မရပါ"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"အော်တို-လည်"</string>
- <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"မျက်နှာပြင်အား အလိုအလျောက်လှည့်ခြင်း"</string>
+ <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"မျက်နှာပြင်အား အလိုအလျောက်လှည့်ရန်"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"တည်နေရာ"</string>
<string name="quick_settings_screensaver_label" msgid="1495003469366524120">"စခရင်နားချိန်ပုံ"</string>
<string name="quick_settings_camera_label" msgid="5612076679385269339">"ကင်မရာသုံးခွင့်"</string>
@@ -398,7 +398,7 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"စခရင် ရိုက်ကူးရန်"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"စတင်ရန်"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"ရပ်ရန်"</string>
- <string name="qs_record_issue_label" msgid="8166290137285529059">"ပြဿနာကို မှတ်တမ်းတင်ခြင်း"</string>
+ <string name="qs_record_issue_label" msgid="8166290137285529059">"ပြဿနာ မှတ်တမ်း"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"စတင်ပါ"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"ရပ်ပါ"</string>
<string name="qs_record_issue_bug_report" msgid="8229031766918650079">"ချွတ်ယွင်းမှု အစီရင်ခံစာ"</string>
@@ -467,6 +467,8 @@
<string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"မိုက်ခရိုဖုန်းခလုတ် အသုံးပြုရန် ဆက်တင်များတွင် မိုက်ခရိုဖုန်းသုံးခွင့်ကို ဖွင့်ပါ။"</string>
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"ဆက်တင်များဖွင့်ရန်"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"အခြားစက်ပစ္စည်း"</string>
+ <string name="media_suggestion_disconnected_text" msgid="8777932417076437525">"<xliff:g id="DEVICE_NAME">%s</xliff:g> တွင် ဖွင့်ရန်"</string>
+ <string name="media_suggestion_failure_text" msgid="8453022394887613774">"ချိတ်ဆက်၍မရပါ။ ထပ်စမ်းကြည့်ပါ။"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"ဖွင့်၊ ပိတ် အနှစ်ချုပ်"</string>
<string name="zen_modes_dialog_title" msgid="8854640808100096934">"မုဒ်များ"</string>
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"ပြီးပြီ"</string>
@@ -582,6 +584,7 @@
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
<skip />
<string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"စခရင်တစ်ခုလုံး မျှဝေရန်"</string>
+ <string name="screen_share_permission_dialog_option_text_entire_screen_for_display" msgid="1545850238689169166">"<xliff:g id="DISPLAY_NAME">%s</xliff:g> ကို မျှဝေရန်"</string>
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
<skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"သင့်စခရင်တစ်ခုလုံးကို မျှဝေနေချိန်တွင် စခရင်ပေါ်ရှိ အရာအားလုံးကို <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> က မြင်နိုင်သည်။ စကားဝှက်၊ ငွေပေးချေမှု အချက်အလက်၊ မက်ဆေ့ဂျ်၊ ဓာတ်ပုံ၊ အသံနှင့် ဗီဒီယိုကဲ့သို့ အရာများကို ဂရုစိုက်ပါ။"</string>
@@ -593,6 +596,7 @@
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"သင့်ဖန်သားပြင်ကို ကာစ်လုပ်မလား။"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"အက်ပ်တစ်ခုကို ကာစ်လုပ်ရန်"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"ဖန်သားပြင်တစ်ခုလုံးကို ကာစ်လုပ်ရန်"</string>
+ <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen_for_display" msgid="5524817761472941086">"<xliff:g id="DISPLAY_NAME">%s</xliff:g> ကို ကာစ်လုပ်ရန်"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"သင့်ဖန်သားပြင်တစ်ခုလုံးကို ကာစ်လုပ်သည့်အခါ ဖန်သားပြင်ပေါ်ရှိ အရာအားလုံးကို မြင်နိုင်သည်။ စကားဝှက်၊ ငွေပေးချေမှု အချက်အလက်၊ မက်ဆေ့ဂျ်၊ ဓာတ်ပုံ၊ အသံနှင့် ဗီဒီယိုကဲ့သို့ အရာများကို ဂရုစိုက်ပါ။"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"အက်ပ်တစ်ခုကို ကာစ်လုပ်သည့်အခါ ယင်းအက်ပ်တွင် ပြထားသော (သို့) ဖွင့်ထားသော အရာအားလုံးကို မြင်နိုင်သည်။ စကားဝှက်၊ ငွေပေးချေမှု အချက်အလက်၊ မက်ဆေ့ဂျ်၊ ဓာတ်ပုံ၊ အသံနှင့် ဗီဒီယိုကဲ့သို့ အရာများကို ဂရုစိုက်ပါ။"</string>
<string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"ဖန်သားပြင်ကာစ်လုပ်ရန်"</string>
@@ -673,6 +677,7 @@
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"TrustAgent ဖြင့် ဆက်ဖွင့်ထားရန်"</string>
<string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"လော့ခ်ကျနေသည်၊ အထောက်အထားစိစစ်ရန် ကြိုးပမ်းကြိမ် များလွန်းသည်"</string>
<string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"စက်လော့ခ်ကျနေသည်\nအထောက်အထားစိစစ်၍ မရပါ"</string>
+ <string name="keyguard_indication_after_watch_disconnected" msgid="6329761892437377710">"သင့်နာရီဖြင့် လော့ခ်ချထားသည်"</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="accessibility_volume_settings" msgid="1458961116951564784">"အသံဆက်တင်များ"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"အလိုအလျောက် စာတန်းထိုးရန်"</string>
@@ -799,6 +804,10 @@
<string name="enable_bluetooth_message" msgid="6740938333772779717">"ကီးဘုတ်ကို တပ်ဘလက်နှင့် ချိတ်ဆက်ရန်၊ ပမထဦးစွာ ဘလူးတုသ်ကို ဖွင့်ပါ။"</string>
<string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"ဖွင့်ပါ"</string>
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"ဖွင့် - မျက်နှာအခြေခံ"</string>
+ <!-- no translation found for notification_guts_bundle_title (1345506995443305361) -->
+ <skip />
+ <!-- no translation found for notification_guts_bundle_summary (3971530802237393600) -->
+ <skip />
<string name="inline_done_button" msgid="6043094985588909584">"ပြီးပြီ"</string>
<string name="inline_ok_button" msgid="603075490581280343">"အသုံးပြုရန်"</string>
<string name="inline_turn_off_notifications" msgid="2653064779176881329">"ပိတ်ရန်"</string>
@@ -957,7 +966,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"ဖွင့်"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"ပိတ်"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"မရနိုင်ပါ"</string>
- <string name="accessibility_tile_disabled_by_policy_action_description" msgid="6958422730461646926">"ပိုမိုလေ့လာရန်"</string>
+ <string name="accessibility_tile_disabled_by_policy_action_description" msgid="6958422730461646926">"ပိုလေ့လာရန်"</string>
<string name="nav_bar" msgid="4642708685386136807">"ရွှေ့လျားရန်ဘားတန်း"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"အပြင်အဆင်"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"လက်ဝဲခလုတ် အမျိုးအစားအပို"</string>
@@ -984,6 +993,7 @@
<string name="right_icon" msgid="1103955040645237425">"လက်ယာသင်္ကေတ"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"အကွက်များထည့်ရန် ဖိဆွဲပါ"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"အကွက်ငယ်များ ပြန်စီစဉ်ရန် ဖိပြီးဆွဲပါ"</string>
+ <string name="select_to_rearrange_tiles" msgid="6166153534235179148">"ပြန်စီပြီး အရွယ်ပြန်ရန် အကွက်ငယ်များကို ရွေးပါ"</string>
<string name="tap_to_position_tile" msgid="6282815817773342757">"အကွက်ငယ်နေရာချရန် တို့ပါ"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"ဖယ်ရှားရန် ဤနေရာသို့ဖိဆွဲပါ"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"အနည်းဆုံး <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> ကွက် ရှိရမည်"</string>
@@ -1003,10 +1013,12 @@
<string name="tuner_low_priority" msgid="8412666814123009820">"အရေးမကြီးသော အကြောင်းကြားချက် သင်္ကေတများ ပြရန်"</string>
<string name="other" msgid="429768510980739978">"အခြား"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"အကွက်ငယ်၏ အရွယ်အစားကို ပြောင်းရန်"</string>
- <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"အကွက်ငယ်ကို ဖယ်ရှားရန်"</string>
+ <!-- no translation found for accessibility_qs_edit_remove_tile_action (4811778796933958616) -->
+ <skip />
<string name="accessibility_qs_edit_toggle_placement_mode" msgid="3870429389210569610">"နေရာချထားမှုမုဒ် ဖွင့်ရန်"</string>
<string name="accessibility_qs_edit_toggle_selection" msgid="2201248304072372239">"ရွေးချယ်မှု ပြောင်းရန်"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"နောက်ဆုံးနေရာတွင် အကွက်ငယ် ထည့်ရန်"</string>
+ <!-- no translation found for accessibility_qs_edit_tile_add_action (7188303979684400620) -->
+ <skip />
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"အကွက်ငယ်ကို ရွှေ့ရန်"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"ဆန္ဒရှိရာ နေရာတွင် အကွက်ငယ် ထည့်ရန်"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> သို့ ရွှေ့ရန်"</string>
@@ -1093,7 +1105,8 @@
<string name="auto_saver_text" msgid="3214960308353838764">"ဘက်ထရီကုန်ခါနီးတွင် ဖွင့်ပါ"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"မလိုပါ"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"အသုံးပြုနေသည်"</string>
- <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"အပလီကေးရှင်းများက သင်၏ <xliff:g id="TYPES_LIST">%s</xliff:g> ကို အသုံးပြုနေသည်။"</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps (4994455315457996796) -->
+ <skip />
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">"၊ "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" နှင့် "</string>
<string name="ongoing_privacy_dialog_using_op" msgid="426635338010011796">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> က အသုံးပြုနေသည်"</string>
@@ -1399,7 +1412,7 @@
<string name="log_access_confirmation_allow" msgid="752147861593202968">"တစ်ခါသုံး ဝင်ခွင့်ပေးရန်"</string>
<string name="log_access_confirmation_deny" msgid="2389461495803585795">"ခွင့်မပြုပါ"</string>
<string name="log_access_confirmation_body" msgid="6883031912003112634">"သင့်စက်ရှိ အဖြစ်အပျက်များကို စက်မှတ်တမ်းများက မှတ်တမ်းတင်သည်။ အက်ပ်များက ပြဿနာများ ရှာဖွေပြီးဖြေရှင်းရန် ဤမှတ်တမ်းများကို သုံးနိုင်သည်။\n\nအချို့မှတ်တမ်းများတွင် သတိထားရမည့်အချက်အလက်များ ပါဝင်နိုင်သဖြင့် ယုံကြည်ရသည့် အက်ပ်များကိုသာ စက်မှတ်တမ်းအားလုံး သုံးခွင့်ပြုပါ။ \n\nဤအက်ပ်ကို စက်မှတ်တမ်းအားလုံး သုံးခွင့်မပြုသော်လည်း ၎င်းက ကိုယ်ပိုင်မှတ်တမ်းများ သုံးနိုင်သေးသည်။ သင့်စက်ရှိ အချို့မှတ်တမ်းများ (သို့) အချက်အလက်များကို သင့်စက်ထုတ်လုပ်သူက သုံးနိုင်ပါသေးသည်။"</string>
- <string name="log_access_confirmation_learn_more" msgid="3134565480986328004">"ပိုမိုလေ့လာရန်"</string>
+ <string name="log_access_confirmation_learn_more" msgid="3134565480986328004">"ပိုလေ့လာရန်"</string>
<string name="log_access_confirmation_learn_more_at" msgid="5635666259505215905">"<xliff:g id="URL">%s</xliff:g> တွင် ပိုမိုလေ့လာရန်"</string>
<string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"<xliff:g id="APPNAME">%1$s</xliff:g> ဖွင့်ရန်"</string>
<string name="wallet_quick_affordance_unavailable_install_the_app" msgid="7298552910007208368">"Wallet အက်ပ်ကို ဖြတ်လမ်းလင့်ခ်အဖြစ်ထည့်ရန် ၎င်းအားထည့်သွင်းထားကြောင်း သေချာပါစေ"</string>
@@ -1508,11 +1521,11 @@
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"ဖြတ်လမ်းလင့်ခ် ထည့်ရန်"</string>
<string name="shortcut_helper_delete_shortcut_button_label" msgid="3148773472696137052">"ဖြတ်လမ်းလင့်ခ် ဖျက်ရန်"</string>
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"သင့်ကီးဘုတ်ကိုသုံး၍ လမ်းညွှန်ခြင်း"</string>
- <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"လက်ကွက်ဖြတ်လမ်းများကို လေ့လာပါ"</string>
+ <string name="launch_keyboard_tutorial_notification_content" msgid="2131074100205028210">"လက်ကွက်ဖြတ်လမ်းများကို လေ့လာပါ"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"သင့်တာ့ချ်ပက်ကိုသုံး၍ လမ်းညွှန်ခြင်း"</string>
<string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"တာ့ချ်ပက်လက်ဟန်များကို လေ့လာပါ"</string>
<string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"သင်၏ ကီးဘုတ်နှင့် တာ့ချ်ပက်တို့ကိုသုံး၍ လမ်းညွှန်ခြင်း"</string>
- <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"တာ့ချ်ပက်လက်ဟန်များ၊ လက်ကွက်ဖြတ်လမ်းများ စသည်တို့ကို လေ့လာပါ"</string>
+ <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="8557098239919786046">"တာ့ချ်ပက်လက်ဟန်များ၊ လက်ကွက်ဖြတ်လမ်းများ စသည်တို့ကို လေ့လာပါ"</string>
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"နောက်သို့"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"ပင်မစာမျက်နှာသို့ သွားရန်"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"မကြာသေးမီကအက်ပ်များကို ကြည့်ရန်"</string>
@@ -1582,4 +1595,7 @@
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"အမြန်ဆက်တင်များ အကွက်ငယ်အားလုံးကို စက်ပစ္စည်း၏ မူရင်းဆက်တင်များသို့ ပြင်ဆင်သတ်မှတ်ပါမည်"</string>
<string name="demote_explain_text" msgid="3942301497888762295">"<xliff:g id="APPLICATION">%1$s</xliff:g> သည် ‘တိုက်ရိုက်အပ်ဒိတ်များ’ ကို ပြတော့မည်မဟုတ်ပါ။ ၎င်းကို ဆက်တင်များတွင် အချိန်မရွေး ပြောင်းနိုင်သည်။"</string>
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>၊ <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
+ <string name="underlay_close_button_content_description" msgid="6518481455548869894">"ပိတ်ရန်"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="7666159428113013319">"အကြောင်းကြားချက်များ ဖွင့်ရန် ဘယ်ဘက်ထိပ်မှ ပွတ်ဆွဲပါ"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="3842355853737057332">"‘အမြန်ဆက်တင်များ’ ဖွင့်ရန်အတွက် ညာဘက်ထိပ်မှ ပွတ်ဆွဲပါ"</string>
</resources>
diff --git a/packages/SystemUI/res/values-nb-ldrtl/strings.xml b/packages/SystemUI/res/values-nb-ldrtl/strings.xml
index 37b97f8..5d7b32d 100644
--- a/packages/SystemUI/res/values-nb-ldrtl/strings.xml
+++ b/packages/SystemUI/res/values-nb-ldrtl/strings.xml
@@ -20,4 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="recents_quick_scrub_onboarding" msgid="2452671841151577157">"Dra til venstre for å bytte app raskt"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="6543179539438220549">"Sveip fra øverst til høyre for å åpne varsler"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="5760503283520058116">"Sveip fra øverst til venstre for å åpne hurtiginnstillingene"</string>
</resources>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index 8dbeadd..8545e71 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -252,8 +252,7 @@
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Koblet til <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"Koblet til <xliff:g id="CAST">%s</xliff:g>."</string>
<string name="accessibility_expand_group" msgid="521237935987978624">"Utvid gruppen."</string>
- <!-- no translation found for accessibility_collapse_group (1842704016768329517) -->
- <skip />
+ <string name="accessibility_collapse_group" msgid="1842704016768329517">"Skjul gruppen."</string>
<string name="accessibility_add_device_to_group" msgid="5446422960697860806">"Legg til enheten i gruppen."</string>
<string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"Fjern enheten fra gruppen."</string>
<string name="accessibility_open_application" msgid="1749126077501259712">"Åpne appen."</string>
@@ -339,7 +338,7 @@
<string name="quick_settings_location_label" msgid="2621868789013389163">"Sted"</string>
<string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Skjermsparer"</string>
<string name="quick_settings_camera_label" msgid="5612076679385269339">"Kameratilgang"</string>
- <string name="quick_settings_mic_label" msgid="8392773746295266375">"Mikrofontilgang"</string>
+ <string name="quick_settings_mic_label" msgid="8392773746295266375">"Mikrofon&#173;tilgang"</string>
<string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Tilgjengelig"</string>
<string name="quick_settings_camera_mic_blocked" msgid="4710884905006788281">"Blokkert"</string>
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"Medieenhet"</string>
@@ -356,8 +355,8 @@
<string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"Ingen enheter er tilgjengelige"</string>
<string name="quick_settings_cast_no_network" msgid="3863016850468559522">"Ingen wifi- eller Ethernet-tilkobling"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Lysstyrke"</string>
- <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Fargeinvertering"</string>
- <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Fargekorrigering"</string>
+ <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Farge&#173;invertering"</string>
+ <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Farge&#173;korrigering"</string>
<string name="quick_settings_font_scaling_label" msgid="5289001009876936768">"Skriftstørrelse"</string>
<string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Brukervalg"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Ferdig"</string>
@@ -412,7 +411,7 @@
<string name="custom" msgid="3337456985275158299">"Egendefinert"</string>
<string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Innstillinger for egendefinert spor"</string>
<string name="restore_default" msgid="5259420807486239755">"Gjenopprett standard"</string>
- <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Enhåndsmodus"</string>
+ <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Enhånds&#173;modus"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Høreapparater"</string>
<string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktiv"</string>
<string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Frakoblet"</string>
@@ -468,6 +467,8 @@
<string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"For å bruke mikrofonknappen, gi mikrofontilgang i innstillingene."</string>
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Åpne Innstillinger"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Annen enhet"</string>
+ <string name="media_suggestion_disconnected_text" msgid="8777932417076437525">"Spill av på <xliff:g id="DEVICE_NAME">%s</xliff:g>"</string>
+ <string name="media_suggestion_failure_text" msgid="8453022394887613774">"Kan ikke tilkoble. Prøv igjen."</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Slå oversikten av eller på"</string>
<string name="zen_modes_dialog_title" msgid="8854640808100096934">"Moduser"</string>
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Ferdig"</string>
@@ -583,6 +584,7 @@
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
<skip />
<string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Del hele skjermen"</string>
+ <string name="screen_share_permission_dialog_option_text_entire_screen_for_display" msgid="1545850238689169166">"Del <xliff:g id="DISPLAY_NAME">%s</xliff:g>"</string>
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
<skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Når du deler hele skjermen, er alt på skjermen synlig for <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Derfor bør du være forsiktig med for eksempel passord, betalingsopplysninger, meldinger, bilder, lyd og video."</string>
@@ -594,6 +596,7 @@
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Vil du caste skjermen?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Cast én app"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Cast hele skjermen"</string>
+ <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen_for_display" msgid="5524817761472941086">"Cast <xliff:g id="DISPLAY_NAME">%s</xliff:g>"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Når du caster hele skjermen, er alt på skjermen synlig. Derfor bør du være forsiktig med for eksempel passord, betalingsopplysninger, meldinger, bilder, lyd og video."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Når du caster en app, er alt som vises eller spilles av i appen, synlig. Derfor bør du være forsiktig med for eksempel passord, betalingsopplysninger, meldinger, bilder, lyd og video."</string>
<string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Cast skjermen"</string>
@@ -674,6 +677,7 @@
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Holdes opplåst med TrustAgent"</string>
<string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"Enheten er låst – for mange autentiseringsforsøk"</string>
<string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"Enheten er låst\nKunne ikke autentisere"</string>
+ <string name="keyguard_indication_after_watch_disconnected" msgid="6329761892437377710">"Låst ute av klokken"</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="accessibility_volume_settings" msgid="1458961116951564784">"Lydinnstillinger"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Automatisk medieteksting"</string>
@@ -800,6 +804,10 @@
<string name="enable_bluetooth_message" msgid="6740938333772779717">"For å koble tastaturet til nettbrettet ditt må du først slå på Bluetooth."</string>
<string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Slå på"</string>
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"På – ansiktsbasert"</string>
+ <!-- no translation found for notification_guts_bundle_title (1345506995443305361) -->
+ <skip />
+ <!-- no translation found for notification_guts_bundle_summary (3971530802237393600) -->
+ <skip />
<string name="inline_done_button" msgid="6043094985588909584">"Ferdig"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Bruk"</string>
<string name="inline_turn_off_notifications" msgid="2653064779176881329">"Slå av"</string>
@@ -985,6 +993,7 @@
<string name="right_icon" msgid="1103955040645237425">"Høyre-ikon"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Hold og dra for å legge til brikker"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Hold og dra for å flytte på rutene"</string>
+ <string name="select_to_rearrange_tiles" msgid="6166153534235179148">"Velg brikker du vil endre rekkefølge og størrelse på"</string>
<string name="tap_to_position_tile" msgid="6282815817773342757">"Trykk for å plassere infobrikken"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Dra hit for å fjerne"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Du trenger minst <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> infobrikker"</string>
@@ -1004,10 +1013,12 @@
<string name="tuner_low_priority" msgid="8412666814123009820">"Vis ikoner for varsler med lav prioritet"</string>
<string name="other" msgid="429768510980739978">"Annet"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"bytt størrelse på brikken"</string>
- <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"fjerne infobrikken"</string>
+ <!-- no translation found for accessibility_qs_edit_remove_tile_action (4811778796933958616) -->
+ <skip />
<string name="accessibility_qs_edit_toggle_placement_mode" msgid="3870429389210569610">"slå plasseringsmodusen av/på"</string>
<string name="accessibility_qs_edit_toggle_selection" msgid="2201248304072372239">"slå valget av/på"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"legge til en brikke på den siste posisjonen"</string>
+ <!-- no translation found for accessibility_qs_edit_tile_add_action (7188303979684400620) -->
+ <skip />
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Flytt infobrikken"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Legg til brikken på ønsket posisjon"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Flytt til <xliff:g id="POSITION">%1$d</xliff:g>"</string>
@@ -1094,7 +1105,8 @@
<string name="auto_saver_text" msgid="3214960308353838764">"Slå på når det er sannsynlig at du går tom for batteri"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Nei takk"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"I bruk"</string>
- <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Apper bruker <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps (4994455315457996796) -->
+ <skip />
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" og "</string>
<string name="ongoing_privacy_dialog_using_op" msgid="426635338010011796">"Blir brukt av <xliff:g id="APPLICATION_NAME">%1$s</xliff:g>"</string>
@@ -1251,15 +1263,13 @@
<string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(frakoblet)"</string>
<string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"Kan ikke bytte. Trykk for å prøve igjen."</string>
<string name="media_output_dialog_pairing_new" msgid="5098212763195577270">"Koble til en enhet"</string>
- <!-- no translation found for media_output_dialog_button_connect_device (2819038239946643550) -->
- <skip />
+ <string name="media_output_dialog_button_connect_device" msgid="2819038239946643550">"Koble til enhet"</string>
<string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Ukjent app"</string>
<string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Stopp castingen"</string>
<string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"Tilgjengelige enheter for lydutgang."</string>
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Volum"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g> %%"</string>
- <!-- no translation found for media_output_group_title_connected_speakers (3413876972919684479) -->
- <skip />
+ <string name="media_output_group_title_connected_speakers" msgid="3413876972919684479">"Tilkoblede høyttalere"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Høyttalere og skjermer"</string>
<string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Foreslåtte enheter"</string>
<string name="media_input_group_title" msgid="2057057473860783021">"Inngang"</string>
@@ -1328,10 +1338,8 @@
<string name="accessibility_enter_hint" msgid="2617864063504824834">"åpne enheten"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Bruk fingeravtrykk for å åpne"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Autentisering kreves. Trykk på fingeravtrykkssensoren for å autentisere."</string>
- <!-- no translation found for resume_animation (9148788411429330671) -->
- <skip />
- <!-- no translation found for pause_animation (1919705510058107504) -->
- <skip />
+ <string name="resume_animation" msgid="9148788411429330671">"Gjenoppta animasjonen"</string>
+ <string name="pause_animation" msgid="1919705510058107504">"Sett animasjonen på pause"</string>
<string name="ongoing_call_content_description" msgid="6394763878322348560">"Aktiv samtale"</string>
<string name="ongoing_notification_extra_content_description" msgid="2098752668861351265">"Kontinuerlig"</string>
<string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobildata"</string>
@@ -1513,11 +1521,11 @@
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"Legg til snarvei"</string>
<string name="shortcut_helper_delete_shortcut_button_label" msgid="3148773472696137052">"Slett snarveien"</string>
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Naviger med tastaturet"</string>
- <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Lær deg hurtigtaster"</string>
+ <string name="launch_keyboard_tutorial_notification_content" msgid="2131074100205028210">"Lær hurtigtaster"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Naviger med styreflaten"</string>
<string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Lær deg styreflatebevegelser"</string>
<string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Naviger med tastaturet og styreflaten"</string>
- <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Lær deg styreflatebevegelser, hurtigtaster med mer"</string>
+ <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="8557098239919786046">"Lær styreflatebevegelser, hurtigtaster med mer"</string>
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Gå tilbake"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Gå til startsiden"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Se nylige apper"</string>
@@ -1587,4 +1595,7 @@
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Alle brikker for hurtiginnstillinger tilbakestilles til enhetens opprinnelige innstillinger"</string>
<string name="demote_explain_text" msgid="3942301497888762295">"<xliff:g id="APPLICATION">%1$s</xliff:g> viser ikke direkteoppdateringer lenger. Du kan endre dette når som helst i innstillingene."</string>
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
+ <string name="underlay_close_button_content_description" msgid="6518481455548869894">"Lukk"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="7666159428113013319">"Sveip fra øverst til venstre for å åpne varsler"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="3842355853737057332">"Sveip fra øverst til høyre for å åpne hurtiginnstillingene"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ne-ldrtl/strings.xml b/packages/SystemUI/res/values-ne-ldrtl/strings.xml
index 4594c55..eef47af 100644
--- a/packages/SystemUI/res/values-ne-ldrtl/strings.xml
+++ b/packages/SystemUI/res/values-ne-ldrtl/strings.xml
@@ -20,4 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="recents_quick_scrub_onboarding" msgid="2452671841151577157">"एपहरू द्रुत गतिमा बदल्न बायाँतिर ड्र्याग गर्नुहोस्"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="6543179539438220549">"नोटिफिकेसनहरू खोल्न सिरानको दायाँतिरबाट तलतिर स्वाइप गर्नुहोस्"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="5760503283520058116">"द्रुत सेटिङ खोल्न सिरानको बायाँतिरबाट तलतिर स्वाइप गर्नुहोस्"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index e32f6c9..a021c7d 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -202,7 +202,7 @@
<string name="biometric_dialog_cancel_authentication" msgid="981316588773442637">"प्रमाणीकरण रद्द गर्नुहोस्"</string>
<string name="biometric_dialog_content_view_more_options_button" msgid="2663810393874865475">"थप विकल्पहरू"</string>
<string name="biometric_dialog_use_pin" msgid="8385294115283000709">"PIN प्रयोग गर्नुहोस्"</string>
- <string name="biometric_dialog_use_pattern" msgid="2315593393167211194">"ढाँचा प्रयोग गर्नुहोस्"</string>
+ <string name="biometric_dialog_use_pattern" msgid="2315593393167211194">"प्याटर्न प्रयोग गर्नुहोस्"</string>
<string name="biometric_dialog_use_password" msgid="3445033859393474779">"पासवर्ड प्रयोग गर्नुहोस्"</string>
<string name="biometric_dialog_wrong_pin" msgid="1878539073972762803">"PIN मिलेन"</string>
<string name="biometric_dialog_wrong_pattern" msgid="8954812279840889029">"प्याटर्न मिलेन"</string>
@@ -467,6 +467,8 @@
<string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"तपाईं माइक्रोफोन बटन प्रयोग गर्न चाहनुहुन्छ भने सेटिङमा गई माइक्रोफोन प्रयोग गर्ने अनुमति दिनुहोस्।"</string>
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"सेटिङ खोल्नुहोस्"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"अर्को डिभाइड"</string>
+ <string name="media_suggestion_disconnected_text" msgid="8777932417076437525">"<xliff:g id="DEVICE_NAME">%s</xliff:g> मा प्ले गर्नुहोस्"</string>
+ <string name="media_suggestion_failure_text" msgid="8453022394887613774">"कनेक्ट भएन। फेरि प्रयास गर्नु…"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"परिदृश्य टगल गर्नुहोस्"</string>
<string name="zen_modes_dialog_title" msgid="8854640808100096934">"मोडहरू"</string>
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"सम्पन्न भयो"</string>
@@ -573,15 +575,16 @@
<string name="user_remove_user_message" msgid="6702834122128031833">"यस प्रयोगकर्ताको सबै एपहरू तथा डेटा हटाइने छ।"</string>
<string name="user_remove_user_remove" msgid="8387386066949061256">"हटाउनुहोस्"</string>
<string name="media_projection_dialog_title" msgid="3316063622495360646">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> मार्फत रेकर्ड गर्न वा cast गर्न थाल्ने हो?"</string>
- <string name="media_projection_dialog_warning" msgid="1303664408388363598">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ले रेकर्ड वा कास्ट गर्दै गर्दा तपाईंको स्क्रिनमा देखिने सबै जानकारी अथवा तपाईंको डिभाइसबाट प्ले गरिने सबै सामग्री हेर्न तथा प्रयोग गर्न सक्छ। यसअन्तर्गत पासवर्ड, भुक्तानीसम्बन्धी विवरण, फोटो, म्यासेज र तपाईंले प्ले गर्ने अडियो जस्ता कुराहरू समावेश हुन्छन्।"</string>
+ <string name="media_projection_dialog_warning" msgid="1303664408388363598">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ले रेकर्ड वा कास्ट गर्दै गर्दा तपाईंको स्क्रिनमा देखिने सबै जानकारी अथवा तपाईंको डिभाइसबाट प्ले गरिने सबै सामग्री एक्सेस गर्न सक्छ। यसअन्तर्गत पासवर्ड, भुक्तानीसम्बन्धी विवरण, फोटो, म्यासेज र तपाईंले प्ले गर्ने अडियो जस्ता कुराहरू समावेश हुन्छन्।"</string>
<string name="media_projection_sys_service_dialog_title" msgid="3751133258891897878">"रेकर्ड वा कास्ट गर्न थाल्ने हो?"</string>
- <string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"यो फङ्सन प्रदान गर्ने सेवाले रेकर्ड वा कास्ट गर्दै गर्दा तपाईंको स्क्रिनमा देखिने सबै जानकारी अथवा तपाईंको डिभाइसबाट प्ले गरिने सबै सामग्री हेर्न तथा प्रयोग गर्न सक्छ। यसअन्तर्गत पासवर्ड, भुक्तानीसम्बन्धी विवरण, फोटो, म्यासेज र तपाईंले प्ले गर्ने अडियो जस्ता कुराहरू समावेश हुन्छन्।"</string>
+ <string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"यो फङ्सन प्रदान गर्ने सेवाले रेकर्ड वा कास्ट गर्दै गर्दा तपाईंको स्क्रिनमा देखिने सबै जानकारी अथवा तपाईंको डिभाइसबाट प्ले गरिने सबै सामग्री एक्सेस गर्न सक्छ। यसअन्तर्गत पासवर्ड, भुक्तानीसम्बन्धी विवरण, फोटो, म्यासेज र तपाईंले प्ले गर्ने अडियो जस्ता कुराहरू समावेश हुन्छन्।"</string>
<string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"कुनै एप सेयर वा रेकर्ड गर्नुहोस्"</string>
<string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"आफ्नो स्क्रिन <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> मा सेयर गर्ने हो?"</string>
<string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"एउटा एप सेयर गर्नुहोस्"</string>
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
<skip />
<string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"पूरै स्क्रिन सेयर गर्नुहोस्"</string>
+ <string name="screen_share_permission_dialog_option_text_entire_screen_for_display" msgid="1545850238689169166">"<xliff:g id="DISPLAY_NAME">%s</xliff:g> सेयर गर्नुहोस्"</string>
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
<skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"तपाईंले पूरै स्क्रिन सेयर गरिरहेका बेला तपाईंको स्क्रिनमा देखिने सबै सामग्री <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> मा देखिन्छ। त्यसैले पासवर्ड, भुक्तानीसम्बन्धी विवरण, म्यासेज, फोटो र अडियो तथा भिडियो जस्ता कुरा हेर्दा वा प्ले गर्दा सावधानी अपनाउनुहोला।"</string>
@@ -593,13 +596,14 @@
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"स्क्रिन कास्ट गर्ने हो?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"एउटा एप कास्ट गर्नुहोस्"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"पूरै स्क्रिन कास्ट गर्नुहोस्"</string>
+ <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen_for_display" msgid="5524817761472941086">"<xliff:g id="DISPLAY_NAME">%s</xliff:g> कास्ट गर्नुहोस्"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"तपाईंले पूरै स्क्रिन कास्ट गरिरहेका बेला तपाईंको स्क्रिनमा देखिने सबै सामग्री अर्को स्क्रिनमा पनि देखिन्छ। त्यसैले पासवर्ड, भुक्तानीसम्बन्धी विवरण, म्यासेज, फोटो र अडियो तथा भिडियो जस्ता कुरा हेर्दा वा प्ले गर्दा सावधानी अपनाउनुहोला।"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"तपाईंले यो एप कास्ट गरिरहेका बेला यो एपमा देखाइने वा प्ले गरिने सबै सामग्री अर्को स्क्रिनमा पनि देखिन्छ। त्यसैले पासवर्ड, भुक्तानीसम्बन्धी विवरण, म्यासेज, फोटो र अडियो तथा भिडियो जस्ता कुरा हेर्दा वा प्ले गर्दा सावधानी अपनाउनुहोला।"</string>
<string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"स्क्रिन कास्ट गर्नुहोस्"</string>
<string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"कास्ट गर्न एप छनौट गर्नुहोस्"</string>
<string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"सेयर गर्न थाल्ने हो?"</string>
- <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"तपाईंले सेयर गर्दा, रेकर्ड गर्दा वा कास्ट गर्दा Android ले तपाईंको स्क्रिनमा देखिने वा डिभाइसमा प्ले गरिने सबै कुरा हेर्न तथा प्रयोग गर्न सक्छ। त्यसैले पासवर्ड, भुक्तानीसम्बन्धी विवरण, म्यासेज, फोटो र अडियो तथा भिडियो जस्ता कुरा हेर्दा वा प्ले गर्दा सावधानी अपनाउनुहोला।"</string>
- <string name="media_projection_entry_generic_permission_dialog_warning_single_app" msgid="3454859977888159495">"तपाईंले कुनै एप सेयर गर्दा, रेकर्ड गर्दा वा कास्ट गर्दा Android ले उक्त एपमा देखाइने वा प्ले गरिने सबै कुरा हेर्न तथा प्रयोग गर्न सक्छ। त्यसैले पासवर्ड, भुक्तानीसम्बन्धी विवरण, म्यासेज, फोटो र अडियो तथा भिडियो जस्ता कुरा हेर्दा वा प्ले गर्दा सावधानी अपनाउनुहोला।"</string>
+ <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"तपाईंले सेयर गर्दा, रेकर्ड गर्दा वा कास्ट गर्दा Android ले तपाईंको स्क्रिनमा देखिने वा डिभाइसमा प्ले गरिने सबै कुरा एक्सेस गर्न सक्छ। त्यसैले पासवर्ड, भुक्तानीसम्बन्धी विवरण, म्यासेज, फोटो र अडियो तथा भिडियो जस्ता कुरा हेर्दा वा प्ले गर्दा सावधानी अपनाउनुहोला।"</string>
+ <string name="media_projection_entry_generic_permission_dialog_warning_single_app" msgid="3454859977888159495">"तपाईंले कुनै एप सेयर गर्दा, रेकर्ड गर्दा वा कास्ट गर्दा Android ले उक्त एपमा देखाइने वा प्ले गरिने सबै कुरा एक्सेस गर्न सक्छ। त्यसैले पासवर्ड, भुक्तानीसम्बन्धी विवरण, म्यासेज, फोटो र अडियो तथा भिडियो जस्ता कुरा हेर्दा वा प्ले गर्दा सावधानी अपनाउनुहोला।"</string>
<string name="media_projection_entry_generic_permission_dialog_continue" msgid="8640381403048097116">"सुरु गर्नुहोस्"</string>
<string name="media_projection_entry_generic_permission_dialog_continue_single_app" msgid="5920814988611877051">"अर्को"</string>
<string name="media_projection_task_switcher_text" msgid="590885489897412359">"तपाईंले एपहरू बदल्दा सेयर गर्ने प्रक्रिया पज हुन्छ"</string>
@@ -673,6 +677,7 @@
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"TrustAgent ले खुला राखेको"</string>
<string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"डिभाइस लक गरिएको छ, निकै धेरै पटक प्रमाणीकरण गर्ने प्रयास भएको छ"</string>
<string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"डिभाइस लक गरिएको छ\nप्रमाणीकरण गर्न सकिएन"</string>
+ <string name="keyguard_indication_after_watch_disconnected" msgid="6329761892437377710">"तपाईंको स्मार्टवाचले लक गरेको छ"</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="accessibility_volume_settings" msgid="1458961116951564784">"ध्वनिसम्बन्धी सेटिङहरू"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"मिडियाको स्वत: क्याप्सन बनाउनुहोस्"</string>
@@ -799,6 +804,10 @@
<string name="enable_bluetooth_message" msgid="6740938333772779717">"आफ्नो ट्याब्लेटसँग किबोर्ड जोड्न, पहिले तपाईँले ब्लुटुथ सक्रिय गर्नुपर्छ।"</string>
<string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"सक्रिय पार्नुहोस्"</string>
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"अन छ - अनुहारमा आधारित"</string>
+ <!-- no translation found for notification_guts_bundle_title (1345506995443305361) -->
+ <skip />
+ <!-- no translation found for notification_guts_bundle_summary (3971530802237393600) -->
+ <skip />
<string name="inline_done_button" msgid="6043094985588909584">"सम्पन्न भयो"</string>
<string name="inline_ok_button" msgid="603075490581280343">"लागू गर्नुहोस्"</string>
<string name="inline_turn_off_notifications" msgid="2653064779176881329">"अफ गर्नुहोस्"</string>
@@ -984,6 +993,7 @@
<string name="right_icon" msgid="1103955040645237425">"दायाँतिरको आइकन"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"टाइलहरू थप्न होल्ड गरी ड्र्याग गर्नुहोस्"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"टाइलहरू पुनः क्रमबद्ध गर्न होल्ड गरी ड्र्याग गर्नुहोस्"</string>
+ <string name="select_to_rearrange_tiles" msgid="6166153534235179148">"पुनः मिलाउनु पर्ने र आकार बदल्नु पर्ने टाइलहरू चयन गर्नुहोस्"</string>
<string name="tap_to_position_tile" msgid="6282815817773342757">"टाइल राख्न ट्याप गर्नुहोस्"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"हटाउनका लागि यहाँ तान्नुहोस्"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"तपाईंलाई कम्तीमा <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> वटा टाइल चाहिन्छ"</string>
@@ -1003,10 +1013,10 @@
<string name="tuner_low_priority" msgid="8412666814123009820">"कम प्राथमिकताका सूचना आइकनहरू देखाउनुहोस्"</string>
<string name="other" msgid="429768510980739978">"अन्य"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"टाइलको आकार टगल गर्नुहोस्"</string>
- <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"टाइल हटाउनुहोस्"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="4811778796933958616">"टाइल हटाउनुहोस्"</string>
<string name="accessibility_qs_edit_toggle_placement_mode" msgid="3870429389210569610">"प्लेसमेन्ट मोड टगल गर्नुहोस्"</string>
<string name="accessibility_qs_edit_toggle_selection" msgid="2201248304072372239">"छनौट टगल गर्नुहोस्"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"अन्तिम स्थानमा टाइल हाल्नुहोस्"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="7188303979684400620">"अन्तिम स्थानमा टाइल हाल्नुहोस्"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"टाइल सार्नुहोस्"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"आफूले चाहेको स्थानमा टाइल हाल्नुहोस्"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"टाइल सारेर <xliff:g id="POSITION">%1$d</xliff:g> मा लैजानुहोस्"</string>
@@ -1093,7 +1103,8 @@
<string name="auto_saver_text" msgid="3214960308353838764">"ब्याट्री सकिने सम्भावना भएमा अन गर्नुहोस्"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"पर्दैन, धन्यवाद"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"प्रयोगमा छ"</string>
- <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"एपहरूले तपाईंको <xliff:g id="TYPES_LIST">%s</xliff:g> प्रयोग गर्दै छन्।"</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps (4994455315457996796) -->
+ <skip />
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" र "</string>
<string name="ongoing_privacy_dialog_using_op" msgid="426635338010011796">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g>ले प्रयोग गरिरहेको छ"</string>
@@ -1508,11 +1519,11 @@
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"सर्टकट हाल्नुहोस्"</string>
<string name="shortcut_helper_delete_shortcut_button_label" msgid="3148773472696137052">"सर्टकट मेटाउनुहोस्"</string>
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"किबोर्ड प्रयोग गरी नेभिगेट गर्नुहोस्"</string>
- <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"किबोर्डका सर्टकटहरू प्रयोग गर्न सिक्नुहोस्"</string>
+ <string name="launch_keyboard_tutorial_notification_content" msgid="2131074100205028210">"किबोर्डका सर्टकट प्रयोग गर्ने तरिका सिक्नुहोस्"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"टचप्याड प्रयोग गरी नेभिगेट गर्नुहोस्"</string>
<string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"टचप्याड जेस्चर प्रयोग गर्न सिक्नुहोस्"</string>
<string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"किबोर्ड र टचप्याड प्रयोग गरी नेभिगेट गर्नुहोस्"</string>
- <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"टचप्याड जेस्चर, किबोर्डका सर्टकट र अन्य कुरा प्रयोग गर्न सिक्नुहोस्"</string>
+ <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="8557098239919786046">"टचप्याड जेस्चर, किबोर्डका सर्टकट र अन्य कुरा प्रयोग गर्ने तरिका सिक्नुहोस्"</string>
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"पछाडि जानुहोस्"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"होम स्क्रिनमा जानुहोस्"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"हालसालै चलाइएका एपहरू हेर्नुहोस्"</string>
@@ -1582,4 +1593,7 @@
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"द्रुत सेटिङका सबै टाइलहरू रिसेट गरी डिभाइसका मूल सेटिङ लागू गरिने छन्"</string>
<string name="demote_explain_text" msgid="3942301497888762295">"<xliff:g id="APPLICATION">%1$s</xliff:g> ले अब उप्रान्त लाइभ अपडेटहरू देखाउने छैन। तपाईं जुनसुकै बेला सेटिङमा गई यो सेटिङ परिवर्तन गर्न सक्नुहुन्छ।"</string>
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
+ <string name="underlay_close_button_content_description" msgid="6518481455548869894">"बन्द गर्नुहोस्"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="7666159428113013319">"नोटिफिकेसनहरू खोल्न सिरानको बायाँतिरबाट तलतिर स्वाइप गर्नुहोस्"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="3842355853737057332">"द्रुत सेटिङ खोल्न सिरानको दायाँतिरबाट तलतिर स्वाइप गर्नुहोस्"</string>
</resources>
diff --git a/packages/SystemUI/res/values-night/colors.xml b/packages/SystemUI/res/values-night/colors.xml
index 3dd0199..8e09c56 100644
--- a/packages/SystemUI/res/values-night/colors.xml
+++ b/packages/SystemUI/res/values-night/colors.xml
@@ -120,7 +120,6 @@
<!-- Dark theme base colors for notification shade/scrim, the alpha component is adjusted
programmatically to match the spec -->
- <color name="shade_panel_base">@android:color/system_accent1_800</color>
<color name="notification_scrim_base">@android:color/system_accent1_800</color>
<!-- Dark theme fallback colors for notification shade/scrim -->
diff --git a/packages/SystemUI/res/values-nl-ldrtl/strings.xml b/packages/SystemUI/res/values-nl-ldrtl/strings.xml
index 623340d..c27756e 100644
--- a/packages/SystemUI/res/values-nl-ldrtl/strings.xml
+++ b/packages/SystemUI/res/values-nl-ldrtl/strings.xml
@@ -20,4 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="recents_quick_scrub_onboarding" msgid="2452671841151577157">"Sleep naar links om snel tussen apps te schakelen"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="6543179539438220549">"Swipe vanaf de rechterbovenhoek om meldingen te openen"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="5760503283520058116">"Swipe vanaf de linkerbovenhoek om Snelle instellingen te openen"</string>
</resources>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index 4663c60..ead8988 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -467,6 +467,8 @@
<string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"Als je de microfoonknop wilt gebruiken, zet je microfoontoegang aan via Instellingen."</string>
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Instellingen openen"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Ander apparaat"</string>
+ <string name="media_suggestion_disconnected_text" msgid="8777932417076437525">"Afspelen op <xliff:g id="DEVICE_NAME">%s</xliff:g>"</string>
+ <string name="media_suggestion_failure_text" msgid="8453022394887613774">"Kan geen verbinding maken. Probeer het opnieuw."</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Overzicht aan- of uitzetten"</string>
<string name="zen_modes_dialog_title" msgid="8854640808100096934">"Modi"</string>
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Klaar"</string>
@@ -582,6 +584,7 @@
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
<skip />
<string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Hele scherm delen"</string>
+ <string name="screen_share_permission_dialog_option_text_entire_screen_for_display" msgid="1545850238689169166">"<xliff:g id="DISPLAY_NAME">%s</xliff:g> delen"</string>
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
<skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Als je het hele scherm deelt, is alles op je scherm zichtbaar voor <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Wees daarom voorzichtig met bijvoorbeeld wachtwoorden, betalingsgegevens, berichten, foto\'s, en audio en video."</string>
@@ -591,8 +594,9 @@
<string name="media_projection_entry_app_permission_dialog_single_app_not_supported" msgid="4860247304058870233">"Niet ondersteund door de app"</string>
<string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"App kiezen om te delen"</string>
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Je scherm casten?"</string>
- <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Eén app casten"</string>
+ <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Een enkele app casten"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Hele scherm casten"</string>
+ <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen_for_display" msgid="5524817761472941086">"Cast <xliff:g id="DISPLAY_NAME">%s</xliff:g>"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Als je het hele scherm cast, is alles op je scherm zichtbaar. Wees daarom voorzichtig met bijvoorbeeld wachtwoorden, betalingsgegevens, berichten, foto\'s, en audio en video."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Als je een app cast, is alles zichtbaar dat wordt getoond of afgespeeld in die app. Wees daarom voorzichtig met bijvoorbeeld wachtwoorden, betalingsgegevens, berichten, foto\'s, en audio en video."</string>
<string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Scherm casten"</string>
@@ -673,6 +677,7 @@
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Ontgrendeld gehouden door TrustAgent"</string>
<string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"Te veel pogingen. Apparaat vergrendeld."</string>
<string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"Apparaat vergrendeld\nVerificatie mislukt"</string>
+ <string name="keyguard_indication_after_watch_disconnected" msgid="6329761892437377710">"Vergrendeld door je smartwatch"</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="accessibility_volume_settings" msgid="1458961116951564784">"Geluidsinstellingen"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Automatisch ondertitelen"</string>
@@ -716,7 +721,7 @@
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Trillen"</string>
<string name="volume_ringer_status_silent" msgid="3691324657849880883">"Geluid staat uit"</string>
<string name="media_device_cast" msgid="4786241789687569892">"Casten"</string>
- <string name="stream_notification_unavailable" msgid="4313854556205836435">"Niet beschikbaar, belgeluid staat uit"</string>
+ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Niet beschikbaar omdat het belgeluid uitstaat"</string>
<string name="stream_alarm_unavailable" msgid="4059817189292197839">"Niet beschikbaar omdat Niet storen aanstaat"</string>
<string name="stream_media_unavailable" msgid="6823020894438959853">"Niet beschikbaar omdat Niet storen aanstaat"</string>
<string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Niet beschikbaar omdat <xliff:g id="MODE">%s</xliff:g> aanstaat"</string>
@@ -799,6 +804,10 @@
<string name="enable_bluetooth_message" msgid="6740938333772779717">"Als je je toetsenbord wilt verbinden met je tablet, moet je eerst Bluetooth aanzetten."</string>
<string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Aanzetten"</string>
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Aan: op basis van gezicht"</string>
+ <!-- no translation found for notification_guts_bundle_title (1345506995443305361) -->
+ <skip />
+ <!-- no translation found for notification_guts_bundle_summary (3971530802237393600) -->
+ <skip />
<string name="inline_done_button" msgid="6043094985588909584">"Klaar"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Toepassen"</string>
<string name="inline_turn_off_notifications" msgid="2653064779176881329">"Uitzetten"</string>
@@ -984,6 +993,7 @@
<string name="right_icon" msgid="1103955040645237425">"Icoon rechts"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Houd een tegel ingedrukt en sleep om die toe te voegen"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Houd een tegel ingedrukt en sleep om die te verplaatsen"</string>
+ <string name="select_to_rearrange_tiles" msgid="6166153534235179148">"Selecteer tegels om de volgorde en het formaat te wijzigen"</string>
<string name="tap_to_position_tile" msgid="6282815817773342757">"Tik om de tegel te positioneren"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Sleep hier naartoe om te verwijderen"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Je hebt minimaal <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> tegels nodig"</string>
@@ -1003,10 +1013,12 @@
<string name="tuner_low_priority" msgid="8412666814123009820">"Iconen voor meldingen met lage prioriteit tonen"</string>
<string name="other" msgid="429768510980739978">"Overig"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"het formaat van de tegel schakelen"</string>
- <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"tegel verwijderen"</string>
+ <!-- no translation found for accessibility_qs_edit_remove_tile_action (4811778796933958616) -->
+ <skip />
<string name="accessibility_qs_edit_toggle_placement_mode" msgid="3870429389210569610">"plaatsingsmodus schakelen"</string>
<string name="accessibility_qs_edit_toggle_selection" msgid="2201248304072372239">"selectie schakelen"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"tegel toevoegen op de laatste positie"</string>
+ <!-- no translation found for accessibility_qs_edit_tile_add_action (7188303979684400620) -->
+ <skip />
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Tegel verplaatsen"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Tegel toevoegen op gewenste positie"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Verplaatsen naar <xliff:g id="POSITION">%1$d</xliff:g>"</string>
@@ -1093,7 +1105,8 @@
<string name="auto_saver_text" msgid="3214960308353838764">"Aanzetten als de batterij waarschijnlijk leeg raakt"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Nee, bedankt"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"In gebruik"</string>
- <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Apps gebruiken je <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps (4994455315457996796) -->
+ <skip />
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" en "</string>
<string name="ongoing_privacy_dialog_using_op" msgid="426635338010011796">"Wordt gebruikt door <xliff:g id="APPLICATION_NAME">%1$s</xliff:g>"</string>
@@ -1508,11 +1521,11 @@
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"Snelkoppeling toevoegen"</string>
<string name="shortcut_helper_delete_shortcut_button_label" msgid="3148773472696137052">"Snelkoppeling verwijderen"</string>
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navigeren met je toetsenbord"</string>
- <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Leer sneltoetsen die je kunt gebruiken"</string>
+ <string name="launch_keyboard_tutorial_notification_content" msgid="2131074100205028210">"Leren werken met sneltoetsen"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navigeren met je touchpad"</string>
<string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Leer touchpadgebaren die je kunt gebruiken"</string>
<string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Navigeren met je toetsenbord en touchpad"</string>
- <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Leer meer over onder andere touchpadgebaren en sneltoetsen"</string>
+ <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="8557098239919786046">"Leer meer over onder andere touchpadgebaren en sneltoetsen"</string>
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Terug"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Naar startscherm"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Recente apps bekijken"</string>
@@ -1582,4 +1595,7 @@
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Alle tegels voor Snelle instellingen worden teruggezet naar de oorspronkelijke instellingen van het apparaat"</string>
<string name="demote_explain_text" msgid="3942301497888762295">"<xliff:g id="APPLICATION">%1$s</xliff:g> toont geen live updates meer. Je kunt dit op elk moment wijzigen in Instellingen."</string>
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
+ <string name="underlay_close_button_content_description" msgid="6518481455548869894">"Sluiten"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="7666159428113013319">"Swipe vanaf de linkerbovenhoek om meldingen te openen"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="3842355853737057332">"Swipe vanaf de rechterbovenhoek om Snelle instellingen te openen"</string>
</resources>
diff --git a/packages/SystemUI/res/values-or-ldrtl/strings.xml b/packages/SystemUI/res/values-or-ldrtl/strings.xml
index 8043f59..eb3f7be 100644
--- a/packages/SystemUI/res/values-or-ldrtl/strings.xml
+++ b/packages/SystemUI/res/values-or-ldrtl/strings.xml
@@ -20,4 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="recents_quick_scrub_onboarding" msgid="2452671841151577157">"ଆପ୍ ମଧ୍ୟରେ ଶୀଘ୍ର ବଦଳ କରିବା ପାଇଁ ବାମକୁ ଡ୍ରାଗ୍ କରନ୍ତୁ"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="6543179539438220549">"ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକୁ ଖୋଲିବା ପାଇଁ ଉପର ଡାହାଣ ପଟରୁ ସ୍ୱାଇପ କରନ୍ତୁ"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="5760503283520058116">"କୁଇକ ସେଟିଂସ ଖୋଲିବାକୁ ଉପର ବାମ ପଟରୁ ସ୍ୱାଇପ କରନ୍ତୁ"</string>
</resources>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index 6025024..c83e467 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -177,7 +177,7 @@
<string name="accessibility_camera_button" msgid="2938898391716647247">"କେମେରା"</string>
<string name="accessibility_phone_button" msgid="4256353121703100427">"ଫୋନ"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"ଭଏସ୍ ସହାୟକ"</string>
- <string name="accessibility_wallet_button" msgid="1458258783460555507">"ୱାଲେଟ୍"</string>
+ <string name="accessibility_wallet_button" msgid="1458258783460555507">"ୱାଲେଟ"</string>
<string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"QR କୋଡ ସ୍କାନର"</string>
<string name="accessibility_unlock_button" msgid="3613812140816244310">"ଅନଲକ କରାଯାଇଛି"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"ଡିଭାଇସ୍ ଲକ୍ ହୋଇଯାଇଛି"</string>
@@ -252,8 +252,7 @@
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g> ସହ ସଂଯୁକ୍ତ"</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"<xliff:g id="CAST">%s</xliff:g> ସହିତ ସଂଯୁକ୍ତ।"</string>
<string name="accessibility_expand_group" msgid="521237935987978624">"ଗ୍ରୁପକୁ ବିସ୍ତାର କରନ୍ତୁ।"</string>
- <!-- no translation found for accessibility_collapse_group (1842704016768329517) -->
- <skip />
+ <string name="accessibility_collapse_group" msgid="1842704016768329517">"ଗୋଷ୍ଠୀକୁ ସଙ୍କୁଚିତ କରନ୍ତୁ।"</string>
<string name="accessibility_add_device_to_group" msgid="5446422960697860806">"ଗ୍ରୁପରେ ଡିଭାଇସ ଯୋଗ କରନ୍ତୁ।"</string>
<string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"ଗ୍ରୁପରୁ ଡିଭାଇସ କାଢ଼ି ଦିଅନ୍ତୁ।"</string>
<string name="accessibility_open_application" msgid="1749126077501259712">"ଆପ୍ଲିକେସନ ଖୋଲନ୍ତୁ।"</string>
@@ -358,7 +357,7 @@
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"ଉଜ୍ଜ୍ୱଳତା"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"କଲର ଇନଭର୍ସନ"</string>
<string name="quick_settings_color_correction_label" msgid="5636617913560474664">"ରଙ୍ଗ ସଂଶୋଧନ"</string>
- <string name="quick_settings_font_scaling_label" msgid="5289001009876936768">"ଫଣ୍ଟର ଆକାର"</string>
+ <string name="quick_settings_font_scaling_label" msgid="5289001009876936768">"ଫଣ୍ଟ ସାଇଜ"</string>
<string name="quick_settings_more_user_settings" msgid="7634653308485206306">"ୟୁଜରଙ୍କୁ ପରିଚାଳନା"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"ହୋଇଗଲା"</string>
<string name="quick_settings_close_user_panel" msgid="5599724542275896849">"ବନ୍ଦ କରନ୍ତୁ"</string>
@@ -468,6 +467,8 @@
<string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"ମାଇକ୍ରୋଫୋନ ବଟନ ବ୍ୟବହାର କରିବା ପାଇଁ ସେଟିଂସରେ ମାଇକ୍ରୋଫୋନ ଆକ୍ସେସକୁ ସକ୍ଷମ କରନ୍ତୁ।"</string>
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"ସେଟିଂସ ଖୋଲନ୍ତୁ"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"ଅନ୍ୟ ଡିଭାଇସ୍"</string>
+ <string name="media_suggestion_disconnected_text" msgid="8777932417076437525">"<xliff:g id="DEVICE_NAME">%s</xliff:g>ରେ ପ୍ଲେ କରନ୍ତୁ"</string>
+ <string name="media_suggestion_failure_text" msgid="8453022394887613774">"କନେକ୍ଟ ହୋଇପାରିବ ନାହିଁ। ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"ସଂକ୍ଷିପ୍ତ ବିବରଣୀକୁ ଟୋଗଲ୍ କରନ୍ତୁ"</string>
<string name="zen_modes_dialog_title" msgid="8854640808100096934">"ମୋଡ"</string>
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"ହୋଇଗଲା"</string>
@@ -583,6 +584,7 @@
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
<skip />
<string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"ସମ୍ପୂର୍ଣ୍ଣ ସ୍କ୍ରିନ ସେୟାର କରନ୍ତୁ"</string>
+ <string name="screen_share_permission_dialog_option_text_entire_screen_for_display" msgid="1545850238689169166">"<xliff:g id="DISPLAY_NAME">%s</xliff:g> ସେୟାର କରନ୍ତୁ"</string>
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
<skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"ଆପଣ ଆପଣଙ୍କ ସମ୍ପୂର୍ଣ୍ଣ ସ୍କ୍ରିନକୁ ସେୟାର କରିବା ସମୟରେ ଆପଣଙ୍କ ସ୍କ୍ରିନରେ ସବୁକିଛି <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>କୁ ଦେଖାଯାଏ। ତେଣୁ ପାସୱାର୍ଡ, ପେମେଣ୍ଟ ବିବରଣୀ, ମେସେଜ, ଫଟୋ ଏବଂ ଅଡିଓ ଓ ଭିଡିଓ ପରି ବିଷୟଗୁଡ଼ିକ ପ୍ରତି ସତର୍କ ରୁହନ୍ତୁ।"</string>
@@ -594,6 +596,7 @@
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"ଆପଣଙ୍କ ସ୍କ୍ରିନକୁ କାଷ୍ଟ କରିବେ?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"ଗୋଟିଏ ଆପକୁ କାଷ୍ଟ କରନ୍ତୁ"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"ସମ୍ପୂର୍ଣ୍ଣ ସ୍କ୍ରିନକୁ କାଷ୍ଟ କରନ୍ତୁ"</string>
+ <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen_for_display" msgid="5524817761472941086">"<xliff:g id="DISPLAY_NAME">%s</xliff:g>କୁ କାଷ୍ଟ କରନ୍ତୁ"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"ଆପଣ ଆପଣଙ୍କ ସମ୍ପୂର୍ଣ୍ଣ ସ୍କ୍ରିନକୁ କାଷ୍ଟ କରିବା ସମୟରେ ଆପଣଙ୍କ ସ୍କ୍ରିନରେ ସବୁକିଛି ଦେଖାଯାଏ। ତେଣୁ ପାସୱାର୍ଡ, ପେମେଣ୍ଟ ବିବରଣୀ, ମେସେଜ, ଫଟୋ ଏବଂ ଅଡିଓ ଓ ଭିଡିଓ ପରି ବିଷୟଗୁଡ଼ିକ ପ୍ରତି ସତର୍କ ରୁହନ୍ତୁ।"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"ଆପଣ ଏକ ଆପ କାଷ୍ଟ କରିବା ସମୟରେ, ସେହି ଆପରେ ଦେଖାଯାଉଥିବା କିମ୍ବା ପ୍ଲେ ହେଉଥିବା ସବୁକିଛି ଦେଖାଯାଏ। ତେଣୁ ପାସୱାର୍ଡ, ପେମେଣ୍ଟ ବିବରଣୀ, ମେସେଜ, ଫଟୋ ଏବଂ ଅଡିଓ ଓ ଭିଡିଓ ପରି ବିଷୟଗୁଡ଼ିକ ପ୍ରତି ସତର୍କ ରୁହନ୍ତୁ।"</string>
<string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"ସ୍କ୍ରିନ କାଷ୍ଟ କରନ୍ତୁ"</string>
@@ -674,6 +677,7 @@
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"TrustAgent ଦ୍ୱାରା ଅନ୍ଲକ୍ ରହିଛି"</string>
<string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"ଡିଭାଇସ ଲକ କରାଯାଇଛି, ଅନେକଗୁଡ଼ିଏ ପ୍ରମାଣୀକରଣ ପ୍ରଚେଷ୍ଟା କରାଯାଇଛି"</string>
<string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"ଡିଭାଇସ ଲକ ହୋଇଛି\nପ୍ରମାଣୀକରଣ ବିଫଳ ହୋଇଛି"</string>
+ <string name="keyguard_indication_after_watch_disconnected" msgid="6329761892437377710">"ଆପଣଙ୍କ ୱାଚ ଦ୍ୱାରା ଲକ କରାଯାଇଛି"</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="accessibility_volume_settings" msgid="1458961116951564784">"ସାଉଣ୍ଡ ସେଟିଂସ୍"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"ସ୍ବଚାଳିତ କ୍ୟାପ୍ସନ୍ ମିଡିଆ"</string>
@@ -800,6 +804,10 @@
<string name="enable_bluetooth_message" msgid="6740938333772779717">"ଆପଣଙ୍କ ଟାବଲେଟ୍ରେ କୀ’ବୋର୍ଡ ସଂଯୋଗ କରିବା ପାଇଁ ଆପଣଙ୍କୁ ପ୍ରଥମେ ବ୍ଲୁଟୂଥ୍ ଅନ୍ କରିବାକୁ ହେବ।"</string>
<string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"ଚାଲୁ କରନ୍ତୁ"</string>
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"ଚାଲୁ ଅଛି - ଫେସ-ଆଧାରିତ"</string>
+ <!-- no translation found for notification_guts_bundle_title (1345506995443305361) -->
+ <skip />
+ <!-- no translation found for notification_guts_bundle_summary (3971530802237393600) -->
+ <skip />
<string name="inline_done_button" msgid="6043094985588909584">"ହୋଇଗଲା"</string>
<string name="inline_ok_button" msgid="603075490581280343">"ଲାଗୁ କରନ୍ତୁ"</string>
<string name="inline_turn_off_notifications" msgid="2653064779176881329">"ବନ୍ଦ କରନ୍ତୁ"</string>
@@ -853,7 +861,7 @@
<string name="snoozed_for_time" msgid="7586689374860469469">"<xliff:g id="TIME_AMOUNT">%1$s</xliff:g> ପାଇଁ ସ୍ନୁଜ୍ କରାଗଲା"</string>
<string name="snoozeHourOptions" msgid="2332819756222425558">"{count,plural, =1{# ଘଣ୍ଟା}=2{# ଘଣ୍ଟା}other{# ଘଣ୍ଟା}}"</string>
<string name="snoozeMinuteOptions" msgid="2222082405822030979">"{count,plural, =1{# ମିନିଟ}other{# ମିନିଟ}}"</string>
- <string name="battery_detail_switch_title" msgid="6940976502957380405">"ବ୍ୟାଟେରୀ ସେଭର୍"</string>
+ <string name="battery_detail_switch_title" msgid="6940976502957380405">"ବେଟେରୀ ସେଭର"</string>
<string name="keyboard_key_button_template" msgid="8005673627272051429">"ବଟନ୍ <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_key_home" msgid="3734400625170020657">"ହୋମ"</string>
<string name="keyboard_key_back" msgid="4185420465469481999">"ଫେରନ୍ତୁ"</string>
@@ -985,6 +993,7 @@
<string name="right_icon" msgid="1103955040645237425">"ଡାହାଣ ଆଇକନ୍"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"ଟାଇଲ୍ ଯୋଗ କରିବା ପାଇଁ ଦାବିଧରି ଡ୍ରାଗ କରନ୍ତୁ"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"ଟାଇଲ ପୁଣି ସଜାଇବାକୁ ଦାବିଧରି ଟାଣନ୍ତୁ"</string>
+ <string name="select_to_rearrange_tiles" msgid="6166153534235179148">"ପୁଣି ସଜାଇବା ଏବଂ ରିସାଇଜ କରିବା ପାଇଁ ଟାଇଲଗୁଡ଼ିକୁ ଚୟନ କରନ୍ତୁ"</string>
<string name="tap_to_position_tile" msgid="6282815817773342757">"ଟାଇଲକୁ ପୋଜିସନ କରିବା ପାଇଁ ଟାପ କରନ୍ତୁ"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"ବାହାର କରିବାକୁ ଏଠାକୁ ଡ୍ରାଗ୍ କରନ୍ତୁ"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"ଆପଣଙ୍କର ଅତିକମ୍ରେ <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g>ଟି ଟାଇଲ୍ ଆବଶ୍ୟକ"</string>
@@ -1004,10 +1013,10 @@
<string name="tuner_low_priority" msgid="8412666814123009820">"କମ୍-ଅଗ୍ରାଧିକାର ବିଜ୍ଞପ୍ତି ଆଇକନ୍ ଦେଖାନ୍ତୁ"</string>
<string name="other" msgid="429768510980739978">"ଅନ୍ୟ"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"ଟାଇଲର ସାଇଜକୁ ଟୋଗଲ କରନ୍ତୁ"</string>
- <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"ଟାଇଲ୍ କାଢ଼ି ଦିଅନ୍ତୁ"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="4811778796933958616">"ଟାଇଲ କାଢ଼ି ଦିଅନ୍ତୁ"</string>
<string name="accessibility_qs_edit_toggle_placement_mode" msgid="3870429389210569610">"ପ୍ଲେସମେଣ୍ଟ ମୋଡ ଟୋଗଲ କରନ୍ତୁ"</string>
<string name="accessibility_qs_edit_toggle_selection" msgid="2201248304072372239">"ଚୟନ ଟୋଗଲ କରନ୍ତୁ"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"ଶେଷ ପୋଜିସନରେ ଟାଇଲ ଯୋଗ କରିବା"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="7188303979684400620">"ଶେଷ ପୋଜିସନରେ ଟାଇଲ ଯୋଗ କରନ୍ତୁ"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"ଟାଇଲ୍ ମୁଭ୍ କରନ୍ତୁ"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"ଇଚ୍ଛିତ ପୋଜିସନରେ ଟାଇଲ ଯୋଗ କରନ୍ତୁ"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g>କୁ ମୁଭ୍ କରନ୍ତୁ"</string>
@@ -1094,7 +1103,8 @@
<string name="auto_saver_text" msgid="3214960308353838764">"ବ୍ୟାଟେରୀ ସରିବାକୁ ଥିବା ସମୟରେ ଚାଲୁ କରନ୍ତୁ"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"ନାହିଁ, ଥାଉ"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"ବ୍ୟବହାର ହେଉଛି"</string>
- <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"ଆପ୍ଲିକେସନ୍ଗୁଡିକ ଆପଣଙ୍କ <xliff:g id="TYPES_LIST">%s</xliff:g> ବ୍ୟବହାର କରୁଛନ୍ତି।"</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps (4994455315457996796) -->
+ <skip />
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" ଏବଂ "</string>
<string name="ongoing_privacy_dialog_using_op" msgid="426635338010011796">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> ଦ୍ୱାରା ବ୍ୟବହାର କରାଯାଉଛି"</string>
@@ -1251,15 +1261,13 @@
<string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(ବିଚ୍ଛିନ୍ନ କରାଯାଇଛି)"</string>
<string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"ସ୍ୱିଚ କରାଯାଇପାରିବ ନାହିଁ। ପୁଣି ଚେଷ୍ଟା କରିବାକୁ ଟାପ କରନ୍ତୁ।"</string>
<string name="media_output_dialog_pairing_new" msgid="5098212763195577270">"ଗୋଟିଏ ଡିଭାଇସ କନେକ୍ଟ କରନ୍ତୁ"</string>
- <!-- no translation found for media_output_dialog_button_connect_device (2819038239946643550) -->
- <skip />
+ <string name="media_output_dialog_button_connect_device" msgid="2819038239946643550">"ଡିଭାଇସ କନେକ୍ଟ କରନ୍ତୁ"</string>
<string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"ଅଜଣା ଆପ"</string>
<string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"କାଷ୍ଟ କରିବା ବନ୍ଦ କରନ୍ତୁ"</string>
<string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"ଅଡିଓ ଆଉଟପୁଟ ପାଇଁ ଉପଲବ୍ଧ ଡିଭାଇସଗୁଡ଼ିକ।"</string>
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"ଭଲ୍ୟୁମ"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
- <!-- no translation found for media_output_group_title_connected_speakers (3413876972919684479) -->
- <skip />
+ <string name="media_output_group_title_connected_speakers" msgid="3413876972919684479">"କନେକ୍ଟେଡ ସ୍ପିକର"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"ସ୍ପିକର ଏବଂ ଡିସପ୍ଲେ"</string>
<string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"ପ୍ରସ୍ତାବିତ ଡିଭାଇସଗୁଡ଼ିକ"</string>
<string name="media_input_group_title" msgid="2057057473860783021">"ଇନପୁଟ"</string>
@@ -1328,10 +1336,8 @@
<string name="accessibility_enter_hint" msgid="2617864063504824834">"ଡିଭାଇସ୍ ବିଷୟରେ ସୂଚନା ଲେଖନ୍ତୁ"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"ଖୋଲିବାକୁ ଟିପଚିହ୍ନ ବ୍ୟବହାର କରନ୍ତୁ"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"ପ୍ରମାଣୀକରଣ ଆବଶ୍ୟକ। ପ୍ରମାଣୀକରଣ କରିବାକୁ ଟିପଚିହ୍ନ ସେନ୍ସରକୁ ସ୍ପର୍ଶ କରନ୍ତୁ।"</string>
- <!-- no translation found for resume_animation (9148788411429330671) -->
- <skip />
- <!-- no translation found for pause_animation (1919705510058107504) -->
- <skip />
+ <string name="resume_animation" msgid="9148788411429330671">"ଆନିମେସନକୁ ରିଜ୍ୟୁମ କରନ୍ତୁ"</string>
+ <string name="pause_animation" msgid="1919705510058107504">"ଆନିମେସନକୁ ବିରତ କରନ୍ତୁ"</string>
<string name="ongoing_call_content_description" msgid="6394763878322348560">"ଚାଲିଥିବା କଲ"</string>
<string name="ongoing_notification_extra_content_description" msgid="2098752668861351265">"ଚାଲୁଥିବା"</string>
<string name="mobile_data_settings_title" msgid="3955246641380064901">"ମୋବାଇଲ ଡାଟା"</string>
@@ -1513,11 +1519,13 @@
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"ସର୍ଟକଟ ଯୋଗ କରନ୍ତୁ"</string>
<string name="shortcut_helper_delete_shortcut_button_label" msgid="3148773472696137052">"ସର୍ଟକଟକୁ ଡିଲିଟ କରନ୍ତୁ"</string>
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"ଆପଣଙ୍କ କୀବୋର୍ଡ ବ୍ୟବହାର କରି ନାଭିଗେଟ କରନ୍ତୁ"</string>
- <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"କୀବୋର୍ଡ ସର୍ଟକଟଗୁଡ଼ିକ ବିଷୟରେ ଜାଣନ୍ତୁ"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2131074100205028210) -->
+ <skip />
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"ଆପଣଙ୍କ ଟଚପେଡ ବ୍ୟବହାର କରି ନାଭିଗେଟ କରନ୍ତୁ"</string>
<string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"ଟଚପେଡର ଜେଶ୍ଚରଗୁଡ଼ିକ ବିଷୟରେ ଜାଣନ୍ତୁ"</string>
<string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"ଆପଣଙ୍କ କୀବୋର୍ଡ ଏବଂ ଟଚପେଡ ବ୍ୟବହାର କରି ନାଭିଗେଟ କରନ୍ତୁ"</string>
- <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"ଟଚପେଡ ଜେଶ୍ଚର, କୀବୋର୍ଡ ସର୍ଟକଟ ଏବଂ ଆହୁରି ଅନେକ କିଛି ବିଷୟରେ ଜାଣନ୍ତୁ"</string>
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (8557098239919786046) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"ପଛକୁ ଫେରନ୍ତୁ"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"ହୋମକୁ ଯାଆନ୍ତୁ"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"ବର୍ତ୍ତମାନର ଆପ୍ସ ଭ୍ୟୁ କରନ୍ତୁ"</string>
@@ -1533,7 +1541,7 @@
<string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"ହୋମକୁ ଯାଆନ୍ତୁ"</string>
<string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"ଆପଣଙ୍କ ଟଚପେଡରେ ତିନୋଟି ଆଙ୍ଗୁଠିରେ ଉପରକୁ ସ୍ୱାଇପ କରନ୍ତୁ"</string>
<string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"ବଢ଼ିଆ କାମ!"</string>
- <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"ଆପଣ \'ହୋମକୁ ଯାଆନ୍ତୁ\' ଜେଶ୍ଚର ସମ୍ପୂର୍ଣ୍ଣ କରିଛନ୍ତି"</string>
+ <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"ଆପଣ ହୋମକୁ ଯାଆନ୍ତୁ ଜେଶ୍ଚର ସମ୍ପୂର୍ଣ୍ଣ କରିଛନ୍ତି"</string>
<string name="touchpad_home_gesture_error_body" msgid="3810674109999513073">"ଆପଣଙ୍କର ହୋମ ସ୍କ୍ରିନକୁ ଯିବା ପାଇଁ ଆପଣଙ୍କ ଟଚପେଡରେ ତିନୋଟି ଆଙ୍ଗୁଠିରେ ଉପରକୁ ସ୍ୱାଇପ କରନ୍ତୁ"</string>
<string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"ବର୍ତ୍ତମାନର ଆପ୍ସକୁ ଭ୍ୟୁ କରନ୍ତୁ"</string>
<string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"ଆପଣଙ୍କ ଟଚପେଡରେ ତିନୋଟି ଆଙ୍ଗୁଠିକୁ ବ୍ୟବହାର କରି ଉପରକୁ ସ୍ୱାଇପ କରି ଧରି ରଖନ୍ତୁ"</string>
@@ -1587,4 +1595,7 @@
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"ସମସ୍ତ କୁଇକ ସେଟିଂସ ଟାଇଲ ଡିଭାଇସର ମୂଳ ସେଟିଂସରେ ରିସେଟ ହୋଇଯିବ"</string>
<string name="demote_explain_text" msgid="3942301497888762295">"<xliff:g id="APPLICATION">%1$s</xliff:g> ଆଉ ଲାଇଭ ଅପଡେଟଗୁଡ଼ିକ ଦେଖାଇବ ନାହିଁ। ଆପଣ ଯେ କୌଣସି ସମୟରେ ସେଟିଂସରେ ଏହାକୁ ପରିବର୍ତ୍ତନ କରିପାରିବେ।"</string>
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
+ <string name="underlay_close_button_content_description" msgid="6518481455548869894">"ବନ୍ଦ କରନ୍ତୁ"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="7666159428113013319">"ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକୁ ଖୋଲିବା ପାଇଁ ଉପର ବାମ ପଟରୁ ସ୍ୱାଇପ କରନ୍ତୁ"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="3842355853737057332">"କୁଇକ ସେଟିଂସ ଖୋଲିବାକୁ ଉପର ଡାହାଣ ପଟରୁ ସ୍ୱାଇପ କରନ୍ତୁ"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pa-ldrtl/strings.xml b/packages/SystemUI/res/values-pa-ldrtl/strings.xml
index 1b971b7..4a4c0f6 100644
--- a/packages/SystemUI/res/values-pa-ldrtl/strings.xml
+++ b/packages/SystemUI/res/values-pa-ldrtl/strings.xml
@@ -20,4 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="recents_quick_scrub_onboarding" msgid="2452671841151577157">"ਐਪਾਂ ਵਿਚਾਲੇ ਤੇਜ਼ੀ ਨਾਲ ਅਦਲਾ-ਬਦਲੀ ਕਰਨ ਲਈ ਖੱਬੇ ਪਾਸੇ ਵੱਲ ਘਸੀਟੋ"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="6543179539438220549">"ਸੂਚਨਾਵਾਂ ਖੋਲ੍ਹਣ ਲਈ ਸਿਖਰਲੇ ਸੱਜੇ ਪਾਸੇ ਤੋਂ ਸਵਾਈਪ ਕਰੋ"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="5760503283520058116">"ਤਤਕਾਲ ਸੈਟਿੰਗਾਂ ਖੋਲ੍ਹਣ ਲਈ ਸਿਖਰਲੇ ਖੱਬੇ ਪਾਸੇ ਤੋਂ ਸਵਾਈਪ ਕਰੋ"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index c4a8f19..6837cc6 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -467,6 +467,8 @@
<string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"ਮਾਈਕ੍ਰੋਫ਼ੋਨ ਬਟਨ ਦੀ ਵਰਤੋਂ ਕਰਨ ਲਈ, ਸੈਟਿੰਗਾਂ ਵਿੱਚ ਮਾਈਕ੍ਰੋਫ਼ੋਨ ਪਹੁੰਚ ਚਾਲੂ ਕਰੋ।"</string>
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"ਸੈਟਿੰਗਾਂ ਖੋਲ੍ਹੋ"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"ਹੋਰ ਡੀਵਾਈਸ"</string>
+ <string name="media_suggestion_disconnected_text" msgid="8777932417076437525">"<xliff:g id="DEVICE_NAME">%s</xliff:g> \'ਤੇ ਚਲਾਓ"</string>
+ <string name="media_suggestion_failure_text" msgid="8453022394887613774">"ਕਨੈਕਟ ਨਹੀਂ ਹੋਇਆ। ਦੁਬਾਰਾ ਕਰੋ।"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"ਰੂਪ-ਰੇਖਾ ਨੂੰ ਟੌਗਲ ਕਰੋ"</string>
<string name="zen_modes_dialog_title" msgid="8854640808100096934">"ਮੋਡ"</string>
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"ਹੋ ਗਿਆ"</string>
@@ -582,6 +584,7 @@
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
<skip />
<string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"ਪੂਰੀ ਸਕ੍ਰੀਨ ਸਾਂਝੀ ਕਰੋ"</string>
+ <string name="screen_share_permission_dialog_option_text_entire_screen_for_display" msgid="1545850238689169166">"<xliff:g id="DISPLAY_NAME">%s</xliff:g> ਨੂੰ ਸਾਂਝਾ ਕਰੋ"</string>
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
<skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"ਪੂਰੀ ਸਕ੍ਰੀਨ ਨੂੰ ਸਾਂਝਾ ਕਰਨ ਦੌਰਾਨ, ਤੁਹਾਡੀ ਸਕ੍ਰੀਨ \'ਤੇ ਦਿਖ ਰਹੀ ਹਰੇਕ ਚੀਜ਼ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> \'ਤੇ ਵੀ ਦਿਖਣਯੋਗ ਹੁੰਦੀ ਹੈ। ਇਸ ਲਈ ਪਾਸਵਰਡਾਂ, ਭੁਗਤਾਨ ਵੇਰਵਿਆਂ, ਸੁਨੇਹਿਆਂ, ਫ਼ੋਟੋਆਂ ਅਤੇ ਆਡੀਓ ਅਤੇ ਵੀਡੀਓ ਵਰਗੀਆਂ ਚੀਜ਼ਾਂ ਵਾਸਤੇ ਸਾਵਧਾਨ ਰਹੋ।"</string>
@@ -593,6 +596,7 @@
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"ਕੀ ਸਕ੍ਰੀਨ ਨੂੰ ਕਾਸਟ ਕਰਨਾ ਹੈ?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"ਇੱਕ ਐਪ ਨੂੰ ਕਾਸਟ ਕਰੋ"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"ਪੂਰੀ ਸਕ੍ਰੀਨ ਨੂੰ ਕਾਸਟ ਕਰੋ"</string>
+ <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen_for_display" msgid="5524817761472941086">"<xliff:g id="DISPLAY_NAME">%s</xliff:g> ਨੂੰ ਕਾਸਟ ਕਰੋ"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"ਪੂਰੀ ਸਕ੍ਰੀਨ ਨੂੰ ਕਾਸਟ ਕਰਨ ਦੌਰਾਨ, ਤੁਹਾਡੀ ਸਕ੍ਰੀਨ \'ਤੇ ਦਿਖ ਰਹੀ ਹਰੇਕ ਚੀਜ਼ ਦੂਸਰੀ ਸਕ੍ਰੀਨ \'ਤੇ ਵੀ ਦਿਖਣਯੋਗ ਹੁੰਦੀ ਹੈ। ਇਸ ਲਈ ਪਾਸਵਰਡਾਂ, ਭੁਗਤਾਨ ਵੇਰਵਿਆਂ, ਸੁਨੇਹਿਆਂ, ਫ਼ੋਟੋਆਂ ਅਤੇ ਆਡੀਓ ਅਤੇ ਵੀਡੀਓ ਵਰਗੀਆਂ ਚੀਜ਼ਾਂ ਵਾਸਤੇ ਸਾਵਧਾਨ ਰਹੋ।"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"ਕਿਸੇ ਐਪ ਨੂੰ ਕਾਸਟ ਕਰਨ ਦੌਰਾਨ, ਉਸ ਐਪ \'ਤੇ ਦਿਖ ਰਹੀ ਜਾਂ ਚਲਾਈ ਗਈ ਹਰੇਕ ਚੀਜ਼ ਸਾਰੇ ਲੋਕਾਂ ਨੂੰ ਦਿਖਣਯੋਗ ਹੁੰਦੀ ਹੈ। ਇਸ ਲਈ ਪਾਸਵਰਡਾਂ, ਭੁਗਤਾਨ ਵੇਰਵਿਆਂ, ਸੁਨੇਹਿਆਂ, ਫ਼ੋਟੋਆਂ ਅਤੇ ਆਡੀਓ ਅਤੇ ਵੀਡੀਓ ਵਰਗੀਆਂ ਚੀਜ਼ਾਂ ਵਾਸਤੇ ਸਾਵਧਾਨ ਰਹੋ।"</string>
<string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"ਸਕ੍ਰੀਨ ਕਾਸਟ ਕਰੋ"</string>
@@ -673,6 +677,7 @@
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"ਟਰੱਸਟ-ਏਜੰਟ ਵੱਲੋਂ ਅਣਲਾਕ ਰੱਖਿਆ ਗਿਆ"</string>
<string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"ਬਹੁਤ ਵਾਰੀ ਪ੍ਰਮਾਣੀਕਰਨ ਦੀਆਂ ਕੋਸ਼ਿਸ਼ਾਂ ਕਰਨ ਕਰਕੇ ਡੀਵਾਈਸ ਲਾਕ ਹੋ ਗਿਆ ਸੀ"</string>
<string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"ਡੀਵਾਈਸ ਲਾਕ ਹੈ\nਪ੍ਰਮਾਣੀਕਰਨ ਅਸਫਲ ਰਿਹਾ"</string>
+ <string name="keyguard_indication_after_watch_disconnected" msgid="6329761892437377710">"ਤੁਹਾਡੀ ਘੜੀ ਨੇ ਲਾਕ ਕੀਤਾ"</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="accessibility_volume_settings" msgid="1458961116951564784">"ਧੁਨੀ ਸੈਟਿੰਗਾਂ"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"ਸਵੈਚਲਿਤ ਸੁਰਖੀ ਮੀਡੀਆ"</string>
@@ -799,6 +804,10 @@
<string name="enable_bluetooth_message" msgid="6740938333772779717">"ਆਪਣੇ ਟੈਬਲੈੱਟ ਨਾਲ ਆਪਣਾ ਕੀ-ਬੋਰਡ ਕਨੈਕਟ ਕਰਨ ਲਈ, ਤੁਹਾਨੂੰ ਪਹਿਲਾਂ ਬਲੂਟੁੱਥ ਚਾਲੂ ਕਰਨ ਦੀ ਲੋੜ ਹੈ।"</string>
<string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"ਚਾਲੂ ਕਰੋ"</string>
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"ਚਾਲੂ ਹੈ - ਚਿਹਰਾ-ਆਧਾਰਿਤ"</string>
+ <!-- no translation found for notification_guts_bundle_title (1345506995443305361) -->
+ <skip />
+ <!-- no translation found for notification_guts_bundle_summary (3971530802237393600) -->
+ <skip />
<string name="inline_done_button" msgid="6043094985588909584">"ਹੋ ਗਿਆ"</string>
<string name="inline_ok_button" msgid="603075490581280343">"ਲਾਗੂ ਕਰੋ"</string>
<string name="inline_turn_off_notifications" msgid="2653064779176881329">"ਬੰਦ ਕਰੋ"</string>
@@ -984,6 +993,7 @@
<string name="right_icon" msgid="1103955040645237425">"ਸੱਜਾ ਪ੍ਰਤੀਕ"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"ਟਾਇਲਾਂ ਸ਼ਾਮਲ ਕਰਨ ਲਈ ਫੜ੍ਹ ਕੇ ਘਸੀਟੋ"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"ਟਾਇਲਾਂ ਨੂੰ ਮੁੜ-ਵਿਵਸਥਿਤ ਕਰਨ ਲਈ ਫੜ੍ਹ ਕੇ ਘਸੀਟੋ"</string>
+ <string name="select_to_rearrange_tiles" msgid="6166153534235179148">"ਮੁੜ-ਵਿਵਸਥਿਤ ਕਰਨ ਅਤੇ ਆਕਾਰ ਬਦਲਣ ਲਈ ਟਾਇਲਾਂ ਨੂੰ ਚੁਣੋ"</string>
<string name="tap_to_position_tile" msgid="6282815817773342757">"ਟਾਇਲ ਨੂੰ ਸਥਿਤੀ \'ਤੇ ਰੱਖਣ ਲਈ ਟੈਪ ਕਰੋ"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"ਹਟਾਉਣ ਲਈ ਇੱਥੇ ਘਸੀਟੋ"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"ਤੁਹਾਨੂੰ ਘੱਟੋ-ਘੱਟ <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> ਟਾਇਲਾਂ ਦੀ ਲੋੜ ਪਵੇਗੀ"</string>
@@ -1003,10 +1013,12 @@
<string name="tuner_low_priority" msgid="8412666814123009820">"ਘੱਟ ਤਰਜੀਹ ਵਾਲੇ ਸੂਚਨਾ ਪ੍ਰਤੀਕਾਂ ਨੂੰ ਦਿਖਾਓ"</string>
<string name="other" msgid="429768510980739978">"ਹੋਰ"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"ਟਾਇਲ ਦੇ ਆਕਾਰ ਨੂੰ ਟੌਗਲ ਕਰੋ"</string>
- <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"ਟਾਇਲ ਹਟਾਓ"</string>
+ <!-- no translation found for accessibility_qs_edit_remove_tile_action (4811778796933958616) -->
+ <skip />
<string name="accessibility_qs_edit_toggle_placement_mode" msgid="3870429389210569610">"ਪਲੇਸਮੈਂਟ ਮੋਡ ਨੂੰ ਟੌਗਲ ਕਰੋ"</string>
<string name="accessibility_qs_edit_toggle_selection" msgid="2201248304072372239">"ਚੋਣ ਨੂੰ ਟੌਗਲ ਕਰੋ"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"ਪਿਛਲੀ ਸਥਿਤੀ \'ਤੇ ਟਾਇਲ ਸ਼ਾਮਲ ਕਰੋ"</string>
+ <!-- no translation found for accessibility_qs_edit_tile_add_action (7188303979684400620) -->
+ <skip />
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"ਟਾਇਲ ਨੂੰ ਲਿਜਾਓ"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"ਮਨਪਸੰਦ ਸਥਿਤੀ \'ਤੇ ਟਾਇਲ ਸ਼ਾਮਲ ਕਰੋ"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> \'ਤੇ ਲਿਜਾਓ"</string>
@@ -1093,7 +1105,8 @@
<string name="auto_saver_text" msgid="3214960308353838764">"ਬੈਟਰੀ ਖਤਮ ਹੋਣ ਦੀ ਸੰਭਾਵਨਾ \'ਤੇ ਚਾਲੂ ਹੁੰਦਾ ਹੈ"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"ਨਹੀਂ ਧੰਨਵਾਦ"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"ਵਰਤੋਂ ਵਿੱਚ"</string>
- <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"ਐਪਲੀਕੇਸ਼ਨਾਂ ਤੁਹਾਡੇ <xliff:g id="TYPES_LIST">%s</xliff:g> ਦੀ ਵਰਤੋਂ ਕਰ ਰਹੀਆਂ ਹਨ।"</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps (4994455315457996796) -->
+ <skip />
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" ਅਤੇ "</string>
<string name="ongoing_privacy_dialog_using_op" msgid="426635338010011796">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> ਵੱਲੋਂ ਵਰਤਿਆ ਜਾ ਰਿਹਾ ਹੈ"</string>
@@ -1508,11 +1521,13 @@
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"ਸ਼ਾਰਟਕੱਟ ਸ਼ਾਮਲ ਕਰੋ"</string>
<string name="shortcut_helper_delete_shortcut_button_label" msgid="3148773472696137052">"ਸ਼ਾਰਟਕੱਟ ਮਿਟਾਓ"</string>
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"ਆਪਣੇ ਕੀ-ਬੋਰਡ ਦੀ ਵਰਤੋਂ ਕਰ ਕੇ ਨੈਵੀਗੇਟ ਕਰੋ"</string>
- <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"ਕੀ-ਬੋਰਡ ਦੇ ਸ਼ਾਰਟਕੱਟਾਂ ਬਾਰੇ ਜਾਣੋ"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2131074100205028210) -->
+ <skip />
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"ਆਪਣੇ ਟੱਚਪੈਡ ਦੀ ਵਰਤੋਂ ਕਰ ਕੇ ਨੈਵੀਗੇਟ ਕਰੋ"</string>
<string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"ਟੱਚਪੈਡ ਇਸ਼ਾਰਿਆਂ ਬਾਰੇ ਜਾਣੋ"</string>
<string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"ਆਪਣੇ ਕੀ-ਬੋਰਡ ਅਤੇ ਟੱਚਪੈਡ ਦੀ ਵਰਤੋਂ ਕਰ ਕੇ ਨੈਵੀਗੇਟ ਕਰੋ"</string>
- <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"ਟੱਚਪੈਡ ਇਸ਼ਾਰੇ, ਕੀ-ਬੋਰਡ ਸ਼ਾਰਟਕੱਟ ਅਤੇ ਹੋਰ ਬਹੁਤ ਕੁਝ ਬਾਰੇ ਜਾਣੋ"</string>
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (8557098239919786046) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"ਵਾਪਸ ਜਾਓ"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"ਹੋਮ \'ਤੇ ਜਾਓ"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"ਹਾਲੀਆ ਐਪਾਂ ਦੇਖੋ"</string>
@@ -1582,4 +1597,7 @@
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"ਸਾਰੀਆਂ ਤਤਕਾਲ ਸੈਟਿੰਗਾਂ ਟਾਇਲਾਂ ਡੀਵਾਈਸ ਦੀਆਂ ਮੂਲ ਸੈਟਿੰਗਾਂ \'ਤੇ ਰੀਸੈੱਟ ਹੋ ਜਾਣਗੀਆਂ"</string>
<string name="demote_explain_text" msgid="3942301497888762295">"<xliff:g id="APPLICATION">%1$s</xliff:g> ਵੱਲੋਂ ਹੁਣ ਲਾਈਵ ਅੱਪਡੇਟ ਨਹੀਂ ਦਿਖਾਏ ਜਾਣਗੇ। ਤੁਸੀਂ ਸੈਟਿੰਗਾਂ ਵਿੱਚ ਜਾ ਕੇ ਕਿਸੇ ਵੇਲੇ ਵੀ ਇਸਨੂੰ ਬਦਲ ਸਕਦੇ ਹੋ।"</string>
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
+ <string name="underlay_close_button_content_description" msgid="6518481455548869894">"ਬੰਦ ਕਰੋ"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="7666159428113013319">"ਸੂਚਨਾਵਾਂ ਖੋਲ੍ਹਣ ਲਈ ਸਿਖਰਲੇ ਖੱਬੇ ਪਾਸੇ ਤੋਂ ਸਵਾਈਪ ਕਰੋ"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="3842355853737057332">"ਤਤਕਾਲ ਸੈਟਿੰਗਾਂ ਖੋਲ੍ਹਣ ਲਈ ਸਿਖਰਲੇ ਸੱਜੇ ਪਾਸੇ ਤੋਂ ਸਵਾਈਪ ਕਰੋ"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pl-ldrtl/strings.xml b/packages/SystemUI/res/values-pl-ldrtl/strings.xml
index 9f43826..9afa0a5 100644
--- a/packages/SystemUI/res/values-pl-ldrtl/strings.xml
+++ b/packages/SystemUI/res/values-pl-ldrtl/strings.xml
@@ -20,4 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="recents_quick_scrub_onboarding" msgid="2452671841151577157">"Przeciągnij w lewo, by szybko przełączyć aplikacje"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="6543179539438220549">"Przesuń palcem z prawego górnego rogu ekranu, aby otworzyć powiadomienia"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="5760503283520058116">"Przesuń palcem z lewego górnego rogu ekranu, aby otworzyć Szybkie ustawienia"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index 9dfcf3f..4a48deb 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -252,8 +252,7 @@
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Połączono z <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"Połączono z urządzeniem <xliff:g id="CAST">%s</xliff:g>."</string>
<string name="accessibility_expand_group" msgid="521237935987978624">"Rozwiń grupę."</string>
- <!-- no translation found for accessibility_collapse_group (1842704016768329517) -->
- <skip />
+ <string name="accessibility_collapse_group" msgid="1842704016768329517">"Zwiń grupę."</string>
<string name="accessibility_add_device_to_group" msgid="5446422960697860806">"Dodaj urządzenie do grupy."</string>
<string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"Usuń urządzenie z grupy."</string>
<string name="accessibility_open_application" msgid="1749126077501259712">"Otwórz aplikację."</string>
@@ -468,6 +467,8 @@
<string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"Aby używać przycisku mikrofonu, włącz dostęp do mikrofonu w Ustawieniach."</string>
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Otwórz Ustawienia"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Inne urządzenie"</string>
+ <string name="media_suggestion_disconnected_text" msgid="8777932417076437525">"Odtwarzaj na urządzeniu <xliff:g id="DEVICE_NAME">%s</xliff:g>"</string>
+ <string name="media_suggestion_failure_text" msgid="8453022394887613774">"Nie udało się połączyć. Spróbuj ponownie."</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Przełącz Przegląd"</string>
<string name="zen_modes_dialog_title" msgid="8854640808100096934">"Tryby"</string>
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Gotowe"</string>
@@ -583,6 +584,7 @@
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
<skip />
<string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Udostępnij cały ekran"</string>
+ <string name="screen_share_permission_dialog_option_text_entire_screen_for_display" msgid="1545850238689169166">"Udostępnij urządzeniu <xliff:g id="DISPLAY_NAME">%s</xliff:g>"</string>
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
<skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Kiedy udostępniasz treści z całego ekranu, aplikacja <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ma dostęp do całości obrazu z wyświetlacza. Dlatego zachowaj ostrożność w zakresie haseł, danych do płatności, wiadomości, zdjęć, audio i filmów."</string>
@@ -594,6 +596,7 @@
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Włączyć przesyłanie treści z ekranu?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Przesyłanie obrazu z 1 aplikacji"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Przesyłanie całego ekranu"</string>
+ <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen_for_display" msgid="5524817761472941086">"Przesyłaj na urządzenie <xliff:g id="DISPLAY_NAME">%s</xliff:g>"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Kiedy przesyłasz treści z całego ekranu, widoczny jest cały obraz z wyświetlacza. Dlatego zachowaj ostrożność w zakresie haseł, danych do płatności, wiadomości, zdjęć, audio i filmów."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Kiedy przesyłasz obraz z aplikacji, widoczne jest wszystko, co jest w niej wyświetlane lub odtwarzane. Uważaj więc na takie treści jak hasła, dane do płatności, wiadomości, zdjęcia, audio czy filmy."</string>
<string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Prześlij ekran"</string>
@@ -674,6 +677,7 @@
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Blokada anulowana przez agenta zaufania"</string>
<string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"Urządzenie zostało zablokowane, zbyt wiele prób uwierzytelnienia"</string>
<string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"Urządzenie zostało zablokowane\nNie udało się uwierzytelnić"</string>
+ <string name="keyguard_indication_after_watch_disconnected" msgid="6329761892437377710">"Urządzenie zostało zablokowane przez zegarek"</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="accessibility_volume_settings" msgid="1458961116951564784">"Ustawienia dźwięku"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Autom. napisy do multimediów"</string>
@@ -800,6 +804,10 @@
<string name="enable_bluetooth_message" msgid="6740938333772779717">"Aby połączyć klawiaturę z tabletem, musisz najpierw włączyć Bluetooth."</string>
<string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Włącz"</string>
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Włączono – na podstawie twarzy"</string>
+ <!-- no translation found for notification_guts_bundle_title (1345506995443305361) -->
+ <skip />
+ <!-- no translation found for notification_guts_bundle_summary (3971530802237393600) -->
+ <skip />
<string name="inline_done_button" msgid="6043094985588909584">"Gotowe"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Zastosuj"</string>
<string name="inline_turn_off_notifications" msgid="2653064779176881329">"Wyłącz"</string>
@@ -907,8 +915,7 @@
<string name="group_system_cycle_back" msgid="8194102916946802902">"Przełącz się między ostatnio używanymi aplikacjami (do tyłu)"</string>
<string name="group_system_access_all_apps_search" msgid="1553588630154197469">"Otwórz listę aplikacji"</string>
<string name="group_system_access_system_settings" msgid="8731721963449070017">"Otwórz ustawienia"</string>
- <!-- no translation found for group_system_access_google_assistant (3769929488906534254) -->
- <skip />
+ <string name="group_system_access_google_assistant" msgid="3769929488906534254">"Otwórz asystenta cyfrowego"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Zablokuj ekran"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Zanotuj"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Wielozadaniowość"</string>
@@ -986,6 +993,7 @@
<string name="right_icon" msgid="1103955040645237425">"Prawa ikona"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Aby dodać kafelki, przytrzymaj je i przeciągnij"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Aby przestawić kafelki, przytrzymaj je i przeciągnij"</string>
+ <string name="select_to_rearrange_tiles" msgid="6166153534235179148">"Wybierz kafelki, których kolejność i rozmiar chcesz zmienić"</string>
<string name="tap_to_position_tile" msgid="6282815817773342757">"Kliknij, aby zmienić położenie kafelka"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Przeciągnij tutaj, by usunąć"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Minimalna liczba kafelków to <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g>"</string>
@@ -1005,10 +1013,12 @@
<string name="tuner_low_priority" msgid="8412666814123009820">"Pokazuj ikony powiadomień o niskim priorytecie"</string>
<string name="other" msgid="429768510980739978">"Inne"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"przełącz rozmiar kafelka"</string>
- <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"usunąć kartę"</string>
+ <!-- no translation found for accessibility_qs_edit_remove_tile_action (4811778796933958616) -->
+ <skip />
<string name="accessibility_qs_edit_toggle_placement_mode" msgid="3870429389210569610">"przełącz tryb rozmieszczania"</string>
<string name="accessibility_qs_edit_toggle_selection" msgid="2201248304072372239">"zaznacz lub odznacz"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"dodaj kafelek do ostatniej pozycji"</string>
+ <!-- no translation found for accessibility_qs_edit_tile_add_action (7188303979684400620) -->
+ <skip />
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Przenieś kartę"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Dodaj kafelek do wybranej pozycji"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Przenieś do pozycji <xliff:g id="POSITION">%1$d</xliff:g>"</string>
@@ -1095,7 +1105,8 @@
<string name="auto_saver_text" msgid="3214960308353838764">"Oszczędzanie baterii włącza się, jeśli bateria jest prawie wyczerpana"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Nie, dziękuję"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"W użyciu"</string>
- <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Aplikacje używają: <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps (4994455315457996796) -->
+ <skip />
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" i "</string>
<string name="ongoing_privacy_dialog_using_op" msgid="426635338010011796">"Używane przez aplikację <xliff:g id="APPLICATION_NAME">%1$s</xliff:g>"</string>
@@ -1252,15 +1263,13 @@
<string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(odłączono)"</string>
<string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"Nie można przełączyć. Spróbuj ponownie."</string>
<string name="media_output_dialog_pairing_new" msgid="5098212763195577270">"Połącz urządzenie"</string>
- <!-- no translation found for media_output_dialog_button_connect_device (2819038239946643550) -->
- <skip />
+ <string name="media_output_dialog_button_connect_device" msgid="2819038239946643550">"Połącz urządzenie"</string>
<string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Nieznana aplikacja"</string>
<string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Zatrzymaj przesyłanie"</string>
<string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"Dostępne urządzenia do odtwarzania dźwięku."</string>
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Głośność"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
- <!-- no translation found for media_output_group_title_connected_speakers (3413876972919684479) -->
- <skip />
+ <string name="media_output_group_title_connected_speakers" msgid="3413876972919684479">"Połączone głośniki"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Głośniki i wyświetlacze"</string>
<string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Proponowane urządzenia"</string>
<string name="media_input_group_title" msgid="2057057473860783021">"Urządzenie wejściowe"</string>
@@ -1329,10 +1338,8 @@
<string name="accessibility_enter_hint" msgid="2617864063504824834">"otwórz urządzenie"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"By otworzyć, użyj odcisku palca"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Wymagane uwierzytelnienie. Dotknij czytnika liniii papilarnych, by uwierzytelnić."</string>
- <!-- no translation found for resume_animation (9148788411429330671) -->
- <skip />
- <!-- no translation found for pause_animation (1919705510058107504) -->
- <skip />
+ <string name="resume_animation" msgid="9148788411429330671">"Wznów animację"</string>
+ <string name="pause_animation" msgid="1919705510058107504">"Wstrzymaj animację"</string>
<string name="ongoing_call_content_description" msgid="6394763878322348560">"Trwa rozmowa"</string>
<string name="ongoing_notification_extra_content_description" msgid="2098752668861351265">"W toku"</string>
<string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobilna transmisja danych"</string>
@@ -1514,11 +1521,13 @@
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"Dodaj skrót"</string>
<string name="shortcut_helper_delete_shortcut_button_label" msgid="3148773472696137052">"Usuń skrót"</string>
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Nawiguj za pomocą klawiatury"</string>
- <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Dowiedz się więcej o skrótach klawiszowych"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2131074100205028210) -->
+ <skip />
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Nawiguj za pomocą touchpada"</string>
<string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Poznaj gesty na touchpada"</string>
<string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Nawiguj za pomocą klawiatury i touchpada"</string>
- <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Poznaj gesty na touchpada, skróty klawiszowe i inne funkcje"</string>
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (8557098239919786046) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Wróć"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Otwórz stronę główną"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Wyświetlanie ostatnich aplikacji"</string>
@@ -1588,4 +1597,7 @@
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Wszystkie kafelki Szybkich ustawień zostaną zresetowane do oryginalnych ustawień urządzenia"</string>
<string name="demote_explain_text" msgid="3942301497888762295">"<xliff:g id="APPLICATION">%1$s</xliff:g> nie będzie już wyświetlać aktualizacji na żywo. Możesz to zmienić w dowolnej chwili w Ustawieniach."</string>
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
+ <string name="underlay_close_button_content_description" msgid="6518481455548869894">"Zamknij"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="7666159428113013319">"Przesuń palcem z lewego górnego rogu ekranu, aby otworzyć powiadomienia"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="3842355853737057332">"Przesuń palcem z prawego górnego rogu ekranu, aby otworzyć Szybkie ustawienia"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pt-ldrtl/strings.xml b/packages/SystemUI/res/values-pt-ldrtl/strings.xml
index 53b2473..b0c11e1 100644
--- a/packages/SystemUI/res/values-pt-ldrtl/strings.xml
+++ b/packages/SystemUI/res/values-pt-ldrtl/strings.xml
@@ -20,4 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="recents_quick_scrub_onboarding" msgid="2452671841151577157">"Arraste para a esquerda para mudar rapidamente de app"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="6543179539438220549">"Deslize do canto superior direito para baixo para abrir as notificações"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="5760503283520058116">"Deslize de cima para baixo no canto esquerdo para abrir as Configurações Rápidas"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pt-rBR-ldrtl/strings.xml b/packages/SystemUI/res/values-pt-rBR-ldrtl/strings.xml
index 53b2473..b0c11e1 100644
--- a/packages/SystemUI/res/values-pt-rBR-ldrtl/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR-ldrtl/strings.xml
@@ -20,4 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="recents_quick_scrub_onboarding" msgid="2452671841151577157">"Arraste para a esquerda para mudar rapidamente de app"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="6543179539438220549">"Deslize do canto superior direito para baixo para abrir as notificações"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="5760503283520058116">"Deslize de cima para baixo no canto esquerdo para abrir as Configurações Rápidas"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index 08d5acf..c1a9770 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -252,8 +252,7 @@
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Conectado a <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"Conectado a <xliff:g id="CAST">%s</xliff:g>."</string>
<string name="accessibility_expand_group" msgid="521237935987978624">"Expandir grupo."</string>
- <!-- no translation found for accessibility_collapse_group (1842704016768329517) -->
- <skip />
+ <string name="accessibility_collapse_group" msgid="1842704016768329517">"Fechar grupo."</string>
<string name="accessibility_add_device_to_group" msgid="5446422960697860806">"Adicionar dispositivo ao grupo."</string>
<string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"Remover dispositivo do grupo."</string>
<string name="accessibility_open_application" msgid="1749126077501259712">"Abrir aplicativo."</string>
@@ -399,7 +398,7 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Gravação de tela"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Iniciar"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Parar"</string>
- <string name="qs_record_issue_label" msgid="8166290137285529059">"Problema na gravação"</string>
+ <string name="qs_record_issue_label" msgid="8166290137285529059">"Gravar problema"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"Iniciar"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"Parar"</string>
<string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Relatório do bug"</string>
@@ -468,6 +467,8 @@
<string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"Ative o acesso ao microfone nas Configurações para usar o botão dele."</string>
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Abrir as Configurações"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Outro dispositivo"</string>
+ <string name="media_suggestion_disconnected_text" msgid="8777932417076437525">"Tocar no <xliff:g id="DEVICE_NAME">%s</xliff:g>"</string>
+ <string name="media_suggestion_failure_text" msgid="8453022394887613774">"Falha ao conectar. Tente de novo."</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Alternar Visão geral"</string>
<string name="zen_modes_dialog_title" msgid="8854640808100096934">"Modos"</string>
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Concluído"</string>
@@ -583,6 +584,7 @@
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
<skip />
<string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Compartilhar a tela inteira"</string>
+ <string name="screen_share_permission_dialog_option_text_entire_screen_for_display" msgid="1545850238689169166">"Compartilhar <xliff:g id="DISPLAY_NAME">%s</xliff:g>"</string>
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
<skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Quando você compartilha a tela inteira, tudo nela fica visível para o app <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Tenha cuidado com senhas, detalhes de pagamento, mensagens, fotos, áudios e vídeos."</string>
@@ -594,6 +596,7 @@
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Transmitir a tela?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Transmitir um app"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Transmitir tela inteira"</string>
+ <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen_for_display" msgid="5524817761472941086">"Transmitir <xliff:g id="DISPLAY_NAME">%s</xliff:g>"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Durante a transmissão, tudo que está na sua tela fica visível. Por isso, tenha cuidado com senhas, detalhes da forma de pagamento, mensagens, fotos, áudios e vídeos."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Quando você transmite um app, todas as informações visíveis ou abertas nele vão aparecer. Por isso, tenha cuidado com senhas, detalhes da forma de pagamento, mensagens, fotos, áudios e vídeos."</string>
<string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Transmitir tela"</string>
@@ -674,6 +677,7 @@
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Desbloqueado pelo TrustAgent"</string>
<string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"O dispositivo foi bloqueado devido a muitas tentativas de autenticação"</string>
<string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"Dispositivo bloqueado\nA autenticação falhou"</string>
+ <string name="keyguard_indication_after_watch_disconnected" msgid="6329761892437377710">"Bloqueado pelo relógio"</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="accessibility_volume_settings" msgid="1458961116951564784">"Configurações de som"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Transcrição automática"</string>
@@ -800,6 +804,10 @@
<string name="enable_bluetooth_message" msgid="6740938333772779717">"Para conectar o teclado ao tablet, é preciso primeiro ativar o Bluetooth."</string>
<string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Ativar"</string>
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Ativada (reconhecimento facial)"</string>
+ <!-- no translation found for notification_guts_bundle_title (1345506995443305361) -->
+ <skip />
+ <!-- no translation found for notification_guts_bundle_summary (3971530802237393600) -->
+ <skip />
<string name="inline_done_button" msgid="6043094985588909584">"Concluído"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Aplicar"</string>
<string name="inline_turn_off_notifications" msgid="2653064779176881329">"Desativar"</string>
@@ -985,6 +993,7 @@
<string name="right_icon" msgid="1103955040645237425">"Ícone à direita"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Mantenha pressionado e arraste para adicionar blocos"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Para reorganizar, toque no bloco sem soltar e arraste"</string>
+ <string name="select_to_rearrange_tiles" msgid="6166153534235179148">"Selecione blocos para reorganizar e redimensionar"</string>
<string name="tap_to_position_tile" msgid="6282815817773342757">"Toque para posicionar o bloco"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Arraste aqui para remover"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"É preciso haver pelo menos <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> blocos"</string>
@@ -1004,10 +1013,10 @@
<string name="tuner_low_priority" msgid="8412666814123009820">"Mostrar ícones de notificações de baixa prioridade"</string>
<string name="other" msgid="429768510980739978">"Outros"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"alternar o tamanho do bloco"</string>
- <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"remover o bloco"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="4811778796933958616">"Remover bloco"</string>
<string name="accessibility_qs_edit_toggle_placement_mode" msgid="3870429389210569610">"alternar o modo de posicionamento"</string>
<string name="accessibility_qs_edit_toggle_selection" msgid="2201248304072372239">"alternar seleção"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"adicionar o bloco à última posição"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="7188303979684400620">"Adicionar o bloco à última posição"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Mover bloco"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Adicionar o bloco à posição desejada"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Mover para <xliff:g id="POSITION">%1$d</xliff:g>"</string>
@@ -1094,7 +1103,8 @@
<string name="auto_saver_text" msgid="3214960308353838764">"Ativada quando há possibilidade de a bateria acabar"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Não"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"Em uso"</string>
- <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Aplicativos estão usando <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps (4994455315457996796) -->
+ <skip />
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" e "</string>
<string name="ongoing_privacy_dialog_using_op" msgid="426635338010011796">"Em uso por <xliff:g id="APPLICATION_NAME">%1$s</xliff:g>"</string>
@@ -1251,15 +1261,13 @@
<string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(sem conexão)"</string>
<string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"Não foi possível mudar. Toque para tentar novamente."</string>
<string name="media_output_dialog_pairing_new" msgid="5098212763195577270">"Conectar dispositivo"</string>
- <!-- no translation found for media_output_dialog_button_connect_device (2819038239946643550) -->
- <skip />
+ <string name="media_output_dialog_button_connect_device" msgid="2819038239946643550">"Conectar dispositivo"</string>
<string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"App desconhecido"</string>
<string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Parar transmissão"</string>
<string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"Dispositivos disponíveis para saída de áudio."</string>
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Volume"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
- <!-- no translation found for media_output_group_title_connected_speakers (3413876972919684479) -->
- <skip />
+ <string name="media_output_group_title_connected_speakers" msgid="3413876972919684479">"Alto-falantes conectados"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Alto-falantes e telas"</string>
<string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Opções de dispositivos"</string>
<string name="media_input_group_title" msgid="2057057473860783021">"Entrada"</string>
@@ -1328,10 +1336,8 @@
<string name="accessibility_enter_hint" msgid="2617864063504824834">"acessar o dispositivo"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Use a impressão digital para abrir"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Autenticação obrigatória. Toque no sensor de impressão digital para autenticar."</string>
- <!-- no translation found for resume_animation (9148788411429330671) -->
- <skip />
- <!-- no translation found for pause_animation (1919705510058107504) -->
- <skip />
+ <string name="resume_animation" msgid="9148788411429330671">"Retomar animação"</string>
+ <string name="pause_animation" msgid="1919705510058107504">"Pausar animação"</string>
<string name="ongoing_call_content_description" msgid="6394763878322348560">"Chamada em andamento"</string>
<string name="ongoing_notification_extra_content_description" msgid="2098752668861351265">"Em andamento"</string>
<string name="mobile_data_settings_title" msgid="3955246641380064901">"Dados móveis"</string>
@@ -1513,11 +1519,13 @@
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"Adicionar atalho"</string>
<string name="shortcut_helper_delete_shortcut_button_label" msgid="3148773472696137052">"Excluir atalho"</string>
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navegue usando o teclado"</string>
- <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Aprenda atalhos do teclado"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2131074100205028210) -->
+ <skip />
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navegue usando o touchpad"</string>
<string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Aprenda gestos do touchpad"</string>
<string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Navegue usando o teclado e o touchpad"</string>
- <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Aprenda gestos do touchpad, atalhos do teclado e muito mais"</string>
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (8557098239919786046) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Voltar"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Ir para a página inicial"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Ver os apps recentes"</string>
@@ -1587,4 +1595,7 @@
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Todos os blocos \"Configurações rápidas\" serão redefinidos para as configurações originais do dispositivo"</string>
<string name="demote_explain_text" msgid="3942301497888762295">"O app <xliff:g id="APPLICATION">%1$s</xliff:g> não vai mais mostrar atualizações em tempo real. É possível mudar isso a qualquer momento nas Configurações."</string>
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
+ <string name="underlay_close_button_content_description" msgid="6518481455548869894">"Fechar"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="7666159428113013319">"Deslize do canto superior esquerdo para baixo para abrir as notificações"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="3842355853737057332">"Deslize de cima para baixo no canto direito para abrir as Configurações Rápidas"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pt-rPT-ldrtl/strings.xml b/packages/SystemUI/res/values-pt-rPT-ldrtl/strings.xml
index 4af54b5..84249e2 100644
--- a/packages/SystemUI/res/values-pt-rPT-ldrtl/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT-ldrtl/strings.xml
@@ -20,4 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="recents_quick_scrub_onboarding" msgid="2452671841151577157">"Arrastar para a esquerda para mudar rapidamente de app"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="6543179539438220549">"Deslize rapidamente a partir da parte superior direita para abrir as notificações"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="5760503283520058116">"Deslize rapidamente a partir da parte superior esquerda para abrir as Definições rápidas"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index 837e07e..845cba5 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -412,7 +412,7 @@
<string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Definições de rastreio personalizadas"</string>
<string name="restore_default" msgid="5259420807486239755">"Restaurar predefinição"</string>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Modo para uma mão"</string>
- <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Disp. auditivos"</string>
+ <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Dispositivos auditivos"</string>
<string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Ativos"</string>
<string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Desligados"</string>
<string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Dispositivos auditivos"</string>
@@ -467,6 +467,8 @@
<string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"Para usar o botão do microfone, ative o acesso do microfone nas Definições."</string>
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Abrir definições"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Outro dispositivo"</string>
+ <string name="media_suggestion_disconnected_text" msgid="8777932417076437525">"Reproduzir em <xliff:g id="DEVICE_NAME">%s</xliff:g>"</string>
+ <string name="media_suggestion_failure_text" msgid="8453022394887613774">"Impossív. ligar. Tente de novo"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Ativar/desativar Vista geral"</string>
<string name="zen_modes_dialog_title" msgid="8854640808100096934">"Modos"</string>
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Concluir"</string>
@@ -582,6 +584,7 @@
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
<skip />
<string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Partilhar ecrã inteiro"</string>
+ <string name="screen_share_permission_dialog_option_text_entire_screen_for_display" msgid="1545850238689169166">"Partilhar <xliff:g id="DISPLAY_NAME">%s</xliff:g>"</string>
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
<skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Quando está a partilhar o ecrã inteiro, tudo o que estiver no ecrã é visível para a app <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Por isso, tenha cuidado com, por exemplo, palavras-passe, detalhes de pagamento, mensagens, fotos, áudio e vídeo."</string>
@@ -593,6 +596,7 @@
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Transmitir o ecrã?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Transmitir uma app"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Transmitir ecrã inteiro"</string>
+ <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen_for_display" msgid="5524817761472941086">"Transmitir <xliff:g id="DISPLAY_NAME">%s</xliff:g>"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Quando está a transmitir todo o ecrã, tudo o que estiver no seu ecrã é visível. Por isso, tenha cuidado com, por exemplo, palavras-passe, detalhes de pagamento, mensagens, fotos, áudio e vídeo."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Quando está a transmitir uma app, tudo o que é mostrado ou reproduzido nessa app fica visível. Por isso, tenha cuidado com, por exemplo, palavras-passe, detalhes de pagamento, mensagens, fotos, áudio e vídeo."</string>
<string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Transmitir ecrã"</string>
@@ -673,6 +677,7 @@
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Mantido desbloqueado pelo TrustAgent"</string>
<string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"O dispositivo foi bloqueado; demasiadas tentativas de autenticação"</string>
<string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"Dispositivo bloqueado\nA autenticação falhou"</string>
+ <string name="keyguard_indication_after_watch_disconnected" msgid="6329761892437377710">"Bloqueado pelo relógio"</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="accessibility_volume_settings" msgid="1458961116951564784">"Definições de som"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Multim. legendas automáticas"</string>
@@ -799,6 +804,10 @@
<string name="enable_bluetooth_message" msgid="6740938333772779717">"Para ligar o teclado ao tablet, tem de ativar primeiro o Bluetooth."</string>
<string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Ativar"</string>
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Ativada – Com base no rosto"</string>
+ <!-- no translation found for notification_guts_bundle_title (1345506995443305361) -->
+ <skip />
+ <!-- no translation found for notification_guts_bundle_summary (3971530802237393600) -->
+ <skip />
<string name="inline_done_button" msgid="6043094985588909584">"Concluído"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Aplicar"</string>
<string name="inline_turn_off_notifications" msgid="2653064779176881329">"Desativar"</string>
@@ -984,6 +993,7 @@
<string name="right_icon" msgid="1103955040645237425">"Ícone direito"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Toque sem soltar e arraste para reorganizar os mosaicos"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Toque sem soltar e arraste para reorganizar os mosaicos"</string>
+ <string name="select_to_rearrange_tiles" msgid="6166153534235179148">"Selecionar os mosaicos para reorganizar e redimensionar"</string>
<string name="tap_to_position_tile" msgid="6282815817773342757">"Tocar para posicionar o mosaico"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Arrastar para aqui para remover"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Necessita de, pelo menos, <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> cartões"</string>
@@ -1003,10 +1013,10 @@
<string name="tuner_low_priority" msgid="8412666814123009820">"Mostrar ícones de notificações de prioridade baixa"</string>
<string name="other" msgid="429768510980739978">"Outro"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"ativar/desativar o tamanho do mosaico"</string>
- <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"remover o cartão"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="4811778796933958616">"Remover mosaico"</string>
<string name="accessibility_qs_edit_toggle_placement_mode" msgid="3870429389210569610">"ativar/desativar modo de posicionamento"</string>
<string name="accessibility_qs_edit_toggle_selection" msgid="2201248304072372239">"ativar/desativar seleção"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"adicionar o mosaico à última posição"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="7188303979684400620">"Adicionar mosaico à última posição"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Mover cartão"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Adicionar mosaico à posição pretendida"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Mova para <xliff:g id="POSITION">%1$d</xliff:g>"</string>
@@ -1093,7 +1103,8 @@
<string name="auto_saver_text" msgid="3214960308353838764">"Ativar quando for provável que a bateria se esgote"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Não"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"Em utilização"</string>
- <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"As aplicações estão a utilizar o(a) <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps (4994455315457996796) -->
+ <skip />
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" e "</string>
<string name="ongoing_privacy_dialog_using_op" msgid="426635338010011796">"Em utilização pela app <xliff:g id="APPLICATION_NAME">%1$s</xliff:g>"</string>
@@ -1508,11 +1519,11 @@
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"Adicionar atalho"</string>
<string name="shortcut_helper_delete_shortcut_button_label" msgid="3148773472696137052">"Eliminar atalho"</string>
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navegue com o teclado"</string>
- <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Aprenda atalhos de teclado"</string>
+ <string name="launch_keyboard_tutorial_notification_content" msgid="2131074100205028210">"Aprenda atalhos de teclado"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navegue com o touchpad"</string>
<string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Aprenda gestos do touchpad"</string>
<string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Navegue com o teclado e o touchpad"</string>
- <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Aprenda gestos do touchpad, atalhos de teclado e muito mais"</string>
+ <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="8557098239919786046">"Aprenda gestos no touchpad, atalhos de teclado e muito mais"</string>
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Voltar"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Aceder ao ecrã principal"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Ver apps recentes"</string>
@@ -1582,4 +1593,7 @@
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Todos os mosaicos de Definições rápidas vão ser repostos para as definições originais do dispositivo"</string>
<string name="demote_explain_text" msgid="3942301497888762295">"A app <xliff:g id="APPLICATION">%1$s</xliff:g> vai deixar de mostrar atualizações em direto. Pode alterar esta definição em qualquer altura nas Definições."</string>
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
+ <string name="underlay_close_button_content_description" msgid="6518481455548869894">"Fechar"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="7666159428113013319">"Deslize rapidamente a partir da parte superior esquerda para abrir as notificações"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="3842355853737057332">"Deslize rapidamente a partir da parte superior direita para abrir as Definições rápidas"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pt-rPT/tiles_states_strings.xml b/packages/SystemUI/res/values-pt-rPT/tiles_states_strings.xml
index bedcab0..1c3690d 100644
--- a/packages/SystemUI/res/values-pt-rPT/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/tiles_states_strings.xml
@@ -84,7 +84,7 @@
<string-array name="tile_states_location">
<item msgid="3316542218706374405">"Indisponível"</item>
<item msgid="4813655083852587017">"Desligado"</item>
- <item msgid="6744077414775180687">"Ligado"</item>
+ <item msgid="6744077414775180687">"Ligada"</item>
</string-array>
<string-array name="tile_states_hotspot">
<item msgid="3145597331197351214">"Indisponível"</item>
@@ -124,7 +124,7 @@
<string-array name="tile_states_night">
<item msgid="7857498964264855466">"Indisponível"</item>
<item msgid="2744885441164350155">"Desligada"</item>
- <item msgid="151121227514952197">"Ligado"</item>
+ <item msgid="151121227514952197">"Ligada"</item>
</string-array>
<string-array name="tile_states_screenrecord">
<item msgid="1085836626613341403">"Indisponível"</item>
@@ -138,7 +138,7 @@
</string-array>
<string-array name="tile_states_reverse">
<item msgid="3574611556622963971">"Indisponível"</item>
- <item msgid="8707481475312432575">"Desligado"</item>
+ <item msgid="8707481475312432575">"Desligada"</item>
<item msgid="8031106212477483874">"Ligado"</item>
</string-array>
<string-array name="tile_states_reduce_brightness">
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index 08d5acf..c1a9770 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -252,8 +252,7 @@
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Conectado a <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"Conectado a <xliff:g id="CAST">%s</xliff:g>."</string>
<string name="accessibility_expand_group" msgid="521237935987978624">"Expandir grupo."</string>
- <!-- no translation found for accessibility_collapse_group (1842704016768329517) -->
- <skip />
+ <string name="accessibility_collapse_group" msgid="1842704016768329517">"Fechar grupo."</string>
<string name="accessibility_add_device_to_group" msgid="5446422960697860806">"Adicionar dispositivo ao grupo."</string>
<string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"Remover dispositivo do grupo."</string>
<string name="accessibility_open_application" msgid="1749126077501259712">"Abrir aplicativo."</string>
@@ -399,7 +398,7 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Gravação de tela"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Iniciar"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Parar"</string>
- <string name="qs_record_issue_label" msgid="8166290137285529059">"Problema na gravação"</string>
+ <string name="qs_record_issue_label" msgid="8166290137285529059">"Gravar problema"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"Iniciar"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"Parar"</string>
<string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Relatório do bug"</string>
@@ -468,6 +467,8 @@
<string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"Ative o acesso ao microfone nas Configurações para usar o botão dele."</string>
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Abrir as Configurações"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Outro dispositivo"</string>
+ <string name="media_suggestion_disconnected_text" msgid="8777932417076437525">"Tocar no <xliff:g id="DEVICE_NAME">%s</xliff:g>"</string>
+ <string name="media_suggestion_failure_text" msgid="8453022394887613774">"Falha ao conectar. Tente de novo."</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Alternar Visão geral"</string>
<string name="zen_modes_dialog_title" msgid="8854640808100096934">"Modos"</string>
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Concluído"</string>
@@ -583,6 +584,7 @@
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
<skip />
<string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Compartilhar a tela inteira"</string>
+ <string name="screen_share_permission_dialog_option_text_entire_screen_for_display" msgid="1545850238689169166">"Compartilhar <xliff:g id="DISPLAY_NAME">%s</xliff:g>"</string>
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
<skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Quando você compartilha a tela inteira, tudo nela fica visível para o app <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Tenha cuidado com senhas, detalhes de pagamento, mensagens, fotos, áudios e vídeos."</string>
@@ -594,6 +596,7 @@
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Transmitir a tela?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Transmitir um app"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Transmitir tela inteira"</string>
+ <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen_for_display" msgid="5524817761472941086">"Transmitir <xliff:g id="DISPLAY_NAME">%s</xliff:g>"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Durante a transmissão, tudo que está na sua tela fica visível. Por isso, tenha cuidado com senhas, detalhes da forma de pagamento, mensagens, fotos, áudios e vídeos."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Quando você transmite um app, todas as informações visíveis ou abertas nele vão aparecer. Por isso, tenha cuidado com senhas, detalhes da forma de pagamento, mensagens, fotos, áudios e vídeos."</string>
<string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Transmitir tela"</string>
@@ -674,6 +677,7 @@
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Desbloqueado pelo TrustAgent"</string>
<string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"O dispositivo foi bloqueado devido a muitas tentativas de autenticação"</string>
<string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"Dispositivo bloqueado\nA autenticação falhou"</string>
+ <string name="keyguard_indication_after_watch_disconnected" msgid="6329761892437377710">"Bloqueado pelo relógio"</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="accessibility_volume_settings" msgid="1458961116951564784">"Configurações de som"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Transcrição automática"</string>
@@ -800,6 +804,10 @@
<string name="enable_bluetooth_message" msgid="6740938333772779717">"Para conectar o teclado ao tablet, é preciso primeiro ativar o Bluetooth."</string>
<string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Ativar"</string>
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Ativada (reconhecimento facial)"</string>
+ <!-- no translation found for notification_guts_bundle_title (1345506995443305361) -->
+ <skip />
+ <!-- no translation found for notification_guts_bundle_summary (3971530802237393600) -->
+ <skip />
<string name="inline_done_button" msgid="6043094985588909584">"Concluído"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Aplicar"</string>
<string name="inline_turn_off_notifications" msgid="2653064779176881329">"Desativar"</string>
@@ -985,6 +993,7 @@
<string name="right_icon" msgid="1103955040645237425">"Ícone à direita"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Mantenha pressionado e arraste para adicionar blocos"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Para reorganizar, toque no bloco sem soltar e arraste"</string>
+ <string name="select_to_rearrange_tiles" msgid="6166153534235179148">"Selecione blocos para reorganizar e redimensionar"</string>
<string name="tap_to_position_tile" msgid="6282815817773342757">"Toque para posicionar o bloco"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Arraste aqui para remover"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"É preciso haver pelo menos <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> blocos"</string>
@@ -1004,10 +1013,10 @@
<string name="tuner_low_priority" msgid="8412666814123009820">"Mostrar ícones de notificações de baixa prioridade"</string>
<string name="other" msgid="429768510980739978">"Outros"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"alternar o tamanho do bloco"</string>
- <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"remover o bloco"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="4811778796933958616">"Remover bloco"</string>
<string name="accessibility_qs_edit_toggle_placement_mode" msgid="3870429389210569610">"alternar o modo de posicionamento"</string>
<string name="accessibility_qs_edit_toggle_selection" msgid="2201248304072372239">"alternar seleção"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"adicionar o bloco à última posição"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="7188303979684400620">"Adicionar o bloco à última posição"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Mover bloco"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Adicionar o bloco à posição desejada"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Mover para <xliff:g id="POSITION">%1$d</xliff:g>"</string>
@@ -1094,7 +1103,8 @@
<string name="auto_saver_text" msgid="3214960308353838764">"Ativada quando há possibilidade de a bateria acabar"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Não"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"Em uso"</string>
- <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Aplicativos estão usando <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps (4994455315457996796) -->
+ <skip />
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" e "</string>
<string name="ongoing_privacy_dialog_using_op" msgid="426635338010011796">"Em uso por <xliff:g id="APPLICATION_NAME">%1$s</xliff:g>"</string>
@@ -1251,15 +1261,13 @@
<string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(sem conexão)"</string>
<string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"Não foi possível mudar. Toque para tentar novamente."</string>
<string name="media_output_dialog_pairing_new" msgid="5098212763195577270">"Conectar dispositivo"</string>
- <!-- no translation found for media_output_dialog_button_connect_device (2819038239946643550) -->
- <skip />
+ <string name="media_output_dialog_button_connect_device" msgid="2819038239946643550">"Conectar dispositivo"</string>
<string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"App desconhecido"</string>
<string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Parar transmissão"</string>
<string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"Dispositivos disponíveis para saída de áudio."</string>
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Volume"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
- <!-- no translation found for media_output_group_title_connected_speakers (3413876972919684479) -->
- <skip />
+ <string name="media_output_group_title_connected_speakers" msgid="3413876972919684479">"Alto-falantes conectados"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Alto-falantes e telas"</string>
<string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Opções de dispositivos"</string>
<string name="media_input_group_title" msgid="2057057473860783021">"Entrada"</string>
@@ -1328,10 +1336,8 @@
<string name="accessibility_enter_hint" msgid="2617864063504824834">"acessar o dispositivo"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Use a impressão digital para abrir"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Autenticação obrigatória. Toque no sensor de impressão digital para autenticar."</string>
- <!-- no translation found for resume_animation (9148788411429330671) -->
- <skip />
- <!-- no translation found for pause_animation (1919705510058107504) -->
- <skip />
+ <string name="resume_animation" msgid="9148788411429330671">"Retomar animação"</string>
+ <string name="pause_animation" msgid="1919705510058107504">"Pausar animação"</string>
<string name="ongoing_call_content_description" msgid="6394763878322348560">"Chamada em andamento"</string>
<string name="ongoing_notification_extra_content_description" msgid="2098752668861351265">"Em andamento"</string>
<string name="mobile_data_settings_title" msgid="3955246641380064901">"Dados móveis"</string>
@@ -1513,11 +1519,13 @@
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"Adicionar atalho"</string>
<string name="shortcut_helper_delete_shortcut_button_label" msgid="3148773472696137052">"Excluir atalho"</string>
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navegue usando o teclado"</string>
- <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Aprenda atalhos do teclado"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2131074100205028210) -->
+ <skip />
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navegue usando o touchpad"</string>
<string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Aprenda gestos do touchpad"</string>
<string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Navegue usando o teclado e o touchpad"</string>
- <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Aprenda gestos do touchpad, atalhos do teclado e muito mais"</string>
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (8557098239919786046) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Voltar"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Ir para a página inicial"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Ver os apps recentes"</string>
@@ -1587,4 +1595,7 @@
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Todos os blocos \"Configurações rápidas\" serão redefinidos para as configurações originais do dispositivo"</string>
<string name="demote_explain_text" msgid="3942301497888762295">"O app <xliff:g id="APPLICATION">%1$s</xliff:g> não vai mais mostrar atualizações em tempo real. É possível mudar isso a qualquer momento nas Configurações."</string>
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
+ <string name="underlay_close_button_content_description" msgid="6518481455548869894">"Fechar"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="7666159428113013319">"Deslize do canto superior esquerdo para baixo para abrir as notificações"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="3842355853737057332">"Deslize de cima para baixo no canto direito para abrir as Configurações Rápidas"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ro-ldrtl/strings.xml b/packages/SystemUI/res/values-ro-ldrtl/strings.xml
index a7cd33c..fb03b51 100644
--- a/packages/SystemUI/res/values-ro-ldrtl/strings.xml
+++ b/packages/SystemUI/res/values-ro-ldrtl/strings.xml
@@ -20,4 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="recents_quick_scrub_onboarding" msgid="2452671841151577157">"Trage spre stânga pentru a comuta rapid între aplicații"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="6543179539438220549">"Glisează din dreapta sus pentru a deschide notificările"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="5760503283520058116">"Glisează din stânga sus pentru a deschide Setările rapide"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index a0819948..4a97c84 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -252,8 +252,7 @@
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Conectat la <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"S-a stabilit conexiunea la <xliff:g id="CAST">%s</xliff:g>."</string>
<string name="accessibility_expand_group" msgid="521237935987978624">"Extinde grupul."</string>
- <!-- no translation found for accessibility_collapse_group (1842704016768329517) -->
- <skip />
+ <string name="accessibility_collapse_group" msgid="1842704016768329517">"Restrânge grupul."</string>
<string name="accessibility_add_device_to_group" msgid="5446422960697860806">"Adaugă dispozitivul în grup."</string>
<string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"Elimină dispozitivul din grup."</string>
<string name="accessibility_open_application" msgid="1749126077501259712">"Deschide aplicația."</string>
@@ -468,6 +467,8 @@
<string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"Pentru a folosi butonul microfonului, activează accesul la microfon în Setări."</string>
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Deschide Setări"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Alt dispozitiv"</string>
+ <string name="media_suggestion_disconnected_text" msgid="8777932417076437525">"Redă pe <xliff:g id="DEVICE_NAME">%s</xliff:g>"</string>
+ <string name="media_suggestion_failure_text" msgid="8453022394887613774">"Nu se poate conecta. Încearcă din nou."</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Comută secțiunea Recente"</string>
<string name="zen_modes_dialog_title" msgid="8854640808100096934">"Moduri"</string>
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Gata"</string>
@@ -583,6 +584,7 @@
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
<skip />
<string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Permite accesul la tot ecranul"</string>
+ <string name="screen_share_permission_dialog_option_text_entire_screen_for_display" msgid="1545850238689169166">"Trimite <xliff:g id="DISPLAY_NAME">%s</xliff:g>"</string>
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
<skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Când permiți accesul la tot ecranul, tot conținutul de pe ecran este vizibil pentru <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Prin urmare, ai grijă cu parolele, detaliile de plată, mesajele, fotografiile și conținutul audio și video."</string>
@@ -594,6 +596,7 @@
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Proiectezi ecranul?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Proiectează o aplicație"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Proiectează tot ecranul"</string>
+ <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen_for_display" msgid="5524817761472941086">"Proiectează <xliff:g id="DISPLAY_NAME">%s</xliff:g>"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Când proiectezi tot ecranul, tot conținutul de pe ecran este vizibil. Prin urmare, ai grijă cu parolele, detaliile de plată, mesajele, fotografiile și conținutul audio și video."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Când proiectezi o aplicație, orice conținut se afișează sau se redă în aplicație este vizibil. Prin urmare, ai grijă cu parolele, detaliile de plată, mesajele, fotografiile și conținutul audio și video."</string>
<string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Proiectează ecranul"</string>
@@ -674,6 +677,7 @@
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Deblocat de TrustAgent"</string>
<string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"Dispozitivul a fost blocat. Prea multe încercări de autentificare."</string>
<string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"Dispozitiv blocat\nAutentificare nereușită"</string>
+ <string name="keyguard_indication_after_watch_disconnected" msgid="6329761892437377710">"Blocat de ceas"</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="accessibility_volume_settings" msgid="1458961116951564784">"Setări de sunet"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Adaugă subtitrări automate la fișierele media"</string>
@@ -800,6 +804,10 @@
<string name="enable_bluetooth_message" msgid="6740938333772779717">"Pentru a conecta tastatura la tabletă, mai întâi trebuie să activezi Bluetooth."</string>
<string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Activează"</string>
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Activată – În funcție de chip"</string>
+ <!-- no translation found for notification_guts_bundle_title (1345506995443305361) -->
+ <skip />
+ <!-- no translation found for notification_guts_bundle_summary (3971530802237393600) -->
+ <skip />
<string name="inline_done_button" msgid="6043094985588909584">"Gata"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Aplică"</string>
<string name="inline_turn_off_notifications" msgid="2653064779176881329">"Dezactivează"</string>
@@ -985,6 +993,7 @@
<string name="right_icon" msgid="1103955040645237425">"Pictograma din dreapta"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Ține apăsat și trage pentru a adăuga carduri"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Ține apăsat și trage pentru a rearanja cardurile"</string>
+ <string name="select_to_rearrange_tiles" msgid="6166153534235179148">"Selectează carduri pentru a le rearanja și redimensiona"</string>
<string name="tap_to_position_tile" msgid="6282815817773342757">"Atinge pentru a poziționa cardul"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Trage aici pentru a elimina"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Ai nevoie de cel puțin <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> carduri"</string>
@@ -1004,10 +1013,12 @@
<string name="tuner_low_priority" msgid="8412666814123009820">"Afișează pictogramele de notificare cu prioritate redusă"</string>
<string name="other" msgid="429768510980739978">"Altele"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"comută dimensiunea cardului"</string>
- <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"elimină cardul"</string>
+ <!-- no translation found for accessibility_qs_edit_remove_tile_action (4811778796933958616) -->
+ <skip />
<string name="accessibility_qs_edit_toggle_placement_mode" msgid="3870429389210569610">"comuta modul de plasare"</string>
<string name="accessibility_qs_edit_toggle_selection" msgid="2201248304072372239">"comuta selecția"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"adaugă cardul în ultima poziție"</string>
+ <!-- no translation found for accessibility_qs_edit_tile_add_action (7188303979684400620) -->
+ <skip />
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Mută cardul"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Adaugă cardul în poziția dorită"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Mută pe poziția <xliff:g id="POSITION">%1$d</xliff:g>"</string>
@@ -1094,7 +1105,8 @@
<string name="auto_saver_text" msgid="3214960308353838764">"Pornește dacă e probabil ca bateria să se descarce"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Nu, mulțumesc"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"În uz"</string>
- <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Aplicațiile folosesc <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps (4994455315457996796) -->
+ <skip />
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" și "</string>
<string name="ongoing_privacy_dialog_using_op" msgid="426635338010011796">"Este folosit de <xliff:g id="APPLICATION_NAME">%1$s</xliff:g>"</string>
@@ -1251,15 +1263,13 @@
<string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(deconectat)"</string>
<string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"Nu se poate comuta. Atinge pentru a încerca din nou."</string>
<string name="media_output_dialog_pairing_new" msgid="5098212763195577270">"Conectează un dispozitiv"</string>
- <!-- no translation found for media_output_dialog_button_connect_device (2819038239946643550) -->
- <skip />
+ <string name="media_output_dialog_button_connect_device" msgid="2819038239946643550">"Conectează dispozitivul"</string>
<string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Aplicație necunoscută"</string>
<string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Nu mai proiecta"</string>
<string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"Dispozitive disponibile pentru ieșire audio."</string>
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Volum"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
- <!-- no translation found for media_output_group_title_connected_speakers (3413876972919684479) -->
- <skip />
+ <string name="media_output_group_title_connected_speakers" msgid="3413876972919684479">"Difuzoare conectate"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Difuzoare și ecrane"</string>
<string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Dispozitive sugerate"</string>
<string name="media_input_group_title" msgid="2057057473860783021">"Intrare"</string>
@@ -1328,10 +1338,8 @@
<string name="accessibility_enter_hint" msgid="2617864063504824834">"Accesează dispozitivul"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Folosește amprenta ca să deschizi"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Autentificare obligatorie. Atinge senzorul de amprentă pentru a te autentifica."</string>
- <!-- no translation found for resume_animation (9148788411429330671) -->
- <skip />
- <!-- no translation found for pause_animation (1919705510058107504) -->
- <skip />
+ <string name="resume_animation" msgid="9148788411429330671">"Reia animația"</string>
+ <string name="pause_animation" msgid="1919705510058107504">"Întrerupe animația"</string>
<string name="ongoing_call_content_description" msgid="6394763878322348560">"Apel în desfășurare"</string>
<string name="ongoing_notification_extra_content_description" msgid="2098752668861351265">"În desfășurare"</string>
<string name="mobile_data_settings_title" msgid="3955246641380064901">"Date mobile"</string>
@@ -1513,11 +1521,13 @@
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"Adaugă o comandă rapidă"</string>
<string name="shortcut_helper_delete_shortcut_button_label" msgid="3148773472696137052">"Șterge comanda rapidă"</string>
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navighează folosind tastatura"</string>
- <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Învață comenzile rapide de la tastatură"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2131074100205028210) -->
+ <skip />
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navighează folosind touchpadul"</string>
<string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Învață gesturi pentru touchpad"</string>
<string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Navighează folosind tastatura și touchpadul"</string>
- <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Învață gesturi pentru touchpad, comenzi rapide de la tastatură și altele"</string>
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (8557098239919786046) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Înapoi"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Înapoi la pagina de pornire"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Vezi aplicațiile recente"</string>
@@ -1587,4 +1597,7 @@
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Toate cardurile Setări rapide se vor reseta la setările inițiale ale dispozitivului"</string>
<string name="demote_explain_text" msgid="3942301497888762295">"<xliff:g id="APPLICATION">%1$s</xliff:g> nu va mai afișa actualizări live. Poți modifica oricând opțiunea în Setări."</string>
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
+ <string name="underlay_close_button_content_description" msgid="6518481455548869894">"Închide"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="7666159428113013319">"Glisează din stânga sus pentru a deschide notificările"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="3842355853737057332">"Glisează din dreapta sus pentru a deschide Setările rapide"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ru-ldrtl/strings.xml b/packages/SystemUI/res/values-ru-ldrtl/strings.xml
index 62d653d..7571bbc 100644
--- a/packages/SystemUI/res/values-ru-ldrtl/strings.xml
+++ b/packages/SystemUI/res/values-ru-ldrtl/strings.xml
@@ -20,4 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="recents_quick_scrub_onboarding" msgid="2452671841151577157">"Перетащите влево, чтобы быстро переключиться между приложениями"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="6543179539438220549">"Чтобы открыть уведомления, проведите вниз от правого верхнего угла экрана"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="5760503283520058116">"Чтобы открыть быстрые настройки, проведите вниз от левого верхнего угла экрана"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index 9e07eeb..baece7a 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -252,8 +252,7 @@
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g>: подключено."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"Подключено к: <xliff:g id="CAST">%s</xliff:g>."</string>
<string name="accessibility_expand_group" msgid="521237935987978624">"Развернуть группу."</string>
- <!-- no translation found for accessibility_collapse_group (1842704016768329517) -->
- <skip />
+ <string name="accessibility_collapse_group" msgid="1842704016768329517">"Свернуть группу"</string>
<string name="accessibility_add_device_to_group" msgid="5446422960697860806">"Добавить устройство в группу."</string>
<string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"Удалить устройство из группы."</string>
<string name="accessibility_open_application" msgid="1749126077501259712">"Открыть приложение."</string>
@@ -412,7 +411,7 @@
<string name="custom" msgid="3337456985275158299">"Свой вариант"</string>
<string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Свои настройки трассировки"</string>
<string name="restore_default" msgid="5259420807486239755">"Восстановить настройки по умолчанию"</string>
- <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Режим управления одной рукой"</string>
+ <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Управление одной рукой"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Слуховые аппараты"</string>
<string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Активно"</string>
<string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Не подключено"</string>
@@ -468,6 +467,8 @@
<string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"Чтобы использовать кнопку микрофона, разрешите доступ к микрофону в настройках."</string>
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Открыть настройки"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Другое устройство"</string>
+ <string name="media_suggestion_disconnected_text" msgid="8777932417076437525">"Воспроизвести на устройстве \"<xliff:g id="DEVICE_NAME">%s</xliff:g>\""</string>
+ <string name="media_suggestion_failure_text" msgid="8453022394887613774">"Ошибка. Повторите попытку."</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Переключить режим обзора"</string>
<string name="zen_modes_dialog_title" msgid="8854640808100096934">"Режимы"</string>
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Готово"</string>
@@ -583,6 +584,7 @@
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
<skip />
<string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Показать весь экран"</string>
+ <string name="screen_share_permission_dialog_option_text_entire_screen_for_display" msgid="1545850238689169166">"Показывать экран \"<xliff:g id="DISPLAY_NAME">%s</xliff:g>\""</string>
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
<skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"При показе экрана целиком все, что на нем происходит, будет видно в приложении \"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>\". Поэтому будьте осторожны с паролями, сведениями о способах оплаты, сообщениями, фотографиями, аудио- и видеозаписями."</string>
@@ -594,6 +596,7 @@
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Начать трансляцию экрана?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Транслировать одно приложение"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Транслировать весь экран"</string>
+ <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen_for_display" msgid="5524817761472941086">"Транслировать экран \"<xliff:g id="DISPLAY_NAME">%s</xliff:g>\""</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Во время трансляции будет видно все, что происходит на экране. Поэтому будьте осторожны с паролями, сведениями о способах оплаты, сообщениями, фотографиями, аудио- и видеозаписями."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Во время трансляции будет видно все, что происходит в выбранном приложении. Поэтому будьте осторожны с паролями, сведениями о способах оплаты, сообщениями, фотографиями, аудио- и видеозаписями."</string>
<string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Транслировать экран"</string>
@@ -674,6 +677,7 @@
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Разблокировано агентом доверия"</string>
<string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"Устройство заблокировано. Слишком много попыток аутентификации."</string>
<string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"Устройство заблокировано\nСбой аутентификации"</string>
+ <string name="keyguard_indication_after_watch_disconnected" msgid="6329761892437377710">"Доступ заблокирован вашими часами"</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="accessibility_volume_settings" msgid="1458961116951564784">"Настройки звука"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Автоматически добавлять субтитры"</string>
@@ -800,6 +804,10 @@
<string name="enable_bluetooth_message" msgid="6740938333772779717">"Чтобы подключить клавиатуру к планшету, включите Bluetooth."</string>
<string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Включить"</string>
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Включить (на основе распознавания лиц)"</string>
+ <!-- no translation found for notification_guts_bundle_title (1345506995443305361) -->
+ <skip />
+ <!-- no translation found for notification_guts_bundle_summary (3971530802237393600) -->
+ <skip />
<string name="inline_done_button" msgid="6043094985588909584">"Готово"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Применить"</string>
<string name="inline_turn_off_notifications" msgid="2653064779176881329">"Отключить"</string>
@@ -985,6 +993,7 @@
<string name="right_icon" msgid="1103955040645237425">"Значок \"Вправо\""</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Чтобы добавить элементы, перетащите их."</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Чтобы изменить порядок элементов, перетащите их."</string>
+ <string name="select_to_rearrange_tiles" msgid="6166153534235179148">"Выберите параметры, чтобы изменить их порядок или размер"</string>
<string name="tap_to_position_tile" msgid="6282815817773342757">"Чтобы разместить параметр, нажмите здесь."</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Чтобы удалить, перетащите сюда"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Должно остаться не менее <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> элементов"</string>
@@ -1004,10 +1013,10 @@
<string name="tuner_low_priority" msgid="8412666814123009820">"Показывать значки уведомлений с низким приоритетом"</string>
<string name="other" msgid="429768510980739978">"Другое"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"изменить размер параметра"</string>
- <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"удалить панель"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="4811778796933958616">"Удалить параметр"</string>
<string name="accessibility_qs_edit_toggle_placement_mode" msgid="3870429389210569610">"включить или выключить режим размещения"</string>
<string name="accessibility_qs_edit_toggle_selection" msgid="2201248304072372239">"выбрать или отменить выбор"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"добавить параметр в конец"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="7188303979684400620">"Добавить параметр в конец"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Переместить панель"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Выбрать, куда добавить параметр"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Переместить на позицию <xliff:g id="POSITION">%1$d</xliff:g>"</string>
@@ -1094,7 +1103,8 @@
<string name="auto_saver_text" msgid="3214960308353838764">"Включать, если высока вероятность, что батарея скоро разрядится"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Нет, спасибо"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"Используется"</string>
- <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"В приложениях используется <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps (4994455315457996796) -->
+ <skip />
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" и "</string>
<string name="ongoing_privacy_dialog_using_op" msgid="426635338010011796">"Используется приложением \"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g>\""</string>
@@ -1251,15 +1261,13 @@
<string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(нет подключения)"</string>
<string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"Не удается переключиться. Нажмите, чтобы повторить попытку."</string>
<string name="media_output_dialog_pairing_new" msgid="5098212763195577270">"Подключить устройство"</string>
- <!-- no translation found for media_output_dialog_button_connect_device (2819038239946643550) -->
- <skip />
+ <string name="media_output_dialog_button_connect_device" msgid="2819038239946643550">"Подключить устройство"</string>
<string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Неизвестное приложение"</string>
<string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Остановить трансляцию"</string>
<string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"Доступные устройства для вывода звука."</string>
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Громкость"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g> %%"</string>
- <!-- no translation found for media_output_group_title_connected_speakers (3413876972919684479) -->
- <skip />
+ <string name="media_output_group_title_connected_speakers" msgid="3413876972919684479">"Подключенные колонки"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Колонки и дисплеи"</string>
<string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Рекомендуемые устройства"</string>
<string name="media_input_group_title" msgid="2057057473860783021">"Устройства ввода"</string>
@@ -1320,7 +1328,7 @@
<string name="person_available" msgid="2318599327472755472">"Онлайн"</string>
<string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Не удалось узнать уровень заряда батареи"</string>
<string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Нажмите, чтобы узнать больше."</string>
- <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Нет будильников"</string>
+ <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Нет"</string>
<string name="accessibility_bouncer" msgid="5896923685673320070">"разблокировать экран"</string>
<string name="accessibility_side_fingerprint_indicator_label" msgid="1673807833352363712">"Коснитесь сканера отпечатков пальцев. Это самая короткая кнопка сбоку на телефоне."</string>
<string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Сканер отпечатков пальцев"</string>
@@ -1328,10 +1336,8 @@
<string name="accessibility_enter_hint" msgid="2617864063504824834">"указать устройство"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Используйте отпечаток пальца для входа."</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Требуется аутентификация. Приложите палец к сканеру отпечатков."</string>
- <!-- no translation found for resume_animation (9148788411429330671) -->
- <skip />
- <!-- no translation found for pause_animation (1919705510058107504) -->
- <skip />
+ <string name="resume_animation" msgid="9148788411429330671">"Возобновить анимацию"</string>
+ <string name="pause_animation" msgid="1919705510058107504">"Приостановить анимацию"</string>
<string name="ongoing_call_content_description" msgid="6394763878322348560">"Текущий вызов"</string>
<string name="ongoing_notification_extra_content_description" msgid="2098752668861351265">"Сейчас"</string>
<string name="mobile_data_settings_title" msgid="3955246641380064901">"Мобильный интернет"</string>
@@ -1513,11 +1519,13 @@
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"Добавить сочетание клавиш"</string>
<string name="shortcut_helper_delete_shortcut_button_label" msgid="3148773472696137052">"Удалить сочетание клавиш"</string>
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Навигация с помощью клавиатуры"</string>
- <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Узнайте о сочетаниях клавиш."</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2131074100205028210) -->
+ <skip />
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Навигация с помощью сенсорной панели"</string>
<string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Узнайте о жестах на сенсорной панели."</string>
<string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Навигация с помощью клавиатуры и сенсорной панели"</string>
- <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Выучите жесты на сенсорной панели, сочетания клавиш и другие варианты навигации."</string>
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (8557098239919786046) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Назад"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"На главный экран"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Просмотр недавних приложений"</string>
@@ -1587,4 +1595,7 @@
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Для всех параметров быстрых настроек будут восстановлены значения по умолчанию."</string>
<string name="demote_explain_text" msgid="3942301497888762295">"Данные приложения \"<xliff:g id="APPLICATION">%1$s</xliff:g>\" перестанут обновляться в реальном времени. Этот параметр можно в любой момент изменить в настройках."</string>
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
+ <string name="underlay_close_button_content_description" msgid="6518481455548869894">"Закрыть"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="7666159428113013319">"Чтобы открыть уведомления, проведите вниз от левого верхнего угла экрана"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="3842355853737057332">"Чтобы открыть быстрые настройки, проведите вниз от правого верхнего угла экрана"</string>
</resources>
diff --git a/packages/SystemUI/res/values-si-ldrtl/strings.xml b/packages/SystemUI/res/values-si-ldrtl/strings.xml
index 6511ba5..faf0506 100644
--- a/packages/SystemUI/res/values-si-ldrtl/strings.xml
+++ b/packages/SystemUI/res/values-si-ldrtl/strings.xml
@@ -20,4 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="recents_quick_scrub_onboarding" msgid="2452671841151577157">"ඉක්මනින් යෙදුම් මාරු කිරීමට වමට අදින්න"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="6543179539438220549">"දැනුම්දීම් විවෘත කිරීමට ඉහළ දකුණේ සිට ස්වයිප් කරන්න"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="5760503283520058116">"ඉක්මන් සැකසීම් විවෘත කිරීමට ඉහළ වමේ සිට ස්වයිප් කරන්න"</string>
</resources>
diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml
index f6f5f9c..a98edd6d 100644
--- a/packages/SystemUI/res/values-si/strings.xml
+++ b/packages/SystemUI/res/values-si/strings.xml
@@ -467,6 +467,8 @@
<string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"මයික්රෆෝන බොත්තම භාවිතය සඳහා, සැකසීම් තුළ මයික්රෆෝන ප්රවේශය සබල කරන්න."</string>
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"සැකසීම් විවෘත කරන්න"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"වෙනත් උපාංගය"</string>
+ <string name="media_suggestion_disconnected_text" msgid="8777932417076437525">"<xliff:g id="DEVICE_NAME">%s</xliff:g> හි වාදනය කරන්න"</string>
+ <string name="media_suggestion_failure_text" msgid="8453022394887613774">"සම්බන්ධ විය නොහැක. නැවත උත්සාහ කරන්න."</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"දළ විශ්ලේෂණය ටොගල කරන්න"</string>
<string name="zen_modes_dialog_title" msgid="8854640808100096934">"ප්රකාර"</string>
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"නිමයි"</string>
@@ -582,6 +584,7 @@
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
<skip />
<string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"සම්පූර්ණ තිරය බෙදා ගන්න"</string>
+ <string name="screen_share_permission_dialog_option_text_entire_screen_for_display" msgid="1545850238689169166">"<xliff:g id="DISPLAY_NAME">%s</xliff:g> බෙදා ගන්න"</string>
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
<skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"ඔබ ඔබේ සම්පූර්ණ තිරය බෙදා ගන්නා විට, ඔබේ තිරයේ ඇති ඕනෑම දෙයක් <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> වෙත දෘශ්යමාන වේ. ඒ නිසා මුරපද, ගෙවීම් විස්තර, පණිවිඩ, ඡායාරූප, සහ ශ්රව්ය සහ දෘශ්ය වැනි දේවල් පිළිබඳ ප්රවේශම් වන්න."</string>
@@ -593,6 +596,7 @@
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"ඔබේ තිරය විකාශය කරන්න ද?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"එක් යෙදුමක් විකාශය කරන්න"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"සමස්ත තිරය විකාශය කරන්න"</string>
+ <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen_for_display" msgid="5524817761472941086">"<xliff:g id="DISPLAY_NAME">%s</xliff:g> විකාශය"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"ඔබ ඔබේ සම්පූර්ණ තිරය විකාශය කරන විට, ඔබේ තිරයේ ඇති ඕනෑම දෙයක් දෘශ්යමාන වේ. ඒ නිසා මුරපද, ගෙවීම් විස්තර, පණිවුඩ, ඡායාරූප, සහ ශ්රව්ය සහ දෘශ්ය වැනි දේවල් පිළිබඳ ප්රවේශම් වන්න."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"ඔබ යෙදුමක් විකාශය කරන විට, එම යෙදුමේ පෙන්වන හෝ වාදනය කරන ඕනෑම දෙයක් දෘශ්යමාන වේ. ඒ නිසා මුරපද, ගෙවීම් විස්තර, පණිවුඩ, ඡායාරූප, සහ ශ්රව්ය සහ දෘශ්ය වැනි දේවල් පිළිබඳ ප්රවේශම් වන්න."</string>
<string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"විකාශ තිරය"</string>
@@ -673,6 +677,7 @@
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"TrustAgent මඟින් අඟුලු දමා තබා ගන්න"</string>
<string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"උපාංගය අගුළු දමා ඇත, බොහෝ සත්යාපන උත්සාහයන් ගණනකි"</string>
<string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"උපාංගය අගුළු දමා ඇත\nසත්යාපනය අසමත් විය"</string>
+ <string name="keyguard_indication_after_watch_disconnected" msgid="6329761892437377710">"ඔබේ ඔරලෝසුවෙන් අගුළු දමා ඇත"</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="accessibility_volume_settings" msgid="1458961116951564784">"ශබ්ද සැකසීම්"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"මාධ්ය ස්වයංක්රීයව සිරස්තල"</string>
@@ -799,6 +804,10 @@
<string name="enable_bluetooth_message" msgid="6740938333772779717">"ඔබේ යතුරු පුවරුව ඔබේ ටැබ්ලට් පරිගණකයට සම්බන්ධ කිරීමට, ඔබ පළමුව බ්ලූටූත් ක්රියාත්මක කළ යුතුය."</string>
<string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"ක්රියාත්මක කරන්න"</string>
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"ක්රියාත්මකයි - මුහුණ-පදනම්ව"</string>
+ <!-- no translation found for notification_guts_bundle_title (1345506995443305361) -->
+ <skip />
+ <!-- no translation found for notification_guts_bundle_summary (3971530802237393600) -->
+ <skip />
<string name="inline_done_button" msgid="6043094985588909584">"නිමයි"</string>
<string name="inline_ok_button" msgid="603075490581280343">"යොදන්න"</string>
<string name="inline_turn_off_notifications" msgid="2653064779176881329">"ක්රියාවිරහිත කරන්න"</string>
@@ -984,6 +993,7 @@
<string name="right_icon" msgid="1103955040645237425">"දකුණු නිරූපකය"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"ටයිල් එක් කිරීමට අල්ලාගෙන සිට අදින්න"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"ටයිල් නැවත සකස් කිරීමට අල්ලාගෙන සිට අදින්න"</string>
+ <string name="select_to_rearrange_tiles" msgid="6166153534235179148">"නැවත සකස් කිරීමට සහ ප්රමාණය වෙනස් කිරීමට ටයිල් තෝරන්න"</string>
<string name="tap_to_position_tile" msgid="6282815817773342757">"ටයිල් ස්ථානගත කිරීමට තට්ටු කරන්න"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"ඉවත් කිරීමට මෙතැනට අදින්න"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"ඔබ අවම වශයෙන් ටයිල් <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> ක් අවශ්ය වෙයි"</string>
@@ -1003,10 +1013,12 @@
<string name="tuner_low_priority" msgid="8412666814123009820">"අඩු ප්රමුඛතා දැනුම්දීම් අයිකන පෙන්වන්න"</string>
<string name="other" msgid="429768510980739978">"වෙනත්"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"ටයිල් එකේ ප්රමාණය මාරු කරන්න"</string>
- <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"ටයිල් ඉවත් කරන්න"</string>
+ <!-- no translation found for accessibility_qs_edit_remove_tile_action (4811778796933958616) -->
+ <skip />
<string name="accessibility_qs_edit_toggle_placement_mode" msgid="3870429389210569610">"ස්ථානගත කිරීමේ ප්රකාරය ටොගල කරන්න"</string>
<string name="accessibility_qs_edit_toggle_selection" msgid="2201248304072372239">"තේරීම ටොගල් කරන්න"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"ටයිල් එක අවසාන ස්ථානයට එක් කරන්න"</string>
+ <!-- no translation found for accessibility_qs_edit_tile_add_action (7188303979684400620) -->
+ <skip />
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"ටයිල් ගෙන යන්න"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"අපේක්ෂිත ස්ථානයට ටයිල් එක එක් කරන්න"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> වෙත ගෙන යන්න"</string>
@@ -1093,7 +1105,8 @@
<string name="auto_saver_text" msgid="3214960308353838764">"බැටරිය අවසන් වීමට යන විට සක්රීය කරන්න"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"එපා ස්තූතියි"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"භාවිතයේ ඇත"</string>
- <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"යෙදුම් ඔබේ <xliff:g id="TYPES_LIST">%s</xliff:g> භාවිත කරමින් සිටී."</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps (4994455315457996796) -->
+ <skip />
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" සහ "</string>
<string name="ongoing_privacy_dialog_using_op" msgid="426635338010011796">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> මගින් භාවිත කරමින් ඇත"</string>
@@ -1508,11 +1521,13 @@
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"කෙටිමඟ එක් කරන්න"</string>
<string name="shortcut_helper_delete_shortcut_button_label" msgid="3148773472696137052">"කෙටිමඟ මකන්න"</string>
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"ඔබේ යතුරු පුවරුව භාවිතයෙන් සංචාලනය කරන්න"</string>
- <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"යතුරුපුවරු කෙටිමං ඉගෙන ගන්න"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2131074100205028210) -->
+ <skip />
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"ඔබේ ස්පර්ශ පෑඩ් භාවිතයෙන් සංචාලනය කරන්න"</string>
<string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"ස්පර්ශක පුවරු අභිනයන් ඉගෙන ගන්න"</string>
<string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"ඔබේ යතුරු පුවරුව සහ ස්පර්ශ පෑඩ් භාවිතයෙන් සංචාලනය කරන්න"</string>
- <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"ස්පර්ශ පෑඩ් අභිනයන්, යතුරුපුවරු කෙටිමං සහ තවත් දේ ඉගෙන ගන්න"</string>
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (8557098239919786046) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"ආපසු යන්න"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"මුල් පිටුවට යන්න"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"මෑත යෙදුම් බලන්න"</string>
@@ -1582,4 +1597,7 @@
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"සියලු ඉක්මන් සැකසීම් ටයිල් උපාංගයේ මුල් සැකසීම් වෙත නැවත සකසනු ඇත"</string>
<string name="demote_explain_text" msgid="3942301497888762295">"<xliff:g id="APPLICATION">%1$s</xliff:g> තවදුරටත් සජීවී යාවත්කාලීන නොපෙන්වයි. ඔබට සැකසීම් තුළ මෙය ඕනෑම වේලාවක වෙනස් කළ හැක."</string>
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
+ <string name="underlay_close_button_content_description" msgid="6518481455548869894">"වසන්න"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="7666159428113013319">"දැනුම්දීම් විවෘත කිරීමට ඉහළ වමේ සිට ස්වයිප් කරන්න"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="3842355853737057332">"ඉක්මන් සැකසීම් විවෘත කිරීමට ඉහළ දකුණේ සිට ස්වයිප් කරන්න"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sk-ldrtl/strings.xml b/packages/SystemUI/res/values-sk-ldrtl/strings.xml
index d74e26e..ea8d0bb 100644
--- a/packages/SystemUI/res/values-sk-ldrtl/strings.xml
+++ b/packages/SystemUI/res/values-sk-ldrtl/strings.xml
@@ -20,4 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="recents_quick_scrub_onboarding" msgid="2452671841151577157">"Presunutím doľava rýchlo prepnete aplikácie"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="6543179539438220549">"Upozornenia otvoríte potiahnutím z pravého horného rohu"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="5760503283520058116">"Rýchle nastavenia otvoríte potiahnutím z ľavého horného rohu"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index 13e4f05..1b295fd 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -334,7 +334,7 @@
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Zapína sa…"</string>
<string name="quick_settings_brightness_unable_adjust_msg" msgid="4124028416057617517">"Jas sa nedá upraviť, pretože ho ovláda horná aplikácia"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Automatické otáčanie"</string>
- <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Automatické otáčanie obrazovky"</string>
+ <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Automaticky otáčať obrazovku"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Poloha"</string>
<string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Šetrič obrazovky"</string>
<string name="quick_settings_camera_label" msgid="5612076679385269339">"Prístup ku kamere"</string>
@@ -467,6 +467,8 @@
<string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"Ak chcete použiť tlačidlo mikrofónu, povoľte prístup k mikrofónu v Nastaveniach."</string>
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Otvoriť Nastavenia"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Iné zariadenie"</string>
+ <string name="media_suggestion_disconnected_text" msgid="8777932417076437525">"Prehrať v zariadení <xliff:g id="DEVICE_NAME">%s</xliff:g>"</string>
+ <string name="media_suggestion_failure_text" msgid="8453022394887613774">"Nedá sa pripojiť. Skús. znova."</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Prepnúť prehľad"</string>
<string name="zen_modes_dialog_title" msgid="8854640808100096934">"Režimy"</string>
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Hotovo"</string>
@@ -582,6 +584,7 @@
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
<skip />
<string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Zdieľať celú obrazovku"</string>
+ <string name="screen_share_permission_dialog_option_text_entire_screen_for_display" msgid="1545850238689169166">"Zdieľať <xliff:g id="DISPLAY_NAME">%s</xliff:g>"</string>
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
<skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Pri zdieľaní celej obrazovky vidí aplikácia <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> všetko, čo sa na nej zobrazuje. Preto zvýšte pozornosť v prípade položiek, ako sú heslá, platobné údaje, správy, fotky a zvuk či video."</string>
@@ -593,6 +596,7 @@
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Chcete prenášať obrazovku?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Prenášať jednu aplikáciu"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Prenášať celú obrazovku"</string>
+ <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen_for_display" msgid="5524817761472941086">"Prenášať <xliff:g id="DISPLAY_NAME">%s</xliff:g>"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Pri prenášaní celej obrazovky je viditeľný všetok obsah na obrazovke. Preto zvýšte pozornosť v prípade položiek, ako sú heslá, platobné údaje, správy, fotky a zvuk či video."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Pri prenášaní aplikácie je viditeľný všetok obsah zobrazený alebo prehrávaný v tejto aplikácii. Preto zvýšte pozornosť v prípade položiek, ako sú heslá, platobné údaje, správy, fotky a zvuk či video."</string>
<string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Prenášať obrazovku"</string>
@@ -673,6 +677,7 @@
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Odomknutie udržiava TrustAgent"</string>
<string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"Zariadenie bolo uzamknuté, príliš mnoho pokusov o overenie"</string>
<string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"Zariadenie je uzamknuté\nOverenie sa nepodarilo"</string>
+ <string name="keyguard_indication_after_watch_disconnected" msgid="6329761892437377710">"Uzamknuté hodinkami"</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="accessibility_volume_settings" msgid="1458961116951564784">"Nastavenia zvuku"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Automatické titulkovanie médií"</string>
@@ -799,6 +804,10 @@
<string name="enable_bluetooth_message" msgid="6740938333772779717">"Ak chcete klávesnicu pripojiť k tabletu, najprv musíte zapnúť Bluetooth."</string>
<string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Zapnúť"</string>
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Zapnuté – podľa tváre"</string>
+ <!-- no translation found for notification_guts_bundle_title (1345506995443305361) -->
+ <skip />
+ <!-- no translation found for notification_guts_bundle_summary (3971530802237393600) -->
+ <skip />
<string name="inline_done_button" msgid="6043094985588909584">"Hotovo"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Použiť"</string>
<string name="inline_turn_off_notifications" msgid="2653064779176881329">"Vypnúť"</string>
@@ -984,6 +993,7 @@
<string name="right_icon" msgid="1103955040645237425">"Pravá ikona"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Pridržaním a presunutím pridáte karty"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Karty môžete usporiadať pridržaním a presunutím"</string>
+ <string name="select_to_rearrange_tiles" msgid="6166153534235179148">"Vyberte karty na zmenu usporiadania a veľkosti"</string>
<string name="tap_to_position_tile" msgid="6282815817773342757">"Klepnutím umiestnite kartu"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Presunutím sem odstránite"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Minimálny počet vyžadovaných dlaždíc: <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g>"</string>
@@ -1003,10 +1013,12 @@
<string name="tuner_low_priority" msgid="8412666814123009820">"Zobraziť ikony upozornení s nízkou prioritou"</string>
<string name="other" msgid="429768510980739978">"Ďalšie"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"prepnúť veľkosť karty"</string>
- <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"odstrániť kartu"</string>
+ <!-- no translation found for accessibility_qs_edit_remove_tile_action (4811778796933958616) -->
+ <skip />
<string name="accessibility_qs_edit_toggle_placement_mode" msgid="3870429389210569610">"prepnúť režim umiestnenia"</string>
<string name="accessibility_qs_edit_toggle_selection" msgid="2201248304072372239">"prepnúť výber"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"pridáte kartu na poslednú pozíciu"</string>
+ <!-- no translation found for accessibility_qs_edit_tile_add_action (7188303979684400620) -->
+ <skip />
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Presunúť kartu"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Pridať kartu na požadovanú pozíciu"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Presunúť na <xliff:g id="POSITION">%1$d</xliff:g>. pozíciu"</string>
@@ -1035,8 +1047,8 @@
<string name="high_temp_alarm_title" msgid="8654754369605452169">"Odpojte zariadenie"</string>
<string name="high_temp_alarm_notify_message" msgid="3917622943609118956">"Zariadenie sa zahrieva pri nabíjacom porte. Ak je pripojené k nabíjačke alebo príslušenstvu USB, odpojte ho a dajte pozor, lebo môže byť horúci aj kábel."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Zobraziť opatrenia"</string>
- <string name="lockscreen_shortcut_left" msgid="1238765178956067599">"Ľavá skratka"</string>
- <string name="lockscreen_shortcut_right" msgid="4138414674531853719">"Pravá skratka"</string>
+ <string name="lockscreen_shortcut_left" msgid="1238765178956067599">"Odkaz vľavo"</string>
+ <string name="lockscreen_shortcut_right" msgid="4138414674531853719">"Odkaz vpravo"</string>
<string name="lockscreen_unlock_left" msgid="1417801334370269374">"Ľavá skratka tiež odomkne"</string>
<string name="lockscreen_unlock_right" msgid="4658008735541075346">"Pravá skratka tiež odomkne"</string>
<string name="lockscreen_none" msgid="4710862479308909198">"Žiadna"</string>
@@ -1093,7 +1105,8 @@
<string name="auto_saver_text" msgid="3214960308353838764">"Zapnite, keď je batéria takmer vybitá"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Nie, vďaka"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"Používa sa"</string>
- <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Aplikácie používajú zoznam <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps (4994455315457996796) -->
+ <skip />
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" a "</string>
<string name="ongoing_privacy_dialog_using_op" msgid="426635338010011796">"Využíva aplikácia <xliff:g id="APPLICATION_NAME">%1$s</xliff:g>"</string>
@@ -1508,11 +1521,13 @@
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"Pridať skratku"</string>
<string name="shortcut_helper_delete_shortcut_button_label" msgid="3148773472696137052">"Odstrániť skratku"</string>
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Pohybujte sa v systéme pomocou klávesnice"</string>
- <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Naučte sa klávesové skratky"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2131074100205028210) -->
+ <skip />
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Pohybujte sa v systéme pomocou touchpadu"</string>
<string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Naučte sa gestá touchpadu"</string>
<string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Pohybujte sa v systéme pomocou klávesnice a touchpadu"</string>
- <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Naučte sa gestá touchpadu, klávesové skratky a ďalšie funkcie"</string>
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (8557098239919786046) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Späť"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Prejsť na plochu"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Zobraziť nedávne aplikácie"</string>
@@ -1582,4 +1597,7 @@
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Všetky karty rýchlych nastavení sa resetujú na pôvodné nastavenia zariadenia"</string>
<string name="demote_explain_text" msgid="3942301497888762295">"Aplikácia <xliff:g id="APPLICATION">%1$s</xliff:g> už nebude zobrazovať aktuality naživo. Kedykoľvek to môžete zmeniť v Nastaveniach."</string>
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
+ <string name="underlay_close_button_content_description" msgid="6518481455548869894">"Zavrieť"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="7666159428113013319">"Upozornenia otvoríte potiahnutím z ľavého horného rohu"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="3842355853737057332">"Rýchle nastavenia otvoríte potiahnutím z pravého horného rohu"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sl-ldrtl/strings.xml b/packages/SystemUI/res/values-sl-ldrtl/strings.xml
index da6121f..49a51460 100644
--- a/packages/SystemUI/res/values-sl-ldrtl/strings.xml
+++ b/packages/SystemUI/res/values-sl-ldrtl/strings.xml
@@ -20,4 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="recents_quick_scrub_onboarding" msgid="2452671841151577157">"Povlecite v levo za hiter preklop med aplikacijami"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="6543179539438220549">"Povlecite iz zgornjega desnega kota, da odprete obvestila."</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="5760503283520058116">"Povlecite iz zgornjega levega kota, da odprete hitre nastavitve"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index 8909952..638a95c 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -467,6 +467,8 @@
<string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"Za uporabo gumba z mikrofonom omogočite dostop do mikrofona v nastavitvah."</string>
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Odpri nastavitve"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Druga naprava"</string>
+ <string name="media_suggestion_disconnected_text" msgid="8777932417076437525">"Predvajaj v napravi <xliff:g id="DEVICE_NAME">%s</xliff:g>"</string>
+ <string name="media_suggestion_failure_text" msgid="8453022394887613774">"Povezava ni mogoča. Poskusite znova."</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Vklop/izklop pregleda"</string>
<string name="zen_modes_dialog_title" msgid="8854640808100096934">"Načini"</string>
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Končano"</string>
@@ -582,6 +584,7 @@
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
<skip />
<string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Deli celoten zaslon"</string>
+ <string name="screen_share_permission_dialog_option_text_entire_screen_for_display" msgid="1545850238689169166">"Deljenje zaslona <xliff:g id="DISPLAY_NAME">%s</xliff:g>"</string>
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
<skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Pri deljenju celotnega zaslona je aplikaciji <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> vidno vse na zaslonu. Zato bodite previdni z gesli, podatki za plačilo, sporočili, fotografijami ter z zvokom in videom."</string>
@@ -593,6 +596,7 @@
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Želite predvajati vsebino zaslona?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Predvajanje ene aplikacije"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Predvajanje celotnega zaslona"</string>
+ <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen_for_display" msgid="5524817761472941086">"Predvajanje zaslona <xliff:g id="DISPLAY_NAME">%s</xliff:g>"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Pri predvajanju vsebine celotnega zaslona je vidno vse na zaslonu. Zato bodite previdni z gesli, podatki za plačilo, sporočili, fotografijami ter z zvokom in videom."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Pri predvajanju vsebine aplikacije je vidno vse, kar je prikazano ali predvajano v tej aplikaciji. Zato bodite previdni z gesli, podatki za plačilo, sporočili, fotografijami ter z zvokom in videom."</string>
<string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Predvajanje zaslona"</string>
@@ -673,6 +677,7 @@
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"TrustAgent ohranja odklenjeno"</string>
<string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"Naprava se je zaklenila, preveč poskusov preverjanja pristnosti"</string>
<string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"Naprava se je zaklenila\nPreverjanje pristnosti ni uspelo"</string>
+ <string name="keyguard_indication_after_watch_disconnected" msgid="6329761892437377710">"Zaklenjeno iz ure"</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="accessibility_volume_settings" msgid="1458961116951564784">"Nastavitve zvoka"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Sam. podnapisi predstavnosti"</string>
@@ -799,6 +804,8 @@
<string name="enable_bluetooth_message" msgid="6740938333772779717">"Če želite povezati tipkovnico in tablični računalnik, vklopite Bluetooth."</string>
<string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Vklop"</string>
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Vklopljeno – na podlagi obraza"</string>
+ <string name="notification_guts_bundle_title" msgid="1345506995443305361">"Uporaba organizatorja obvestil"</string>
+ <string name="notification_guts_bundle_summary" msgid="3971530802237393600">"Za aplikacijo <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="inline_done_button" msgid="6043094985588909584">"Končano"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Uporabi"</string>
<string name="inline_turn_off_notifications" msgid="2653064779176881329">"Izklopi"</string>
@@ -984,6 +991,7 @@
<string name="right_icon" msgid="1103955040645237425">"Desna ikona"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Držite in povlecite, da dodate ploščice."</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Držite in povlecite, da prerazporedite ploščice."</string>
+ <string name="select_to_rearrange_tiles" msgid="6166153534235179148">"Izberite ploščice za prerazporeditev in spremembo velikosti"</string>
<string name="tap_to_position_tile" msgid="6282815817773342757">"Dotaknite se, da umestite ploščico"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Če želite odstraniti, povlecite sem"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Imeti morate vsaj toliko ploščic: <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g>"</string>
@@ -1003,10 +1011,10 @@
<string name="tuner_low_priority" msgid="8412666814123009820">"Pokaži ikone obvestil z nizko stopnjo prednosti"</string>
<string name="other" msgid="429768510980739978">"Drugo"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"preklop velikosti ploščice"</string>
- <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"odstranitev ploščice"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="4811778796933958616">"Odstranitev ploščice"</string>
<string name="accessibility_qs_edit_toggle_placement_mode" msgid="3870429389210569610">"preklop načina umestitve"</string>
<string name="accessibility_qs_edit_toggle_selection" msgid="2201248304072372239">"preklop izbire"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"dodajanje ploščice na zadnji položaj"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="7188303979684400620">"Dodajanje ploščice na zadnje mesto"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Premik ploščice"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Dodaj ploščico na želeno mesto"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Premik na položaj <xliff:g id="POSITION">%1$d</xliff:g>"</string>
@@ -1093,7 +1101,7 @@
<string name="auto_saver_text" msgid="3214960308353838764">"Vklop, če je verjetno, da se bo baterija izpraznila"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Ne, hvala"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"V uporabi"</string>
- <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Aplikacije uporabljajo <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
+ <string name="ongoing_privacy_chip_content_multiple_apps" msgid="4994455315457996796">"V uporabi pri: <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" in "</string>
<string name="ongoing_privacy_dialog_using_op" msgid="426635338010011796">"Trenutno uporablja aplikacija <xliff:g id="APPLICATION_NAME">%1$s</xliff:g>"</string>
@@ -1508,11 +1516,11 @@
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"Dodajanje bližnjice"</string>
<string name="shortcut_helper_delete_shortcut_button_label" msgid="3148773472696137052">"Izbris bližnjice"</string>
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Krmarjenje s tipkovnico"</string>
- <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Učenje bližnjičnih tipk"</string>
+ <string name="launch_keyboard_tutorial_notification_content" msgid="2131074100205028210">"Učenje bližnjičnih tipk"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Krmarjenje s sledilno ploščico"</string>
<string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Naučite se uporabljati poteze na sledilni ploščici."</string>
<string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Krmarjenje s tipkovnico in sledilno ploščico"</string>
- <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Učenje potez na sledilni ploščici, bližnjičnih tipk in drugega"</string>
+ <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="8557098239919786046">"Učenje potez na sledilni ploščici, bližnjičnih tipk in drugega"</string>
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Pomik nazaj"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Pomik na začetni zaslon"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Ogled nedavnih aplikacij"</string>
@@ -1582,4 +1590,7 @@
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Vse ploščice v hitrih nastavitvah bodo ponastavljene na prvotne nastavitve naprave."</string>
<string name="demote_explain_text" msgid="3942301497888762295">"Aplikacija <xliff:g id="APPLICATION">%1$s</xliff:g> ne bo več prikazovala sprotnih obvestil. To lahko kadar koli spremenite v nastavitvah."</string>
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
+ <string name="underlay_close_button_content_description" msgid="6518481455548869894">"Zapri"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="7666159428113013319">"Povlecite iz zgornjega levega kota, da odprete obvestila."</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="3842355853737057332">"Povlecite iz zgornjega desnega kota, da odprete hitre nastavitve"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sq-ldrtl/strings.xml b/packages/SystemUI/res/values-sq-ldrtl/strings.xml
index 77971a4..e4aefa5 100644
--- a/packages/SystemUI/res/values-sq-ldrtl/strings.xml
+++ b/packages/SystemUI/res/values-sq-ldrtl/strings.xml
@@ -20,4 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="recents_quick_scrub_onboarding" msgid="2452671841151577157">"Zvarrit majtas për të ndërruar aplikacionet me shpejtësi"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="6543179539438220549">"Rrëshqit shpejt nga lart djathtas për të hapur njoftimet"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="5760503283520058116">"Rrëshqit shpejt nga lart majtas për të hapur \"Cilësimet e shpejta\""</string>
</resources>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index 7a1e71f..93b408c 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -252,8 +252,7 @@
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Lidhur me <xliff:g id="BLUETOOTH">%s</xliff:g>"</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"Është lidhur me <xliff:g id="CAST">%s</xliff:g>."</string>
<string name="accessibility_expand_group" msgid="521237935987978624">"Zgjero grupin."</string>
- <!-- no translation found for accessibility_collapse_group (1842704016768329517) -->
- <skip />
+ <string name="accessibility_collapse_group" msgid="1842704016768329517">"Palos grupin."</string>
<string name="accessibility_add_device_to_group" msgid="5446422960697860806">"Shto pajisjen te grupi."</string>
<string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"Hiq pajisjen nga grupi."</string>
<string name="accessibility_open_application" msgid="1749126077501259712">"Hap aplikacionin."</string>
@@ -381,7 +380,7 @@
<string name="quick_settings_work_mode_label" msgid="6440531507319809121">"Aplikacionet e punës"</string>
<string name="quick_settings_work_mode_paused_state" msgid="6681788236383735976">"Vendosur në pauzë"</string>
<string name="quick_settings_night_display_label" msgid="8180030659141778180">"Drita e natës"</string>
- <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Aktiv në perëndim"</string>
+ <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Aktive në perëndim"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Deri në lindje të diellit"</string>
<string name="quick_settings_night_secondary_label_on_at" msgid="3584738542293528235">"Aktive në <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_secondary_label_until" msgid="1883981263191927372">"Deri në <xliff:g id="TIME">%s</xliff:g>"</string>
@@ -468,6 +467,8 @@
<string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"Për të përdorur butonin e mikrofonit, aktivizo qasjen te mikrofoni te \"Cilësimet\"."</string>
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Hap \"Cilësimet\""</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Pajisje tjetër"</string>
+ <string name="media_suggestion_disconnected_text" msgid="8777932417076437525">"Luaje në <xliff:g id="DEVICE_NAME">%s</xliff:g>"</string>
+ <string name="media_suggestion_failure_text" msgid="8453022394887613774">"Nuk mund të lidhet. Provo përsëri."</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Kalo te përmbledhja"</string>
<string name="zen_modes_dialog_title" msgid="8854640808100096934">"Modalitetet"</string>
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"U krye"</string>
@@ -583,6 +584,7 @@
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
<skip />
<string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Ndaj të gjithë ekranin"</string>
+ <string name="screen_share_permission_dialog_option_text_entire_screen_for_display" msgid="1545850238689169166">"Ndaj <xliff:g id="DISPLAY_NAME">%s</xliff:g>"</string>
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
<skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Kur ti ndan të gjithë ekranin, çdo gjë në ekranin tënd është e dukshme për <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Prandaj, ki kujdes me gjërat si fjalëkalimet, detajet e pagesave, mesazhet, fotografitë, si dhe audion dhe videon."</string>
@@ -594,6 +596,7 @@
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Të transmetohet ekrani yt?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Transmeto një aplikacion"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Transmeto të gjithë ekranin"</string>
+ <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen_for_display" msgid="5524817761472941086">"Transmeto <xliff:g id="DISPLAY_NAME">%s</xliff:g>"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Kur ti transmeton të gjithë ekranin, çdo gjë në ekranin tënd është e dukshme. Prandaj, ki kujdes me gjërat si fjalëkalimet, detajet e pagesave, mesazhet, fotografitë, si dhe audion dhe videon."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Kur ti transmeton një aplikacion, çdo gjë që shfaqet ose luhet në atë aplikacion është e dukshme. Prandaj, ki kujdes me gjërat si fjalëkalimet, detajet e pagesave, mesazhet, fotografitë, si dhe audion dhe videon."</string>
<string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Transmeto ekranin"</string>
@@ -674,6 +677,7 @@
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Mbajtur shkyçur nga TrustAgent"</string>
<string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"Pajisja u kyç. Shumë përpjekje vërtetimi"</string>
<string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"Pajisja është kyçur\nVërtetimi dështoi"</string>
+ <string name="keyguard_indication_after_watch_disconnected" msgid="6329761892437377710">"Kyçur nga ora jote"</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="accessibility_volume_settings" msgid="1458961116951564784">"Cilësimet e zërit"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Media me titra automatike"</string>
@@ -800,6 +804,10 @@
<string name="enable_bluetooth_message" msgid="6740938333772779717">"Për të lidhur tastierën me tabletin, në fillim duhet të aktivizosh \"bluetooth-in\"."</string>
<string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Aktivizo"</string>
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Aktiv - Në bazë të fytyrës"</string>
+ <!-- no translation found for notification_guts_bundle_title (1345506995443305361) -->
+ <skip />
+ <!-- no translation found for notification_guts_bundle_summary (3971530802237393600) -->
+ <skip />
<string name="inline_done_button" msgid="6043094985588909584">"U krye"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Zbato"</string>
<string name="inline_turn_off_notifications" msgid="2653064779176881329">"Çaktivizo"</string>
@@ -985,6 +993,7 @@
<string name="right_icon" msgid="1103955040645237425">"Ikona djathtas"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Mbaje të shtypur dhe zvarrit për të shtuar pllakëza"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Mbaje të shtypur dhe zvarrit për të risistemuar pllakëzat"</string>
+ <string name="select_to_rearrange_tiles" msgid="6166153534235179148">"Zgjidh pllakëzat për t\'i riorganizuar dhe ndryshuar përmasat"</string>
<string name="tap_to_position_tile" msgid="6282815817773342757">"Trokit për të pozicionuar pllakëzën"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Zvarrit këtu për ta hequr"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Të duhen të paktën <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> pllakëza"</string>
@@ -1004,10 +1013,12 @@
<string name="tuner_low_priority" msgid="8412666814123009820">"Shfaq ikonat e njoftimeve me përparësi të ulët"</string>
<string name="other" msgid="429768510980739978">"Të tjera"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"ndrysho madhësinë e pllakëzës"</string>
- <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"hiq pllakëzën"</string>
+ <!-- no translation found for accessibility_qs_edit_remove_tile_action (4811778796933958616) -->
+ <skip />
<string name="accessibility_qs_edit_toggle_placement_mode" msgid="3870429389210569610">"aktivizuar/çaktivizuar modalitetin e vendosjes"</string>
<string name="accessibility_qs_edit_toggle_selection" msgid="2201248304072372239">"aktivizuar/çaktivizuar përzgjedhjen"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"shtuar pllakëzën në pozicionin e fundit"</string>
+ <!-- no translation found for accessibility_qs_edit_tile_add_action (7188303979684400620) -->
+ <skip />
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Zhvendos pllakëzën"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Shto pllakëzën në pozicionin e dëshiruar"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Zhvendos te <xliff:g id="POSITION">%1$d</xliff:g>"</string>
@@ -1094,7 +1105,8 @@
<string name="auto_saver_text" msgid="3214960308353838764">"Aktivizoje kur bateria mund të mbarojë"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Jo"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"Në përdorim"</string>
- <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Aplikacionet po përdorin <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps (4994455315457996796) -->
+ <skip />
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" dhe "</string>
<string name="ongoing_privacy_dialog_using_op" msgid="426635338010011796">"Po përdoret nga <xliff:g id="APPLICATION_NAME">%1$s</xliff:g>"</string>
@@ -1251,15 +1263,13 @@
<string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(shkëputur)"</string>
<string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"Nuk mund të ndërrohet. Trokit për të provuar përsëri."</string>
<string name="media_output_dialog_pairing_new" msgid="5098212763195577270">"Lidh një pajisje"</string>
- <!-- no translation found for media_output_dialog_button_connect_device (2819038239946643550) -->
- <skip />
+ <string name="media_output_dialog_button_connect_device" msgid="2819038239946643550">"Lidh pajisjen"</string>
<string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Aplikacion i panjohur"</string>
<string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Ndalo transmetimin"</string>
<string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"Pajisjet që ofrohen për daljen e audios."</string>
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Volumi"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
- <!-- no translation found for media_output_group_title_connected_speakers (3413876972919684479) -->
- <skip />
+ <string name="media_output_group_title_connected_speakers" msgid="3413876972919684479">"Altoparlantët e lidhur"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Altoparlantët dhe ekranet"</string>
<string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Pajisjet e sugjeruara"</string>
<string name="media_input_group_title" msgid="2057057473860783021">"Hyrja"</string>
@@ -1328,10 +1338,8 @@
<string name="accessibility_enter_hint" msgid="2617864063504824834">"për të hyrë në pajisje"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Përdor gjurmën e gishtit për ta hapur"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Kërkohet vërtetimi. Prek sensorin e gjurmës së gishtit për t\'u vërtetuar."</string>
- <!-- no translation found for resume_animation (9148788411429330671) -->
- <skip />
- <!-- no translation found for pause_animation (1919705510058107504) -->
- <skip />
+ <string name="resume_animation" msgid="9148788411429330671">"Vazhdo animacionin"</string>
+ <string name="pause_animation" msgid="1919705510058107504">"Vendos në pauzë animacionin"</string>
<string name="ongoing_call_content_description" msgid="6394763878322348560">"Telefonatë në vazhdim"</string>
<string name="ongoing_notification_extra_content_description" msgid="2098752668861351265">"Në vazhdim"</string>
<string name="mobile_data_settings_title" msgid="3955246641380064901">"Të dhënat celulare"</string>
@@ -1513,11 +1521,13 @@
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"Shto shkurtore"</string>
<string name="shortcut_helper_delete_shortcut_button_label" msgid="3148773472696137052">"Fshi shkurtoren"</string>
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navigo duke përdorur tastierën tënde"</string>
- <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Mëso shkurtoret e tastierës"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2131074100205028210) -->
+ <skip />
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navigo duke përdorur bllokun me prekje"</string>
<string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Mëso gjestet e bllokut me prekje"</string>
<string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Navigo duke përdorur tastierën dhe bllokun me prekje"</string>
- <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Mëso gjestet e bllokut me prekje, shkurtoret e tastierës etj."</string>
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (8557098239919786046) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Kthehu prapa"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Shko tek ekrani bazë"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Shiko aplikacionet e fundit"</string>
@@ -1572,10 +1582,10 @@
<string name="overview_edu_notification_content" msgid="3578204677648432500">"Rrëshqit shpejt lart dhe mbaj shtypur me tre gishta. Trokit për të mësuar më shumë gjeste."</string>
<string name="all_apps_edu_notification_title" msgid="372262997265569063">"Përdor tastierën për të shikuar të gjitha aplikacionet"</string>
<string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Shtyp tastin e veprimit në çdo kohë. Trokit për të mësuar më shumë gjeste."</string>
- <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="910988771011857460">"Modaliteti \"Shumë më i zbehtë\" tani është pjesë e rrëshqitësit të ndriçimit"</string>
- <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="4453123359258743230">"Tani mund ta bësh ekranin shumë më të zbehtë duke e ulur nivelin e ndriçimit edhe më tej.\n\nDuke qenë se kjo veçori tani është pjesë e rrëshqitësit të ndriçimit, shkurtoret e modalitetit \"Shumë më i zbehtë\" janë hequr."</string>
- <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="3947537827396916005">"Hiq shkurtoret e modalitetit \"Shumë më i zbehtë\""</string>
- <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="165474092660941104">"Shkurtoret e modalitetit \"Shumë më i zbehtë\" u hoqën"</string>
+ <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="910988771011857460">"Veçoria \"Shumë më i zbehtë\" tani është pjesë e rrëshqitësit të ndriçimit"</string>
+ <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="4453123359258743230">"Tani mund ta bësh ekranin shumë më të zbehtë duke e ulur nivelin e ndriçimit edhe më tej.\n\nDuke qenë se kjo veçori tani është pjesë e rrëshqitësit të ndriçimit, shkurtoret e veçorisë \"Shumë më i zbehtë\" janë hequr."</string>
+ <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="3947537827396916005">"Hiq shkurtoret e veçorisë \"Shumë më i zbehtë\""</string>
+ <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="165474092660941104">"Shkurtoret e veçorisë \"Shumë më i zbehtë\" u hoqën"</string>
<string name="qs_edit_mode_category_connectivity" msgid="4559726936546032672">"Lidhshmëria"</string>
<string name="qs_edit_mode_category_accessibility" msgid="7969091385071475922">"Qasshmëria"</string>
<string name="qs_edit_mode_category_utilities" msgid="8123080090108420095">"Aplikacione utilitare"</string>
@@ -1587,4 +1597,7 @@
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Të gjitha pllakëzat e \"Cilësimeve të shpejta\" do të rivendosen te cilësimet origjinale të pajisjes"</string>
<string name="demote_explain_text" msgid="3942301497888762295">"<xliff:g id="APPLICATION">%1$s</xliff:g> nuk do të shfaqë më përditësime drejtpërdrejt. Këtë mund ta ndryshosh në çdo kohë te \"Cilësimet\"."</string>
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
+ <string name="underlay_close_button_content_description" msgid="6518481455548869894">"Mbyll"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="7666159428113013319">"Rrëshqit shpejt nga lart majtas për të hapur njoftimet"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="3842355853737057332">"Rrëshqit shpejt nga lart djathtas për të hapur \"Cilësimet e shpejta\""</string>
</resources>
diff --git a/packages/SystemUI/res/values-sr-ldrtl/strings.xml b/packages/SystemUI/res/values-sr-ldrtl/strings.xml
index f4106f1..541154f 100644
--- a/packages/SystemUI/res/values-sr-ldrtl/strings.xml
+++ b/packages/SystemUI/res/values-sr-ldrtl/strings.xml
@@ -20,4 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="recents_quick_scrub_onboarding" msgid="2452671841151577157">"Превуците улево да бисте брзо променили апликације"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="6543179539438220549">"Превуците од горњег десног угла да бисте отворили обавештења"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="5760503283520058116">"Превуците од горњег левог угла да бисте отворили Брза подешавања"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index 8068a80..d4addbf 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -398,7 +398,7 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Снимање екрана"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Почните"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Зауставите"</string>
- <string name="qs_record_issue_label" msgid="8166290137285529059">"Евидентирајте проблем"</string>
+ <string name="qs_record_issue_label" msgid="8166290137285529059">"Евидентирај проблем"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"Покрени"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"Заустави"</string>
<string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Извештај о грешци"</string>
@@ -467,6 +467,8 @@
<string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"Да бисте користили дугме микрофона, омогућите приступ микрофону у Подешавањима."</string>
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Отвори Подешавања"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Други уређај"</string>
+ <string name="media_suggestion_disconnected_text" msgid="8777932417076437525">"Пусти на: <xliff:g id="DEVICE_NAME">%s</xliff:g>"</string>
+ <string name="media_suggestion_failure_text" msgid="8453022394887613774">"Повез. није успело. Поновите."</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Укључи/искључи преглед"</string>
<string name="zen_modes_dialog_title" msgid="8854640808100096934">"Режими"</string>
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Готово"</string>
@@ -582,6 +584,7 @@
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
<skip />
<string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Дели цео екран"</string>
+ <string name="screen_share_permission_dialog_option_text_entire_screen_for_display" msgid="1545850238689169166">"Дели: <xliff:g id="DISPLAY_NAME">%s</xliff:g>"</string>
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
<skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Када делите цео екран, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> види све што је на њему. Зато пазите на лозинке, информације о плаћању, поруке, слике, аудио и видео садржај."</string>
@@ -593,6 +596,7 @@
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Желите да пребаците екран?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Пребаци једну апликацију"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Пребаци цео екран"</string>
+ <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen_for_display" msgid="5524817761472941086">"Пребаци: <xliff:g id="DISPLAY_NAME">%s</xliff:g>"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Када пребацујете цео екран, види се све што је на њему. Зато пазите на лозинке, информације о плаћању, поруке, слике, аудио и видео садржај."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Када пребацујете апликацију, види се сав садржај који се приказује или пушта у њој. Зато пазите на лозинке, информације о плаћању, поруке, слике, аудио и видео садржај."</string>
<string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Пребаци екран"</string>
@@ -673,6 +677,7 @@
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Поуздани агент спречава закључавање"</string>
<string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"Уређај је закључан, превише покушаја потврде идентитета"</string>
<string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"Уређај је закључан\nПотврда идентитета није успела"</string>
+ <string name="keyguard_indication_after_watch_disconnected" msgid="6329761892437377710">"Сат је онемогућио приступ"</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="accessibility_volume_settings" msgid="1458961116951564784">"Подешавања звука"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Аутоматски титл за медије"</string>
@@ -799,6 +804,10 @@
<string name="enable_bluetooth_message" msgid="6740938333772779717">"Да бисте повезали тастатуру са таблетом, прво морате да укључите Bluetooth."</string>
<string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Укључи"</string>
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Укључено – на основу лица"</string>
+ <!-- no translation found for notification_guts_bundle_title (1345506995443305361) -->
+ <skip />
+ <!-- no translation found for notification_guts_bundle_summary (3971530802237393600) -->
+ <skip />
<string name="inline_done_button" msgid="6043094985588909584">"Готово"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Примени"</string>
<string name="inline_turn_off_notifications" msgid="2653064779176881329">"Искључи"</string>
@@ -984,6 +993,7 @@
<string name="right_icon" msgid="1103955040645237425">"Десна икона"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Задржите и превуците да бисте додали плочице"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Задржите и превуците да бисте променили распоред плочица"</string>
+ <string name="select_to_rearrange_tiles" msgid="6166153534235179148">"Изаберите плочице да бисте им променили распоред и величину"</string>
<string name="tap_to_position_tile" msgid="6282815817773342757">"Додирните да бисте поставили плочицу"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Превуците овде да бисте уклонили"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Минималан број плочица је <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g>"</string>
@@ -1003,10 +1013,10 @@
<string name="tuner_low_priority" msgid="8412666814123009820">"Прикажи иконе обавештења ниског приоритета"</string>
<string name="other" msgid="429768510980739978">"Друго"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"укључивање или искључивање величине плочице"</string>
- <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"уклонили плочицу"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="4811778796933958616">"Уклоните плочицу"</string>
<string name="accessibility_qs_edit_toggle_placement_mode" msgid="3870429389210569610">"укључивање или искључивање режима постављања"</string>
<string name="accessibility_qs_edit_toggle_selection" msgid="2201248304072372239">"потврдили или опозвали избор"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"додали плочицу на последњу позицију"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="7188303979684400620">"Додајте плочицу на последњу позицију"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Преместите плочицу"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Додајте плочицу на жељену позицију"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Преместите на <xliff:g id="POSITION">%1$d</xliff:g>. позицију"</string>
@@ -1093,7 +1103,8 @@
<string name="auto_saver_text" msgid="3214960308353838764">"Укључите ако ће батерија вероватно да се испразни"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Не, хвала"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"У употреби"</string>
- <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Апликације користе <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps (4994455315457996796) -->
+ <skip />
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" и "</string>
<string name="ongoing_privacy_dialog_using_op" msgid="426635338010011796">"Користи: <xliff:g id="APPLICATION_NAME">%1$s</xliff:g>"</string>
@@ -1508,11 +1519,11 @@
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"Додајте пречицу"</string>
<string name="shortcut_helper_delete_shortcut_button_label" msgid="3148773472696137052">"Избришите пречицу"</string>
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Крећите се помоћу тастатуре"</string>
- <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Сазнајте више о тастерским пречицама"</string>
+ <string name="launch_keyboard_tutorial_notification_content" msgid="2131074100205028210">"Сазнајте више о тастерским пречицама"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Крећите се помоћу тачпеда"</string>
<string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Научите покрете за тачпед"</string>
<string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Крећите се помоћу тастатуре и тачпeда"</string>
- <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Научите покрете за тачпед, тастерске пречице и друго"</string>
+ <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="8557098239919786046">"Научите покрете на тачпеду, тастерске пречице и друго"</string>
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Назад"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Иди на почетни екран"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Прикажи недавно коришћене апликације"</string>
@@ -1582,4 +1593,7 @@
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Све плочице Брзих подешавања ће се ресетовати на првобитна подешавања уређаја"</string>
<string name="demote_explain_text" msgid="3942301497888762295">"<xliff:g id="APPLICATION">%1$s</xliff:g> више неће приказивати новости уживо. Ово можете да промените у сваком тренутку у Подешавањима."</string>
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
+ <string name="underlay_close_button_content_description" msgid="6518481455548869894">"Затвори"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="7666159428113013319">"Превуците од горњег левог угла да бисте отворили обавештења"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="3842355853737057332">"Превуците од горњег десног угла да бисте отворили Брза подешавања"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sv-ldrtl/strings.xml b/packages/SystemUI/res/values-sv-ldrtl/strings.xml
index 1e965f5..065514b 100644
--- a/packages/SystemUI/res/values-sv-ldrtl/strings.xml
+++ b/packages/SystemUI/res/values-sv-ldrtl/strings.xml
@@ -20,4 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="recents_quick_scrub_onboarding" msgid="2452671841151577157">"Tryck och dra åt vänster för att snabbt byta mellan appar"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="6543179539438220549">"Svep från övre högra hörnet för att öppna aviseringar"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="5760503283520058116">"Svep från det övre vänstra hörnet för att öppna snabbinställningar"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index 5be2ce6..fcabf7d 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -252,8 +252,7 @@
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Ansluten till <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"Ansluten till <xliff:g id="CAST">%s</xliff:g>."</string>
<string name="accessibility_expand_group" msgid="521237935987978624">"Utöka gruppen."</string>
- <!-- no translation found for accessibility_collapse_group (1842704016768329517) -->
- <skip />
+ <string name="accessibility_collapse_group" msgid="1842704016768329517">"Komprimera grupp."</string>
<string name="accessibility_add_device_to_group" msgid="5446422960697860806">"Lägg till enheten i gruppen."</string>
<string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"Ta bort enheten från gruppen."</string>
<string name="accessibility_open_application" msgid="1749126077501259712">"Öppna appen."</string>
@@ -338,8 +337,8 @@
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Rotera skärmen automatiskt"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Plats"</string>
<string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Skärmsläckare"</string>
- <string name="quick_settings_camera_label" msgid="5612076679385269339">"Kameraåtkomst"</string>
- <string name="quick_settings_mic_label" msgid="8392773746295266375">"Mikrofonåtkomst"</string>
+ <string name="quick_settings_camera_label" msgid="5612076679385269339">"Kameraåtkomst"</string>
+ <string name="quick_settings_mic_label" msgid="8392773746295266375">"Mikrofonåtkomst"</string>
<string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Tillgänglig"</string>
<string name="quick_settings_camera_mic_blocked" msgid="4710884905006788281">"Blockerad"</string>
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"Medieenhet"</string>
@@ -356,8 +355,8 @@
<string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"Inga tillgängliga enheter"</string>
<string name="quick_settings_cast_no_network" msgid="3863016850468559522">"Ingen wifi- eller Ethernet-anslutning"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Ljusstyrka"</string>
- <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Färginvertering"</string>
- <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Färgkorrigering"</string>
+ <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Färginvertering"</string>
+ <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Färgkorrigering"</string>
<string name="quick_settings_font_scaling_label" msgid="5289001009876936768">"Teckenstorlek"</string>
<string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Välj användare"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Klart"</string>
@@ -468,6 +467,8 @@
<string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"Aktivera mikrofonåtkomst i inställningarna om du vill använda mikrofonknappen."</string>
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Öppna Inställningar"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Annan enhet"</string>
+ <string name="media_suggestion_disconnected_text" msgid="8777932417076437525">"Spela upp på <xliff:g id="DEVICE_NAME">%s</xliff:g>"</string>
+ <string name="media_suggestion_failure_text" msgid="8453022394887613774">"Kan inte ansluta. Försök igen."</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Aktivera och inaktivera översikten"</string>
<string name="zen_modes_dialog_title" msgid="8854640808100096934">"Lägen"</string>
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Klar"</string>
@@ -583,6 +584,7 @@
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
<skip />
<string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Dela hela skärmen"</string>
+ <string name="screen_share_permission_dialog_option_text_entire_screen_for_display" msgid="1545850238689169166">"Dela <xliff:g id="DISPLAY_NAME">%s</xliff:g>"</string>
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
<skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"När du delar hela skärmen är allt på skärmen synligt för <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Var försiktig med sådant som lösenord, betalningsuppgifter, meddelanden, foton, ljud och video."</string>
@@ -594,6 +596,7 @@
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Vill du casta skärmen?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Casta en app"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Casta hela skärmen"</string>
+ <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen_for_display" msgid="5524817761472941086">"Casta <xliff:g id="DISPLAY_NAME">%s</xliff:g>"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"När du castar hela skärmen är allt på skärmen synligt. Var försiktig med sådant som lösenord, betalningsuppgifter, meddelanden, foton, ljud och video."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"När du castar en app är allt som visas eller spelas i den appen synligt. Var försiktig med sådant som lösenord, betalningsuppgifter, meddelanden, foton, ljud och video."</string>
<string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Casta skärmen"</string>
@@ -674,6 +677,7 @@
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Hålls olåst med TrustAgent"</string>
<string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"Enheten låstes på grund av för många autentiseringsförsök"</string>
<string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"Enheten har låsts\nAutentiseringen misslyckades"</string>
+ <string name="keyguard_indication_after_watch_disconnected" msgid="6329761892437377710">"Utelåst av klockan"</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="accessibility_volume_settings" msgid="1458961116951564784">"Ljudinställningar"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Texta media automatiskt"</string>
@@ -800,6 +804,10 @@
<string name="enable_bluetooth_message" msgid="6740938333772779717">"Om du vill ansluta tangentbordet till surfplattan måste du först aktivera Bluetooth."</string>
<string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Aktivera"</string>
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"På – ansiktsbaserad"</string>
+ <!-- no translation found for notification_guts_bundle_title (1345506995443305361) -->
+ <skip />
+ <!-- no translation found for notification_guts_bundle_summary (3971530802237393600) -->
+ <skip />
<string name="inline_done_button" msgid="6043094985588909584">"Klart"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Tillämpa"</string>
<string name="inline_turn_off_notifications" msgid="2653064779176881329">"Inaktivera"</string>
@@ -985,6 +993,7 @@
<string name="right_icon" msgid="1103955040645237425">"Höger ikon"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Lägg till rutor genom att trycka och dra"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Ordna om rutor genom att trycka och dra"</string>
+ <string name="select_to_rearrange_tiles" msgid="6166153534235179148">"Välj rutor som du vill ordna om och ändra storlek på"</string>
<string name="tap_to_position_tile" msgid="6282815817773342757">"Tryck för att placera rutan"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Ta bort genom att dra här"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Minst <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> rutor måste finnas kvar"</string>
@@ -1004,10 +1013,12 @@
<string name="tuner_low_priority" msgid="8412666814123009820">"Visa ikoner för aviseringar med låg prioritet"</string>
<string name="other" msgid="429768510980739978">"Annat"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"växla rutstorlek"</string>
- <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"ta bort ruta"</string>
+ <!-- no translation found for accessibility_qs_edit_remove_tile_action (4811778796933958616) -->
+ <skip />
<string name="accessibility_qs_edit_toggle_placement_mode" msgid="3870429389210569610">"aktivera eller inaktivera placeringsläge"</string>
<string name="accessibility_qs_edit_toggle_selection" msgid="2201248304072372239">"aktivera eller inaktivera val"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"lägg till en ruta på den sista platsen"</string>
+ <!-- no translation found for accessibility_qs_edit_tile_add_action (7188303979684400620) -->
+ <skip />
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Flytta ruta"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Lägg till en ruta på önskad plats"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Flytta till <xliff:g id="POSITION">%1$d</xliff:g>"</string>
@@ -1094,7 +1105,8 @@
<string name="auto_saver_text" msgid="3214960308353838764">"Aktivera när batteriet håller på att ta slut"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Nej tack"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"Används"</string>
- <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"<xliff:g id="TYPES_LIST">%s</xliff:g> används av appar."</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps (4994455315457996796) -->
+ <skip />
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" och "</string>
<string name="ongoing_privacy_dialog_using_op" msgid="426635338010011796">"Används just nu av <xliff:g id="APPLICATION_NAME">%1$s</xliff:g>"</string>
@@ -1251,15 +1263,13 @@
<string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(frånkopplad)"</string>
<string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"Misslyckat byte. Tryck och försök igen."</string>
<string name="media_output_dialog_pairing_new" msgid="5098212763195577270">"Anslut en enhet"</string>
- <!-- no translation found for media_output_dialog_button_connect_device (2819038239946643550) -->
- <skip />
+ <string name="media_output_dialog_button_connect_device" msgid="2819038239946643550">"Anslut enhet"</string>
<string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Okänd app"</string>
<string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Sluta casta"</string>
<string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"Enheter som är tillgängliga för ljudutdata."</string>
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Volym"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g> %%"</string>
- <!-- no translation found for media_output_group_title_connected_speakers (3413876972919684479) -->
- <skip />
+ <string name="media_output_group_title_connected_speakers" msgid="3413876972919684479">"Anslutna högtalare"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Högtalare och skärmar"</string>
<string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Förslag på enheter"</string>
<string name="media_input_group_title" msgid="2057057473860783021">"Ingång"</string>
@@ -1328,10 +1338,8 @@
<string name="accessibility_enter_hint" msgid="2617864063504824834">"ange enhet"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Öppna med fingeravtryck"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Autentisering krävs. Identifiera dig genom att trycka på fingeravtryckssensorn."</string>
- <!-- no translation found for resume_animation (9148788411429330671) -->
- <skip />
- <!-- no translation found for pause_animation (1919705510058107504) -->
- <skip />
+ <string name="resume_animation" msgid="9148788411429330671">"Återuppta animation"</string>
+ <string name="pause_animation" msgid="1919705510058107504">"Pausa animation"</string>
<string name="ongoing_call_content_description" msgid="6394763878322348560">"Pågående samtal"</string>
<string name="ongoing_notification_extra_content_description" msgid="2098752668861351265">"Pågående"</string>
<string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobildata"</string>
@@ -1513,11 +1521,11 @@
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"Lägg till genväg"</string>
<string name="shortcut_helper_delete_shortcut_button_label" msgid="3148773472696137052">"Radera genväg"</string>
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navigera med tangentbordet"</string>
- <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Lär dig kortkommandon"</string>
+ <string name="launch_keyboard_tutorial_notification_content" msgid="2131074100205028210">"Lär dig kortkommandon"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navigera med styrplattan"</string>
<string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Lär dig rörelser för styrplattan"</string>
<string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Navigera med tangentbordet och styrplattan"</string>
- <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Lär dig rörelser för styrplattan, kortkommandon med mera"</string>
+ <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="8557098239919786046">"Lär dig styrplatterörelser, kortkommandon med mera"</string>
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Tillbaka"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Återvänd till startsidan"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Se de senaste apparna"</string>
@@ -1587,4 +1595,7 @@
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Alla Snabbinställningsrutor återställs till enhetens ursprungliga inställningar"</string>
<string name="demote_explain_text" msgid="3942301497888762295">"<xliff:g id="APPLICATION">%1$s</xliff:g> visar inte längre liveuppdateringar. Du kan ändra detta när som helst i inställningarna."</string>
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
+ <string name="underlay_close_button_content_description" msgid="6518481455548869894">"Stäng"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="7666159428113013319">"Svep från övre vänstra hörnet för att öppna aviseringar"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="3842355853737057332">"Svep från det övre högra hörnet för att öppna snabbinställningar"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sw-ldrtl/strings.xml b/packages/SystemUI/res/values-sw-ldrtl/strings.xml
index 4525dbe..8f68bf2 100644
--- a/packages/SystemUI/res/values-sw-ldrtl/strings.xml
+++ b/packages/SystemUI/res/values-sw-ldrtl/strings.xml
@@ -20,4 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="recents_quick_scrub_onboarding" msgid="2452671841151577157">"Buruta kushoto ili ubadili kati ya programu haraka"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="6543179539438220549">"Telezesha kidole kutoka sehemu ya juu kulia ili ufungue arifa"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="5760503283520058116">"Telezesha kidole kutoka sehemu ya juu kushoto ili ufungue Mipangilio ya Haraka"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index e8025fa..62b3c31 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -252,8 +252,7 @@
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Imeunganishwa kwenye <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"Imeunganishwa kwenye <xliff:g id="CAST">%s</xliff:g>."</string>
<string name="accessibility_expand_group" msgid="521237935987978624">"Panua kikundi."</string>
- <!-- no translation found for accessibility_collapse_group (1842704016768329517) -->
- <skip />
+ <string name="accessibility_collapse_group" msgid="1842704016768329517">"Kunja kikundi."</string>
<string name="accessibility_add_device_to_group" msgid="5446422960697860806">"Weka kifaa kwenye kikundi."</string>
<string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"Ondoa kifaa kwenye kikundi."</string>
<string name="accessibility_open_application" msgid="1749126077501259712">"Fungua programu."</string>
@@ -357,7 +356,7 @@
<string name="quick_settings_cast_no_network" msgid="3863016850468559522">"Hakuna Muunganisho wa Wi-Fi au ethaneti"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Ung\'avu"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"Ugeuzaji rangi"</string>
- <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Usahihishaji wa rangirangi"</string>
+ <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Usahihishaji wa rangi"</string>
<string name="quick_settings_font_scaling_label" msgid="5289001009876936768">"Ukubwa wa fonti"</string>
<string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Dhibiti watumiaji"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Nimemaliza"</string>
@@ -402,7 +401,7 @@
<string name="qs_record_issue_label" msgid="8166290137285529059">"Rekodi Hitilafu"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"Anza"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"Simamisha"</string>
- <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Ripoti ya Hitilafu"</string>
+ <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Ripoti Hitilafu"</string>
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Ni sehemu gani ya matumizi ya kifaa iliathiriwa?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Chagua aina ya tatizo"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Rekodi ya skrini"</string>
@@ -412,7 +411,7 @@
<string name="custom" msgid="3337456985275158299">"Maalum"</string>
<string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Mipangilio Maalum ya Historia ya Shughuli"</string>
<string name="restore_default" msgid="5259420807486239755">"Rejesha Mipangilio Chaguomsingi"</string>
- <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Hali ya kutumia kwa mkono mmoja"</string>
+ <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Kutumia kwa mkono mmoja"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Vifaa vya kusikilizia"</string>
<string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Vimeunganishwa"</string>
<string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Havijaunganishwa"</string>
@@ -468,6 +467,8 @@
<string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"Ili utumie kitufe cha maikrofoni, ruhusu ufikiaji wa maikrofoni kwenye Mipangilio."</string>
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Fungua Mipangilio"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Kifaa kingine"</string>
+ <string name="media_suggestion_disconnected_text" msgid="8777932417076437525">"Cheza kwenye <xliff:g id="DEVICE_NAME">%s</xliff:g>"</string>
+ <string name="media_suggestion_failure_text" msgid="8453022394887613774">"Imeshindwa kuunganisha. Jaribu tena."</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Washa Muhtasari"</string>
<string name="zen_modes_dialog_title" msgid="8854640808100096934">"Hali"</string>
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Nimemaliza"</string>
@@ -583,6 +584,7 @@
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
<skip />
<string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Ruhusu ufikiaji wa skrini nzima"</string>
+ <string name="screen_share_permission_dialog_option_text_entire_screen_for_display" msgid="1545850238689169166">"Rekodi <xliff:g id="DISPLAY_NAME">%s</xliff:g>"</string>
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
<skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Unaporuhusu ufikiaji wa skrini nzima, chochote kilicho katika skrini yako kitaonekana kwa <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Kwa hivyo kuwa mwangalifu na vitu kama vile manenosiri, maelezo ya malipo, ujumbe, picha, sauti na video."</string>
@@ -594,6 +596,7 @@
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Ungependa kutuma maudhui yaliyo katika skrini yako?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Tuma maudhui ya programu moja"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Tuma maudhui katika skrini nzima"</string>
+ <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen_for_display" msgid="5524817761472941086">"Tuma <xliff:g id="DISPLAY_NAME">%s</xliff:g>"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Unapotuma maudhui katika skrini yako nzima, chochote kilicho kwenye skrini yako kitaonekana. Kwa hivyo kuwa mwangalifu na vitu kama vile manenosiri, maelezo ya malipo, ujumbe, picha, sauti na video."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Unapotuma maudhui ya programu moja, chochote kinachoonekana au kucheza katika programu hiyo kitaonekana. Kwa hivyo kuwa mwangalifu na vitu kama vile manenosiri, maelezo ya malipo, ujumbe, picha, sauti na video."</string>
<string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Tuma maudhui yaliyo kwenye skrini"</string>
@@ -672,8 +675,9 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Kifaa hiki kinadhibitiwa na mzazi wako. Mzazi wako anaweza kuona na kudhibiti maelezo kama vile programu unazotumia, mahali ulipo na muda unaotumia kwenye kifaa."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Imefunguliwa na kipengele cha kutathmini hali ya kuaminika"</string>
- <string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"Kifaa kimefungwa, majaribio ya uthibitishaji ni mengi mno"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"Kifaa kimefungwa, umejaribu kuthibitisha mara nyingi mno"</string>
<string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"Kifaa kimefungwa\nImeshindwa kuthibitisha"</string>
+ <string name="keyguard_indication_after_watch_disconnected" msgid="6329761892437377710">"Imefungwa na saa yako"</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="accessibility_volume_settings" msgid="1458961116951564784">"Mipangilio ya sauti"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Wekea maudhui manukuu kiotomatiki"</string>
@@ -706,18 +710,18 @@
<string name="screen_pinning_exit" msgid="4553787518387346893">"Programu imebanduliwa"</string>
<string name="stream_voice_call" msgid="7468348170702375660">"Piga simu"</string>
<string name="stream_system" msgid="7663148785370565134">"Mfumo"</string>
- <string name="stream_ring" msgid="7550670036738697526">"Piga"</string>
+ <string name="stream_ring" msgid="7550670036738697526">"Mlio"</string>
<string name="stream_music" msgid="2188224742361847580">"Maudhui"</string>
<string name="stream_alarm" msgid="16058075093011694">"Kengele"</string>
<string name="stream_notification" msgid="7930294049046243939">"Arifa"</string>
<string name="stream_bluetooth_sco" msgid="6234562365528664331">"Bluetooth"</string>
<string name="stream_dtmf" msgid="7322536356554673067">"Masafa ya ishara ya kampuni ya simu"</string>
<string name="stream_accessibility" msgid="3873610336741987152">"Zana za walio na matatizo ya kuona au kusikia"</string>
- <string name="volume_ringer_status_normal" msgid="1339039682222461143">"Piga"</string>
+ <string name="volume_ringer_status_normal" msgid="1339039682222461143">"Mlio"</string>
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Kutetema"</string>
<string name="volume_ringer_status_silent" msgid="3691324657849880883">"Zima sauti"</string>
<string name="media_device_cast" msgid="4786241789687569892">"Tuma"</string>
- <string name="stream_notification_unavailable" msgid="4313854556205836435">"Halipatikani kwa sababu sauti imezimwa"</string>
+ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Haipatikani kwa sababu sauti imezimwa"</string>
<string name="stream_alarm_unavailable" msgid="4059817189292197839">"Imeshindwa kwa sababu Usinisumbue imewashwa"</string>
<string name="stream_media_unavailable" msgid="6823020894438959853">"Imeshindwa kwa sababu Usinisumbue imewashwa"</string>
<string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Haipatikani kwa sababu umewasha hali ya <xliff:g id="MODE">%s</xliff:g>"</string>
@@ -728,10 +732,10 @@
<string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Gusa ili uweke mtetemo."</string>
<string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Gusa ili usitishe."</string>
<string name="volume_panel_noise_control_title" msgid="7413949943872304474">"Kidhibiti cha Kelele"</string>
- <string name="volume_panel_spatial_audio_title" msgid="3367048857932040660">"Sauti Inayojirekebisha Kulingana na Hali"</string>
+ <string name="volume_panel_spatial_audio_title" msgid="3367048857932040660">"Sauti Inayojirekebisha"</string>
<string name="volume_panel_spatial_audio_off" msgid="4177490084606772989">"Imezimwa"</string>
- <string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Imerekebishwa"</string>
- <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Ufuatilizi wa Kichwa"</string>
+ <string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Isiyojirekebisha"</string>
+ <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Kufuatilia Kichwa"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Gusa ili ubadilishe hali ya programu inayotoa milio ya simu"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"hali ya programu inayotoa milio ya simu"</string>
<string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, gusa ili ubadilishe hali ya programu inayotoa milio ya simu"</string>
@@ -800,6 +804,10 @@
<string name="enable_bluetooth_message" msgid="6740938333772779717">"Ili uunganishe Kibodi yako kwenye kompyuta yako kibao, lazima kwanza uwashe Bluetooth."</string>
<string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Washa"</string>
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Imewashwa - Inayolenga nyuso"</string>
+ <!-- no translation found for notification_guts_bundle_title (1345506995443305361) -->
+ <skip />
+ <!-- no translation found for notification_guts_bundle_summary (3971530802237393600) -->
+ <skip />
<string name="inline_done_button" msgid="6043094985588909584">"Nimemaliza"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Tumia"</string>
<string name="inline_turn_off_notifications" msgid="2653064779176881329">"Zima"</string>
@@ -985,6 +993,7 @@
<string name="right_icon" msgid="1103955040645237425">"Aikoni ya kulia"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Shikilia na uburute ili uongeze vigae"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Shikilia na uburute ili upange vigae upya"</string>
+ <string name="select_to_rearrange_tiles" msgid="6166153534235179148">"Chagua vigae ili upange upya na ubadilishe ukubwa"</string>
<string name="tap_to_position_tile" msgid="6282815817773342757">"Gusa ili upangilie kigae"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Buruta hapa ili uondoe"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Unahitaji angalau vigae <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g>"</string>
@@ -1004,10 +1013,12 @@
<string name="tuner_low_priority" msgid="8412666814123009820">"Onyesha aikoni za arifa zisizo muhimu"</string>
<string name="other" msgid="429768510980739978">"Nyingine"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"ubadilishe ukubwa wa kigae"</string>
- <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"ondoa kigae"</string>
+ <!-- no translation found for accessibility_qs_edit_remove_tile_action (4811778796933958616) -->
+ <skip />
<string name="accessibility_qs_edit_toggle_placement_mode" msgid="3870429389210569610">"kugeuza hali ya uwekaji"</string>
<string name="accessibility_qs_edit_toggle_selection" msgid="2201248304072372239">"kugeuza uteuzi"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"weka kigae kwenye nafasi ya mwisho"</string>
+ <!-- no translation found for accessibility_qs_edit_tile_add_action (7188303979684400620) -->
+ <skip />
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Hamisha kigae"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Weka kigae kwenye nafasi unayopenda"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Hamishia kwenye <xliff:g id="POSITION">%1$d</xliff:g>"</string>
@@ -1094,7 +1105,8 @@
<string name="auto_saver_text" msgid="3214960308353838764">"Washa wakati betri inakaribia kuisha"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Hapana"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"Inatumika"</string>
- <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Programu zinatumia <xliff:g id="TYPES_LIST">%s</xliff:g> yako."</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps (4994455315457996796) -->
+ <skip />
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" na "</string>
<string name="ongoing_privacy_dialog_using_op" msgid="426635338010011796">"Inatumiwa na <xliff:g id="APPLICATION_NAME">%1$s</xliff:g>"</string>
@@ -1251,15 +1263,13 @@
<string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(imetenganishwa)"</string>
<string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"Imeshindwa kubadilisha. Gusa ili ujaribu tena."</string>
<string name="media_output_dialog_pairing_new" msgid="5098212763195577270">"Unganisha kifaa"</string>
- <!-- no translation found for media_output_dialog_button_connect_device (2819038239946643550) -->
- <skip />
+ <string name="media_output_dialog_button_connect_device" msgid="2819038239946643550">"Unganisha Kifaa"</string>
<string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Programu isiyojulikana"</string>
<string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Acha kutuma"</string>
<string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"Vifaa vya kutoa sauti vilivyopo"</string>
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Sauti"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
- <!-- no translation found for media_output_group_title_connected_speakers (3413876972919684479) -->
- <skip />
+ <string name="media_output_group_title_connected_speakers" msgid="3413876972919684479">"Spika zilizounganishwa"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Spika na Skrini"</string>
<string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Vifaa Vilivyopendekezwa"</string>
<string name="media_input_group_title" msgid="2057057473860783021">"Vifaa vya kuingiza maudhui"</string>
@@ -1328,10 +1338,8 @@
<string name="accessibility_enter_hint" msgid="2617864063504824834">"weka kifaa"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Tumia alama ya kidole kufungua"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Uthibitishaji unahitajika. Gusa kitambua alama ya kidole ili uthibitishe."</string>
- <!-- no translation found for resume_animation (9148788411429330671) -->
- <skip />
- <!-- no translation found for pause_animation (1919705510058107504) -->
- <skip />
+ <string name="resume_animation" msgid="9148788411429330671">"Endelea kuhuisha"</string>
+ <string name="pause_animation" msgid="1919705510058107504">"Sitisha uhuishaji"</string>
<string name="ongoing_call_content_description" msgid="6394763878322348560">"Simu inayoendelea"</string>
<string name="ongoing_notification_extra_content_description" msgid="2098752668861351265">"Inaendelea"</string>
<string name="mobile_data_settings_title" msgid="3955246641380064901">"Data ya mtandao wa simu"</string>
@@ -1513,11 +1521,11 @@
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"Weka njia ya mkato"</string>
<string name="shortcut_helper_delete_shortcut_button_label" msgid="3148773472696137052">"Futa njia ya mkato"</string>
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Kusogeza kwa kutumia kibodi yako"</string>
- <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Fahamu kuhusu mikato ya kibodi"</string>
+ <string name="launch_keyboard_tutorial_notification_content" msgid="2131074100205028210">"Pata maelezo kuhusu mikato ya kibodi"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Kusogeza kwa kutumia padi yako ya kugusa"</string>
<string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Fahamu miguso ya padi ya kugusa"</string>
<string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Kusogeza kwa kutumia kibodi na padi yako ya kugusa"</string>
- <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Fahamu kuhusu miguso ya padi ya kugusa, mikato ya kibodi na mengineyo"</string>
+ <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="8557098239919786046">"Pata maelezo kuhusu miguso kwenye padi ya kugusa, mikato ya kibodi na mengineyo"</string>
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Rudi nyuma"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Nenda kwenye ukurasa wa mwanzo"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Angalia programu za hivi majuzi"</string>
@@ -1533,7 +1541,7 @@
<string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Nenda kwenye skrini ya kwanza"</string>
<string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Telezesha vidole vitatu juu kwenye padi yako ya kugusa"</string>
<string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Kazi nzuri!"</string>
- <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Umeweka ishara ya kwenda kwenye skrini ya kwanza"</string>
+ <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Umekamilisha mguso wa kwenda kwenye skrini ya kwanza"</string>
<string name="touchpad_home_gesture_error_body" msgid="3810674109999513073">"Telezesha vidole vitatu juu kwenye padi yako ya kugusa ili uende kwenye skrini yako ya kwanza"</string>
<string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Angalia programu za hivi majuzi"</string>
<string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Telezesha vidole vitatu juu kisha ushikilie kwenye padi yako ya kugusa"</string>
@@ -1587,4 +1595,7 @@
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Vigae vyote vya Mipangilio ya Haraka vitabadilishwa kuwa katika mipangilio halisi ya kifaa"</string>
<string name="demote_explain_text" msgid="3942301497888762295">"<xliff:g id="APPLICATION">%1$s</xliff:g> haitaonyesha tena Taarifa za Moja kwa Moja. Unaweza kubadilisha hali hii wakati wowote katika Mipangilio."</string>
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
+ <string name="underlay_close_button_content_description" msgid="6518481455548869894">"Funga"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="7666159428113013319">"Telezesha kidole kutoka sehemu ya juu kushoto ili ufungue arifa"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="3842355853737057332">"Telezesha kidole kutoka sehemu ya juu kulia ili ufungue Mipangilio ya Haraka"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sw900dp/config.xml b/packages/SystemUI/res/values-sw900dp/config.xml
index 221b013..2a1c4ec 100644
--- a/packages/SystemUI/res/values-sw900dp/config.xml
+++ b/packages/SystemUI/res/values-sw900dp/config.xml
@@ -21,4 +21,7 @@
<!-- Nav bar button default ordering/layout -->
<string name="config_navBarLayout" translatable="false">back,home,left;space;right,recent</string>
+ <!-- The number of columns in the Split Shade QuickSettings -->
+ <integer name="quick_settings_split_shade_num_columns">6</integer>
+
</resources>
diff --git a/packages/SystemUI/res/values-ta-ldrtl/strings.xml b/packages/SystemUI/res/values-ta-ldrtl/strings.xml
index 22a7b45..68e95c1 100644
--- a/packages/SystemUI/res/values-ta-ldrtl/strings.xml
+++ b/packages/SystemUI/res/values-ta-ldrtl/strings.xml
@@ -20,4 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="recents_quick_scrub_onboarding" msgid="2452671841151577157">"ஆப்ஸை வேகமாக மாற்ற, இடப்புறம் இழுக்கவும்"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="6543179539438220549">"அறிவிப்புகளைத் திறக்க மேல் வலதுபுறத்தில் இருந்து ஸ்வைப் செய்யலாம்"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="5760503283520058116">"விரைவு அமைப்புகளைத் திறக்க மேல் இடதுபுறத்தில் இருந்து ஸ்வைப் செய்யுங்கள்"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index 9c56c8e..a861724 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -252,8 +252,7 @@
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g>க்கு இணைக்கப்பட்டது."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"<xliff:g id="CAST">%s</xliff:g> உடன் இணைக்கப்பட்டுள்ளது."</string>
<string name="accessibility_expand_group" msgid="521237935987978624">"குழுவை விரிவாக்கும்."</string>
- <!-- no translation found for accessibility_collapse_group (1842704016768329517) -->
- <skip />
+ <string name="accessibility_collapse_group" msgid="1842704016768329517">"குழுவைச் சுருக்கும்."</string>
<string name="accessibility_add_device_to_group" msgid="5446422960697860806">"குழுவில் சாதனத்தைச் சேர்க்கும்."</string>
<string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"குழுவிலிருந்து சாதனத்தை அகற்றும்."</string>
<string name="accessibility_open_application" msgid="1749126077501259712">"ஆப்ஸைத் திறக்கும்."</string>
@@ -382,13 +381,13 @@
<string name="quick_settings_work_mode_paused_state" msgid="6681788236383735976">"இடைநிறுத்தப்பட்டது"</string>
<string name="quick_settings_night_display_label" msgid="8180030659141778180">"நைட் லைட்"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"மாலையில் ஆன் செய்"</string>
- <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"காலை வரை"</string>
+ <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"சூரிய உதயம் வரை"</string>
<string name="quick_settings_night_secondary_label_on_at" msgid="3584738542293528235">"<xliff:g id="TIME">%s</xliff:g>க்கு ஆன் செய்"</string>
<string name="quick_settings_secondary_label_until" msgid="1883981263191927372">"<xliff:g id="TIME">%s</xliff:g> வரை"</string>
<string name="quick_settings_ui_mode_night_label" msgid="1398928270610780470">"டார்க் தீம்"</string>
<string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"பேட்டரி சேமிப்பு"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"மாலையில் ஆன் செய்"</string>
- <string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"காலை வரை"</string>
+ <string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"சூரிய உதயம் வரை"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"<xliff:g id="TIME">%s</xliff:g>க்கு ஆன் செய்"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g> வரை"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at_bedtime" msgid="2274300599408864897">"உறக்க நேரத்தின்போது இயக்கப்படும்"</string>
@@ -468,6 +467,8 @@
<string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"மைக்ரோஃபோன் பட்டனைப் பயன்படுத்த, மைக்ரோஃபோன் அணுகலை அமைப்புகளில் இயக்கவும்."</string>
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"அமைப்புகளைத் திற"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"பிற சாதனம்"</string>
+ <string name="media_suggestion_disconnected_text" msgid="8777932417076437525">"<xliff:g id="DEVICE_NAME">%s</xliff:g> இல் பிளே செய்"</string>
+ <string name="media_suggestion_failure_text" msgid="8453022394887613774">"தோல்வி. மீண்டும் முயலவும்."</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"மேலோட்டப் பார்வையை நிலைமாற்று"</string>
<string name="zen_modes_dialog_title" msgid="8854640808100096934">"பயன்முறைகள்"</string>
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"முடிந்தது"</string>
@@ -583,6 +584,7 @@
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
<skip />
<string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"முழுத் திரையையும் பகிர்"</string>
+ <string name="screen_share_permission_dialog_option_text_entire_screen_for_display" msgid="1545850238689169166">"<xliff:g id="DISPLAY_NAME">%s</xliff:g>ஐப் பகிர்தல்"</string>
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
<skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"உங்கள் முழுத்திரையையும் பகிரும்போது, திரையில் உள்ள அனைத்தும் <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> இல் தெரியும். எனவே கடவுச்சொற்கள், பேமெண்ட் விவரங்கள், மெசேஜ்கள், படங்கள், ஆடியோ, வீடியோ போன்றவை குறித்துக் கவனத்துடன் இருங்கள்."</string>
@@ -594,6 +596,7 @@
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"உங்கள் திரையை அலைபரப்ப வேண்டுமா?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"ஓர் ஆப்ஸை அலைபரப்பு"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"முழுத்திரையையும் அலைபரப்பு"</string>
+ <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen_for_display" msgid="5524817761472941086">"<xliff:g id="DISPLAY_NAME">%s</xliff:g>ஐ அலைபரப்புதல்"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"உங்கள் முழுத்திரையையும் அலைபரப்பும்போது திரையில் உள்ள அனைத்தையும் பார்க்க முடியும். எனவே கடவுச்சொற்கள், பேமெண்ட் விவரங்கள், மெசேஜ்கள், படங்கள், ஆடியோ, வீடியோ போன்றவை குறித்துக் கவனத்துடன் இருங்கள்."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"ஓர் ஆப்ஸை அலைபரப்பும்போது அதில் காட்டப்படுகின்ற அல்லது அதில் பிளே செய்யப்படுகின்ற அனைத்தையும் பார்க்க முடியும். எனவே கடவுச்சொற்கள், பேமெண்ட் விவரங்கள், மெசேஜ்கள், படங்கள், ஆடியோ, வீடியோ போன்றவை குறித்துக் கவனத்துடன் இருங்கள்."</string>
<string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"திரையை அலைபரப்பு"</string>
@@ -674,6 +677,7 @@
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"TrustAgent இதைத் திறந்தே வைத்துள்ளது"</string>
<string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"அளவுக்கதிகமான அங்கீகரிப்பு முயற்சிகளால் சாதனம் பூட்டப்பட்டது"</string>
<string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"சாதனம் பூட்டப்பட்டது\nஅங்கீகரிக்க முடியவில்லை"</string>
+ <string name="keyguard_indication_after_watch_disconnected" msgid="6329761892437377710">"உங்கள் வாட்ச் மூலம் பூட்டப்பட்டது"</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="accessibility_volume_settings" msgid="1458961116951564784">"ஒலி அமைப்புகள்"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"வசன உரைகளைத் தானாக எழுதும்"</string>
@@ -800,6 +804,10 @@
<string name="enable_bluetooth_message" msgid="6740938333772779717">"உங்கள் டேப்லெட்டுடன் கீபோர்டை இணைக்க, முதலில் புளூடூத்தை இயக்க வேண்டும்."</string>
<string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"இயக்கு"</string>
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"ஆன் - முகம் அடிப்படையிலானது"</string>
+ <!-- no translation found for notification_guts_bundle_title (1345506995443305361) -->
+ <skip />
+ <!-- no translation found for notification_guts_bundle_summary (3971530802237393600) -->
+ <skip />
<string name="inline_done_button" msgid="6043094985588909584">"முடிந்தது"</string>
<string name="inline_ok_button" msgid="603075490581280343">"பயன்படுத்து"</string>
<string name="inline_turn_off_notifications" msgid="2653064779176881329">"ஆஃப் செய்"</string>
@@ -985,6 +993,7 @@
<string name="right_icon" msgid="1103955040645237425">"வலப்புற ஐகான்"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"கட்டங்களைச் சேர்க்க, அவற்றைப் பிடித்து இழுக்கவும்"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"கட்டங்களை மறுவரிசைப்படுத்த அவற்றைப் பிடித்து இழுக்கவும்"</string>
+ <string name="select_to_rearrange_tiles" msgid="6166153534235179148">"வரிசையையும் அளவையும் மாற்ற, கட்டங்களைத் தேர்ந்தெடுங்கள்"</string>
<string name="tap_to_position_tile" msgid="6282815817773342757">"விரும்பிய இடத்தில் கட்டத்தை வைக்க தட்டவும்"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"அகற்ற, இங்கே இழுக்கவும்"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"குறைந்தது <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> கட்டங்கள் தேவை"</string>
@@ -1004,10 +1013,10 @@
<string name="tuner_low_priority" msgid="8412666814123009820">"குறைந்த முன்னுரிமை உள்ள அறிவிப்பு ஐகான்களைக் காட்டு"</string>
<string name="other" msgid="429768510980739978">"மற்றவை"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"கட்டத்தின் அளவை நிலைமாற்றும்"</string>
- <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"கட்டத்தை அகற்றும்"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="4811778796933958616">"கட்டத்தை அகற்றும்"</string>
<string name="accessibility_qs_edit_toggle_placement_mode" msgid="3870429389210569610">"இடப் பயன்முறையை நிலைமாற்ற"</string>
<string name="accessibility_qs_edit_toggle_selection" msgid="2201248304072372239">"தேர்வை நிலைமாற்ற"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"கடைசி இடத்தில் கட்டத்தைச் சேர்க்கலாம்"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="7188303979684400620">"கடைசி இடத்தில் கட்டத்தைச் சேர்க்கும்"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"கட்டத்தை நகர்த்து"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"விரும்பிய இடத்தில் கட்டத்தைச் சேர்க்கலாம்"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g>க்கு நகர்த்தும்"</string>
@@ -1094,7 +1103,8 @@
<string name="auto_saver_text" msgid="3214960308353838764">"பேட்டரி தீர்ந்துபோகும் நிலையில் இருக்கும் போது ஆன் செய்யப்படும்"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"வேண்டாம்"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"உபயோகத்தில் உள்ளது"</string>
- <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"உங்கள் <xliff:g id="TYPES_LIST">%s</xliff:g> ஆகியவற்றை ஆப்ஸ் பயன்படுத்துகின்றன."</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps (4994455315457996796) -->
+ <skip />
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" மற்றும் "</string>
<string name="ongoing_privacy_dialog_using_op" msgid="426635338010011796">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> பயன்படுத்துகிறது"</string>
@@ -1251,15 +1261,13 @@
<string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(துண்டிக்கப்பட்டது)"</string>
<string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"இணைக்க முடியவில்லை. மீண்டும் முயல தட்டவும்."</string>
<string name="media_output_dialog_pairing_new" msgid="5098212763195577270">"சாதனத்தை இணை"</string>
- <!-- no translation found for media_output_dialog_button_connect_device (2819038239946643550) -->
- <skip />
+ <string name="media_output_dialog_button_connect_device" msgid="2819038239946643550">"சாதனத்தை இணை"</string>
<string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"அறியப்படாத ஆப்ஸ்"</string>
<string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"அலைபரப்புவதை நிறுத்து"</string>
<string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"ஆடியோ அவுட்புட்டுக்குக் கிடைக்கும் சாதனங்கள்."</string>
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"ஒலியளவு"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
- <!-- no translation found for media_output_group_title_connected_speakers (3413876972919684479) -->
- <skip />
+ <string name="media_output_group_title_connected_speakers" msgid="3413876972919684479">"இணைக்கப்பட்டுள்ள ஸ்பீக்கர்கள்"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"ஸ்பீக்கர்கள் & டிஸ்ப்ளேக்கள்"</string>
<string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"பரிந்துரைக்கப்படும் சாதனங்கள்"</string>
<string name="media_input_group_title" msgid="2057057473860783021">"உள்ளீடு"</string>
@@ -1328,10 +1336,8 @@
<string name="accessibility_enter_hint" msgid="2617864063504824834">"சாதனத்தைத் திற"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"கைரேகையைப் பயன்படுத்தி திறந்திடுங்கள்"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"அங்கீகாரம் தேவை. கைரேகை சென்சாரைத் தொட்டு அங்கீகரியுங்கள்."</string>
- <!-- no translation found for resume_animation (9148788411429330671) -->
- <skip />
- <!-- no translation found for pause_animation (1919705510058107504) -->
- <skip />
+ <string name="resume_animation" msgid="9148788411429330671">"அனிமேஷனை மீண்டும் தொடங்கும்"</string>
+ <string name="pause_animation" msgid="1919705510058107504">"அனிமேஷனை இடைநிறுத்தும்"</string>
<string name="ongoing_call_content_description" msgid="6394763878322348560">"செயலில் உள்ள அழைப்பு"</string>
<string name="ongoing_notification_extra_content_description" msgid="2098752668861351265">"செயலிலுள்ளது"</string>
<string name="mobile_data_settings_title" msgid="3955246641380064901">"மொபைல் டேட்டா"</string>
@@ -1513,11 +1519,11 @@
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"ஷார்ட்கட்டைச் சேர்க்கும்"</string>
<string name="shortcut_helper_delete_shortcut_button_label" msgid="3148773472696137052">"ஷார்ட்கட்டை அகற்றும்"</string>
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"கீபோர்டைப் பயன்படுத்திச் செல்லுதல்"</string>
- <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"கீபோர்டு ஷார்ட்கட்கள் குறித்துத் தெரிந்துகொள்ளுங்கள்"</string>
+ <string name="launch_keyboard_tutorial_notification_content" msgid="2131074100205028210">"கீபோர்டு ஷார்ட்கட்களைத் தெரிந்துகொள்ளுங்கள்"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"டச்பேடைப் பயன்படுத்திச் செல்லுதல்"</string>
<string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"டச்பேட் சைகைள் குறித்துத் தெரிந்துகொள்ளுங்கள்"</string>
<string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"உங்கள் டச்பேட் மற்றும் கீபோர்டைப் பயன்படுத்திச் செல்லுதல்"</string>
- <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"டச்பேட் சைகைகள், கீபோர்டு ஷார்ட்கட்கள் மற்றும் பலவற்றைத் தெரிந்துகொள்ளுங்கள்"</string>
+ <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="8557098239919786046">"டச்பேட் சைகைகள், கீபோர்டு ஷார்ட்கட்கள் மற்றும் பலவற்றைத் தெரிந்துகொள்ளுங்கள்"</string>
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"பின்செல்"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"முகப்பிற்குச் செல்"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"சமீபத்திய ஆப்ஸைக் காட்டுதல்"</string>
@@ -1587,4 +1593,7 @@
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"விரைவு அமைப்புகளின் கட்டங்கள் அனைத்தும் சாதனத்தின் அசல் அமைப்புகளுக்கு மீட்டமைக்கப்படும்"</string>
<string name="demote_explain_text" msgid="3942301497888762295">"<xliff:g id="APPLICATION">%1$s</xliff:g> இனி நேரலை அறிவிப்புகளைக் காட்டாது. இதை எப்போது வேண்டுமானாலும் அமைப்புகளில் மாற்றலாம்."</string>
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
+ <string name="underlay_close_button_content_description" msgid="6518481455548869894">"மூடும்"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="7666159428113013319">"அறிவிப்புகளைத் திறக்க மேல் இடதுபுறத்தில் இருந்து ஸ்வைப் செய்யலாம்"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="3842355853737057332">"விரைவு அமைப்புகளைத் திறக்க மேல் வலதுபுறத்தில் இருந்து ஸ்வைப் செய்யுங்கள்"</string>
</resources>
diff --git a/packages/SystemUI/res/values-te-ldrtl/strings.xml b/packages/SystemUI/res/values-te-ldrtl/strings.xml
index 94bdbcf..7b8dd6c 100644
--- a/packages/SystemUI/res/values-te-ldrtl/strings.xml
+++ b/packages/SystemUI/res/values-te-ldrtl/strings.xml
@@ -20,4 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="recents_quick_scrub_onboarding" msgid="2452671841151577157">"యాప్లను శీఘ్రంగా స్విచ్ చేయడానికి ఎడమ వైపునకు లాగండి"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="6543179539438220549">"పైన కుడి వైపు నుండి స్వైప్ చేసి నోటిఫికేషన్లను తెరవండి"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="5760503283520058116">"ఎగువ ఎడమ వైపు నుండి స్వైప్ చేసి క్విక్ సెట్టింగ్లను తెరవండి"</string>
</resources>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index d19a0cf..18c699f 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -118,7 +118,7 @@
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"స్క్రీన్ను రికార్డ్ చేయండి"</string>
<string name="screenrecord_app_selector_title" msgid="3854492366333954736">"రికార్డ్ చేయడానికి యాప్ను ఎంచుకోండి"</string>
<string name="screenrecord_audio_label" msgid="6183558856175159629">"ఆడియోను రికార్డ్ చేయండి"</string>
- <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"పరికరం ఆడియో"</string>
+ <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"డివైజ్ ఆడియో"</string>
<string name="screenrecord_device_audio_description" msgid="4922694220572186193">"మీ పరికరం నుండి వచ్చే మ్యూజిక్, కాల్స్, రింగ్టోన్ల వంటి ధ్వనులు"</string>
<string name="screenrecord_mic_label" msgid="2111264835791332350">"మైక్రోఫోన్"</string>
<string name="screenrecord_device_audio_and_mic_label" msgid="1831323771978646841">"పరికరం ఆడియో, మైక్రోఫోన్"</string>
@@ -234,7 +234,7 @@
<string name="face_re_enroll_dialog_title" msgid="6392173708176069994">"ఫేస్ అన్లాక్ను సెటప్ చేయండి"</string>
<string name="face_re_enroll_dialog_content" msgid="7353502359464038511">"ఫేస్ అన్లాక్ను మళ్లీ సెటప్ చేయడానికి, మీ ప్రస్తుత ఫేస్ మోడల్ను తొలగించడం జరుగుతుంది.\n\nమీ ఫోన్ను అన్లాక్ చేసేందుకు మీ ముఖాన్ని ఉపయోగించడానికి మీరు ఈ ఫీచర్ను మళ్లీ సెటప్ చేయాల్సి ఉంటుంది."</string>
<string name="face_reenroll_failure_dialog_content" msgid="7073947334397236935">"ఫేస్ అన్లాక్ను సెటప్ చేయడం సాధ్యపడలేదు. సెట్టింగ్లకు వెళ్లి, ఆపై మళ్లీ ట్రై చేయండి."</string>
- <string name="fingerprint_dialog_touch_sensor" msgid="2817887108047658975">"వేలిముద్ర సెన్సార్ను తాకండి"</string>
+ <string name="fingerprint_dialog_touch_sensor" msgid="2817887108047658975">"వేలిముద్ర (ఫింగర్ప్రింట్) సెన్సర్ను తాకండి"</string>
<string name="fingerprint_dialog_authenticated_confirmation" msgid="1603899612957562862">"కొనసాగించడానికి అన్లాక్ చిహ్నాన్ని నొక్కండి"</string>
<string name="fingerprint_dialog_use_fingerprint_instead" msgid="5542430577183894219">"ముఖం గుర్తించలేదు. బదులుగా వేలిముద్ర ఉపయోగించండి."</string>
<!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) -->
@@ -247,7 +247,7 @@
<string name="accessibility_bluetooth_device_settings_gear" msgid="3314916468105272540">"పరికర వివరాలను కాన్ఫిగర్ చేయడానికి క్లిక్ చేయండి"</string>
<string name="accessibility_bluetooth_device_settings_gear_with_name" msgid="114373701123165491">"<xliff:g id="DEVICE_NAME">%s</xliff:g>. పరికర వివరాలను కాన్ఫిగర్ చేయండి"</string>
<string name="accessibility_bluetooth_device_settings_see_all" msgid="5260390270128256620">"అన్ని పరికరాలను చూడండి"</string>
- <string name="accessibility_bluetooth_device_settings_pair_new_device" msgid="7988547106800504256">"కొత్త పరికరాన్ని పెయిర్ చేయండి"</string>
+ <string name="accessibility_bluetooth_device_settings_pair_new_device" msgid="7988547106800504256">"కొత్త డివైస్ని పెయిర్ చేయండి"</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"బ్యాటరీ శాతం తెలియదు."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g>కి కనెక్ట్ చేయబడింది."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"<xliff:g id="CAST">%s</xliff:g>కి కనెక్ట్ చేయబడింది."</string>
@@ -467,6 +467,8 @@
<string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"మైక్రోఫోన్ బటన్ను ఉపయోగించడానికి, సెట్టింగ్లలో మైక్రోఫోన్ యాక్సెస్ను ఎనేబుల్ చేయండి."</string>
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"సెట్టింగ్లను తెరవండి"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"ఇతర పరికరం"</string>
+ <string name="media_suggestion_disconnected_text" msgid="8777932417076437525">"<xliff:g id="DEVICE_NAME">%s</xliff:g>లో ప్లే చేయండి"</string>
+ <string name="media_suggestion_failure_text" msgid="8453022394887613774">"కనెక్ట్ కాలేదు. రీట్రై చేయండి."</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"స్థూలదృష్టిని టోగుల్ చేయి"</string>
<string name="zen_modes_dialog_title" msgid="8854640808100096934">"మోడ్లు"</string>
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"పూర్తయింది"</string>
@@ -582,6 +584,7 @@
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
<skip />
<string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"మొత్తం స్క్రీన్ను షేర్ చేయండి"</string>
+ <string name="screen_share_permission_dialog_option_text_entire_screen_for_display" msgid="1545850238689169166">"<xliff:g id="DISPLAY_NAME">%s</xliff:g>ను షేర్ చేయండి"</string>
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
<skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"మీ మొత్తం స్క్రీన్ను మీరు షేర్ చేసేటప్పుడు, మీ స్క్రీన్పై ఉన్నవన్నీ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>కు కనిపిస్తాయి. కాబట్టి పాస్వర్డ్లు, పేమెంట్ వివరాలు, మెసేజ్లు, ఫోటోలు, ఆడియో, ఇంకా వీడియో వంటి విషయాల్లో జాగ్రత్త వహించండి."</string>
@@ -593,6 +596,7 @@
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"మీ స్క్రీన్ను ప్రసారం చేయాలా?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"ఒక యాప్ను ప్రసారం చేయండి"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"మొత్తం స్క్రీన్ను ప్రసారం చేయండి"</string>
+ <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen_for_display" msgid="5524817761472941086">"<xliff:g id="DISPLAY_NAME">%s</xliff:g>ను ప్రసారం చేయండి"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"మీ స్క్రీన్ను మీరు ప్రసారం చేసేటప్పుడు, మీ స్క్రీన్పై ఉన్నవన్నీ కనిపిస్తాయి. కాబట్టి పాస్వర్డ్లు, పేమెంట్ వివరాలు, మెసేజ్లు, ఫోటోలు, ఆడియో, ఇంకా వీడియో వంటి విషయాల్లో జాగ్రత్త వహించండి."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"మీరు యాప్ను ప్రసారం చేసేటప్పుడు, సంబంధిత యాప్లో చూపబడేవన్నీ లేదా ప్లే అయ్యేవన్నీ కనిపిస్తాయి. కాబట్టి పాస్వర్డ్లు, పేమెంట్ వివరాలు, మెసేజ్లు, ఫోటోలు, ఆడియో, ఇంకా వీడియో వంటి విషయాల్లో జాగ్రత్త వహించండి."</string>
<string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"స్క్రీన్ను ప్రసారం చేయండి"</string>
@@ -673,6 +677,7 @@
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"TrustAgent ద్వారా అన్లాక్ చేయబడింది"</string>
<string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"చాలాసార్లు ప్రామాణీకరించడానికి ప్రయత్నించినందున పరికరం లాక్ అయింది"</string>
<string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"ప్రామాణీకరణ విఫలమైంది\nపరికరం లాక్ అయింది"</string>
+ <string name="keyguard_indication_after_watch_disconnected" msgid="6329761892437377710">"మీ వాచ్ ద్వారా లాక్ చేయబడింది"</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="accessibility_volume_settings" msgid="1458961116951564784">"ధ్వని సెట్టింగ్లు"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"మీడియాకు ఆటోమేటిక్ క్యాప్షన్లు"</string>
@@ -717,8 +722,8 @@
<string name="volume_ringer_status_silent" msgid="3691324657849880883">"మ్యూట్"</string>
<string name="media_device_cast" msgid="4786241789687569892">"ప్రసారం చేయండి"</string>
<string name="stream_notification_unavailable" msgid="4313854556205836435">"వాల్యూమ్ మ్యూట్ అయినందున అందుబాటులో లేదు"</string>
- <string name="stream_alarm_unavailable" msgid="4059817189292197839">"అంతరాయంకలిగించవద్దు ఆన్లో ఉన్నందున అందుబాటులోలేదు"</string>
- <string name="stream_media_unavailable" msgid="6823020894438959853">"అంతరాయంకలిగించవద్దు ఆన్లో ఉన్నందున అందుబాటులోలేదు"</string>
+ <string name="stream_alarm_unavailable" msgid="4059817189292197839">"అంతరాయం కలిగించవద్దు ఆన్లో ఉన్నందున అందుబాటులోలేదు"</string>
+ <string name="stream_media_unavailable" msgid="6823020894438959853">"అంతరాయం కలిగించవద్దు ఆన్లో ఉన్నందున అందుబాటులోలేదు"</string>
<string name="stream_unavailable_by_modes" msgid="3674139029490353683">"<xliff:g id="MODE">%s</xliff:g> ఆన్లో ఉన్నందున అందుబాటులో లేదు"</string>
<string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"అందుబాటులో లేదు"</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. అన్మ్యూట్ చేయడానికి నొక్కండి."</string>
@@ -799,6 +804,10 @@
<string name="enable_bluetooth_message" msgid="6740938333772779717">"మీ కీబోర్డ్ను మీ టాబ్లెట్తో కనెక్ట్ చేయడానికి, మీరు ముందుగా బ్లూటూత్ ఆన్ చేయాలి."</string>
<string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"ఆన్ చేయి"</string>
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"\'ముఖం ఆధారం\'ను - ఆన్ చేయండి"</string>
+ <!-- no translation found for notification_guts_bundle_title (1345506995443305361) -->
+ <skip />
+ <!-- no translation found for notification_guts_bundle_summary (3971530802237393600) -->
+ <skip />
<string name="inline_done_button" msgid="6043094985588909584">"పూర్తయింది"</string>
<string name="inline_ok_button" msgid="603075490581280343">"అప్లయి చేయి"</string>
<string name="inline_turn_off_notifications" msgid="2653064779176881329">"ఆఫ్ చేయండి"</string>
@@ -984,6 +993,7 @@
<string name="right_icon" msgid="1103955040645237425">"కుడివైపు ఉన్న చిహ్నం"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"టైల్స్ను జోడించడానికి పట్టుకుని, లాగండి"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"టైల్స్ను వేరే క్రమంలో అమర్చడానికి వాటిని పట్టుకుని, లాగండి"</string>
+ <string name="select_to_rearrange_tiles" msgid="6166153534235179148">"క్రమాన్ని, సైజ్ను మార్చడానికి టైల్స్ను ఎంచుకోండి"</string>
<string name="tap_to_position_tile" msgid="6282815817773342757">"పొజిషన్ టైల్ను ట్యాప్ చేయండి"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"తీసివేయడానికి ఇక్కడికి లాగండి"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"మీ వద్ద కనీసం <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> టైల్లు ఉండాలి"</string>
@@ -1003,10 +1013,10 @@
<string name="tuner_low_priority" msgid="8412666814123009820">"తక్కువ ప్రాధాన్యత నోటిఫికేషన్ చిహ్నాలను చూపించు"</string>
<string name="other" msgid="429768510980739978">"ఇతరం"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"టైల్ సైజ్ను టోగుల్ చేయండి"</string>
- <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"టైల్ను తీసివేయండి"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="4811778796933958616">"టైల్ను తీసివేయండి"</string>
<string name="accessibility_qs_edit_toggle_placement_mode" msgid="3870429389210569610">"ప్లేస్మెంట్ మోడ్ను టోగుల్ చేయండి"</string>
<string name="accessibility_qs_edit_toggle_selection" msgid="2201248304072372239">"టోగుల్ ఎంపిక"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"చివరి పొజిషన్కు టైల్ను జోడించండి"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="7188303979684400620">"చివరి పొజిషన్కు టైల్ను జోడించండి"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"టైల్ను తరలించండి"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"కావలసిన పొజిషన్కు టైల్ను జోడించండి"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g>కు తరలించండి"</string>
@@ -1093,7 +1103,8 @@
<string name="auto_saver_text" msgid="3214960308353838764">"బ్యాటరీ ఛార్జింగ్ పూర్తిగా అయిపోతున్న తరుణంలో ఆన్ చేస్తుంది"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"వద్దు, ధన్యవాదాలు"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"వినియోగంలో ఉంది"</string>
- <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"అప్లికేషన్లు మీ <xliff:g id="TYPES_LIST">%s</xliff:g>ని ఉపయోగిస్తున్నాయి."</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps (4994455315457996796) -->
+ <skip />
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" మరియు "</string>
<string name="ongoing_privacy_dialog_using_op" msgid="426635338010011796">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> ద్వారా ఉపయోగించబడుతోంది"</string>
@@ -1249,7 +1260,7 @@
<string name="media_output_dialog_multiple_devices" msgid="1093771040315422350">"<xliff:g id="COUNT">%1$d</xliff:g> పరికరాలు ఎంచుకోబడ్డాయి"</string>
<string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(డిస్కనెక్ట్ అయ్యింది)"</string>
<string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"స్విచ్ చేయడం సాధ్యం కాదు. మళ్ళీ ట్రై చేయడానికి ట్యాప్ చేయండి."</string>
- <string name="media_output_dialog_pairing_new" msgid="5098212763195577270">"పరికరాన్ని కనెక్ట్ చేయండి"</string>
+ <string name="media_output_dialog_pairing_new" msgid="5098212763195577270">"డివైజ్ను కనెక్ట్ చేయండి"</string>
<string name="media_output_dialog_button_connect_device" msgid="2819038239946643550">"పరికరాన్ని కనెక్ట్ చేయండి"</string>
<string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"తెలియని యాప్"</string>
<string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"ప్రసారాన్ని ఆపివేయండి"</string>
@@ -1508,11 +1519,11 @@
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"షార్ట్కట్ను జోడించండి"</string>
<string name="shortcut_helper_delete_shortcut_button_label" msgid="3148773472696137052">"షార్ట్కట్ను తొలగించండి"</string>
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"మీ కీబోర్డ్ ఉపయోగించి నావిగేట్ చేయండి"</string>
- <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"కీబోర్డ్ షార్ట్కట్ల గురించి తెలుసుకోండి"</string>
+ <string name="launch_keyboard_tutorial_notification_content" msgid="2131074100205028210">"కీబోర్డ్ షార్ట్కట్లను నేర్చుకోండి"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"మీ టచ్ప్యాడ్ను ఉపయోగించి నావిగేట్ చేయండి"</string>
<string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"టచ్ప్యాడ్ సంజ్ఞల గురించి తెలుసుకోండి"</string>
<string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"మీ కీబోర్డ్, టచ్ప్యాడ్ను ఉపయోగించి నావిగేట్ చేయండి"</string>
- <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"టచ్ప్యాడ్ సంజ్ఞలు, కీబోర్డ్ షార్ట్కట్లు మొదలైన వాటి గురించి తెలుసుకోండి"</string>
+ <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="8557098239919786046">"టచ్ప్యాడ్ సంజ్ఞలు, కీబోర్డ్ షార్ట్కట్లు మొదలైన వాటి గురించి తెలుసుకోండి"</string>
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"వెనుకకు వెళ్లండి"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"మొదటి ట్యాబ్కు వెళ్లండి"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"ఇటీవలి యాప్లను చూడండి"</string>
@@ -1582,4 +1593,7 @@
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"అన్ని క్విక్ సెట్టింగ్ల టైల్స్, పరికరం తాలూకు ఒరిజినల్ సెట్టింగ్లకు రీసెట్ చేయబడతాయి"</string>
<string name="demote_explain_text" msgid="3942301497888762295">"<xliff:g id="APPLICATION">%1$s</xliff:g> ఇకపై లైవ్ అప్డేట్లను చూపించదు. మీరు దీన్ని ఎప్పుడైనా సెట్టింగ్లలో మార్చవచ్చు."</string>
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
+ <string name="underlay_close_button_content_description" msgid="6518481455548869894">"మూసివేయండి"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="7666159428113013319">"పైన ఎడమ వైపు నుండి స్వైప్ చేసి నోటిఫికేషన్లను తెరవండి"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="3842355853737057332">"ఎగువ కుడి వైపు నుండి స్వైప్ చేసి క్విక్ సెట్టింగ్లను తెరవండి"</string>
</resources>
diff --git a/packages/SystemUI/res/values-th-ldrtl/strings.xml b/packages/SystemUI/res/values-th-ldrtl/strings.xml
index 2efb4aa..60a6f00 100644
--- a/packages/SystemUI/res/values-th-ldrtl/strings.xml
+++ b/packages/SystemUI/res/values-th-ldrtl/strings.xml
@@ -20,4 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="recents_quick_scrub_onboarding" msgid="2452671841151577157">"ลากไปทางซ้ายเพื่อสลับแอปอย่างรวดเร็ว"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="6543179539438220549">"ปัดจากด้านขวาบนเพื่อเปิดการแจ้งเตือน"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="5760503283520058116">"ปัดจากด้านซ้ายบนเพื่อเปิดการตั้งค่าด่วน"</string>
</resources>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index 13695a0..71a91c2 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -467,6 +467,8 @@
<string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"หากต้องการใช้ปุ่มไมโครโฟน ให้เปิดการเข้าถึงไมโครโฟนในการตั้งค่า"</string>
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"เปิดการตั้งค่า"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"อุปกรณ์อื่น"</string>
+ <string name="media_suggestion_disconnected_text" msgid="8777932417076437525">"เล่นใน <xliff:g id="DEVICE_NAME">%s</xliff:g>"</string>
+ <string name="media_suggestion_failure_text" msgid="8453022394887613774">"เชื่อมต่อไม่ได้ ลองอีกครั้ง"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"สลับภาพรวม"</string>
<string name="zen_modes_dialog_title" msgid="8854640808100096934">"โหมด"</string>
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"เสร็จสิ้น"</string>
@@ -582,6 +584,7 @@
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
<skip />
<string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"แชร์ทั้งหน้าจอ"</string>
+ <string name="screen_share_permission_dialog_option_text_entire_screen_for_display" msgid="1545850238689169166">"แชร์ <xliff:g id="DISPLAY_NAME">%s</xliff:g>"</string>
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
<skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"เมื่อกำลังแชร์ทั้งหน้าจอ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> จะมองเห็นทุกสิ่งที่อยู่บนหน้าจอ ดังนั้นโปรดระวังสิ่งต่างๆ อย่างเช่นรหัสผ่าน รายละเอียดการชำระเงิน ข้อความ รูปภาพ รวมถึงเสียงและวิดีโอ"</string>
@@ -593,6 +596,7 @@
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"แคสต์หน้าจอของคุณไหม"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"แคสต์แอปเดียว"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"แคสต์ทั้งหน้าจอ"</string>
+ <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen_for_display" msgid="5524817761472941086">"แคสต์ไปยัง <xliff:g id="DISPLAY_NAME">%s</xliff:g>"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"เมื่อกำลังแคสต์ทั้งหน้าจอ ทุกสิ่งที่อยู่บนหน้าจอจะมองเห็นได้ ดังนั้นโปรดระวังสิ่งต่างๆ อย่างเช่นรหัสผ่าน รายละเอียดการชำระเงิน ข้อความ รูปภาพ รวมถึงเสียงและวิดีโอ"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"เมื่อกำลังแคสต์แอป ทุกสิ่งที่แสดงหรือเล่นอยู่ในแอปดังกล่าวจะมองเห็นได้ ดังนั้นโปรดระวังสิ่งต่างๆ อย่างเช่นรหัสผ่าน รายละเอียดการชำระเงิน ข้อความ รูปภาพ รวมถึงเสียงและวิดีโอ"</string>
<string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"แคสต์หน้าจอ"</string>
@@ -673,6 +677,7 @@
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"ปลดล็อกไว้โดย TrustAgent"</string>
<string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"ล็อกอุปกรณ์แล้ว เนื่องจากตรวจสอบสิทธิ์หลายครั้งเกินไป"</string>
<string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"ล็อกอุปกรณ์แล้ว\nการตรวจสอบสิทธิ์ไม่สำเร็จ"</string>
+ <string name="keyguard_indication_after_watch_disconnected" msgid="6329761892437377710">"ล็อกโดยนาฬิกาของคุณ"</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="accessibility_volume_settings" msgid="1458961116951564784">"การตั้งค่าเสียง"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"แสดงคำบรรยายสื่อโดยอัตโนมัติ"</string>
@@ -719,7 +724,7 @@
<string name="stream_notification_unavailable" msgid="4313854556205836435">"เปลี่ยนไม่ได้เนื่องจากปิดเสียงเรียกเข้า"</string>
<string name="stream_alarm_unavailable" msgid="4059817189292197839">"ไม่พร้อมใช้งานเนื่องจากโหมดห้ามรบกวนเปิดอยู่"</string>
<string name="stream_media_unavailable" msgid="6823020894438959853">"ไม่พร้อมใช้งานเนื่องจากโหมดห้ามรบกวนเปิดอยู่"</string>
- <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"ใช้งานไม่ได้เนื่องจากเปิด<xliff:g id="MODE">%s</xliff:g>อยู่"</string>
+ <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"ใช้งานไม่ได้เนื่องจากเปิดโหมด<xliff:g id="MODE">%s</xliff:g>อยู่"</string>
<string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"ไม่พร้อมใช้งาน"</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s แตะเพื่อเปิดเสียง"</string>
<string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s แตะเพื่อตั้งค่าให้สั่น อาจมีการปิดเสียงบริการการเข้าถึง"</string>
@@ -799,6 +804,10 @@
<string name="enable_bluetooth_message" msgid="6740938333772779717">"หากต้องการเชื่อมต่อแป้นพิมพ์กับแท็บเล็ต คุณต้องเปิดบลูทูธก่อน"</string>
<string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"เปิด"</string>
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"เปิด - ตามใบหน้า"</string>
+ <!-- no translation found for notification_guts_bundle_title (1345506995443305361) -->
+ <skip />
+ <!-- no translation found for notification_guts_bundle_summary (3971530802237393600) -->
+ <skip />
<string name="inline_done_button" msgid="6043094985588909584">"เสร็จสิ้น"</string>
<string name="inline_ok_button" msgid="603075490581280343">"ใช้"</string>
<string name="inline_turn_off_notifications" msgid="2653064779176881329">"ปิด"</string>
@@ -984,6 +993,7 @@
<string name="right_icon" msgid="1103955040645237425">"ไอคอนทางขวา"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"กดค้างแล้วลากเพื่อเพิ่มการ์ด"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"กดค้างแล้วลากเพื่อจัดเรียงการ์ดใหม่"</string>
+ <string name="select_to_rearrange_tiles" msgid="6166153534235179148">"เลือกการ์ดเพื่อจัดเรียงใหม่และปรับขนาด"</string>
<string name="tap_to_position_tile" msgid="6282815817773342757">"แตะเพื่อกำหนดตำแหน่งการ์ด"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"ลากมาที่นี่เพื่อนำออก"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"คุณต้องมีการ์ดอย่างน้อย <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> รายการ"</string>
@@ -1003,10 +1013,10 @@
<string name="tuner_low_priority" msgid="8412666814123009820">"แสดงไอคอนการแจ้งเตือนลำดับความสำคัญต่ำ"</string>
<string name="other" msgid="429768510980739978">"อื่นๆ"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"สลับขนาดของการ์ด"</string>
- <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"นำชิ้นส่วนออก"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="4811778796933958616">"นำการ์ดออก"</string>
<string name="accessibility_qs_edit_toggle_placement_mode" msgid="3870429389210569610">"สลับโหมดตำแหน่ง"</string>
<string name="accessibility_qs_edit_toggle_selection" msgid="2201248304072372239">"สลับการเลือก"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"เพิ่มการ์ดไปยังตำแหน่งสุดท้าย"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="7188303979684400620">"เพิ่มการ์ดไปยังตำแหน่งสุดท้าย"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"ย้ายชิ้นส่วน"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"เพิ่มการ์ดไปยังตำแหน่งที่ต้องการ"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"ย้ายไปที่ <xliff:g id="POSITION">%1$d</xliff:g>"</string>
@@ -1093,7 +1103,8 @@
<string name="auto_saver_text" msgid="3214960308353838764">"เปิดเมื่อมีแนวโน้มว่าแบตเตอรี่จะหมด"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"ไม่เป็นไร"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"ใช้งานอยู่"</string>
- <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"หลายแอปพลิเคชันใช้<xliff:g id="TYPES_LIST">%s</xliff:g>ของคุณอยู่"</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps (4994455315457996796) -->
+ <skip />
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" และ "</string>
<string name="ongoing_privacy_dialog_using_op" msgid="426635338010011796">"ใช้อยู่โดย<xliff:g id="APPLICATION_NAME">%1$s</xliff:g>"</string>
@@ -1508,11 +1519,11 @@
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"เพิ่มทางลัด"</string>
<string name="shortcut_helper_delete_shortcut_button_label" msgid="3148773472696137052">"ลบทางลัด"</string>
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"ไปยังส่วนต่างๆ โดยใช้แป้นพิมพ์"</string>
- <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"ดูข้อมูลเกี่ยวกับแป้นพิมพ์ลัด"</string>
+ <string name="launch_keyboard_tutorial_notification_content" msgid="2131074100205028210">"ดูข้อมูลเกี่ยวกับแป้นพิมพ์ลัด"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"ไปยังส่วนต่างๆ โดยใช้ทัชแพด"</string>
<string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"ดูข้อมูลเกี่ยวกับท่าทางสัมผัสของทัชแพด"</string>
<string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"ไปยังส่วนต่างๆ โดยใช้แป้นพิมพ์และทัชแพด"</string>
- <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"ดูข้อมูลเกี่ยวกับท่าทางสัมผัสของทัชแพด แป้นพิมพ์ลัด และอื่นๆ"</string>
+ <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="8557098239919786046">"ดูข้อมูลเกี่ยวกับท่าทางสัมผัสของทัชแพด แป้นพิมพ์ลัด และอื่นๆ"</string>
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"ย้อนกลับ"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"ไปที่หน้าแรก"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"ดูแอปล่าสุด"</string>
@@ -1528,7 +1539,7 @@
<string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"ไปที่หน้าแรก"</string>
<string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"ใช้ 3 นิ้วปัดขึ้นบนทัชแพด"</string>
<string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"เก่งมาก"</string>
- <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"คุณทำท่าทางสัมผัสเพื่อไปที่หน้าแรกสำเร็จแล้ว"</string>
+ <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"คุณทำท่าทางสัมผัสเพื่อไปที่หน้าจอหลักสำเร็จแล้ว"</string>
<string name="touchpad_home_gesture_error_body" msgid="3810674109999513073">"ใช้ 3 นิ้วปัดขึ้นบนทัชแพดเพื่อไปยังหน้าจอหลัก"</string>
<string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"ดูแอปล่าสุด"</string>
<string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"ใช้ 3 นิ้วปัดขึ้นแล้วค้างไว้บนทัชแพด"</string>
@@ -1549,7 +1560,7 @@
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"ไฟแบ็กไลต์ของแป้นพิมพ์"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"ระดับที่ %1$d จาก %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"ระบบควบคุมอุปกรณ์สมาร์ทโฮม"</string>
- <string name="home_controls_dream_description" msgid="4644150952104035789">"เข้าถึงระบบควบคุมอุปกรณ์สมาร์ทโฮมได้อย่างรวดเร็วผ่านภาพพักหน้าจอ"</string>
+ <string name="home_controls_dream_description" msgid="4644150952104035789">"เข้าถึงระบบควบคุมอุปกรณ์สมาร์ทโฮมได้อย่างรวดเร็วผ่านภาพพักหน้าจอ"</string>
<string name="volume_undo_action" msgid="5815519725211877114">"เลิกทำ"</string>
<string name="back_edu_toast_content" msgid="4530314597378982956">"หากต้องการย้อนกลับ ให้ใช้ 3 นิ้วปัดไปทางซ้ายหรือขวาบนทัชแพด"</string>
<string name="home_edu_toast_content" msgid="3381071147871955415">"หากต้องการไปที่หน้าแรก ให้ใช้ 3 นิ้วปัดขึ้นบนทัชแพด"</string>
@@ -1582,4 +1593,7 @@
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"การ์ดการตั้งค่าด่วนทั้งหมดจะรีเซ็ตเป็นการตั้งค่าเดิมของอุปกรณ์"</string>
<string name="demote_explain_text" msgid="3942301497888762295">"<xliff:g id="APPLICATION">%1$s</xliff:g> จะไม่แสดงข้อมูลอัปเดตแบบเรียลไทม์อีกต่อไป คุณเปลี่ยนการตั้งค่านี้ได้ทุกเมื่อในการตั้งค่า"</string>
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
+ <string name="underlay_close_button_content_description" msgid="6518481455548869894">"ปิด"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="7666159428113013319">"ปัดจากด้านซ้ายบนเพื่อเปิดการแจ้งเตือน"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="3842355853737057332">"ปัดจากด้านบนขวาเพื่อเปิดการตั้งค่าด่วน"</string>
</resources>
diff --git a/packages/SystemUI/res/values-tl-ldrtl/strings.xml b/packages/SystemUI/res/values-tl-ldrtl/strings.xml
index 679f9f9..a26c4d1 100644
--- a/packages/SystemUI/res/values-tl-ldrtl/strings.xml
+++ b/packages/SystemUI/res/values-tl-ldrtl/strings.xml
@@ -20,4 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="recents_quick_scrub_onboarding" msgid="2452671841151577157">"I-drag pakaliwa para mabilisang magpalipat-lipat ng app."</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="6543179539438220549">"Mag-swipe mula sa kanang bahagi sa itaas para buksan ang mga notification"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="5760503283520058116">"Mag-swipe mula sa kaliwang bahagi sa itaas para buksan ang Mga Mabilisang Setting"</string>
</resources>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index 04f6fa7..164f45b 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -467,6 +467,8 @@
<string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"Para gamitin ang button ng mikropono, i-enable ang access sa mikropono sa Mga Setting."</string>
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Buksan ang Mga Setting"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Iba pang device"</string>
+ <string name="media_suggestion_disconnected_text" msgid="8777932417076437525">"I-play sa <xliff:g id="DEVICE_NAME">%s</xliff:g>"</string>
+ <string name="media_suggestion_failure_text" msgid="8453022394887613774">"Di\' makakonekta. Subukan ulit."</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"I-toggle ang Overview"</string>
<string name="zen_modes_dialog_title" msgid="8854640808100096934">"Mga Mode"</string>
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Tapos na"</string>
@@ -582,6 +584,7 @@
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
<skip />
<string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"I-share ang buong screen"</string>
+ <string name="screen_share_permission_dialog_option_text_entire_screen_for_display" msgid="1545850238689169166">"I-share ang <xliff:g id="DISPLAY_NAME">%s</xliff:g>"</string>
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
<skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Kapag ibinahagi mo ang iyong buong screen, makikita ng <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ang kahit anong nasa screen mo. Kaya mag-ingat sa mga bagay-bagay tulad ng mga password, detalye ng pagbabayad, mensahe, larawan, at audio at video."</string>
@@ -593,6 +596,7 @@
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"I-cast ang iyong screen?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Mag-cast ng isang app"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"I-cast ang buong screen"</string>
+ <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen_for_display" msgid="5524817761472941086">"I-cast ang <xliff:g id="DISPLAY_NAME">%s</xliff:g>"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Kapag na-cast mo ang iyong buong screen, makikita ang anumang nasa screen mo. Kaya mag-ingat sa mga bagay-bagay tulad ng mga password, detalye ng pagbabayad, mensahe, larawan, at audio at video."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Kapag nagka-cast ka ng app, makikita ang anumang ipinapakita o pine-play sa app na iyon. Kaya mag-ingat sa mga bagay-bagay tulad ng mga password, detalye ng pagbabayad, mensahe, larawan, at audio at video."</string>
<string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"I-cast ang screen"</string>
@@ -673,6 +677,7 @@
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Pinanatiling naka-unlock ng TrustAgent"</string>
<string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"Na-lock ang device, masyadong maraming pagsubok sa pag-authenticate"</string>
<string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"Na-lock ang device\nHindi pumasa sa pag-authenticate"</string>
+ <string name="keyguard_indication_after_watch_disconnected" msgid="6329761892437377710">"Na-lock out ng iyong relo"</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="accessibility_volume_settings" msgid="1458961116951564784">"Mga setting ng tunog"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"I-autocaption ang media"</string>
@@ -799,6 +804,8 @@
<string name="enable_bluetooth_message" msgid="6740938333772779717">"Upang ikonekta ang iyong keyboard sa iyong tablet, kailangan mo munang i-on ang Bluetooth."</string>
<string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"I-on"</string>
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Naka-on - Batay sa mukha"</string>
+ <string name="notification_guts_bundle_title" msgid="1345506995443305361">"Gamitin ang Organizer ng Notification"</string>
+ <string name="notification_guts_bundle_summary" msgid="3971530802237393600">"Para sa <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="inline_done_button" msgid="6043094985588909584">"Tapos na"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Ilapat"</string>
<string name="inline_turn_off_notifications" msgid="2653064779176881329">"I-off"</string>
@@ -984,6 +991,7 @@
<string name="right_icon" msgid="1103955040645237425">"Icon ng kanan"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Pindutin nang matagal at i-drag para magdagdag ng mga tile"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Pindutin nang matagal at i-drag para ayusin ulit ang tile"</string>
+ <string name="select_to_rearrange_tiles" msgid="6166153534235179148">"Pumili ng mga tile na aayusin ulit at ire-resize"</string>
<string name="tap_to_position_tile" msgid="6282815817773342757">"I-tap para iposisyon ang tile"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"I-drag dito upang alisin"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Kailangan mo ng kahit <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> (na) tile"</string>
@@ -1003,10 +1011,10 @@
<string name="tuner_low_priority" msgid="8412666814123009820">"Ipakita ang mga icon ng notification na may mababang priority"</string>
<string name="other" msgid="429768510980739978">"Iba pa"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"i-toggle ang laki ng tile"</string>
- <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"alisin ang tile"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="4811778796933958616">"Alisin ang tile"</string>
<string name="accessibility_qs_edit_toggle_placement_mode" msgid="3870429389210569610">"i-toggle ang placement mode"</string>
<string name="accessibility_qs_edit_toggle_selection" msgid="2201248304072372239">"i-toggle ang pagpili"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"magdagdag ng tile sa huling posisyon"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="7188303979684400620">"Magdagdag ng tile sa huling posisyon"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Ilipat ang tile"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Magdagdag ng tile sa gustong posisyon"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Ilipat sa <xliff:g id="POSITION">%1$d</xliff:g>"</string>
@@ -1093,7 +1101,7 @@
<string name="auto_saver_text" msgid="3214960308353838764">"I-on kapag malamang na maubos ang baterya"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Hindi, salamat na lang"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"Ginagamit"</string>
- <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Ginagamit ng mga application ang iyong <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
+ <string name="ongoing_privacy_chip_content_multiple_apps" msgid="4994455315457996796">"Ginagamit ang <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" at "</string>
<string name="ongoing_privacy_dialog_using_op" msgid="426635338010011796">"Ginagamit ng <xliff:g id="APPLICATION_NAME">%1$s</xliff:g>"</string>
@@ -1508,11 +1516,11 @@
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"Maglagay ng shortcut"</string>
<string name="shortcut_helper_delete_shortcut_button_label" msgid="3148773472696137052">"Mag-delete ng shortcut"</string>
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Mag-navigate gamit ang iyong keyboard"</string>
- <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Matuto ng mga keyboard shortcut"</string>
+ <string name="launch_keyboard_tutorial_notification_content" msgid="2131074100205028210">"Matuto ng mga keyboard shortcut"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Mag-navigate gamit ang iyong touchpad"</string>
<string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Matuto ng mga galaw sa touchpad"</string>
<string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Mag-navigate gamit ang iyong keyboard at touchpad"</string>
- <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Matuto ng mga galaw sa touchpad, keyboard shortcut, at higit pa"</string>
+ <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="8557098239919786046">"Matuto ng mga galaw sa touchpad, keyboard shortcut, at marami pang iba"</string>
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Bumalik"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Pumunta sa home"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Tingnan ang mga kamakailang app"</string>
@@ -1582,4 +1590,7 @@
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Magre-reset sa mga orihinal na setting ng device ang lahat ng tile ng Mga Mabilisang Setting"</string>
<string name="demote_explain_text" msgid="3942301497888762295">"Hindi na magpapakita ang <xliff:g id="APPLICATION">%1$s</xliff:g> ng Mga Live na Update. Puwede mo itong baguhin anumang oras sa Mga Setting."</string>
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
+ <string name="underlay_close_button_content_description" msgid="6518481455548869894">"Isara"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="7666159428113013319">"Mag-swipe mula sa kaliwang bahagi sa itaas para buksan ang mga notification"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="3842355853737057332">"Mag-swipe mula sa kanang bahagi sa itaas para buksan ang Mga Mabilisang Setting"</string>
</resources>
diff --git a/packages/SystemUI/res/values-tr-ldrtl/strings.xml b/packages/SystemUI/res/values-tr-ldrtl/strings.xml
index a423d77..56ddf63 100644
--- a/packages/SystemUI/res/values-tr-ldrtl/strings.xml
+++ b/packages/SystemUI/res/values-tr-ldrtl/strings.xml
@@ -20,4 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="recents_quick_scrub_onboarding" msgid="2452671841151577157">"Uygulamaları hızlıca değiştirmek için sola sürükleyin"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="6543179539438220549">"Bildirimleri açmak için sağ üstten kaydırın"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="5760503283520058116">"Hızlı Ayarlar\'ı açmak için sol üstten kaydırın"</string>
</resources>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index 69fdc1e..a31f63c 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -252,8 +252,7 @@
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g> ile bağlı."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"<xliff:g id="CAST">%s</xliff:g> bağlantısı kuruldu."</string>
<string name="accessibility_expand_group" msgid="521237935987978624">"Grubu genişlet."</string>
- <!-- no translation found for accessibility_collapse_group (1842704016768329517) -->
- <skip />
+ <string name="accessibility_collapse_group" msgid="1842704016768329517">"Grubu daralt"</string>
<string name="accessibility_add_device_to_group" msgid="5446422960697860806">"Cihazı gruba ekle."</string>
<string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"Cihazı gruptan kaldır."</string>
<string name="accessibility_open_application" msgid="1749126077501259712">"Uygulama aç."</string>
@@ -468,6 +467,8 @@
<string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"Mikrofon düğmesini kullanmak için Ayarlar\'da mikrofon erişimini etkinleştirin."</string>
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Ayarlar\'ı aç"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Diğer cihaz"</string>
+ <string name="media_suggestion_disconnected_text" msgid="8777932417076437525">"<xliff:g id="DEVICE_NAME">%s</xliff:g> cihazında oynat"</string>
+ <string name="media_suggestion_failure_text" msgid="8453022394887613774">"Bağlanılamıyor. Yine deneyin."</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Genel bakışı aç/kapat"</string>
<string name="zen_modes_dialog_title" msgid="8854640808100096934">"Modlar"</string>
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Bitti"</string>
@@ -583,6 +584,7 @@
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
<skip />
<string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Tüm ekranı paylaş"</string>
+ <string name="screen_share_permission_dialog_option_text_entire_screen_for_display" msgid="1545850238689169166">"<xliff:g id="DISPLAY_NAME">%s</xliff:g> ekranını paylaş"</string>
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
<skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>, tüm ekranınızı paylaştığınızda ekranınızdaki her şeyi görebilir. Bu nedenle şifre, ödeme ayrıntıları, mesaj, fotoğraf, ses ve video gibi öğeler konusunda dikkatli olun."</string>
@@ -594,6 +596,7 @@
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Ekranınız yayınlansın mı?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"1 uygulamayı yayınla"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Tüm ekranı yayınla"</string>
+ <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen_for_display" msgid="5524817761472941086">"<xliff:g id="DISPLAY_NAME">%s</xliff:g> sitesini yayınla"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Tüm ekranınızı yayınladığınızda ekranınızdaki her şey görünür. Bu nedenle şifre, ödeme ayrıntıları, mesaj, fotoğraf, ses ve video gibi öğeler konusunda dikkatli olun."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Bir uygulamayı yayınladığınızda o uygulamada gösterilen veya oynatılan her şey görünür. Bu nedenle şifre, ödeme ayrıntıları, mesaj, fotoğraf, ses ve video gibi öğeler konusunda dikkatli olun."</string>
<string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Ekranı yayınla"</string>
@@ -674,6 +677,7 @@
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"TrustAgent tarafından kilit açık tutuldu"</string>
<string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"Çok fazla kimlik doğrulama denemesi yapıldığından cihaz kilitlendi."</string>
<string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"Cihaz kilitli\nKimlik doğrulama yapılamadı"</string>
+ <string name="keyguard_indication_after_watch_disconnected" msgid="6329761892437377710">"Kol saatiniz tarafından kilitlendi"</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="accessibility_volume_settings" msgid="1458961116951564784">"Ses ayarları"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Otomatik medya altyazısı"</string>
@@ -800,6 +804,10 @@
<string name="enable_bluetooth_message" msgid="6740938333772779717">"Klavyenizi tabletinize bağlamak için önce Bluetooth\'u açmanız gerekir."</string>
<string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Aç"</string>
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Açık - Yüze göre"</string>
+ <!-- no translation found for notification_guts_bundle_title (1345506995443305361) -->
+ <skip />
+ <!-- no translation found for notification_guts_bundle_summary (3971530802237393600) -->
+ <skip />
<string name="inline_done_button" msgid="6043094985588909584">"Bitti"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Uygula"</string>
<string name="inline_turn_off_notifications" msgid="2653064779176881329">"Kapat"</string>
@@ -985,6 +993,7 @@
<string name="right_icon" msgid="1103955040645237425">"Sağ simge"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Kutu eklemek için basılı tutup sürükleyin"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Kutuları yeniden düzenlemek için basılı tutun ve sürükleyin"</string>
+ <string name="select_to_rearrange_tiles" msgid="6166153534235179148">"Yeniden düzenlenecek ve boyutlandırılacak kutuları seçin"</string>
<string name="tap_to_position_tile" msgid="6282815817773342757">"Kutuyu yerleştirmek için dokunun"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Kaldırmak için buraya sürükleyin"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"En az <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> kutu gerekiyor"</string>
@@ -1004,10 +1013,12 @@
<string name="tuner_low_priority" msgid="8412666814123009820">"Düşük öncelikli bildirim simgelerini göster"</string>
<string name="other" msgid="429768510980739978">"Diğer"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"kutu boyutunu değiştir"</string>
- <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"Kutuyu kaldırmak için"</string>
+ <!-- no translation found for accessibility_qs_edit_remove_tile_action (4811778796933958616) -->
+ <skip />
<string name="accessibility_qs_edit_toggle_placement_mode" msgid="3870429389210569610">"yerleşim modunu açın/kapatın"</string>
<string name="accessibility_qs_edit_toggle_selection" msgid="2201248304072372239">"seçimi değiştirin"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"kutuyu son konuma ekleyin"</string>
+ <!-- no translation found for accessibility_qs_edit_tile_add_action (7188303979684400620) -->
+ <skip />
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Kutuyu taşı"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"kutuyu istediğiniz konuma ekleyin"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> konumuna taşı"</string>
@@ -1094,7 +1105,8 @@
<string name="auto_saver_text" msgid="3214960308353838764">"Piliniz bitecek gibiyse bu özelliği açın"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Hayır, teşekkürler"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"Kullanımda"</string>
- <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Uygulamalar şunları kullanıyor: <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps (4994455315457996796) -->
+ <skip />
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" ve "</string>
<string name="ongoing_privacy_dialog_using_op" msgid="426635338010011796">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> tarafından kullanılıyor"</string>
@@ -1251,15 +1263,13 @@
<string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(bağlantı kesildi)"</string>
<string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"Geçiş yapılamıyor. Tekrar denemek için dokunun."</string>
<string name="media_output_dialog_pairing_new" msgid="5098212763195577270">"Cihaz bağla"</string>
- <!-- no translation found for media_output_dialog_button_connect_device (2819038239946643550) -->
- <skip />
+ <string name="media_output_dialog_button_connect_device" msgid="2819038239946643550">"Cihazı bağla"</string>
<string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Bilinmeyen uygulama"</string>
<string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Yayını durdur"</string>
<string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"Ses çıkışı için kullanılabilir cihazlar."</string>
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Ses düzeyi"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"%%<xliff:g id="PERCENTAGE">%1$d</xliff:g>"</string>
- <!-- no translation found for media_output_group_title_connected_speakers (3413876972919684479) -->
- <skip />
+ <string name="media_output_group_title_connected_speakers" msgid="3413876972919684479">"Bağlı hoparlörler"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Hoparlörler ve Ekranlar"</string>
<string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Önerilen Cihazlar"</string>
<string name="media_input_group_title" msgid="2057057473860783021">"Giriş"</string>
@@ -1328,10 +1338,8 @@
<string name="accessibility_enter_hint" msgid="2617864063504824834">"cihaz girin"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Açmak için parmak izi kullanın"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Kimlik doğrulaması gerekiyor. Kimlik doğrulaması için parmak izi sensörüne dokunun."</string>
- <!-- no translation found for resume_animation (9148788411429330671) -->
- <skip />
- <!-- no translation found for pause_animation (1919705510058107504) -->
- <skip />
+ <string name="resume_animation" msgid="9148788411429330671">"Animasyonu devam ettir"</string>
+ <string name="pause_animation" msgid="1919705510058107504">"Animasyonu duraklat"</string>
<string name="ongoing_call_content_description" msgid="6394763878322348560">"Devam eden arama"</string>
<string name="ongoing_notification_extra_content_description" msgid="2098752668861351265">"Devam ediyor"</string>
<string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobil veri"</string>
@@ -1513,11 +1521,13 @@
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"Kısayol ekle"</string>
<string name="shortcut_helper_delete_shortcut_button_label" msgid="3148773472696137052">"Kısayolu sil"</string>
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Klavyenizi kullanarak gezinin"</string>
- <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Klavye kısayollarını öğrenin"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2131074100205028210) -->
+ <skip />
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Dokunmatik alanınızı kullanarak gezinin"</string>
<string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Dokunmatik alan hareketlerini öğrenin"</string>
<string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Klavyenizi ve dokunmatik alanınızı kullanarak gezinin"</string>
- <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Dokunmatik alan hareketlerini, klavye kısayollarını ve daha fazlasını öğrenin"</string>
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (8557098239919786046) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Geri dön"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Ana sayfaya git"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Son uygulamaları görüntüle"</string>
@@ -1584,7 +1594,10 @@
<string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Ekran"</string>
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Bilinmiyor"</string>
<string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Tüm ayar kutuları sıfırlansın mı?"</string>
- <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Tüm Hızlı Ayarlar kutuları cihazın özgün ayarlarına sıfırlanır"</string>
+ <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Tüm Hızlı Ayar kutuları cihazın özgün ayarlarına sıfırlanır"</string>
<string name="demote_explain_text" msgid="3942301497888762295">"<xliff:g id="APPLICATION">%1$s</xliff:g> artık canlı güncellemeleri göstermeyecek. İstediğiniz zaman bunu Ayarlar\'dan değiştirebilirsiniz."</string>
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
+ <string name="underlay_close_button_content_description" msgid="6518481455548869894">"Kapat"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="7666159428113013319">"Bildirimleri açmak için sol üstten kaydırın"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="3842355853737057332">"Hızlı Ayarlar\'ı açmak için sağ üstten kaydırın"</string>
</resources>
diff --git a/packages/SystemUI/res/values-uk-ldrtl/strings.xml b/packages/SystemUI/res/values-uk-ldrtl/strings.xml
index 9ef4850..605bf0e 100644
--- a/packages/SystemUI/res/values-uk-ldrtl/strings.xml
+++ b/packages/SystemUI/res/values-uk-ldrtl/strings.xml
@@ -20,4 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="recents_quick_scrub_onboarding" msgid="2452671841151577157">"Потягніть уліво, щоб швидко переходити між додатками"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="6543179539438220549">"Щоб відкрити сповіщення, проведіть пальцем від верхнього правого кута екрана"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="5760503283520058116">"Щоб відкрити Швидкі налаштування, проведіть пальцем від лівого верхнього кута екрана"</string>
</resources>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index f24b235..a60950d 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -110,12 +110,12 @@
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Обробка записування екрана"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Сповіщення про сеанс запису екрана"</string>
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Записати відео з екрана?"</string>
- <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Записувати один додаток"</string>
+ <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Записати один додаток"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="4882406311415082016">"Записати цей екран"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="4169494703993148253">"Записати екран \"%s\""</string>
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Коли ви записуєте вміст усього екрана, на відео потрапляє все, що на ньому відображається. Тому будьте уважні з паролями, повідомленнями, фотографіями, аудіо, відео, платіжною інформацією тощо."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Коли ви записуєте додаток, на відео потрапляє все, що відображається або відтворюється в ньому. Тому будьте уважні з паролями, повідомленнями, фотографіями, аудіо, відео, платіжною інформацією тощо."</string>
- <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Записувати вміст екрана"</string>
+ <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Записати вміст екрана"</string>
<string name="screenrecord_app_selector_title" msgid="3854492366333954736">"Виберіть додаток для запису"</string>
<string name="screenrecord_audio_label" msgid="6183558856175159629">"Записувати звук"</string>
<string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Звук із пристрою"</string>
@@ -252,8 +252,7 @@
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Підключено до <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"Під’єднано до пристрою <xliff:g id="CAST">%s</xliff:g>."</string>
<string name="accessibility_expand_group" msgid="521237935987978624">"Розгорнути групу"</string>
- <!-- no translation found for accessibility_collapse_group (1842704016768329517) -->
- <skip />
+ <string name="accessibility_collapse_group" msgid="1842704016768329517">"Згорнути групу."</string>
<string name="accessibility_add_device_to_group" msgid="5446422960697860806">"Додати пристрій у групу."</string>
<string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"Вилучити пристрій із групи."</string>
<string name="accessibility_open_application" msgid="1749126077501259712">"Відкрити додаток"</string>
@@ -412,7 +411,7 @@
<string name="custom" msgid="3337456985275158299">"Указати"</string>
<string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Власні налаштування трасування"</string>
<string name="restore_default" msgid="5259420807486239755">"Відновити налаштування за умовчанням"</string>
- <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Режим керування однією рукою"</string>
+ <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Керування однією рукою"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Слухові апарати"</string>
<string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Під’єднано"</string>
<string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Від’єднано"</string>
@@ -468,6 +467,8 @@
<string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"Щоб використовувати кнопку мікрофона, надайте доступ до мікрофона в налаштуваннях."</string>
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Відкрити налаштування"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Інший пристрій"</string>
+ <string name="media_suggestion_disconnected_text" msgid="8777932417076437525">"Відтворити на пристрої \"<xliff:g id="DEVICE_NAME">%s</xliff:g>\""</string>
+ <string name="media_suggestion_failure_text" msgid="8453022394887613774">"Не підключено. Спробуйте ще."</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Увімкнути або вимкнути огляд"</string>
<string name="zen_modes_dialog_title" msgid="8854640808100096934">"Режими"</string>
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Готово"</string>
@@ -583,6 +584,7 @@
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
<skip />
<string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Показати весь екран"</string>
+ <string name="screen_share_permission_dialog_option_text_entire_screen_for_display" msgid="1545850238689169166">"Поділитися дисплеєм \"<xliff:g id="DISPLAY_NAME">%s</xliff:g>\""</string>
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
<skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Коли ви показуєте весь екран, для додатка <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> стає видимим увесь контент на ньому. Тому будьте обережні з паролями, повідомленнями, фотографіями, аудіо, відео, платіжною інформацією тощо."</string>
@@ -594,6 +596,7 @@
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Транслювати екран?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Транслювати один додаток"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Транслювати весь екран"</string>
+ <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen_for_display" msgid="5524817761472941086">"Транслювати дисплей \"<xliff:g id="DISPLAY_NAME">%s</xliff:g>\""</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Коли ви транслюєте весь екран, видимим стає весь контент на ньому. Тому будьте уважні з паролями, повідомленнями, фотографіями, аудіо, відео, платіжною інформацією тощо."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Коли ви транслюєте додаток, видимим стає весь контент, що відображається або відтворюється в ньому. Тому будьте уважні з паролями, повідомленнями, фотографіями, аудіо, відео, платіжною інформацією тощо."</string>
<string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Транслювати екран"</string>
@@ -674,6 +677,7 @@
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Розблоковує довірчий агент"</string>
<string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"Пристрій заблоковано. Забагато спроб автентифікації."</string>
<string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"Пристрій заблоковано\nПомилка автентифікації"</string>
+ <string name="keyguard_indication_after_watch_disconnected" msgid="6329761892437377710">"Заблоковано годинником"</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="accessibility_volume_settings" msgid="1458961116951564784">"Налаштування звуку"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Автоматичні субтитри (медіа)"</string>
@@ -720,7 +724,7 @@
<string name="stream_notification_unavailable" msgid="4313854556205836435">"Недоступно: звук дзвінків вимкнено"</string>
<string name="stream_alarm_unavailable" msgid="4059817189292197839">"Недоступно: увімкнено режим \"Не турбувати\""</string>
<string name="stream_media_unavailable" msgid="6823020894438959853">"Недоступно: увімкнено режим \"Не турбувати\""</string>
- <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Недоступно, оскільки ввімкнено режим \"<xliff:g id="MODE">%s</xliff:g>\""</string>
+ <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Недоступно, оскільки ввімкнено \"<xliff:g id="MODE">%s</xliff:g>\""</string>
<string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Недоступно"</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Торкніться, щоб увімкнути звук."</string>
<string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Торкніться, щоб налаштувати вібросигнал. Спеціальні можливості може бути вимкнено."</string>
@@ -731,7 +735,7 @@
<string name="volume_panel_spatial_audio_title" msgid="3367048857932040660">"Просторове звучання"</string>
<string name="volume_panel_spatial_audio_off" msgid="4177490084606772989">"Вимкнено"</string>
<string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Завжди ввімкнено"</string>
- <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Відстеження рухів голови"</string>
+ <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Рухи голови"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Торкніться, щоб змінити режим дзвінка"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"режим дзвінка"</string>
<string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>. Натисніть, щоб змінити режим дзвінка."</string>
@@ -800,6 +804,10 @@
<string name="enable_bluetooth_message" msgid="6740938333772779717">"Щоб під’єднати клавіатуру до планшета, спершу потрібно ввімкнути Bluetooth."</string>
<string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Увімкнути"</string>
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Увімкнути (за обличчям)"</string>
+ <!-- no translation found for notification_guts_bundle_title (1345506995443305361) -->
+ <skip />
+ <!-- no translation found for notification_guts_bundle_summary (3971530802237393600) -->
+ <skip />
<string name="inline_done_button" msgid="6043094985588909584">"Готово"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Застосувати"</string>
<string name="inline_turn_off_notifications" msgid="2653064779176881329">"Вимкнути"</string>
@@ -985,11 +993,12 @@
<string name="right_icon" msgid="1103955040645237425">"Значок праворуч"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Перетягніть потрібні елементи, щоб додати їх"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Щоб змінити порядок елементів, перетягуйте їх"</string>
+ <string name="select_to_rearrange_tiles" msgid="6166153534235179148">"Виділіть плитки, щоб змінити їх порядок і розмір"</string>
<string name="tap_to_position_tile" msgid="6282815817773342757">"Натисніть, щоб розмістити плитку"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Перетягніть сюди, щоб видалити"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Мінімальна кількість фрагментів: <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g>"</string>
<string name="qs_edit" msgid="5583565172803472437">"Редагувати"</string>
- <string name="qs_edit_tiles" msgid="2105215324060865035">"Редагування плиток"</string>
+ <string name="qs_edit_tiles" msgid="2105215324060865035">"Редагувати плитки"</string>
<string name="tuner_time" msgid="2450785840990529997">"Час"</string>
<string-array name="clock_options">
<item msgid="3986445361435142273">"Показувати години, хвилини та секунди"</item>
@@ -1004,10 +1013,12 @@
<string name="tuner_low_priority" msgid="8412666814123009820">"Показувати значки сповіщень із низьким пріоритетом"</string>
<string name="other" msgid="429768510980739978">"Інше"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"змінити розмір плитки"</string>
- <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"вилучити опцію"</string>
+ <!-- no translation found for accessibility_qs_edit_remove_tile_action (4811778796933958616) -->
+ <skip />
<string name="accessibility_qs_edit_toggle_placement_mode" msgid="3870429389210569610">"увімкнути або вимкнути режим розміщення"</string>
<string name="accessibility_qs_edit_toggle_selection" msgid="2201248304072372239">"вибрати або скасувати вибір"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"додати панель на останню позицію"</string>
+ <!-- no translation found for accessibility_qs_edit_tile_add_action (7188303979684400620) -->
+ <skip />
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Перемістити опцію"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"додати панель на потрібну позицію"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Перемістити на позицію <xliff:g id="POSITION">%1$d</xliff:g>"</string>
@@ -1094,7 +1105,8 @@
<string name="auto_saver_text" msgid="3214960308353838764">"Вмикати, коли заряд акумулятора закінчується"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Ні, дякую"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"Використовується"</string>
- <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Додатки використовують <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps (4994455315457996796) -->
+ <skip />
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" і "</string>
<string name="ongoing_privacy_dialog_using_op" msgid="426635338010011796">"Використовується в додатку <xliff:g id="APPLICATION_NAME">%1$s</xliff:g>"</string>
@@ -1251,15 +1263,13 @@
<string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(від’єднано)"</string>
<string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"Не вдалося змінити підключення. Натисніть, щоб повторити спробу."</string>
<string name="media_output_dialog_pairing_new" msgid="5098212763195577270">"Під’єднати пристрій"</string>
- <!-- no translation found for media_output_dialog_button_connect_device (2819038239946643550) -->
- <skip />
+ <string name="media_output_dialog_button_connect_device" msgid="2819038239946643550">"Підключити пристрій"</string>
<string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Невідомий додаток"</string>
<string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Припинити трансляцію"</string>
<string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"Доступні пристрої для відтворення звуку."</string>
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Гучність"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
- <!-- no translation found for media_output_group_title_connected_speakers (3413876972919684479) -->
- <skip />
+ <string name="media_output_group_title_connected_speakers" msgid="3413876972919684479">"Підключені колонки"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Колонки й екрани"</string>
<string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Пропоновані пристрої"</string>
<string name="media_input_group_title" msgid="2057057473860783021">"Ввід"</string>
@@ -1328,10 +1338,8 @@
<string name="accessibility_enter_hint" msgid="2617864063504824834">"відкрити пристрій"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Щоб відкрити, використайте відбиток пальця"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Пройдіть автентифікацію. Для цього торкніться сканера відбитків пальців."</string>
- <!-- no translation found for resume_animation (9148788411429330671) -->
- <skip />
- <!-- no translation found for pause_animation (1919705510058107504) -->
- <skip />
+ <string name="resume_animation" msgid="9148788411429330671">"Відновити анімацію"</string>
+ <string name="pause_animation" msgid="1919705510058107504">"Призупинити анімацію"</string>
<string name="ongoing_call_content_description" msgid="6394763878322348560">"Поточний дзвінок"</string>
<string name="ongoing_notification_extra_content_description" msgid="2098752668861351265">"Поточна активність"</string>
<string name="mobile_data_settings_title" msgid="3955246641380064901">"Мобільний трафік"</string>
@@ -1513,11 +1521,13 @@
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"Додати комбінацію клавіш"</string>
<string name="shortcut_helper_delete_shortcut_button_label" msgid="3148773472696137052">"Видалити комбінацію клавіш"</string>
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Навігація за допомогою клавіатури"</string>
- <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Дізнайтеся більше про комбінації клавіш"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2131074100205028210) -->
+ <skip />
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Навігація за допомогою сенсорної панелі"</string>
<string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Дізнатися про жести на сенсорній панелі"</string>
<string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Навігація за допомогою клавіатури й сенсорної панелі"</string>
- <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Дізнатися про жести на сенсорній панелі, комбінації клавіш і багато іншого"</string>
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (8557098239919786046) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Назад"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Перейти на головний екран"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Переглянути нещодавні додатки"</string>
@@ -1583,8 +1593,11 @@
<string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Надано додатками"</string>
<string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Екран"</string>
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Невідомо"</string>
- <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Скинути всі панелі?"</string>
- <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Усі панелі швидких налаштувань буде скинуто до стандартних налаштувань пристрою"</string>
+ <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Скинути всі плитки?"</string>
+ <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Усі плитки швидких налаштувань буде скинуто до стандартних налаштувань пристрою"</string>
<string name="demote_explain_text" msgid="3942301497888762295">"Додаток <xliff:g id="APPLICATION">%1$s</xliff:g> більше не показуватиме новини в реальному часі. Це можна будь-коли змінити в налаштуваннях."</string>
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
+ <string name="underlay_close_button_content_description" msgid="6518481455548869894">"Закрити"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="7666159428113013319">"Щоб відкрити сповіщення, проведіть пальцем від верхнього лівого кута екрана"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="3842355853737057332">"Щоб відкрити Швидкі налаштування, проведіть пальцем від правого верхнього кута екрана"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ur-ldrtl/strings.xml b/packages/SystemUI/res/values-ur-ldrtl/strings.xml
index d3e7607..fbe43ee 100644
--- a/packages/SystemUI/res/values-ur-ldrtl/strings.xml
+++ b/packages/SystemUI/res/values-ur-ldrtl/strings.xml
@@ -20,4 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="recents_quick_scrub_onboarding" msgid="2452671841151577157">"تیزی سے ایپس کو سوئچ کرنے کے لیے بائيں طرف گھسیٹیں"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="6543179539438220549">"اطلاعات کو کھولنے کے لیے اوپر بائیں طرف سے سوائپ کریں"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="5760503283520058116">"فوری ترتیبات کو کھولنے کے لیے اوپر دائیں طرف سے سوائپ کریں"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index 9872e6f..5e95a03 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -467,6 +467,8 @@
<string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"مائیکروفون بٹن کو استعمال کرنے کے لیے، ترتیبات میں مائیکروفون تک رسائی کو فعال کریں۔"</string>
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"ترتیبات کھولیں"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"دوسرا آلہ"</string>
+ <string name="media_suggestion_disconnected_text" msgid="8777932417076437525">"<xliff:g id="DEVICE_NAME">%s</xliff:g> پر چلائیں"</string>
+ <string name="media_suggestion_failure_text" msgid="8453022394887613774">"منسلک نہیں ہو سکا۔ دوبارہ کوشش کریں۔"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"مجموعی جائزہ ٹوگل کریں"</string>
<string name="zen_modes_dialog_title" msgid="8854640808100096934">"موڈز"</string>
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"ہو گیا"</string>
@@ -582,6 +584,7 @@
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
<skip />
<string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"پوری اسکرین کا اشتراک کریں"</string>
+ <string name="screen_share_permission_dialog_option_text_entire_screen_for_display" msgid="1545850238689169166">"<xliff:g id="DISPLAY_NAME">%s</xliff:g> کا اشتراک کریں"</string>
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
<skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"آپ کے اپنی پوری اسکرین کا اشتراک کرنے پر آپ کی اسکرین پر ہر چیز <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> کیلئے مرئی ہوجاتی ہے۔ لہذا، پاس ورڈز، ادائیگی کی تفصیلات، پیغامات، تصاویر، ساتھ ہی آڈیو اور ویڈیو جیسی چیزوں کے سلسلے میں محتاط رہیں۔"</string>
@@ -593,6 +596,7 @@
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"اپنی اسکرین کاسٹ کریں؟"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"ایک ایپ کاسٹ کریں"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"پوری اسکرین کاسٹ کریں"</string>
+ <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen_for_display" msgid="5524817761472941086">"<xliff:g id="DISPLAY_NAME">%s</xliff:g> کو کاسٹ کریں"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"جب آپ اپنی پوری اسکرین کاسٹ کر رہے ہوتے ہیں تو آپ کی اسکرین پر ہر چیز مرئی ہو جاتی ہے۔ لہذا، پاس ورڈز، ادائیگی کی تفصیلات، پیغامات، تصاویر، ساتھ ہی آڈیو اور ویڈیو جیسی چیزوں کے سلسلے میں محتاط رہیں۔"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"جب آپ کسی ایپ کو کاسٹ کر رہے ہوتے ہیں تو اس ایپ میں دکھائی گئی یا چلائی گئی ہر چیز مرئی ہو جاتی ہے۔ لہذا، پاس ورڈز، ادائیگی کی تفصیلات، پیغامات، تصاویر، ساتھ ہی آڈیو اور ویڈیو جیسی چیزوں کے سلسلے میں محتاط رہیں۔"</string>
<string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"اسکرین کاسٹ کریں"</string>
@@ -673,6 +677,7 @@
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"ٹرسٹ ایجنٹ نے غیر مقفل رکھا ہے"</string>
<string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"آلہ مقفل ہو گیا، تصدیق کی کافی زیادہ کوششیں کی گئیں"</string>
<string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"آلہ مقفل ہو گیا\nتصدیق ناکام ہو گئی"</string>
+ <string name="keyguard_indication_after_watch_disconnected" msgid="6329761892437377710">"آپ کی گھڑی کے ذریعے مقفل"</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="accessibility_volume_settings" msgid="1458961116951564784">"صوتی ترتیبات"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"خودکار طور پر میڈیا پر کیپشن لگائیں"</string>
@@ -799,6 +804,10 @@
<string name="enable_bluetooth_message" msgid="6740938333772779717">"اپنے کی بورڈ کو اپنے ٹیبلٹ کے ساتھ منسلک کرنے کیلئے پہلے آپ کو اپنا بلو ٹوتھ آن کرنا ہو گا۔"</string>
<string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"آن کریں"</string>
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"آن - چہرے پر مبنی"</string>
+ <!-- no translation found for notification_guts_bundle_title (1345506995443305361) -->
+ <skip />
+ <!-- no translation found for notification_guts_bundle_summary (3971530802237393600) -->
+ <skip />
<string name="inline_done_button" msgid="6043094985588909584">"ہو گیا"</string>
<string name="inline_ok_button" msgid="603075490581280343">"لاگو کریں"</string>
<string name="inline_turn_off_notifications" msgid="2653064779176881329">"آف کریں"</string>
@@ -984,6 +993,7 @@
<string name="right_icon" msgid="1103955040645237425">"دائيں جانب کا آئيکن"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"ٹائلز شامل کرنے کے لئے پکڑ کر گھسیٹیں"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"ٹائلز کو دوبارہ ترتیب دینے کیلئے پکڑ کر گھسیٹیں"</string>
+ <string name="select_to_rearrange_tiles" msgid="6166153534235179148">"سائز تبدیل کرنے اور دوبارہ ترتیب دینے کیلئے ٹائلز منتخب کریں"</string>
<string name="tap_to_position_tile" msgid="6282815817773342757">"ٹائل کی وضع قائم کرنے کے لیے تھپتھپائیں"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"ہٹانے کیلئے یہاں گھسیٹیں؟"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"آپ کو کم از کم <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> ٹائلز کی ضرورت ہے"</string>
@@ -1003,10 +1013,10 @@
<string name="tuner_low_priority" msgid="8412666814123009820">"کم ترجیحی اطلاع کے آئیکنز دکھائیں"</string>
<string name="other" msgid="429768510980739978">"دیگر"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"ٹائل کے سائز کو ٹوگل کریں"</string>
- <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"ٹائل ہٹائیں"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="4811778796933958616">"ٹائل ہٹائیں"</string>
<string name="accessibility_qs_edit_toggle_placement_mode" msgid="3870429389210569610">"اشتہار کے مقام کا موڈ ٹوگل کریں"</string>
<string name="accessibility_qs_edit_toggle_selection" msgid="2201248304072372239">"انتخاب ٹوگل کریں"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"ٹائل کو آخری پوزیشن پر شامل کریں"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="7188303979684400620">"ٹائل کو آخری پوزیشن پر شامل کریں"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"ٹائل منتقل کریں"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"ٹائل کو مطلوبہ پوزیشن پر شامل کریں"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> میں منتقل کریں"</string>
@@ -1093,7 +1103,8 @@
<string name="auto_saver_text" msgid="3214960308353838764">"جب بیٹری کے ختم ہونے کا امکان ہو تو آن کریں"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"نہیں شکریہ"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"زیر استعمال"</string>
- <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"ایپلیکیشنز آپ کی <xliff:g id="TYPES_LIST">%s</xliff:g> کا استعمال کر رہی ہیں۔"</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps (4994455315457996796) -->
+ <skip />
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">"، "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" اور "</string>
<string name="ongoing_privacy_dialog_using_op" msgid="426635338010011796">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> کے ذریعے استعمال کیا جا رہا ہے"</string>
@@ -1169,8 +1180,8 @@
<string name="controls_providers_title" msgid="6879775889857085056">"کنٹرولز شامل کرنے کے لیے ایپ منتخب کریں"</string>
<string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# کنٹرول کو شامل کیا گیا۔}other{# کنٹرولز کو شامل کیا گیا۔}}"</string>
<string name="controls_removed" msgid="3731789252222856959">"ہٹا دیا گیا"</string>
- <string name="controls_panel_authorization_title" msgid="267429338785864842">"<xliff:g id="APPNAME">%s</xliff:g> کو شامل کریں؟"</string>
- <string name="controls_panel_authorization" msgid="7045551688535104194">"<xliff:g id="APPNAME">%s</xliff:g> انتخاب کر سکتی ہے کہ یہاں کون سے کنٹرولز اور مواد دکھایا جائے۔"</string>
+ <string name="controls_panel_authorization_title" msgid="267429338785864842">"<xliff:g id="APPNAME">%s</xliff:g> کو شامل کریں؟"</string>
+ <string name="controls_panel_authorization" msgid="7045551688535104194">"<xliff:g id="APPNAME">%s</xliff:g> انتخاب کر سکتی ہے کہ یہاں کون سے کنٹرولز اور مواد دکھایا جائے۔"</string>
<string name="controls_panel_remove_app_authorization" msgid="5920442084735364674">"<xliff:g id="APPNAME">%s</xliff:g> کے کنٹرولز کو ہٹا دیں؟"</string>
<string name="accessibility_control_favorite" msgid="8694362691985545985">"پسند کردہ"</string>
<string name="accessibility_control_favorite_position" msgid="54220258048929221">"پسند کردہ، پوزیشن <xliff:g id="NUMBER">%d</xliff:g>"</string>
@@ -1508,11 +1519,11 @@
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"شارٹ کٹ شامل کریں"</string>
<string name="shortcut_helper_delete_shortcut_button_label" msgid="3148773472696137052">"شارٹ کٹ حذف کریں"</string>
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"اپنے کی بورڈ کا استعمال کر کے نیویگیٹ کریں"</string>
- <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"کی بورڈ شارٹ کٹس جانیں"</string>
+ <string name="launch_keyboard_tutorial_notification_content" msgid="2131074100205028210">"کی بورڈ شارٹ کٹس جانیں"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"اپنے ٹچ پیڈ کا استعمال کر کے نیویگیٹ کریں"</string>
<string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"ٹچ پیڈ کے اشاروں کو جانیں"</string>
<string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"اپنے کی بورڈ اور ٹچ پیڈ کا استعمال کر کے نیویگیٹ کریں"</string>
- <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"ٹچ پیڈ کے اشارے، کی بورڈ شارٹ کٹس اور مزید جانیں"</string>
+ <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="8557098239919786046">"ٹچ پیڈ کے اشارے، کی بورڈ شارٹ کٹس اور مزید جانیں"</string>
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"واپس جائیں"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"ہوم پر جائیں"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"حالیہ ایپس دیکھیں"</string>
@@ -1582,4 +1593,7 @@
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"سبھی فوری ترتیبات کی ٹائلز آلہ کی اصل ترتیبات پر ری سیٹ ہو جائیں گی"</string>
<string name="demote_explain_text" msgid="3942301497888762295">"اب <xliff:g id="APPLICATION">%1$s</xliff:g> لائیو اپ ڈیٹس نہیں دکھائے گی۔ آپ اسے ترتیبات میں کسی بھی وقت تبدیل کر سکتے ہیں۔"</string>
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>، <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
+ <string name="underlay_close_button_content_description" msgid="6518481455548869894">"بند کریں"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="7666159428113013319">"اطلاعات کو کھولنے کے لیے اوپر دائیں طرف سے سوائپ کریں"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="3842355853737057332">"فوری ترتیبات کو کھولنے کے لیے اوپر بائیں طرف سے سوائپ کریں"</string>
</resources>
diff --git a/packages/SystemUI/res/values-uz-ldrtl/strings.xml b/packages/SystemUI/res/values-uz-ldrtl/strings.xml
index cb8d1d8..bbba84d 100644
--- a/packages/SystemUI/res/values-uz-ldrtl/strings.xml
+++ b/packages/SystemUI/res/values-uz-ldrtl/strings.xml
@@ -20,4 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="recents_quick_scrub_onboarding" msgid="2452671841151577157">"Ilovalarni tezkor almashtirish uchun chapga torting"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="6543179539438220549">"Bildirishnomalarni ochish uchun yuqori oʻngdan suring"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="5760503283520058116">"Tezkor sozlamalarni ochish uchun yuqori chapdan suring"</string>
</resources>
diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml
index cf691d7..dd4efe2 100644
--- a/packages/SystemUI/res/values-uz/strings.xml
+++ b/packages/SystemUI/res/values-uz/strings.xml
@@ -467,6 +467,8 @@
<string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"Mikrofon tugmasidan foydalanish uchun Sozlamalar orqali mikrofon ruxsatini yoqing."</string>
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Sozlamalarni ochish"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Boshqa qurilma"</string>
+ <string name="media_suggestion_disconnected_text" msgid="8777932417076437525">"<xliff:g id="DEVICE_NAME">%s</xliff:g> orqali ijro qilish"</string>
+ <string name="media_suggestion_failure_text" msgid="8453022394887613774">"Ulanish imkonsiz. Qayta urining."</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Umumiy nazar rejimini almashtirish"</string>
<string name="zen_modes_dialog_title" msgid="8854640808100096934">"Rejimlar"</string>
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Tayyor"</string>
@@ -582,6 +584,7 @@
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
<skip />
<string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Butun ekranni namoyish qilish"</string>
+ <string name="screen_share_permission_dialog_option_text_entire_screen_for_display" msgid="1545850238689169166">"Ulashish: <xliff:g id="DISPLAY_NAME">%s</xliff:g>"</string>
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
<skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Butun ekranni namoyish qilayotganingizda, ekrandagi barcha narsalaringiz <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>ga koʻrinadi. Shu sababli parollar, toʻlov tafsilotlari, xabarlar, suratlar, audio va video chiqmasligi uchun ehtiyot boʻling."</string>
@@ -593,6 +596,7 @@
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Ekraningiz uzatilsinmi?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Bitta ilovani uzatish"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Butun ekranni uzatish"</string>
+ <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen_for_display" msgid="5524817761472941086">"Translatsiya qilish: <xliff:g id="DISPLAY_NAME">%s</xliff:g>"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Butun ekran uzatilayotganda, ekrandagi hamma narsa koʻrinadi. Shu sababli parollar, toʻlov tafsilotlari, xabarlar, suratlar, audio va video chiqmasligi uchun ehtiyot boʻling."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Ilovani uzatayotganingizda oʻsha ilova ichida koʻrsatilayotgan yoki ijro qilinayotganlar koʻrinadi. Shu sababli parollar, toʻlov tafsilotlari, xabarlar, suratlar, audio va video chiqmasligi uchun ehtiyot boʻling."</string>
<string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Ekranni translatsiya qilish"</string>
@@ -673,6 +677,7 @@
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"TrustAgent tomonidan ochilgan"</string>
<string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"Qurilma qulflandi. Juda koʻp marta hisob tekshiruvi uchun urinildi"</string>
<string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"Qurilma qulflandi\nHisob tekshiruvi amalga oshmadi"</string>
+ <string name="keyguard_indication_after_watch_disconnected" msgid="6329761892437377710">"Soatingiz bilan qulflangan"</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="accessibility_volume_settings" msgid="1458961116951564784">"Tovush sozlamalari"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Avtomatik taglavha yaratish"</string>
@@ -799,6 +804,8 @@
<string name="enable_bluetooth_message" msgid="6740938333772779717">"Klaviaturani planshetingizga ulash uchun Bluetooth xizmatini yoqishingiz kerak."</string>
<string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Yoqish"</string>
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Yoqish - Yuz asosida"</string>
+ <string name="notification_guts_bundle_title" msgid="1345506995443305361">"Bildirishnoma organayzeridan foydalanish"</string>
+ <string name="notification_guts_bundle_summary" msgid="3971530802237393600">"<xliff:g id="APP_NAME">%1$s</xliff:g> uchun"</string>
<string name="inline_done_button" msgid="6043094985588909584">"Tayyor"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Tatbiq etish"</string>
<string name="inline_turn_off_notifications" msgid="2653064779176881329">"Faolsizlantirish"</string>
@@ -984,6 +991,7 @@
<string name="right_icon" msgid="1103955040645237425">"O‘ngga belgisi"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Keraklisini ushlab torting"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Qayta tartiblash uchun ushlab torting"</string>
+ <string name="select_to_rearrange_tiles" msgid="6166153534235179148">"Tartiblash va oʻzgartirish uchun katakchalarni tanlang"</string>
<string name="tap_to_position_tile" msgid="6282815817773342757">"Katakni joylash uchun bosing"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"O‘chirish uchun bu yerga torting"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Kamida <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> ta katakcha lozim"</string>
@@ -1003,10 +1011,10 @@
<string name="tuner_low_priority" msgid="8412666814123009820">"Muhim boʻlmagan bildirishnoma ikonkalarini koʻrsatish"</string>
<string name="other" msgid="429768510980739978">"Boshqa"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"katak oʻlchamini almashtirish"</string>
- <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"katakchani olib tashlash"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="4811778796933958616">"Katakchani olib tashlash"</string>
<string name="accessibility_qs_edit_toggle_placement_mode" msgid="3870429389210569610">"joylash rejimi tugmasi"</string>
<string name="accessibility_qs_edit_toggle_selection" msgid="2201248304072372239">"tanlash tugmasi"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"kartochkani oxirgi oʻringa qoʻshish"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="7188303979684400620">"Katakchani oxirgi oʻringa qoʻshish"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Katakchani boshqa joyga olish"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Kartochkani kerakli oʻringa qoʻshish"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Bu joyga olish: <xliff:g id="POSITION">%1$d</xliff:g>"</string>
@@ -1093,7 +1101,7 @@
<string name="auto_saver_text" msgid="3214960308353838764">"Batareya quvvati kamayishi aniqlanganda yoqilsin"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Kerak emas"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"Foydalanilmoqda"</string>
- <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Ilovalarda ishlatilmoqda: <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
+ <string name="ongoing_privacy_chip_content_multiple_apps" msgid="4994455315457996796">"<xliff:g id="TYPES_LIST">%s</xliff:g> faol."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" va "</string>
<string name="ongoing_privacy_dialog_using_op" msgid="426635338010011796">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> ishlatmoqda"</string>
@@ -1508,11 +1516,11 @@
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"Yorliq yaratish"</string>
<string name="shortcut_helper_delete_shortcut_button_label" msgid="3148773472696137052">"Yorliqni oʻchirish"</string>
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Klaviatura yordamida kezing"</string>
- <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Tezkor tugmalar haqida"</string>
+ <string name="launch_keyboard_tutorial_notification_content" msgid="2131074100205028210">"Tezkor tugmalar haqida"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Sensorli panel yordamida kezing"</string>
<string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Sensorli panel ishoralari haqida"</string>
<string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Klaviatura va sensorli panel yordamida kezing"</string>
- <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Sensorli panel ishoralari, tezkor tugmalar va boshqalar haqida"</string>
+ <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="8557098239919786046">"Sensorli panel ishoralari, tezkor tugmalar va boshqalar haqida"</string>
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Orqaga"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Boshiga qaytish"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Oxirgi ilovalarni koʻrish"</string>
@@ -1582,4 +1590,7 @@
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Barcha Tezkor sozlamalar katakchalari qurilmaning asl sozlamalariga qaytariladi"</string>
<string name="demote_explain_text" msgid="3942301497888762295">"<xliff:g id="APPLICATION">%1$s</xliff:g> maʼlumotlari endi real vaqtda yangilanmaydi. Bu parametrni istalgan vaqtda sozlamalardan o‘zgartirish mumkin."</string>
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
+ <string name="underlay_close_button_content_description" msgid="6518481455548869894">"Yopish"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="7666159428113013319">"Bildirishnomalarni ochish uchun yuqori chapdan suring"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="3842355853737057332">"Tezkor sozlamalarni ochish uchun yuqori oʻngdan suring"</string>
</resources>
diff --git a/packages/SystemUI/res/values-vi-ldrtl/strings.xml b/packages/SystemUI/res/values-vi-ldrtl/strings.xml
index 8371edc..a6ddbb0 100644
--- a/packages/SystemUI/res/values-vi-ldrtl/strings.xml
+++ b/packages/SystemUI/res/values-vi-ldrtl/strings.xml
@@ -20,4 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="recents_quick_scrub_onboarding" msgid="2452671841151577157">"Kéo sang trái để chuyển nhanh giữa các ứng dụng"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="6543179539438220549">"Vuốt từ trên cùng bên phải để mở thông báo"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="5760503283520058116">"Vuốt từ trên cùng bên trái để mở trình đơn Cài đặt nhanh"</string>
</resources>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index 9b2c81a..f5624c8 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -252,8 +252,7 @@
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Đã kết nối với <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"Đã kết nối với <xliff:g id="CAST">%s</xliff:g>."</string>
<string name="accessibility_expand_group" msgid="521237935987978624">"Mở rộng nhóm."</string>
- <!-- no translation found for accessibility_collapse_group (1842704016768329517) -->
- <skip />
+ <string name="accessibility_collapse_group" msgid="1842704016768329517">"Thu gọn nhóm."</string>
<string name="accessibility_add_device_to_group" msgid="5446422960697860806">"Thêm thiết bị vào nhóm."</string>
<string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"Xoá thiết bị khỏi nhóm."</string>
<string name="accessibility_open_application" msgid="1749126077501259712">"Mở ứng dụng."</string>
@@ -468,6 +467,8 @@
<string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"Để dùng nút micrô, hãy bật quyền truy cập vào micrô trong phần Cài đặt."</string>
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Mở phần Cài đặt"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Thiết bị khác"</string>
+ <string name="media_suggestion_disconnected_text" msgid="8777932417076437525">"Phát trên <xliff:g id="DEVICE_NAME">%s</xliff:g>"</string>
+ <string name="media_suggestion_failure_text" msgid="8453022394887613774">"Không thể kết nối. Hãy thử lại"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Bật/tắt chế độ xem Tổng quan"</string>
<string name="zen_modes_dialog_title" msgid="8854640808100096934">"Chế độ"</string>
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Xong"</string>
@@ -583,6 +584,7 @@
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
<skip />
<string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Chia sẻ toàn bộ màn hình"</string>
+ <string name="screen_share_permission_dialog_option_text_entire_screen_for_display" msgid="1545850238689169166">"Chia sẻ <xliff:g id="DISPLAY_NAME">%s</xliff:g>"</string>
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
<skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Khi bạn chia sẻ toàn bộ màn hình, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> sẽ thấy được mọi nội dung trên màn hình của bạn. Vì vậy, hãy thận trọng để không làm lộ thông tin như mật khẩu, thông tin thanh toán, tin nhắn, ảnh, âm thanh và video."</string>
@@ -594,6 +596,7 @@
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Truyền màn hình?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Truyền một ứng dụng"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Truyền toàn bộ màn hình"</string>
+ <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen_for_display" msgid="5524817761472941086">"Truyền <xliff:g id="DISPLAY_NAME">%s</xliff:g>"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Khi bạn truyền toàn bộ màn hình thì người khác sẽ thấy được mọi nội dung trên màn hình của bạn. Vì vậy, hãy thận trọng đối với những thông tin như mật khẩu, thông tin thanh toán, tin nhắn, ảnh, âm thanh và video."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Khi bạn truyền một ứng dụng, mọi nội dung xuất hiện hoặc phát trên ứng dụng đó đều hiển thị trên thiết bị được truyền tới. Vì vậy, hãy thận trọng đối với những thông tin như mật khẩu, thông tin thanh toán, tin nhắn, ảnh, âm thanh và video."</string>
<string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Màn hình truyền"</string>
@@ -674,6 +677,7 @@
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Luôn được TrustAgent mở khóa"</string>
<string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"Thiết bị đã bị khoá do quá nhiều lần thử xác thực"</string>
<string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"Thiết bị đã bị khoá\nKhông xác thực được"</string>
+ <string name="keyguard_indication_after_watch_disconnected" msgid="6329761892437377710">"Đã bị đồng hồ của bạn khoá"</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="accessibility_volume_settings" msgid="1458961116951564784">"Cài đặt âm thanh"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Tự động tạo phụ đề cho nội dung nghe nhìn"</string>
@@ -800,6 +804,10 @@
<string name="enable_bluetooth_message" msgid="6740938333772779717">"Để kết nối bàn phím với máy tính bảng, trước tiên, bạn phải bật Bluetooth."</string>
<string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Bật"</string>
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Đang bật – Dựa trên khuôn mặt"</string>
+ <!-- no translation found for notification_guts_bundle_title (1345506995443305361) -->
+ <skip />
+ <!-- no translation found for notification_guts_bundle_summary (3971530802237393600) -->
+ <skip />
<string name="inline_done_button" msgid="6043094985588909584">"Xong"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Áp dụng"</string>
<string name="inline_turn_off_notifications" msgid="2653064779176881329">"Tắt"</string>
@@ -985,6 +993,7 @@
<string name="right_icon" msgid="1103955040645237425">"Biểu tượng bên phải"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Giữ và kéo để thêm ô"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Giữ và kéo để sắp xếp lại các ô"</string>
+ <string name="select_to_rearrange_tiles" msgid="6166153534235179148">"Chọn ô để sắp xếp lại và thay đổi kích thước"</string>
<string name="tap_to_position_tile" msgid="6282815817773342757">"Nhấn để đặt thẻ thông tin"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Kéo vào đây để xóa"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Bạn cần ít nhất <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> ô"</string>
@@ -1004,10 +1013,12 @@
<string name="tuner_low_priority" msgid="8412666814123009820">"Hiển thị biểu tượng thông báo có mức ưu tiên thấp"</string>
<string name="other" msgid="429768510980739978">"Khác"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"bật hoặc tắt kích thước của ô"</string>
- <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"xóa ô"</string>
+ <!-- no translation found for accessibility_qs_edit_remove_tile_action (4811778796933958616) -->
+ <skip />
<string name="accessibility_qs_edit_toggle_placement_mode" msgid="3870429389210569610">"bật hoặc tắt chế độ vị trí"</string>
<string name="accessibility_qs_edit_toggle_selection" msgid="2201248304072372239">"bật hoặc tắt chế độ chọn"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"thêm ô vào vị trí cuối cùng"</string>
+ <!-- no translation found for accessibility_qs_edit_tile_add_action (7188303979684400620) -->
+ <skip />
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Di chuyển ô"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Thêm ô vào vị trí mong muốn"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Di chuyển tới <xliff:g id="POSITION">%1$d</xliff:g>"</string>
@@ -1094,7 +1105,8 @@
<string name="auto_saver_text" msgid="3214960308353838764">"Bật khi pin sắp hết"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Không, cảm ơn"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"Đang được sử dụng"</string>
- <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Các ứng dụng đang dùng <xliff:g id="TYPES_LIST">%s</xliff:g> của bạn."</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps (4994455315457996796) -->
+ <skip />
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" và "</string>
<string name="ongoing_privacy_dialog_using_op" msgid="426635338010011796">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> đang dùng"</string>
@@ -1251,15 +1263,13 @@
<string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(đã ngắt kết nối)"</string>
<string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"Không thể chuyển đổi. Hãy nhấn để thử lại."</string>
<string name="media_output_dialog_pairing_new" msgid="5098212763195577270">"Kết nối thiết bị"</string>
- <!-- no translation found for media_output_dialog_button_connect_device (2819038239946643550) -->
- <skip />
+ <string name="media_output_dialog_button_connect_device" msgid="2819038239946643550">"Kết nối thiết bị"</string>
<string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Ứng dụng không xác định"</string>
<string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Dừng truyền"</string>
<string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"Các thiết bị có sẵn để xuất âm thanh."</string>
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Âm lượng"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
- <!-- no translation found for media_output_group_title_connected_speakers (3413876972919684479) -->
- <skip />
+ <string name="media_output_group_title_connected_speakers" msgid="3413876972919684479">"Loa đã kết nối"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Loa và màn hình"</string>
<string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Thiết bị được đề xuất"</string>
<string name="media_input_group_title" msgid="2057057473860783021">"Đầu vào"</string>
@@ -1328,10 +1338,8 @@
<string name="accessibility_enter_hint" msgid="2617864063504824834">"truy cập thiết bị"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Dùng vân tay để mở"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Bạn cần phải xác thực. Hãy chạm vào cảm biến vân tay để xác thực."</string>
- <!-- no translation found for resume_animation (9148788411429330671) -->
- <skip />
- <!-- no translation found for pause_animation (1919705510058107504) -->
- <skip />
+ <string name="resume_animation" msgid="9148788411429330671">"Tiếp tục ảnh động"</string>
+ <string name="pause_animation" msgid="1919705510058107504">"Tạm dừng ảnh động"</string>
<string name="ongoing_call_content_description" msgid="6394763878322348560">"Cuộc gọi đang diễn ra"</string>
<string name="ongoing_notification_extra_content_description" msgid="2098752668861351265">"Đang diễn ra"</string>
<string name="mobile_data_settings_title" msgid="3955246641380064901">"Dữ liệu di động"</string>
@@ -1513,11 +1521,13 @@
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"Thêm phím tắt"</string>
<string name="shortcut_helper_delete_shortcut_button_label" msgid="3148773472696137052">"Xoá phím tắt"</string>
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Di chuyển bằng bàn phím"</string>
- <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Tìm hiểu về phím tắt"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2131074100205028210) -->
+ <skip />
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Di chuyển bằng bàn di chuột"</string>
<string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Tìm hiểu về cử chỉ trên bàn di chuột"</string>
<string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Di chuyển bằng bàn phím và bàn di chuột"</string>
- <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Tìm hiểu về cử chỉ trên bàn di chuột, phím tắt và nhiều mục khác"</string>
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (8557098239919786046) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Quay lại"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Chuyển đến màn hình chính"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Xem các ứng dụng gần đây"</string>
@@ -1587,4 +1597,7 @@
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Mọi ô Cài đặt nhanh sẽ được đặt lại về chế độ cài đặt ban đầu của thiết bị"</string>
<string name="demote_explain_text" msgid="3942301497888762295">"<xliff:g id="APPLICATION">%1$s</xliff:g> sẽ không còn hiển thị Thông tin cập nhật trực tiếp. Bạn có thể thay đổi lựa chọn này bất cứ lúc nào trong phần Cài đặt."</string>
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
+ <string name="underlay_close_button_content_description" msgid="6518481455548869894">"Đóng"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="7666159428113013319">"Vuốt từ trên cùng bên trái để mở thông báo"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="3842355853737057332">"Vuốt từ trên cùng bên phải để mở trình đơn Cài đặt nhanh"</string>
</resources>
diff --git a/packages/SystemUI/res/values-zh-rCN-ldrtl/strings.xml b/packages/SystemUI/res/values-zh-rCN-ldrtl/strings.xml
index 189f851..077d963 100644
--- a/packages/SystemUI/res/values-zh-rCN-ldrtl/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN-ldrtl/strings.xml
@@ -20,4 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="recents_quick_scrub_onboarding" msgid="2452671841151577157">"向左拖动可快速切换应用"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="6543179539438220549">"从右上角向下滑动可打开通知栏"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="5760503283520058116">"从左上角开始滑动可打开“快捷设置”"</string>
</resources>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index 7db5e48..47e99fc 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -113,8 +113,8 @@
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"录制单个应用"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="4882406311415082016">"录制此屏幕"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="4169494703993148253">"录制“%s”屏幕"</string>
- <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"录制整个屏幕时,屏幕上显示的所有内容均会被录制。因此,请务必小心操作,谨防泄露密码、付款详情、消息、照片、音频、视频等敏感信息。"</string>
- <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"录制单个应用时,该应用中显示或播放的所有内容均会被录制。因此,请务必小心操作,谨防泄露密码、付款信息、消息、照片、音频、视频等。"</string>
+ <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"录制整个屏幕时,屏幕上显示的所有内容均会被录制。因此,请务必小心操作,谨防泄露密码、支付信息、消息、照片、音频、视频等。"</string>
+ <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"录制单个应用时,该应用中显示或播放的所有内容均会被录制。因此,请务必小心操作,谨防泄露密码、支付信息、消息、照片、音频、视频等。"</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"录制屏幕"</string>
<string name="screenrecord_app_selector_title" msgid="3854492366333954736">"选择要录制的应用"</string>
<string name="screenrecord_audio_label" msgid="6183558856175159629">"录制音频"</string>
@@ -202,10 +202,10 @@
<string name="biometric_dialog_cancel_authentication" msgid="981316588773442637">"取消身份验证"</string>
<string name="biometric_dialog_content_view_more_options_button" msgid="2663810393874865475">"更多选项"</string>
<string name="biometric_dialog_use_pin" msgid="8385294115283000709">"使用 PIN 码"</string>
- <string name="biometric_dialog_use_pattern" msgid="2315593393167211194">"使用图案"</string>
+ <string name="biometric_dialog_use_pattern" msgid="2315593393167211194">"使用解锁图案"</string>
<string name="biometric_dialog_use_password" msgid="3445033859393474779">"使用密码"</string>
<string name="biometric_dialog_wrong_pin" msgid="1878539073972762803">"PIN 码错误"</string>
- <string name="biometric_dialog_wrong_pattern" msgid="8954812279840889029">"图案错误"</string>
+ <string name="biometric_dialog_wrong_pattern" msgid="8954812279840889029">"解锁图案错误"</string>
<string name="biometric_dialog_wrong_password" msgid="69477929306843790">"密码错误"</string>
<string name="biometric_dialog_credential_too_many_attempts" msgid="3083141271737748716">"输错次数过多。\n请在 <xliff:g id="NUMBER">%d</xliff:g> 秒后重试。"</string>
<string name="work_challenge_emergency_button_text" msgid="8946588434515599288">"紧急呼叫"</string>
@@ -252,8 +252,7 @@
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"已连接到<xliff:g id="BLUETOOTH">%s</xliff:g>。"</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"已连接到 <xliff:g id="CAST">%s</xliff:g>。"</string>
<string name="accessibility_expand_group" msgid="521237935987978624">"展开群组。"</string>
- <!-- no translation found for accessibility_collapse_group (1842704016768329517) -->
- <skip />
+ <string name="accessibility_collapse_group" msgid="1842704016768329517">"收起群组。"</string>
<string name="accessibility_add_device_to_group" msgid="5446422960697860806">"将设备添加到群组。"</string>
<string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"从群组中移除设备。"</string>
<string name="accessibility_open_application" msgid="1749126077501259712">"打开应用。"</string>
@@ -380,7 +379,7 @@
<string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"<xliff:g id="DATA_LIMIT">%s</xliff:g>警告"</string>
<string name="quick_settings_work_mode_label" msgid="6440531507319809121">"工作应用"</string>
<string name="quick_settings_work_mode_paused_state" msgid="6681788236383735976">"已暂停"</string>
- <string name="quick_settings_night_display_label" msgid="8180030659141778180">"护眼模式"</string>
+ <string name="quick_settings_night_display_label" msgid="8180030659141778180">"夜间光效"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"在日落时开启"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"在日出时关闭"</string>
<string name="quick_settings_night_secondary_label_on_at" msgid="3584738542293528235">"在<xliff:g id="TIME">%s</xliff:g> 开启"</string>
@@ -468,6 +467,8 @@
<string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"若要使用麦克风按钮,请在“设置”中启用麦克风访问权限。"</string>
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"打开“设置”"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"其他设备"</string>
+ <string name="media_suggestion_disconnected_text" msgid="8777932417076437525">"在“<xliff:g id="DEVICE_NAME">%s</xliff:g>”上播放"</string>
+ <string name="media_suggestion_failure_text" msgid="8453022394887613774">"无法连接。请重试。"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"切换概览"</string>
<string name="zen_modes_dialog_title" msgid="8854640808100096934">"模式"</string>
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"完成"</string>
@@ -583,24 +584,26 @@
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
<skip />
<string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"共享整个屏幕"</string>
+ <string name="screen_share_permission_dialog_option_text_entire_screen_for_display" msgid="1545850238689169166">"分享“<xliff:g id="DISPLAY_NAME">%s</xliff:g>”"</string>
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
<skip />
- <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"当您共享整个屏幕时,屏幕上的所有内容均对“<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>”可见。因此,请务必小心操作,谨防泄露密码、付款信息、消息、照片、音频、视频等。"</string>
- <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"当您共享一个应用时,该应用中显示或播放的所有内容均对“<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>”可见。因此,请务必小心操作,谨防泄露密码、付款信息、消息、照片、音频、视频等。"</string>
+ <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"共享整个屏幕时,屏幕上的所有内容均对“<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>”可见。因此,请务必小心操作,谨防泄露密码、支付信息、消息、照片、音频、视频等。"</string>
+ <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"共享单个应用时,该应用中显示或播放的所有内容均对“<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>”可见。因此,请务必小心操作,谨防泄露密码、支付信息、消息、照片、音频、视频等。"</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"共享屏幕"</string>
<string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"“<xliff:g id="APP_NAME">%1$s</xliff:g>”已停用此选项"</string>
<string name="media_projection_entry_app_permission_dialog_single_app_not_supported" msgid="4860247304058870233">"应用不支持"</string>
<string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"选择要分享的应用"</string>
- <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"投放您的屏幕?"</string>
+ <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"要投屏吗?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"投放单个应用"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"投放整个屏幕"</string>
- <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"投放整个屏幕时,屏幕上的所有内容均公开可见。因此,请务必小心操作,谨防泄露密码、付款信息、消息、照片、音频、视频等。"</string>
- <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"投放单个应用时,该应用显示或播放的所有内容均公开可见。因此,请务必小心操作,谨防泄露密码、付款信息、消息、照片、音频、视频等。"</string>
- <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"投放屏幕"</string>
+ <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen_for_display" msgid="5524817761472941086">"投放“<xliff:g id="DISPLAY_NAME">%s</xliff:g>”上的内容"</string>
+ <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"投放整个屏幕时,屏幕上的所有内容均公开可见。因此,请务必小心操作,谨防泄露密码、支付信息、消息、照片、音频、视频等。"</string>
+ <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"投放单个应用时,该应用中显示或播放的所有内容均公开可见。因此,请务必小心操作,谨防泄露密码、支付信息、消息、照片、音频、视频等。"</string>
+ <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"投屏"</string>
<string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"选择要投屏的应用"</string>
<string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"开始分享?"</string>
- <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"在分享内容时,Android 可以访问屏幕上显示或设备中播放的所有内容。因此,请务必小心操作,谨防密码、付款信息、消息、照片、音频和视频等内容遭到泄露。"</string>
- <string name="media_projection_entry_generic_permission_dialog_warning_single_app" msgid="3454859977888159495">"在分享、录制或投放内容时,Android 可以访问通过此应用显示或播放的所有内容。因此,请务必小心操作,谨防密码、付款信息、消息、照片、音频和视频等内容遭到泄露。"</string>
+ <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"分享屏幕时,Android 可访问屏幕上显示或设备中播放的所有内容。因此,请务必小心操作,谨防泄露密码、支付信息、消息、照片、音频、视频等。"</string>
+ <string name="media_projection_entry_generic_permission_dialog_warning_single_app" msgid="3454859977888159495">"分享、录制或投放单个应用时,Android 可访问该应用中显示或播放的所有内容。因此,请务必小心操作,谨防泄露密码、支付信息、消息、照片、音频、视频等。"</string>
<string name="media_projection_entry_generic_permission_dialog_continue" msgid="8640381403048097116">"开始"</string>
<string name="media_projection_entry_generic_permission_dialog_continue_single_app" msgid="5920814988611877051">"下一步"</string>
<string name="media_projection_task_switcher_text" msgid="590885489897412359">"切换应用后,分享会暂停"</string>
@@ -674,6 +677,7 @@
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"由 TrustAgent 保持解锁状态"</string>
<string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"尝试验证身份的次数过多,设备已锁定"</string>
<string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"设备已锁定\n未能验证身份"</string>
+ <string name="keyguard_indication_after_watch_disconnected" msgid="6329761892437377710">"已通过您的手表锁定"</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="accessibility_volume_settings" msgid="1458961116951564784">"声音设置"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"自动生成媒体字幕"</string>
@@ -717,7 +721,7 @@
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"振动"</string>
<string name="volume_ringer_status_silent" msgid="3691324657849880883">"静音"</string>
<string name="media_device_cast" msgid="4786241789687569892">"投放"</string>
- <string name="stream_notification_unavailable" msgid="4313854556205836435">"该功能无法使用,因为铃声被静音"</string>
+ <string name="stream_notification_unavailable" msgid="4313854556205836435">"不可用,因为铃声被静音"</string>
<string name="stream_alarm_unavailable" msgid="4059817189292197839">"“勿扰”模式已开启,因此无法调整音量"</string>
<string name="stream_media_unavailable" msgid="6823020894438959853">"“勿扰”模式已开启,因此无法调整音量"</string>
<string name="stream_unavailable_by_modes" msgid="3674139029490353683">"不可用,因为已开启<xliff:g id="MODE">%s</xliff:g>"</string>
@@ -731,7 +735,7 @@
<string name="volume_panel_spatial_audio_title" msgid="3367048857932040660">"空间音频"</string>
<string name="volume_panel_spatial_audio_off" msgid="4177490084606772989">"关闭"</string>
<string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"固定"</string>
- <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"头部跟踪"</string>
+ <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"头部追踪"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"点按即可更改振铃器模式"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"响铃模式"</string>
<string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>,点按即可更改响铃模式"</string>
@@ -800,6 +804,10 @@
<string name="enable_bluetooth_message" msgid="6740938333772779717">"要将您的键盘连接到平板电脑,您必须先开启蓝牙。"</string>
<string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"开启"</string>
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"已开启 - 基于人脸"</string>
+ <!-- no translation found for notification_guts_bundle_title (1345506995443305361) -->
+ <skip />
+ <!-- no translation found for notification_guts_bundle_summary (3971530802237393600) -->
+ <skip />
<string name="inline_done_button" msgid="6043094985588909584">"完成"</string>
<string name="inline_ok_button" msgid="603075490581280343">"应用"</string>
<string name="inline_turn_off_notifications" msgid="2653064779176881329">"关闭"</string>
@@ -985,11 +993,12 @@
<string name="right_icon" msgid="1103955040645237425">"向右图标"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"按住并拖动即可添加功能块"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"按住并拖动即可重新排列功能块"</string>
+ <string name="select_to_rearrange_tiles" msgid="6166153534235179148">"选择要重新排列和调整大小的功能块"</string>
<string name="tap_to_position_tile" msgid="6282815817773342757">"点按即可调整功能块位置"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"拖动到此处即可移除"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"您至少需要 <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> 个卡片"</string>
<string name="qs_edit" msgid="5583565172803472437">"编辑"</string>
- <string name="qs_edit_tiles" msgid="2105215324060865035">"修改功能块"</string>
+ <string name="qs_edit_tiles" msgid="2105215324060865035">"编辑功能块"</string>
<string name="tuner_time" msgid="2450785840990529997">"时间"</string>
<string-array name="clock_options">
<item msgid="3986445361435142273">"显示小时、分钟和秒"</item>
@@ -1004,10 +1013,10 @@
<string name="tuner_low_priority" msgid="8412666814123009820">"显示低优先级的通知图标"</string>
<string name="other" msgid="429768510980739978">"其他"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"切换功能块大小"</string>
- <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"移除功能块"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="4811778796933958616">"移除功能块"</string>
<string name="accessibility_qs_edit_toggle_placement_mode" msgid="3870429389210569610">"切换位置调整模式"</string>
<string name="accessibility_qs_edit_toggle_selection" msgid="2201248304072372239">"切换选择状态"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"将功能块添加到最后一个位置"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="7188303979684400620">"将功能块添加到最后一个位置"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"移动功能块"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"将功能块添加到所需位置"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"移至 <xliff:g id="POSITION">%1$d</xliff:g>"</string>
@@ -1094,7 +1103,8 @@
<string name="auto_saver_text" msgid="3214960308353838764">"在电池电量可能会耗尽时,系统会开启此模式"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"不用了"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"正在使用"</string>
- <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"有多个应用正在使用您的<xliff:g id="TYPES_LIST">%s</xliff:g>。"</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps (4994455315457996796) -->
+ <skip />
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">"、 "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" 和 "</string>
<string name="ongoing_privacy_dialog_using_op" msgid="426635338010011796">"“<xliff:g id="APPLICATION_NAME">%1$s</xliff:g>”正在使用"</string>
@@ -1167,11 +1177,11 @@
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"开启/关闭"</string>
<string name="accessibility_floating_button_action_edit" msgid="1688227814600463987">"修改"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"设备控制器"</string>
- <string name="controls_providers_title" msgid="6879775889857085056">"选择要添加控制器的应用"</string>
+ <string name="controls_providers_title" msgid="6879775889857085056">"选择用于添加控件的应用"</string>
<string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{已添加 # 个控件。}other{已添加 # 个控件。}}"</string>
<string name="controls_removed" msgid="3731789252222856959">"已移除"</string>
<string name="controls_panel_authorization_title" msgid="267429338785864842">"添加“<xliff:g id="APPNAME">%s</xliff:g>”?"</string>
- <string name="controls_panel_authorization" msgid="7045551688535104194">"“<xliff:g id="APPNAME">%s</xliff:g>”可以选择在此处显示哪些控件和内容。"</string>
+ <string name="controls_panel_authorization" msgid="7045551688535104194">"添加后,“<xliff:g id="APPNAME">%s</xliff:g>”可以选择在此处显示哪些控件和内容。"</string>
<string name="controls_panel_remove_app_authorization" msgid="5920442084735364674">"要移除<xliff:g id="APPNAME">%s</xliff:g>的控制器吗?"</string>
<string name="accessibility_control_favorite" msgid="8694362691985545985">"已收藏"</string>
<string name="accessibility_control_favorite_position" msgid="54220258048929221">"已收藏,位置:<xliff:g id="NUMBER">%d</xliff:g>"</string>
@@ -1251,15 +1261,13 @@
<string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(已断开连接)"</string>
<string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"无法切换。点按即可重试。"</string>
<string name="media_output_dialog_pairing_new" msgid="5098212763195577270">"连接设备"</string>
- <!-- no translation found for media_output_dialog_button_connect_device (2819038239946643550) -->
- <skip />
+ <string name="media_output_dialog_button_connect_device" msgid="2819038239946643550">"连接设备"</string>
<string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"未知应用"</string>
<string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"停止投放"</string>
<string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"音频输出的可用设备。"</string>
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"音量"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
- <!-- no translation found for media_output_group_title_connected_speakers (3413876972919684479) -->
- <skip />
+ <string name="media_output_group_title_connected_speakers" msgid="3413876972919684479">"已连接的扬声器"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"音箱和显示屏"</string>
<string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"建议的设备"</string>
<string name="media_input_group_title" msgid="2057057473860783021">"输入"</string>
@@ -1328,10 +1336,8 @@
<string name="accessibility_enter_hint" msgid="2617864063504824834">"进入设备"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"使用指纹即可打开"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"需要进行身份验证。请轻触指纹传感器以验证身份。"</string>
- <!-- no translation found for resume_animation (9148788411429330671) -->
- <skip />
- <!-- no translation found for pause_animation (1919705510058107504) -->
- <skip />
+ <string name="resume_animation" msgid="9148788411429330671">"继续播放动画"</string>
+ <string name="pause_animation" msgid="1919705510058107504">"暂停播放动画"</string>
<string name="ongoing_call_content_description" msgid="6394763878322348560">"正在通话"</string>
<string name="ongoing_notification_extra_content_description" msgid="2098752668861351265">"正在进行"</string>
<string name="mobile_data_settings_title" msgid="3955246641380064901">"移动数据网络"</string>
@@ -1513,11 +1519,11 @@
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"添加快捷方式"</string>
<string name="shortcut_helper_delete_shortcut_button_label" msgid="3148773472696137052">"删除快捷方式"</string>
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"使用键盘导航"</string>
- <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"了解键盘快捷键"</string>
+ <string name="launch_keyboard_tutorial_notification_content" msgid="2131074100205028210">"了解键盘快捷键"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"使用触控板导航"</string>
<string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"了解触控板手势"</string>
<string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"使用键盘和触控板进行导航"</string>
- <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"了解触控板手势、键盘快捷键等"</string>
+ <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="8557098239919786046">"了解触控板手势、键盘快捷键等"</string>
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"返回"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"前往主屏幕"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"查看最近用过的应用"</string>
@@ -1533,7 +1539,7 @@
<string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"前往主屏幕"</string>
<string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"在触控板上用三根手指向上滑动"</string>
<string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"太棒了!"</string>
- <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"您已完成“前往主屏幕”手势"</string>
+ <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"您已完成“前往主屏幕”手势教程"</string>
<string name="touchpad_home_gesture_error_body" msgid="3810674109999513073">"在触控板上用三根手指向上滑动,即可前往主屏幕"</string>
<string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"查看最近用过的应用"</string>
<string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"在触控板上用三根手指向上滑动并按住"</string>
@@ -1543,7 +1549,7 @@
<string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"切换应用"</string>
<string name="touchpad_switch_apps_gesture_guidance" msgid="2554844933805502538">"在触控板上用四根手指向右滑动"</string>
<string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"太棒了!"</string>
- <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"您完成了应用切换手势教程。"</string>
+ <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"您已完成“切换应用”手势教程。"</string>
<string name="touchpad_switch_gesture_error_body" msgid="5895231916964677918">"在触控板上用四根手指向右滑动可切换应用"</string>
<string name="tutorial_action_key_title" msgid="8172535792469008169">"查看所有应用"</string>
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"按键盘上的快捷操作按键"</string>
@@ -1587,4 +1593,7 @@
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"所有“快捷设置”功能块都将重置为设备的原始设置"</string>
<string name="demote_explain_text" msgid="3942301497888762295">"“<xliff:g id="APPLICATION">%1$s</xliff:g>”将不再显示实时动态。您可以随时在“设置”中更改这项设置。"</string>
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>,<xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
+ <string name="underlay_close_button_content_description" msgid="6518481455548869894">"关闭"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="7666159428113013319">"从左上角向下滑动可打开通知栏"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="3842355853737057332">"从右上角开始滑动可打开“快捷设置”"</string>
</resources>
diff --git a/packages/SystemUI/res/values-zh-rHK-ldrtl/strings.xml b/packages/SystemUI/res/values-zh-rHK-ldrtl/strings.xml
index d1b00c3..53c8d8c 100644
--- a/packages/SystemUI/res/values-zh-rHK-ldrtl/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK-ldrtl/strings.xml
@@ -20,4 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="recents_quick_scrub_onboarding" msgid="2452671841151577157">"向左拖曳即可快速切換應用程式"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="6543179539438220549">"從右上方向下滑動以開啟通知欄"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="5760503283520058116">"從螢幕左上方向下滑動以開啟「快速設定」"</string>
</resources>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index 578c270..e7d37c0 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -252,8 +252,7 @@
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"已連線至<xliff:g id="BLUETOOTH">%s</xliff:g>。"</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"已連接至 <xliff:g id="CAST">%s</xliff:g>。"</string>
<string name="accessibility_expand_group" msgid="521237935987978624">"展開群組。"</string>
- <!-- no translation found for accessibility_collapse_group (1842704016768329517) -->
- <skip />
+ <string name="accessibility_collapse_group" msgid="1842704016768329517">"收合群組。"</string>
<string name="accessibility_add_device_to_group" msgid="5446422960697860806">"將裝置新增至群組。"</string>
<string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"從群組移除裝置。"</string>
<string name="accessibility_open_application" msgid="1749126077501259712">"開啟應用程式。"</string>
@@ -468,6 +467,8 @@
<string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"如要使用麥克風按鈕,請在「設定」中啟用麥克風存取權。"</string>
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"開啟「設定」"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"其他裝置"</string>
+ <string name="media_suggestion_disconnected_text" msgid="8777932417076437525">"在「<xliff:g id="DEVICE_NAME">%s</xliff:g>」上播放"</string>
+ <string name="media_suggestion_failure_text" msgid="8453022394887613774">"無法連接,請再試一次。"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"切換概覽"</string>
<string name="zen_modes_dialog_title" msgid="8854640808100096934">"模式"</string>
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"完成"</string>
@@ -583,6 +584,7 @@
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
<skip />
<string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"分享整個螢幕畫面"</string>
+ <string name="screen_share_permission_dialog_option_text_entire_screen_for_display" msgid="1545850238689169166">"分享「<xliff:g id="DISPLAY_NAME">%s</xliff:g>」"</string>
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
<skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"當你分享整個螢幕畫面時,「<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>」可看到你畫面上的任何內容。因此,請謹慎處理密碼、付款資料、訊息、相片、音訊和影片等。"</string>
@@ -594,6 +596,7 @@
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"要投放螢幕嗎?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"投放一個應用程式"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"投放整個螢幕畫面"</string>
+ <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen_for_display" msgid="5524817761472941086">"投放「<xliff:g id="DISPLAY_NAME">%s</xliff:g>」"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"當你投放整個螢幕畫面時,其他人可看到你畫面上的任何內容。因此,請謹慎處理密碼、付款資料、訊息、相片、音訊和影片等。"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"當你投放應用程式時,其他人可看到該應用程式中顯示或播放的任何內容。因此,請謹慎處理密碼、付款資料、訊息、相片、音訊和影片等。"</string>
<string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"投放螢幕畫面"</string>
@@ -674,6 +677,7 @@
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"由信任的代理保持解鎖狀態"</string>
<string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"嘗試驗證次數過多,裝置已鎖定"</string>
<string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"裝置已鎖定\n驗證失敗"</string>
+ <string name="keyguard_indication_after_watch_disconnected" msgid="6329761892437377710">"已由手錶鎖定"</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="accessibility_volume_settings" msgid="1458961116951564784">"音效設定"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"自動為媒體加入字幕"</string>
@@ -800,6 +804,10 @@
<string name="enable_bluetooth_message" msgid="6740938333772779717">"如要將鍵盤連接至平板電腦,請先開啟藍牙。"</string>
<string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"開啟"</string>
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"已開啟 - 根據面孔偵測"</string>
+ <!-- no translation found for notification_guts_bundle_title (1345506995443305361) -->
+ <skip />
+ <!-- no translation found for notification_guts_bundle_summary (3971530802237393600) -->
+ <skip />
<string name="inline_done_button" msgid="6043094985588909584">"完成"</string>
<string name="inline_ok_button" msgid="603075490581280343">"套用"</string>
<string name="inline_turn_off_notifications" msgid="2653064779176881329">"關閉"</string>
@@ -985,6 +993,7 @@
<string name="right_icon" msgid="1103955040645237425">"向右圖示"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"按住並拖曳即可新增圖塊"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"按住並拖曳即可重新排列圖塊"</string>
+ <string name="select_to_rearrange_tiles" msgid="6166153534235179148">"選取圖塊以重新排列和調整大小"</string>
<string name="tap_to_position_tile" msgid="6282815817773342757">"輕按即可調整圖塊位置"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"拖曳這裡即可移除"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"你需要有至少 <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> 個資訊方塊"</string>
@@ -1004,10 +1013,12 @@
<string name="tuner_low_priority" msgid="8412666814123009820">"顯示低優先順序通知圖示"</string>
<string name="other" msgid="429768510980739978">"其他"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"切換圖塊大小"</string>
- <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"移除圖塊"</string>
+ <!-- no translation found for accessibility_qs_edit_remove_tile_action (4811778796933958616) -->
+ <skip />
<string name="accessibility_qs_edit_toggle_placement_mode" msgid="3870429389210569610">"切換位置模式"</string>
<string name="accessibility_qs_edit_toggle_selection" msgid="2201248304072372239">"切換選取項目"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"加圖塊去上一個位置"</string>
+ <!-- no translation found for accessibility_qs_edit_tile_add_action (7188303979684400620) -->
+ <skip />
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"移動圖塊"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"加圖塊去目標位置"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"移去 <xliff:g id="POSITION">%1$d</xliff:g>"</string>
@@ -1094,7 +1105,8 @@
<string name="auto_saver_text" msgid="3214960308353838764">"在電池電量可能耗盡前啟用「慳電模式」"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"不用了,謝謝"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"使用中"</string>
- <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"有多個應用程式正在使用<xliff:g id="TYPES_LIST">%s</xliff:g>。"</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps (4994455315457996796) -->
+ <skip />
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">"、 "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" 和 "</string>
<string name="ongoing_privacy_dialog_using_op" msgid="426635338010011796">"「<xliff:g id="APPLICATION_NAME">%1$s</xliff:g>」正在使用"</string>
@@ -1251,15 +1263,13 @@
<string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(已中斷連線)"</string>
<string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"無法切換,輕按即可重試。"</string>
<string name="media_output_dialog_pairing_new" msgid="5098212763195577270">"連接裝置"</string>
- <!-- no translation found for media_output_dialog_button_connect_device (2819038239946643550) -->
- <skip />
+ <string name="media_output_dialog_button_connect_device" msgid="2819038239946643550">"連接裝置"</string>
<string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"不明應用程式"</string>
<string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"停止投放"</string>
<string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"可用作音訊輸出的裝置"</string>
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"音量"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
- <!-- no translation found for media_output_group_title_connected_speakers (3413876972919684479) -->
- <skip />
+ <string name="media_output_group_title_connected_speakers" msgid="3413876972919684479">"已連接喇叭"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"喇叭和螢幕"</string>
<string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"建議的裝置"</string>
<string name="media_input_group_title" msgid="2057057473860783021">"輸入"</string>
@@ -1328,10 +1338,8 @@
<string name="accessibility_enter_hint" msgid="2617864063504824834">"進入裝置"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"使用指紋即可開啟"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"需要驗證。掂一下指紋感應器就可以驗證。"</string>
- <!-- no translation found for resume_animation (9148788411429330671) -->
- <skip />
- <!-- no translation found for pause_animation (1919705510058107504) -->
- <skip />
+ <string name="resume_animation" msgid="9148788411429330671">"恢復播放動畫"</string>
+ <string name="pause_animation" msgid="1919705510058107504">"停播動畫"</string>
<string name="ongoing_call_content_description" msgid="6394763878322348560">"通話中"</string>
<string name="ongoing_notification_extra_content_description" msgid="2098752668861351265">"進行中"</string>
<string name="mobile_data_settings_title" msgid="3955246641380064901">"流動數據"</string>
@@ -1513,11 +1521,11 @@
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"新增捷徑"</string>
<string name="shortcut_helper_delete_shortcut_button_label" msgid="3148773472696137052">"刪除捷徑"</string>
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"使用鍵盤導覽"</string>
- <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"瞭解鍵盤快速鍵"</string>
+ <string name="launch_keyboard_tutorial_notification_content" msgid="2131074100205028210">"瞭解鍵盤快速鍵"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"使用觸控板導覽"</string>
<string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"瞭解觸控板手勢"</string>
<string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"使用鍵盤和觸控板導覽"</string>
- <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"瞭解觸控板手勢、鍵盤快速鍵等等"</string>
+ <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="8557098239919786046">"瞭解觸控板手勢、鍵盤快速鍵等等"</string>
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"返回"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"返回主畫面"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"查看最近使用的應用程式"</string>
@@ -1587,4 +1595,7 @@
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"所有「快速設定」圖塊將重設為裝置的原始設定"</string>
<string name="demote_explain_text" msgid="3942301497888762295">"「<xliff:g id="APPLICATION">%1$s</xliff:g>」不會再顯示「即時消息」。你可隨時前往「設定」變更此設定。"</string>
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>,<xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
+ <string name="underlay_close_button_content_description" msgid="6518481455548869894">"關閉"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="7666159428113013319">"從左上方向下滑動以開啟通知欄"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="3842355853737057332">"從螢幕右上方向下滑動以開啟「快速設定」"</string>
</resources>
diff --git a/packages/SystemUI/res/values-zh-rHK/tiles_states_strings.xml b/packages/SystemUI/res/values-zh-rHK/tiles_states_strings.xml
index 2dcb0cd..2a6be0f 100644
--- a/packages/SystemUI/res/values-zh-rHK/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/tiles_states_strings.xml
@@ -83,8 +83,8 @@
</string-array>
<string-array name="tile_states_location">
<item msgid="3316542218706374405">"無法使用"</item>
- <item msgid="4813655083852587017">"已關閉"</item>
- <item msgid="6744077414775180687">"已開啟"</item>
+ <item msgid="4813655083852587017">"關閉"</item>
+ <item msgid="6744077414775180687">"開啟"</item>
</string-array>
<string-array name="tile_states_hotspot">
<item msgid="3145597331197351214">"無法使用"</item>
@@ -103,7 +103,7 @@
</string-array>
<string-array name="tile_states_saver">
<item msgid="39714521631367660">"無法使用"</item>
- <item msgid="6983679487661600728">"已關閉"</item>
+ <item msgid="6983679487661600728">"關閉"</item>
<item msgid="7520663805910678476">"已開啟"</item>
</string-array>
<string-array name="tile_states_dark">
@@ -124,7 +124,7 @@
<string-array name="tile_states_night">
<item msgid="7857498964264855466">"無法使用"</item>
<item msgid="2744885441164350155">"已關閉"</item>
- <item msgid="151121227514952197">"已開啟"</item>
+ <item msgid="151121227514952197">"開啟"</item>
</string-array>
<string-array name="tile_states_screenrecord">
<item msgid="1085836626613341403">"無法使用"</item>
@@ -138,7 +138,7 @@
</string-array>
<string-array name="tile_states_reverse">
<item msgid="3574611556622963971">"無法使用"</item>
- <item msgid="8707481475312432575">"已關閉"</item>
+ <item msgid="8707481475312432575">"關閉"</item>
<item msgid="8031106212477483874">"已開啟"</item>
</string-array>
<string-array name="tile_states_reduce_brightness">
diff --git a/packages/SystemUI/res/values-zh-rTW-ldrtl/strings.xml b/packages/SystemUI/res/values-zh-rTW-ldrtl/strings.xml
index d1b00c3..5d0af6e 100644
--- a/packages/SystemUI/res/values-zh-rTW-ldrtl/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW-ldrtl/strings.xml
@@ -20,4 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="recents_quick_scrub_onboarding" msgid="2452671841151577157">"向左拖曳即可快速切換應用程式"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="6543179539438220549">"從右上角向下滑動即可開啟通知欄"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="5760503283520058116">"從左上角向下滑動即可開啟「快速設定」"</string>
</resources>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index 411a781..0975a24 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -252,8 +252,7 @@
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"已連線至<xliff:g id="BLUETOOTH">%s</xliff:g>。"</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"已連線至 <xliff:g id="CAST">%s</xliff:g>。"</string>
<string name="accessibility_expand_group" msgid="521237935987978624">"展開群組。"</string>
- <!-- no translation found for accessibility_collapse_group (1842704016768329517) -->
- <skip />
+ <string name="accessibility_collapse_group" msgid="1842704016768329517">"收合群組。"</string>
<string name="accessibility_add_device_to_group" msgid="5446422960697860806">"將裝置加入群組。"</string>
<string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"從群組中移除裝置。"</string>
<string name="accessibility_open_application" msgid="1749126077501259712">"開啟應用程式。"</string>
@@ -468,6 +467,8 @@
<string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"如要使用麥克風按鈕,請前往「設定」啟用麥克風存取權。"</string>
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"開啟「設定」"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"其他裝置"</string>
+ <string name="media_suggestion_disconnected_text" msgid="8777932417076437525">"在「<xliff:g id="DEVICE_NAME">%s</xliff:g>」上播放"</string>
+ <string name="media_suggestion_failure_text" msgid="8453022394887613774">"無法連線,請再試一次。"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"切換總覽"</string>
<string name="zen_modes_dialog_title" msgid="8854640808100096934">"模式"</string>
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"完成"</string>
@@ -583,6 +584,7 @@
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
<skip />
<string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"分享整個畫面"</string>
+ <string name="screen_share_permission_dialog_option_text_entire_screen_for_display" msgid="1545850238689169166">"共用「<xliff:g id="DISPLAY_NAME">%s</xliff:g>」"</string>
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
<skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"當你分享整個畫面時,「<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>」可存取畫面上的所有內容。因此,請謹慎處理密碼、付款資料、訊息、相片和影音內容等資訊。"</string>
@@ -594,6 +596,7 @@
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"要投放畫面嗎?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"投放一個應用程式"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"投放整個畫面"</string>
+ <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen_for_display" msgid="5524817761472941086">"投放「<xliff:g id="DISPLAY_NAME">%s</xliff:g>」"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"當你投放整個畫面時,畫面上的所有內容都會顯示出來。因此,請謹慎處理密碼、付款資料、訊息、相片和影音內容等資訊。"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"投放應用程式畫面時,該應用程式呈現或播放的所有內容都會投放出來。因此,請謹慎處理密碼、付款資料、訊息、相片和影音內容等資訊。"</string>
<string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"投放螢幕"</string>
@@ -674,6 +677,7 @@
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"由 TrustAgent 維持解鎖狀態"</string>
<string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"驗證錯誤次數過多,裝置已鎖定"</string>
<string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"裝置已鎖定\n驗證失敗"</string>
+ <string name="keyguard_indication_after_watch_disconnected" msgid="6329761892437377710">"已由手錶鎖定"</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="accessibility_volume_settings" msgid="1458961116951564784">"音效設定"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"自動產生媒體字幕"</string>
@@ -800,6 +804,10 @@
<string name="enable_bluetooth_message" msgid="6740938333772779717">"如要將鍵盤連線到平板電腦,你必須先開啟藍牙。"</string>
<string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"開啟"</string>
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"已開啟 - 依臉部方向旋轉"</string>
+ <!-- no translation found for notification_guts_bundle_title (1345506995443305361) -->
+ <skip />
+ <!-- no translation found for notification_guts_bundle_summary (3971530802237393600) -->
+ <skip />
<string name="inline_done_button" msgid="6043094985588909584">"完成"</string>
<string name="inline_ok_button" msgid="603075490581280343">"套用"</string>
<string name="inline_turn_off_notifications" msgid="2653064779176881329">"關閉"</string>
@@ -985,6 +993,7 @@
<string name="right_icon" msgid="1103955040645237425">"向右圖示"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"按住並拖曳即可新增設定方塊"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"按住並拖曳即可重新排列設定方塊"</string>
+ <string name="select_to_rearrange_tiles" msgid="6166153534235179148">"選取要重新排列及調整大小的設定方塊"</string>
<string name="tap_to_position_tile" msgid="6282815817773342757">"輕觸即可調整設定方塊的位置"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"拖曳到這裡即可移除"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"你至少必須要有 <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> 個設定方塊"</string>
@@ -1004,10 +1013,12 @@
<string name="tuner_low_priority" msgid="8412666814123009820">"顯示低優先順序通知圖示"</string>
<string name="other" msgid="429768510980739978">"其他"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"切換設定方塊大小"</string>
- <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"移除圖塊"</string>
+ <!-- no translation found for accessibility_qs_edit_remove_tile_action (4811778796933958616) -->
+ <skip />
<string name="accessibility_qs_edit_toggle_placement_mode" msgid="3870429389210569610">"切換位置調整模式"</string>
<string name="accessibility_qs_edit_toggle_selection" msgid="2201248304072372239">"切換選取項目"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"將設定方塊新增到最後一個位置"</string>
+ <!-- no translation found for accessibility_qs_edit_tile_add_action (7188303979684400620) -->
+ <skip />
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"移動圖塊"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"將設定方塊新增到所需位置"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"移至 <xliff:g id="POSITION">%1$d</xliff:g>"</string>
@@ -1094,7 +1105,8 @@
<string name="auto_saver_text" msgid="3214960308353838764">"在電池電量即將耗盡時開啟"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"不用了,謝謝"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"使用中"</string>
- <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"有多個應用程式正在使用<xliff:g id="TYPES_LIST">%s</xliff:g>。"</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps (4994455315457996796) -->
+ <skip />
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">"、 "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" 和 "</string>
<string name="ongoing_privacy_dialog_using_op" msgid="426635338010011796">"「<xliff:g id="APPLICATION_NAME">%1$s</xliff:g>」正在使用這項權限"</string>
@@ -1251,15 +1263,13 @@
<string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(連線中斷)"</string>
<string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"無法切換,輕觸即可重試。"</string>
<string name="media_output_dialog_pairing_new" msgid="5098212763195577270">"連接裝置"</string>
- <!-- no translation found for media_output_dialog_button_connect_device (2819038239946643550) -->
- <skip />
+ <string name="media_output_dialog_button_connect_device" msgid="2819038239946643550">"連結裝置"</string>
<string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"不明的應用程式"</string>
<string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"停止投放"</string>
<string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"可用於輸出音訊的裝置。"</string>
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"音量"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
- <!-- no translation found for media_output_group_title_connected_speakers (3413876972919684479) -->
- <skip />
+ <string name="media_output_group_title_connected_speakers" msgid="3413876972919684479">"外接喇叭"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"喇叭和螢幕"</string>
<string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"建議的裝置"</string>
<string name="media_input_group_title" msgid="2057057473860783021">"輸入"</string>
@@ -1328,10 +1338,8 @@
<string name="accessibility_enter_hint" msgid="2617864063504824834">"進入裝置"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"使用指紋即可開啟"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"需要驗證。輕觸指紋感應器即可進行驗證。"</string>
- <!-- no translation found for resume_animation (9148788411429330671) -->
- <skip />
- <!-- no translation found for pause_animation (1919705510058107504) -->
- <skip />
+ <string name="resume_animation" msgid="9148788411429330671">"繼續播放動畫"</string>
+ <string name="pause_animation" msgid="1919705510058107504">"暫停播放動畫"</string>
<string name="ongoing_call_content_description" msgid="6394763878322348560">"通話中"</string>
<string name="ongoing_notification_extra_content_description" msgid="2098752668861351265">"進行中"</string>
<string name="mobile_data_settings_title" msgid="3955246641380064901">"行動數據"</string>
@@ -1513,11 +1521,13 @@
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"新增快速鍵"</string>
<string name="shortcut_helper_delete_shortcut_button_label" msgid="3148773472696137052">"刪除快速鍵"</string>
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"使用鍵盤操作"</string>
- <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"學習鍵盤快速鍵"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2131074100205028210) -->
+ <skip />
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"使用觸控板操作"</string>
<string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"學習觸控板手勢"</string>
<string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"使用鍵盤和觸控板操作"</string>
- <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"學習觸控板手勢、鍵盤快速鍵等"</string>
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (8557098239919786046) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"返回"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"返回主畫面"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"查看最近使用的應用程式"</string>
@@ -1587,4 +1597,7 @@
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"所有快速設定方塊都會恢復裝置的原始設定"</string>
<string name="demote_explain_text" msgid="3942301497888762295">"「<xliff:g id="APPLICATION">%1$s</xliff:g>」將不再顯示最新即時資訊,你隨時可以前往「設定」進行變更。"</string>
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>、<xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
+ <string name="underlay_close_button_content_description" msgid="6518481455548869894">"關閉"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="7666159428113013319">"從左上角向下滑動即可開啟通知欄"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="3842355853737057332">"從右上角向下滑動即可開啟「快速設定」"</string>
</resources>
diff --git a/packages/SystemUI/res/values-zu-ldrtl/strings.xml b/packages/SystemUI/res/values-zu-ldrtl/strings.xml
index 5c94e33..3693e46 100644
--- a/packages/SystemUI/res/values-zu-ldrtl/strings.xml
+++ b/packages/SystemUI/res/values-zu-ldrtl/strings.xml
@@ -20,4 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="recents_quick_scrub_onboarding" msgid="2452671841151577157">"Hudulela ngakwesokunxele ukuze ushintshe ngokushesha izinhlelo zokusebenza"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="6543179539438220549">"Swayipha kusukela phezulu ngakwesokudla ukuze uvule izaziso"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="5760503283520058116">"Swayipha kusulela phezulu ngakwesokunxele ukuze uvule Amasethingi Asheshayo"</string>
</resources>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index 821d78f..653373c 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -112,9 +112,7 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Rekhoda isikrini sakho?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Rekhoda i-app eyodwa"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="4882406311415082016">"Rekhoda lesi sikrini"</string>
- <!-- String.format failed for translation -->
- <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (4169494703993148253) -->
- <skip />
+ <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="4169494703993148253">"Rekhoda okuthi %s"</string>
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Uma urekhoda sonke isikrini sakho, noma yini evela esikrinini iyarekhodwa. Ngakho-ke qaphela ngezinto ezifana namaphasiwedi, imininingwane yenkokhelo, imilayezo, izithombe, nomsindo nevidiyo."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Uma urekhoda i-app, noma yini evezwa noma edlala kuleyo app iyarekhodwa. Ngakho-ke qaphela ngezinto ezifana namaphasiwedi, imininingwane yenkokhelo, imilayezo, izithombe, nomsindo nevidiyo."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Rekhoda isikrini"</string>
@@ -254,8 +252,7 @@
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Xhuma ku-<xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"Ixhumeke ku-<xliff:g id="CAST">%s</xliff:g>."</string>
<string name="accessibility_expand_group" msgid="521237935987978624">"Nweba iqembu."</string>
- <!-- no translation found for accessibility_collapse_group (1842704016768329517) -->
- <skip />
+ <string name="accessibility_collapse_group" msgid="1842704016768329517">"Goqa iqembu."</string>
<string name="accessibility_add_device_to_group" msgid="5446422960697860806">"Faka idivayisi eqenjini."</string>
<string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"Susa idivayisi eqenjini."</string>
<string name="accessibility_open_application" msgid="1749126077501259712">"Vula i-application."</string>
@@ -470,6 +467,8 @@
<string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"Ukuze usebenzise inkinobho yemakrofoni, nika amandla ukufinyelela kumakrofoni kokuthi Amasethingi."</string>
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Vula Amasethingi"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Enye idivayisi"</string>
+ <string name="media_suggestion_disconnected_text" msgid="8777932417076437525">"Dlala ku-<xliff:g id="DEVICE_NAME">%s</xliff:g>"</string>
+ <string name="media_suggestion_failure_text" msgid="8453022394887613774">"Ayikwazi ukuxhuma. Zama futhi."</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Guqula ukubuka konke"</string>
<string name="zen_modes_dialog_title" msgid="8854640808100096934">"Amamodi"</string>
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Kwenziwe"</string>
@@ -585,6 +584,7 @@
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
<skip />
<string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Yabelana ngesikrini sonke"</string>
+ <string name="screen_share_permission_dialog_option_text_entire_screen_for_display" msgid="1545850238689169166">"Yabelana nge-<xliff:g id="DISPLAY_NAME">%s</xliff:g>"</string>
<!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
<skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Uma wabelana ngesikrini sakho sonke, noma yini esesikrinini sakho ibonakala ku-<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Ngakho-ke qaphela ngezinto ezifana namaphasiwedi, imininingwane yenkokhelo, imilayezo, izithombe, nomsindo nevidiyo."</string>
@@ -596,6 +596,7 @@
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Sakaza isikrini sakho?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Sakaza i-app eyodwa"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Sakaza isikrini sonke"</string>
+ <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen_for_display" msgid="5524817761472941086">"Sakaza <xliff:g id="DISPLAY_NAME">%s</xliff:g>"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Uma usakaza isikrini sakho sonke, noma yini esesikrinini sakho iyabonakala. Ngakho-ke qaphela ngezinto ezifana namaphasiwedi, imininingwane yokukhokha, imilayezo, izithombe, nomsindo nevidiyo."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Uma usakaza i-app, noma yini ekhonjiswe noma edlalwe kuleyo-app iyabonakala. Ngakho-ke qaphela ngezinto ezifana namaphasiwedi, imininingwane yokukhokha, imilayezo, izithombe, nomsindo nevidiyo."</string>
<string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Isikrini sokusakaza"</string>
@@ -676,6 +677,7 @@
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Igcinwa ivuliwe ngo-TrustAgent"</string>
<string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"Idivayisi ibikhiyiwe, imizamo eminingi kakhulu yokufakazela ubuqiniso"</string>
<string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"Idivayisi ikhiyiwe\nUkufakazela ubuqiniso kwehlulekile"</string>
+ <string name="keyguard_indication_after_watch_disconnected" msgid="6329761892437377710">"Ikhiyelwe ngaphandle iwashi lakho"</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="accessibility_volume_settings" msgid="1458961116951564784">"Izilungiselelo zomsindo"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Yenza amagama-ngcazo ngokuzenzakalela emidiya"</string>
@@ -802,6 +804,10 @@
<string name="enable_bluetooth_message" msgid="6740938333772779717">"Ukuze uxhume ikhibhodi yakho nethebhulethi yakho, kufanele uqale ngokuvula i-Bluetooth."</string>
<string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Vula"</string>
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Vuliwe - Kususelwe kubuso"</string>
+ <!-- no translation found for notification_guts_bundle_title (1345506995443305361) -->
+ <skip />
+ <!-- no translation found for notification_guts_bundle_summary (3971530802237393600) -->
+ <skip />
<string name="inline_done_button" msgid="6043094985588909584">"Kwenziwe"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Faka"</string>
<string name="inline_turn_off_notifications" msgid="2653064779176881329">"Vala"</string>
@@ -987,6 +993,7 @@
<string name="right_icon" msgid="1103955040645237425">"Isithonjana sangakwesokudla"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Bamba uphinde uhudule ukuze ungeze amathayela"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Bamba uphinde uhudule ukuze uphinde ulungise amathayela"</string>
+ <string name="select_to_rearrange_tiles" msgid="6166153534235179148">"Khetha amathayela ozowahlela kabusha futhi ushintshe usayizi"</string>
<string name="tap_to_position_tile" msgid="6282815817773342757">"Thepha ukuze ubeke ithayela"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Hudulela lapha ukuze ususe"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Udinga okungenani amathayela angu-<xliff:g id="MIN_NUM_TILES">%1$d</xliff:g>"</string>
@@ -1006,10 +1013,12 @@
<string name="tuner_low_priority" msgid="8412666814123009820">"Bonisa izithonjana zesaziso zokubaluleka okuncane"</string>
<string name="other" msgid="429768510980739978">"Okunye"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"guqula usayizi wethayela"</string>
- <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"susa ithayela"</string>
+ <!-- no translation found for accessibility_qs_edit_remove_tile_action (4811778796933958616) -->
+ <skip />
<string name="accessibility_qs_edit_toggle_placement_mode" msgid="3870429389210569610">"guqula imodi yokufaka"</string>
<string name="accessibility_qs_edit_toggle_selection" msgid="2201248304072372239">"Guqula ukukhetha"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"faka ithayela endaweni yokugcina"</string>
+ <!-- no translation found for accessibility_qs_edit_tile_add_action (7188303979684400620) -->
+ <skip />
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Hambisa ithayela"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Faka ithayela endaweni oyifunayo"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Hambisa ku-<xliff:g id="POSITION">%1$d</xliff:g>"</string>
@@ -1096,7 +1105,8 @@
<string name="auto_saver_text" msgid="3214960308353838764">"Vula uma ibhethri sekungenzeka liphele"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Cha ngiyabonga"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"Kuyasebenza"</string>
- <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Izinhlelo zokusebenza zisebenzisa i-<xliff:g id="TYPES_LIST">%s</xliff:g> yakho."</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps (4994455315457996796) -->
+ <skip />
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" kanye "</string>
<string name="ongoing_privacy_dialog_using_op" msgid="426635338010011796">"Kusetshenziswa i-<xliff:g id="APPLICATION_NAME">%1$s</xliff:g>"</string>
@@ -1253,15 +1263,13 @@
<string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(inqamukile)"</string>
<string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"Akukwazi ukushintsha. Thepha ukuze uzame futhi."</string>
<string name="media_output_dialog_pairing_new" msgid="5098212763195577270">"Xhuma idivayisi"</string>
- <!-- no translation found for media_output_dialog_button_connect_device (2819038239946643550) -->
- <skip />
+ <string name="media_output_dialog_button_connect_device" msgid="2819038239946643550">"Xhuma Idivayisi"</string>
<string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"I-app engaziwa"</string>
<string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Misa ukusakaza"</string>
<string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"Amadivayisi atholakalayo okukhipha umsindo."</string>
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Ivolumu"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
- <!-- no translation found for media_output_group_title_connected_speakers (3413876972919684479) -->
- <skip />
+ <string name="media_output_group_title_connected_speakers" msgid="3413876972919684479">"Izipikha ezixhunyiwe"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Izipikha Neziboniso"</string>
<string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Amadivayisi Aphakanyisiwe"</string>
<string name="media_input_group_title" msgid="2057057473860783021">"Okufakwayo"</string>
@@ -1330,10 +1338,8 @@
<string name="accessibility_enter_hint" msgid="2617864063504824834">"faka idivayisi"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Sebenzisa izigxivizo zeminwe ukuvula"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Ukufakazela ubuqiniso budingekile. Thinta inzwa yezigxivizo zeminwe ukuze uqinisekise."</string>
- <!-- no translation found for resume_animation (9148788411429330671) -->
- <skip />
- <!-- no translation found for pause_animation (1919705510058107504) -->
- <skip />
+ <string name="resume_animation" msgid="9148788411429330671">"Qhubekisa opopayi"</string>
+ <string name="pause_animation" msgid="1919705510058107504">"Misa opopayi"</string>
<string name="ongoing_call_content_description" msgid="6394763878322348560">"Ikholi eqhubekayo"</string>
<string name="ongoing_notification_extra_content_description" msgid="2098752668861351265">"Okuqhubekayo"</string>
<string name="mobile_data_settings_title" msgid="3955246641380064901">"Idatha yeselula"</string>
@@ -1515,11 +1521,13 @@
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"Faka isinqamuleli"</string>
<string name="shortcut_helper_delete_shortcut_button_label" msgid="3148773472696137052">"Sula isinqamuleli"</string>
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Funa usebenzisa ikhibhodi yakho"</string>
- <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Funda izinqamuleli zamakhibhodi"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2131074100205028210) -->
+ <skip />
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Funa usebenzisa iphedi yokuthinta"</string>
<string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Funda ukunyakaza kwephedi lokuthinta"</string>
<string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Funa usebenzisa ikhibhodi yakho nephedi yokuthinta"</string>
- <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Funda ukunyakaza kwephedi yokuthinta, izinqamuleli zamakhibhodi, nokuningi"</string>
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (8557098239919786046) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Iya emuva"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Iya ekhasini lokuqala"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Buka ama-app akamuva"</string>
@@ -1589,4 +1597,7 @@
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Ithayela Lamasethingi Asheshayo lizosetha kabusha libuyele kumasethingi okuqala edivayisi"</string>
<string name="demote_explain_text" msgid="3942301497888762295">"I-<xliff:g id="APPLICATION">%1$s</xliff:g> ngeke isabonisa Izibuyekezo Ezibukhoma. Ungashintsha lokhu nganoma isiphi isikhathi kumaSethingi."</string>
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
+ <string name="underlay_close_button_content_description" msgid="6518481455548869894">"Vala"</string>
+ <string name="dual_shade_educational_tooltip_notifs" msgid="7666159428113013319">"Swayipha kusukela phezulu ngakwesokunxele ukuze uvule izaziso"</string>
+ <string name="dual_shade_educational_tooltip_qs" msgid="3842355853737057332">"Swayipha kusukela phezulu ngakwesokudla ukuze uvule Amasethingi Asheshayo"</string>
</resources>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index e1318dd..034862f 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -34,7 +34,6 @@
<!-- Base colors for notification shade/scrim, the alpha component is adjusted programmatically
to match the spec -->
- <color name="shade_panel_base">@android:color/system_accent1_100</color>
<color name="notification_scrim_base">@android:color/system_accent1_100</color>
<!-- Fallback colors for notification shade/scrim -->
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 0fd4029..3ce2790 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -177,7 +177,7 @@
<integer name="heads_up_notification_minimum_time_with_throttling">500</integer>
<!-- Minimum display time for a heads up notification that was shown from a user action (like tapping on a different part of the UI), in milliseconds. -->
- <integer name="heads_up_notification_minimum_time_for_user_initiated">3000</integer>
+ <integer name="heads_up_notification_minimum_time_for_user_initiated">5000</integer>
<!-- Display time for a sticky heads up notification, in milliseconds. -->
<integer name="sticky_heads_up_notification_time">60000</integer>
@@ -1112,11 +1112,6 @@
-->
<bool name="config_enableDesktopFeatureSet">false</bool>
- <!--
- Whether the user switching can only happen by logging out and going through the system user (login screen).
- -->
- <bool name="config_userSwitchingMustGoThroughLoginScreen">false</bool>
-
<!-- The dream component used when the device is low light environment. -->
<string translatable="false" name="config_lowLightDreamComponent"/>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 9b0275e..9644321 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -764,6 +764,9 @@
<dimen name="qs_panel_padding_bottom">@dimen/footer_actions_height</dimen>
<dimen name="qs_panel_padding_top">80dp</dimen>
+ <!-- Size is in sp because the icon is inlined in text -->
+ <dimen name="qs_edit_mode_app_icon">14sp</dimen>
+
<dimen name="qs_data_usage_text_size">14sp</dimen>
<dimen name="qs_data_usage_usage_text_size">36sp</dimen>
@@ -2251,6 +2254,7 @@
<dimen name="tile_details_switch_track_height">24dp</dimen>
<dimen name="tile_details_button_row_height">40dp</dimen>
<dimen name="tile_details_button_size">20dp</dimen>
+ <dimen name="tile_details_entry_gap">2dp</dimen>
<!-- Tile Details end -->
</resources>
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index 2d0f0f9..78d08ac 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -239,7 +239,6 @@
<item type="id" name="split_shade_guideline" />
<item type="id" name="start_button" />
<item type="id" name="status_view_media_container" />
- <item type="id" name="weather_clock_date_and_icons_barrier_bottom" />
<item type="id" name="weather_clock_bc_smartspace_bottom" />
<!-- Privacy dialog -->
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 9fd1a7d..f497fa1 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -168,6 +168,9 @@
<!-- Button label to share wifi [CHAR_LIMIT=20] -->
<string name="share_wifi_button_text">Share Wi\u2011Fi</string>
+ <!-- Button label to add a new network [CHAR_LIMIT=20] -->
+ <string name="add_network_button_text">Add network</string>
+
<!-- Title of confirmation dialog for wireless debugging [CHAR LIMIT=NONE] -->
<string name="wifi_debugging_title">Allow wireless debugging on this network?</string>
@@ -2074,6 +2077,12 @@
<!-- [CHAR LIMIT=NONE] Notification camera based rotation enabled description -->
<string name="rotation_lock_camera_rotation_on">On - Face-based</string>
+ <!-- [CHAR LIMIT=100] Notification bundle inline settings title -->
+ <string name="notification_guts_bundle_title">Use Notification Organizer</string>
+
+ <!-- [CHAR LIMIT=100] Notification bundle inline settings summary -->
+ <string name="notification_guts_bundle_summary">For <xliff:g id="app_name" example="YouTube">%1$s</xliff:g></string>
+
<!-- Notification Inline controls: button to dismiss the blocking helper [CHAR_LIMIT=20] -->
<string name="inline_done_button">Done</string>
@@ -2614,8 +2623,8 @@
<!-- Accessibility description of action to toggle QS tile size on click. It will read as "Double-tap to toggle the tile's size" in screen readers [CHAR LIMIT=NONE] -->
<string name="accessibility_qs_edit_toggle_tile_size_action">toggle the tile\'s size</string>
- <!-- Accessibility description of action to remove QS tile on click. It will read as "Double-tap to remove tile" in screen readers [CHAR LIMIT=NONE] -->
- <string name="accessibility_qs_edit_remove_tile_action">remove tile</string>
+ <!-- Accessibility description of action to remove QS tile on click. [CHAR LIMIT=NONE] -->
+ <string name="accessibility_qs_edit_remove_tile_action">Remove tile</string>
<!-- Accessibility description of action to select the QS tile to place on click. It will read as "Double-tap to toggle placement mode" in screen readers [CHAR LIMIT=NONE] -->
<string name="accessibility_qs_edit_toggle_placement_mode">toggle placement mode</string>
@@ -2623,8 +2632,8 @@
<!-- Accessibility description of action to toggle the QS tile selection. It will read as "Double-tap to toggle selection" in screen readers [CHAR LIMIT=NONE] -->
<string name="accessibility_qs_edit_toggle_selection">toggle selection</string>
- <!-- Accessibility action of action to add QS tile to end. It will read as "Double-tap to add tile to the last position" in screen readers [CHAR LIMIT=NONE] -->
- <string name="accessibility_qs_edit_tile_add_action">add tile to the last position</string>
+ <!-- Accessibility action of action to add QS tile to end. [CHAR LIMIT=NONE] -->
+ <string name="accessibility_qs_edit_tile_add_action">Add tile to the last position</string>
<!-- Accessibility action for context menu to move QS tile [CHAR LIMIT=NONE] -->
<string name="accessibility_qs_edit_tile_start_move">Move tile</string>
@@ -2923,7 +2932,7 @@
<string name="ongoing_privacy_dialog_a11y_title">In use</string>
<!-- Content description for ongoing privacy chip. Use with multiple apps [CHAR LIMIT=NONE]-->
- <string name="ongoing_privacy_chip_content_multiple_apps">Applications are using your <xliff:g id="types_list" example="camera, location">%s</xliff:g>.</string>
+ <string name="ongoing_privacy_chip_content_multiple_apps"><xliff:g id="types_list" example="camera, location">%s</xliff:g> in use.</string>
<!-- Separator for types. Include spaces before and after if needed [CHAR LIMIT=10] -->
<string name="ongoing_privacy_dialog_separator">,\u0020</string>
@@ -4045,7 +4054,7 @@
<!-- Notification title for launching keyboard tutorial [CHAR_LIMIT=100] -->
<string name="launch_keyboard_tutorial_notification_title">Navigate using your keyboard</string>
<!-- Notification text for launching keyboard tutorial [CHAR_LIMIT=100] -->
- <string name="launch_keyboard_tutorial_notification_content">Learn keyboards shortcuts</string>
+ <string name="launch_keyboard_tutorial_notification_content">Learn keyboard shortcuts</string>
<!-- Notification title for launching touchpad tutorial [CHAR_LIMIT=100] -->
<string name="launch_touchpad_tutorial_notification_title">Navigate using your touchpad</string>
@@ -4055,7 +4064,7 @@
<!-- Notification title for launching keyboard tutorial [CHAR_LIMIT=100] -->
<string name="launch_keyboard_touchpad_tutorial_notification_title">Navigate using your keyboard and touchpad</string>
<!-- Notification text for launching keyboard tutorial [CHAR_LIMIT=100] -->
- <string name="launch_keyboard_touchpad_tutorial_notification_content">Learn touchpad gestures, keyboards shortcuts, and more</string>
+ <string name="launch_keyboard_touchpad_tutorial_notification_content">Learn touchpad gestures, keyboard shortcuts, and more</string>
<!-- TOUCHPAD TUTORIAL-->
<!-- Label for button opening tutorial for back gesture on touchpad [CHAR LIMIT=NONE] -->
@@ -4207,9 +4216,13 @@
<string name="qs_edit_mode_category_privacy">
Privacy
</string>
- <!-- Label for category in QS Edit mode list of tiles, for tiles that are provided by an app, e.g. Calculator. [CHAR LIMIT=NONE] -->
+ <!-- Label for category in QS Edit mode list of tiles, for tiles that are provided by a system app, e.g. Calculator. [CHAR LIMIT=NONE] -->
+ <string name="qs_edit_mode_category_providedBySystemApps">
+ From system apps
+ </string>
+ <!-- Label for category in QS Edit mode list of tiles, for tiles that are provided by a third party app. [CHAR LIMIT=NONE] -->
<string name="qs_edit_mode_category_providedByApps">
- Provided by apps
+ From apps you installed
</string>
<!-- Label for category in QS Edit mode list of tiles, for tiles that are related to the display, e.g. Dark theme. [CHAR LIMIT=NONE] -->
<string name="qs_edit_mode_category_display">
@@ -4244,7 +4257,7 @@
Content of user education tooltip shown to teach the user that they can swipe down from the top
left edge of the display to expand the notification shade panel.
-->
- <string name="dual_shade_educational_tooltip_notifs">Swipe from the top left to open Notifications</string>
+ <string name="dual_shade_educational_tooltip_notifs">Swipe from the top left to open notifications</string>
<!--
Content of user education tooltip shown to teach the user that they can swipe down from the top
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 97cf304..0d5124f 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -566,6 +566,7 @@
<item name="android:backgroundDimEnabled">false</item>
<item name="android:showWhenLocked">true</item>
<item name="android:windowBackground">@color/transparent</item>
+ <item name="android:enforceNavigationBarContrast">false</item>
<item name="android:windowIsFloating">false</item>
<item name="android:windowNoTitle">true</item>
</style>
@@ -749,62 +750,96 @@
<item name="android:windowOptOutEdgeToEdgeEnforcement">true</item>
</style>
+ <!-- unused -->
<style name="TextAppearance.NotificationInfo.Confirmation">
<item name="android:textSize">14sp</item>
<item name="android:alpha">0.87</item>
</style>
+ <!-- unused -->
<style name="TextAppearance.NotificationInfo">
<item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
</style>
<style name="TextAppearance.NotificationInfo.Button">
- <item name="android:fontFamily">@*android:string/config_headlineFontFamilyMedium</item>
- <item name="android:textSize">14sp</item>
- <item name="android:textColor">?android:attr/colorAccent</item>
+ <item name="android:fontFamily" android:featureFlag="android.app.notifications_redesign_fonts">variable-title-small-emphasized</item>
+ <item name="android:fontFamily" android:featureFlag="!android.app.notifications_redesign_fonts">@*android:string/config_headlineFontFamilyMedium</item>
+
+ <item name="android:textSize" android:featureFlag="!android.app.notifications_redesign_fonts">14sp</item>
+
+ <item name="android:textColor" android:featureFlag="android.app.notifications_redesign_fonts">@androidprv:color/materialColorPrimary</item>
+ <item name="android:textColor" android:featureFlag="!android.app.notifications_redesign_fonts">?android:attr/colorAccent</item>
+
<item name="android:background">@drawable/btn_borderless_rect</item>
<item name="android:gravity">center_vertical</item>
<item name="android:focusable">true</item>
</style>
<style name="TextAppearance.NotificationImportanceChannel">
- <item name="android:textSize">@dimen/notification_importance_channel_text</item>
- <item name="android:fontFamily">@*android:string/config_headlineFontFamilyMedium</item>
+ <item name="android:textSize" android:featureFlag="android.app.notifications_redesign_fonts">14sp</item>
+ <item name="android:textSize" android:featureFlag="!android.app.notifications_redesign_fonts">@dimen/notification_importance_channel_text</item>
+
+ <item name="android:fontFamily" android:featureFlag="android.app.notifications_redesign_fonts">variable-title-small-emphasized</item>
+ <item name="android:fontFamily" android:featureFlag="!android.app.notifications_redesign_fonts">@*android:string/config_headlineFontFamilyMedium</item>
+
<item name="android:textColor">@androidprv:color/materialColorOnSurface</item>
- <item name="android:textSize">@dimen/notification_importance_channel_text</item>
</style>
<style name="TextAppearance.NotificationImportanceChannelGroup">
- <item name="android:textSize">@dimen/notification_importance_channel_group_text</item>
- <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
- <item name="android:textColor">@androidprv:color/materialColorOnSurface</item>
- <item name="android:textSize">@dimen/notification_importance_channel_group_text</item>
+ <item name="android:textSize" android:featureFlag="android.app.notifications_redesign_fonts">14sp</item>
+ <item name="android:textSize" android:featureFlag="!android.app.notifications_redesign_fonts">@dimen/notification_importance_channel_group_text</item>
+
+ <item name="android:fontFamily" android:featureFlag="android.app.notifications_redesign_fonts">variable-body-medium</item>
+ <item name="android:fontFamily" android:featureFlag="!android.app.notifications_redesign_fonts">@*android:string/config_headlineFontFamily</item>
+
+ <item name="android:textColor" android:featureFlag="android.app.notifications_redesign_fonts">@androidprv:color/materialColorOnSurface</item>
+ <item name="android:textColor" android:featureFlag="!android.app.notifications_redesign_fonts">@androidprv:color/materialColorOnSurfaceVariant</item>
</style>
<style name="TextAppearance.NotificationImportanceApp">
- <item name="android:textSize">@dimen/notification_importance_channel_group_text</item>
- <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
- <item name="android:textColor">@androidprv:color/materialColorOnSurfaceVariant</item>
- <item name="android:textSize">@dimen/notification_importance_channel_group_text</item>
+ <item name="android:textSize" android:featureFlag="android.app.notifications_redesign_fonts">14sp</item>
+ <item name="android:textSize" android:featureFlag="!android.app.notifications_redesign_fonts">@dimen/notification_importance_channel_group_text</item>
+
+ <item name="android:fontFamily" android:featureFlag="android.app.notifications_redesign_fonts">variable-body-medium</item>
+ <item name="android:fontFamily" android:featureFlag="!android.app.notifications_redesign_fonts">@*android:string/config_headlineFontFamily</item>
+
+ <item name="android:textColor" android:featureFlag="android.app.notifications_redesign_fonts">@androidprv:color/materialColorOnSurface</item>
+ <item name="android:textColor" android:featureFlag="!android.app.notifications_redesign_fonts">@androidprv:color/materialColorOnSurfaceVariant</item>
</style>
<style name="TextAppearance.NotificationImportanceHeader">
- <item name="android:textSize">@dimen/notification_importance_header_text</item>
- <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
+ <item name="android:textSize" android:featureFlag="android.app.notifications_redesign_fonts">14sp</item>
+ <item name="android:textSize" android:featureFlag="!android.app.notifications_redesign_fonts">@dimen/notification_importance_header_text</item>
+
+ <item name="android:fontFamily" android:featureFlag="android.app.notifications_redesign_fonts">variable-body-medium</item>
+ <item name="android:fontFamily" android:featureFlag="!android.app.notifications_redesign_fonts">@*android:string/config_headlineFontFamily</item>
+
<item name="android:textColor">@androidprv:color/materialColorOnSurface</item>
</style>
<style name="TextAppearance.NotificationImportanceDetail">
- <item name="android:textSize">@dimen/notification_importance_description_text</item>
- <item name="android:fontFamily">@*android:string/config_bodyFontFamily</item>
- <item name="android:textColor">@androidprv:color/materialColorOnSurfaceVariant</item>
+ <item name="android:textSize" android:featureFlag="android.app.notifications_redesign_fonts">14sp</item>
+ <item name="android:textSize" android:featureFlag="!android.app.notifications_redesign_fonts">@dimen/notification_importance_description_text</item>
+
+ <item name="android:fontFamily" android:featureFlag="android.app.notifications_redesign_fonts">variable-body-medium</item>
+ <item name="android:fontFamily" android:featureFlag="!android.app.notifications_redesign_fonts">@*android:string/config_bodyFontFamily</item>
+
+ <item name="android:textColor" android:featureFlag="android.app.notifications_redesign_fonts">@androidprv:color/materialColorOnSurface</item>
+ <item name="android:textColor" android:featureFlag="!android.app.notifications_redesign_fonts">@androidprv:color/materialColorOnSurfaceVariant</item>
+
<item name="android:gravity">center</item>
</style>
<style name="TextAppearance.NotificationImportanceButton">
- <item name="android:textSize">@dimen/notification_importance_button_text</item>
- <item name="android:fontFamily">@*android:string/config_headlineFontFamilyMedium</item>
- <item name="android:textColor">@color/notification_guts_priority_contents</item>
+ <item name="android:textSize" android:featureFlag="android.app.notifications_redesign_fonts">14sp</item>
+ <item name="android:textSize" android:featureFlag="!android.app.notifications_redesign_fonts">@dimen/notification_importance_button_text</item>
+
+ <item name="android:fontFamily" android:featureFlag="android.app.notifications_redesign_fonts">variable-title-small-emphasized</item>
+ <item name="android:fontFamily" android:featureFlag="!android.app.notifications_redesign_fonts">@*android:string/config_headlineFontFamilyMedium</item>
+
+ <item name="android:textColor" android:featureFlag="android.app.notifications_redesign_fonts">@androidprv:color/materialColorOnSurface</item>
+ <item name="android:textColor" android:featureFlag="!android.app.notifications_redesign_fonts">@color/notification_guts_priority_contents</item>
+
<item name="android:gravity">center</item>
</style>
@@ -813,10 +848,12 @@
parent="@android:style/Widget.DeviceDefault.Button.Borderless">
<item name="android:textColor">@androidprv:color/materialColorOnSurface</item>
<item name="android:textAllCaps">false</item>
+ <item name="android:fontFamily" android:featureFlag="android.app.notifications_redesign_fonts">variable-title-small-emphasized</item>
<item name="android:textSize">14sp</item>
<item name="android:minWidth">0dp</item>
</style>
+ <!-- unused -->
<style
name="TextAppearance.NotificationSectionHeaderButton"
parent="@android:style/Widget.DeviceDefault.Button.Borderless">
@@ -835,9 +872,11 @@
<item name="android:minWidth">0dp</item>
</style>
+ <!-- TODO: b/375010573 - merge with style above when inlining the flag. -->
<style
name="TextAppearance.NotificationFooterButtonRedesign"
parent="@android:style/Widget.DeviceDefault.Button.Borderless">
+ <item name="android:fontFamily" android:featureFlag="android.app.notifications_redesign_fonts">variable-title-medium-emphasized</item>
<item name="android:textColor">@androidprv:color/materialColorOnSurface</item>
<item name="android:drawableTint">@androidprv:color/materialColorOnSurface</item>
<item name="android:textAllCaps">false</item>
@@ -845,6 +884,13 @@
<item name="android:minWidth">0dp</item>
</style>
+ <style name="TextAppearance.NotificationShadeText"
+ parent="@android:style/TextAppearance.DeviceDefault.Widget.Button">
+ <item name="android:textColor" android:featureFlag="android.app.notifications_redesign_fonts">@androidprv:color/materialColorOnSurface</item>
+ <item name="android:fontFamily" android:featureFlag="android.app.notifications_redesign_fonts">variable-title-medium-emphasized</item>
+ <item name="android:textSize" android:featureFlag="android.app.notifications_redesign_fonts">16sp</item>
+ </style>
+
<style name="TextAppearance.NotificationMenuButtonText">
<item name="android:textSize">@dimen/notification_importance_header_text</item>
<item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
@@ -852,6 +898,11 @@
<item name="android:gravity">center</item>
</style>
+ <style name="TextAppearance.NotificationSmartReply">
+ <item name="android:textSize">12sp</item>
+ <item name="android:textColor">@androidprv:color/materialColorOnSurface</item>
+ <item name="android:fontFamily">variable-label-medium</item>
+ </style>
<style name="TextAppearance.HeadsUpStatusBarText"
parent="@*android:style/TextAppearance.DeviceDefault.Notification.Info">
diff --git a/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/Utils.kt b/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/Utils.kt
index 7aa09cf..8b1438f 100644
--- a/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/Utils.kt
+++ b/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/Utils.kt
@@ -162,8 +162,10 @@
fun ActivityTaskManager.isSystemAppOrInBackground(
context: Context,
clientPackage: String,
- clientClassNameIfItIsConfirmDeviceCredentialActivity: String?
+ clientClassNameIfItIsConfirmDeviceCredentialActivity: String?,
): Boolean {
+ // TODO (b/409812027): Consolidate and scope out auth requirements for biometric prompt
+
Log.v(TAG, "Checking if the authenticating is in background, clientPackage:$clientPackage")
val tasks = getTasks(Int.MAX_VALUE)
if (tasks == null || tasks.isEmpty()) {
@@ -176,11 +178,74 @@
val topPackageEqualsToClient = topActivity!!.packageName == clientPackage
val isClientConfirmDeviceCredentialActivity =
clientClassNameIfItIsConfirmDeviceCredentialActivity != null
- // b/339532378: If it's ConfirmDeviceCredentialActivity, we need to check further on
- // class name.
- return !(isSystemApp || topPackageEqualsToClient) ||
- (isClientConfirmDeviceCredentialActivity &&
- topActivity.className != clientClassNameIfItIsConfirmDeviceCredentialActivity)
+
+ if (
+ !isClientInBackgroundOrNotVisible(
+ isSystemApp,
+ isVisible = true,
+ topPackageEqualsToClient,
+ isClientConfirmDeviceCredentialActivity,
+ clientClassNameIfItIsConfirmDeviceCredentialActivity,
+ topActivity!!.className,
+ isTopPackage = true,
+ )
+ ) {
+ return false
+ }
+
+ for (task in tasks) {
+ val packageName = task.topActivity!!.packageName
+ val className = task.topActivity!!.className
+ val isVisible = task.isVisible
+
+ Log.v(TAG, "Running task, top: $packageName, isVisible: $isVisible")
+
+ if (
+ !isClientInBackgroundOrNotVisible(
+ isSystemApp = false,
+ isVisible,
+ taskPackageEqualsClientPackage = packageName == clientPackage,
+ isClientConfirmDeviceCredentialActivity,
+ clientClassNameIfItIsConfirmDeviceCredentialActivity,
+ className,
+ )
+ ) {
+ return false
+ }
+ }
+
+ return true
+ }
+
+ fun isClientInBackgroundOrNotVisible(
+ isSystemApp: Boolean,
+ isVisible: Boolean,
+ taskPackageEqualsClientPackage: Boolean,
+ isClientConfirmDeviceCredentialActivity: Boolean,
+ clientClassNameIfItIsConfirmDeviceCredentialActivity: String?,
+ topActivityClassName: String,
+ isTopPackage: Boolean = false,
+ ): Boolean {
+
+ if (isVisible) {
+ if (isSystemApp || taskPackageEqualsClientPackage) {
+ // b/339532378: If it's ConfirmDeviceCredentialActivity, we need to check further on
+ // class name.
+ if (isClientConfirmDeviceCredentialActivity) {
+ return !isTopPackage ||
+ clientClassNameIfItIsConfirmDeviceCredentialActivity != topActivityClassName
+ }
+ return false
+ } else if (
+ isTopPackage &&
+ isClientConfirmDeviceCredentialActivity &&
+ clientClassNameIfItIsConfirmDeviceCredentialActivity == topActivityClassName
+ ) {
+ return false
+ }
+ }
+
+ return true
}
// LINT.ThenChange(frameworks/base/services/core/java/com/android/server/biometrics/Utils.java)
}
diff --git a/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/shared/model/FingerprintSensorType.kt b/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/shared/model/FingerprintSensorType.kt
index 9a15b51..7c674c8 100644
--- a/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/shared/model/FingerprintSensorType.kt
+++ b/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/shared/model/FingerprintSensorType.kt
@@ -31,10 +31,6 @@
return (this == UDFPS_OPTICAL) || (this == UDFPS_ULTRASONIC)
}
- fun isUltrasonic(): Boolean {
- return this == UDFPS_ULTRASONIC
- }
-
fun isPowerButton(): Boolean {
return this == POWER_BUTTON
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ILauncherProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ILauncherProxy.aidl
index ade63b1..386e8fa 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ILauncherProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ILauncherProxy.aidl
@@ -159,4 +159,10 @@
* Sent when {@link TaskbarDelegate#onDisplayRemoveSystemDecorations} is called.
*/
void onDisplayRemoveSystemDecorations(int displayId) = 38;
+
+ /**
+ * Sent when active action corner is received in {@link ActionCornerInteractor}. Please refer to
+ * {@link ActionCornerConstants.Action} for all possible actions.
+ */
+ void onActionCornerActivated(int action, int displayId) = 39;
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/PreviewPositionHelper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/PreviewPositionHelper.java
index b8cd5be..01c1f79 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/PreviewPositionHelper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/PreviewPositionHelper.java
@@ -5,11 +5,17 @@
import static android.view.Surface.ROTATION_270;
import static android.view.Surface.ROTATION_90;
+import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_10_90;
+import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED;
+
import android.graphics.Matrix;
import android.graphics.Rect;
import android.graphics.RectF;
+import androidx.annotation.Nullable;
+
import com.android.systemui.shared.recents.model.ThumbnailData;
+import com.android.wm.shell.shared.split.SplitBounds;
/**
* Utility class to position the thumbnail in the TaskView
@@ -34,6 +40,18 @@
private final Matrix mMatrix = new Matrix();
private boolean mIsOrientationChanged;
+ /**
+ * Only used when this helper is being used for an app in split screen. Refers to the position
+ * of the app in the pair.
+ * See {@link com.android.wm.shell.shared.split.SplitScreenConstants#@SplitPosition}
+ */
+ private int mStagePosition = SPLIT_POSITION_UNDEFINED;
+ /**
+ * Guarded by enableFlexibleTwoAppSplit() flag, but this class doesn't have access so the
+ * caller is responsible for checking. If the flag is disabled this will be null
+ */
+ @Nullable
+ private SplitBounds mSplitBounds;
public Matrix getMatrix() {
return mMatrix;
@@ -160,6 +178,16 @@
}
thumbnailScale = targetW / (croppedWidth * scale);
+
+ if (mSplitBounds != null
+ && mSplitBounds.snapPosition == SNAP_TO_2_10_90
+ && mStagePosition == STAGE_POSITION_TOP_OR_LEFT) {
+ if (mSplitBounds.appsStackedVertically) {
+ thumbnailClipHint.top += availableHeight - croppedHeight;
+ } else {
+ thumbnailClipHint.left += availableWidth - croppedWidth;
+ }
+ }
}
if (!isRotated) {
@@ -209,6 +237,11 @@
mMatrix.postTranslate(translateX, translateY);
}
+ public void setSplitBounds(SplitBounds splitBounds, int stagePosition) {
+ mSplitBounds = splitBounds;
+ mStagePosition = stagePosition;
+ }
+
/**
* A factory that returns a new instance of the {@link PreviewPositionHelper}.
* <p>{@link PreviewPositionHelper} is a stateful helper, and hence when using it in distinct
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
index 0372a6c6..1bf12bc 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
@@ -414,6 +414,8 @@
* Corner radius that should be used on windows in order to cover the display.
* These values are expressed in pixels because they should not respect display or font
* scaling. The corner radius may change when folding/unfolding the device.
+ *
+ * @param context A display associated context.
*/
public static float getWindowCornerRadius(Context context) {
return ScreenDecorationsUtils.getWindowCornerRadius(context);
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/actioncorner/ActionCornerConstants.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/actioncorner/ActionCornerConstants.java
new file mode 100644
index 0000000..e934256
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/actioncorner/ActionCornerConstants.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.shared.system.actioncorner;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+public class ActionCornerConstants {
+ /** Go to the home screen. */
+ public static final int HOME = 0;
+ /** Open the overview screen. */
+ public static final int OVERVIEW = 1;
+ /** Open the notification shade. */
+ public static final int NOTIFICATION = 2;
+ /** Open the quick settings panel. */
+ public static final int QUICK_SETTING = 3;
+
+ @IntDef({HOME, OVERVIEW, NOTIFICATION, QUICK_SETTING})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Action {
+ }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
index c9a4289..bb0b141 100644
--- a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
+++ b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
@@ -37,7 +37,7 @@
import androidx.lifecycle.repeatOnLifecycle
import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.broadcast.BroadcastDispatcher
-import com.android.systemui.customization.R
+import com.android.systemui.customization.clocks.R as clocksR
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.DisplaySpecific
import com.android.systemui.dagger.qualifiers.Main
@@ -563,14 +563,14 @@
}
private fun getSmallClockSizePx(): Float {
- return resources.getDimensionPixelSize(R.dimen.small_clock_text_size).toFloat()
+ return resources.getDimensionPixelSize(clocksR.dimen.small_clock_text_size).toFloat()
}
private fun getLargeClockSizePx(): Float {
return if (largeClockOnSecondaryDisplay) {
- resources.getDimensionPixelSize(R.dimen.presentation_clock_text_size).toFloat()
+ resources.getDimensionPixelSize(clocksR.dimen.presentation_clock_text_size).toFloat()
} else {
- resources.getDimensionPixelSize(R.dimen.large_clock_text_size).toFloat()
+ resources.getDimensionPixelSize(clocksR.dimen.large_clock_text_size).toFloat()
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/ConnectedDisplayKeyguardPresentation.kt b/packages/SystemUI/src/com/android/keyguard/ConnectedDisplayKeyguardPresentation.kt
index 8f6a54c..e45ff65 100644
--- a/packages/SystemUI/src/com/android/keyguard/ConnectedDisplayKeyguardPresentation.kt
+++ b/packages/SystemUI/src/com/android/keyguard/ConnectedDisplayKeyguardPresentation.kt
@@ -60,7 +60,7 @@
private val clockChangedListener =
object : ClockRegistry.ClockChangeListener {
override fun onCurrentClockChanged() {
- setClock(clockRegistry.createCurrentClock())
+ setClock(clockRegistry.createCurrentClock(context))
}
override fun onAvailableClocksChanged() {}
@@ -104,7 +104,7 @@
setFullscreen()
- setClock(clockRegistry.createCurrentClock())
+ setClock(clockRegistry.createCurrentClock(context))
}
override fun onAttachedToWindow() {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt
index 40a86dc..2fae87b 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt
@@ -18,8 +18,8 @@
import android.content.Context
import android.view.View
-import com.android.systemui.customization.R as customR
import com.android.systemui.keyguard.ui.view.KeyguardRootView
+import com.android.systemui.plugins.clocks.ClockViewIds
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.res.R
import com.android.systemui.shade.NotificationShadeWindowView
@@ -90,12 +90,12 @@
viewsIdToTranslate =
setOf(
ViewIdToTranslate(
- viewId = customR.id.lockscreen_clock_view_large,
+ viewId = ClockViewIds.LOCKSCREEN_CLOCK_VIEW_LARGE,
direction = START,
shouldBeAnimated = filterKeyguardAndSplitShadeOnly,
),
ViewIdToTranslate(
- viewId = customR.id.lockscreen_clock_view,
+ viewId = ClockViewIds.LOCKSCREEN_CLOCK_VIEW_SMALL,
direction = START,
shouldBeAnimated = filterKeyguard,
),
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 60ec051..982cfab 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -44,6 +44,7 @@
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN;
+import static com.android.systemui.Flags.fingerprintCancelRaceMitigation;
import static com.android.systemui.Flags.glanceableHubV2;
import static com.android.systemui.Flags.simPinBouncerReset;
import static com.android.systemui.Flags.simPinUseSlotId;
@@ -112,7 +113,6 @@
import com.android.compose.animation.scene.ObservableTransitionState;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.foldables.FoldGracePeriodProvider;
import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.logging.InstanceId;
import com.android.internal.logging.UiEventLogger;
@@ -276,13 +276,6 @@
public static final int BIOMETRIC_HELP_FACE_NOT_RECOGNIZED = -2;
public static final int BIOMETRIC_HELP_FACE_NOT_AVAILABLE = -3;
- /**
- * If no cancel signal has been received after this amount of time, set the biometric running
- * state to stopped to allow Keyguard to retry authentication.
- */
- @VisibleForTesting
- protected static final int DEFAULT_CANCEL_SIGNAL_TIMEOUT = 3000;
-
private static final ComponentName FALLBACK_HOME_COMPONENT = new ComponentName(
"com.android.settings", "com.android.settings.FallbackHome");
@@ -394,9 +387,6 @@
private final BiometricManager mBiometricManager;
@Nullable
private DeviceEntryFaceAuthInteractor mFaceAuthInteractor;
- @VisibleForTesting
- protected FoldGracePeriodProvider mFoldGracePeriodProvider =
- new FoldGracePeriodProvider();
private final DevicePostureController mDevicePostureController;
private final TaskStackChangeListeners mTaskStackChangeListeners;
private final IActivityTaskManager mActivityTaskManager;
@@ -406,6 +396,12 @@
@DevicePostureInt
protected int mConfigFaceAuthSupportedPosture;
+ /**
+ * If no cancel signal has been received after this amount of time, set the fingerprint running
+ * state to stopped to allow Keyguard to retry authentication.
+ */
+ private int mDefaultCancelSignalTimeout;
+
private KeyguardBypassController mKeyguardBypassController;
private List<SubscriptionInfo> mSubscriptionInfo;
@VisibleForTesting
@@ -1433,10 +1429,7 @@
* Whether the keyguard should be kept unlocked for the folding grace period.
*/
public boolean forceIsDismissibleIsKeepingDeviceUnlocked() {
- if (mFoldGracePeriodProvider.isEnabled()) {
- return mForceIsDismissible && isUnlockingWithForceKeyguardDismissibleAllowed();
- }
- return false;
+ return mForceIsDismissible && isUnlockingWithForceKeyguardDismissibleAllowed();
}
public boolean getUserHasTrust(int userId) {
@@ -2254,6 +2247,10 @@
mBiometricManager = biometricManager;
mConfigFaceAuthSupportedPosture = mContext.getResources().getInteger(
R.integer.config_face_auth_supported_posture);
+ mDefaultCancelSignalTimeout = 3000;
+ if (fingerprintCancelRaceMitigation()) {
+ mDefaultCancelSignalTimeout = 5000;
+ }
mFaceWakeUpTriggersConfig = faceWakeUpTriggersConfig;
mAllowFingerprintOnOccludingActivitiesFromPackage = Arrays.stream(
mContext.getResources().getStringArray(
@@ -3238,7 +3235,7 @@
mFingerprintCancelSignal.cancel();
mFingerprintCancelSignal = null;
mHandler.removeCallbacks(mFpCancelNotReceived);
- mHandler.postDelayed(mFpCancelNotReceived, DEFAULT_CANCEL_SIGNAL_TIMEOUT);
+ mHandler.postDelayed(mFpCancelNotReceived, mDefaultCancelSignalTimeout);
}
setFingerprintRunningState(BIOMETRIC_STATE_CANCELLING);
}
@@ -3915,10 +3912,6 @@
private void setForceIsDismissibleKeyguard(boolean forceIsDismissible) {
Assert.isMainThread();
- if (!mFoldGracePeriodProvider.isEnabled()) {
- // never send updates if the feature isn't enabled
- return;
- }
if (mKeyguardShowing && forceIsDismissible) {
// never keep the device unlocked if the keyguard was already showing
mLogger.d("Skip setting forceIsDismissibleKeyguard to true. "
@@ -4227,6 +4220,7 @@
@Override
public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
pw.println("KeyguardUpdateMonitor state:");
+ pw.println(" mDefaultCancelSignalTimeout=" + mDefaultCancelSignalTimeout);
pw.println(" forceIsDismissible=" + mForceIsDismissible);
pw.println(" forceIsDismissibleIsKeepingDeviceUnlocked="
+ forceIsDismissibleIsKeepingDeviceUnlocked());
diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java b/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java
index 6e2adc0..71b13a9 100644
--- a/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java
+++ b/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java
@@ -70,7 +70,6 @@
|| featureFlags.isEnabled(Flags.LOCKSCREEN_CUSTOM_CLOCKS),
/* handleAllUsers= */ true,
new DefaultClockProvider(
- context,
layoutInflater,
resources,
com.android.systemui.shared.Flags.clockReactiveVariants(),
diff --git a/packages/SystemUI/src/com/android/systemui/LauncherProxyService.java b/packages/SystemUI/src/com/android/systemui/LauncherProxyService.java
index 70c47f5..37adf59 100644
--- a/packages/SystemUI/src/com/android/systemui/LauncherProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/LauncherProxyService.java
@@ -16,10 +16,10 @@
package com.android.systemui;
-import static android.app.Flags.keyguardPrivateNotifications;
import static android.content.Intent.ACTION_PACKAGE_ADDED;
import static android.content.Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST;
import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
+import static android.view.Display.INVALID_DISPLAY;
import static android.view.MotionEvent.ACTION_CANCEL;
import static android.view.MotionEvent.ACTION_DOWN;
import static android.view.MotionEvent.ACTION_UP;
@@ -119,6 +119,7 @@
import com.android.systemui.shared.recents.ISystemUiProxy;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags;
+import com.android.systemui.shared.system.actioncorner.ActionCornerConstants.Action;
import com.android.systemui.shared.system.smartspace.ISysuiUnlockAnimationController;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationShadeWindowController;
@@ -338,15 +339,16 @@
@Override
public void onBackEvent(@Nullable KeyEvent keyEvent) throws RemoteException {
+ final int displayId = keyEvent == null ? INVALID_DISPLAY : keyEvent.getDisplayId();
if (predictiveBackThreeButtonNav() && predictiveBackSwipeEdgeNoneApi()
&& mBackAnimation != null && keyEvent != null) {
mBackAnimation.setTriggerBack(!keyEvent.isCanceled());
mBackAnimation.onBackMotion(/* touchX */ 0, /* touchY */ 0, keyEvent.getAction(),
- EDGE_NONE);
+ EDGE_NONE, displayId);
} else {
verifyCallerAndClearCallingIdentityPostMain("onBackPressed", () -> {
- sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK);
- sendEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK);
+ sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK, displayId);
+ sendEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK, displayId);
});
}
}
@@ -405,14 +407,13 @@
onTaskbarAutohideSuspend(suspend));
}
- private boolean sendEvent(int action, int code) {
+ private boolean sendEvent(int action, int code, int displayId) {
long when = SystemClock.uptimeMillis();
final KeyEvent ev = new KeyEvent(when, when, action, code, 0 /* repeat */,
0 /* metaState */, KeyCharacterMap.VIRTUAL_KEYBOARD, 0 /* scancode */,
KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY,
InputDevice.SOURCE_KEYBOARD);
-
- ev.setDisplayId(mContext.getDisplay().getDisplayId());
+ ev.setDisplayId(displayId);
return InputManagerGlobal.getInstance()
.injectInputEvent(ev, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
}
@@ -545,12 +546,10 @@
@Override
public void onReceive(Context context, Intent intent) {
if (Objects.equals(intent.getAction(), Intent.ACTION_USER_UNLOCKED)) {
- if (keyguardPrivateNotifications()) {
- // Start the launcher connection to the launcher service
- // Connect if user hasn't connected yet
- if (getProxy() == null) {
- startConnectionToCurrentUser();
- }
+ // Start the launcher connection to the launcher service
+ // Connect if user hasn't connected yet
+ if (getProxy() == null) {
+ startConnectionToCurrentUser();
}
}
}
@@ -805,11 +804,9 @@
filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
mContext.registerReceiver(mLauncherStateChangedReceiver, filter);
- if (keyguardPrivateNotifications()) {
- mBroadcastDispatcher.registerReceiver(mUserEventReceiver,
- new IntentFilter(Intent.ACTION_USER_UNLOCKED),
- null /* executor */, UserHandle.ALL);
- }
+ mBroadcastDispatcher.registerReceiver(mUserEventReceiver,
+ new IntentFilter(Intent.ACTION_USER_UNLOCKED),
+ null /* executor */, UserHandle.ALL);
// Listen for status bar state changes
statusBarWinController.registerCallback(mStatusBarWindowCallback);
@@ -953,6 +950,20 @@
}
}
+ /**
+ * Notifies the launcher that an action corner has been activated, sending the corresponding
+ * action and display id.
+ */
+ public void onActionCornerActivated(@Action int action, int displayId) {
+ try {
+ if (mLauncherProxy != null) {
+ mLauncherProxy.onActionCornerActivated(action, displayId);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG_OPS, "Failed to call onActionCornerActivated()", e);
+ }
+ }
+
public void cleanupAfterDeath() {
if (mInputFocusTransferStarted) {
mHandler.post(() -> {
diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
index b131534..22db404 100644
--- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
@@ -197,6 +197,17 @@
return vt.getXVelocity();
}
+ /**
+ * @return the current swipe velocity, or zero if there is no swipe in progress.
+ */
+ public float getCurrentVelocity() {
+ if (mVelocityTracker != null) {
+ mVelocityTracker.computeCurrentVelocity(1000 /* px/sec */, getMaxVelocity());
+ return getVelocity(mVelocityTracker);
+ } else {
+ return 0f;
+ }
+ }
protected Animator getViewTranslationAnimator(View view, float target,
AnimatorUpdateListener listener) {
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
index 9cd77e7..49de3b7 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
@@ -169,6 +169,11 @@
*/
private SurfaceControlViewHost mSurfaceControlViewHost;
+ /**
+ * The SurfaceControl provided by SurfaceControlViewHost.
+ */
+ private SurfaceControl mMirrorViewLeash;
+
// The root of the mirrored content
private SurfaceControl mMirrorSurface;
@@ -469,6 +474,12 @@
mSurfaceControlViewHost = null;
}
+ if (mMirrorViewLeash != null) {
+ mTransaction.reparent(mMirrorViewLeash, null).apply();
+ mMirrorViewLeash.release();
+ mMirrorViewLeash = null;
+ }
+
mMirrorViewBounds.setEmpty();
mSourceBounds.setEmpty();
updateSystemUIStateIfNeeded();
@@ -659,23 +670,22 @@
mSurfaceControlViewHost = mScvhSupplier.get();
mSurfaceControlViewHost.setView(mMirrorView, params);
- SurfaceControl surfaceControl = mSurfaceControlViewHost
- .getSurfacePackage().getSurfaceControl();
+ mMirrorViewLeash = mSurfaceControlViewHost.getSurfacePackage().getSurfaceControl();
int x = mMagnificationFrame.left - mMirrorSurfaceMargin;
int y = mMagnificationFrame.top - mMirrorSurfaceMargin;
mTransaction
- .setCrop(surfaceControl, new Rect(0, 0, windowWidth, windowHeight))
- .setPosition(surfaceControl, x, y)
- .setLayer(surfaceControl, Integer.MAX_VALUE)
- .show(surfaceControl)
+ .setCrop(mMirrorViewLeash, new Rect(0, 0, windowWidth, windowHeight))
+ .setPosition(mMirrorViewLeash, x, y)
+ .setLayer(mMirrorViewLeash, Integer.MAX_VALUE)
+ .show(mMirrorViewLeash)
.apply();
mMirrorViewBounds.set(x, y, x + windowWidth, y + windowHeight);
AccessibilityManager accessibilityManager = mContext
.getSystemService(AccessibilityManager.class);
- accessibilityManager.attachAccessibilityOverlayToDisplay(mDisplayId, surfaceControl);
+ accessibilityManager.attachAccessibilityOverlayToDisplay(mDisplayId, mMirrorViewLeash);
SurfaceHolder holder = mMirrorSurfaceView.getHolder();
holder.addCallback(this);
@@ -964,13 +974,11 @@
params.width = width;
params.height = height;
mSurfaceControlViewHost.relayout(params);
- mTransaction.setCrop(mSurfaceControlViewHost.getSurfacePackage().getSurfaceControl(),
- new Rect(0, 0, width, height));
+ mTransaction.setCrop(mMirrorViewLeash, new Rect(0, 0, width, height));
}
mMirrorViewBounds.set(x, y, x + width, y + height);
- mTransaction.setPosition(
- mSurfaceControlViewHost.getSurfacePackage().getSurfaceControl(), x, y);
+ mTransaction.setPosition(mMirrorViewLeash, x, y);
if (computeWindowSize) {
mSurfaceControlViewHost.getRootSurfaceControl().applyTransactionOnDraw(mTransaction);
} else {
@@ -1751,4 +1759,4 @@
}
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/data/repository/AccessibilityRepository.kt b/packages/SystemUI/src/com/android/systemui/accessibility/data/repository/AccessibilityRepository.kt
index a09e954..0a79f0d 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/data/repository/AccessibilityRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/data/repository/AccessibilityRepository.kt
@@ -16,16 +16,24 @@
package com.android.systemui.accessibility.data.repository
+import android.accessibilityservice.AccessibilityServiceInfo
+import android.os.Handler
import android.view.accessibility.AccessibilityManager
import android.view.accessibility.AccessibilityManager.TouchExplorationStateChangeListener
import com.android.app.tracing.FlowTracing.tracedAwaitClose
import com.android.app.tracing.FlowTracing.tracedConflatedCallbackFlow
+import com.android.systemui.dagger.qualifiers.Background
import dagger.Module
import dagger.Provides
+import java.util.concurrent.Executor
import kotlin.time.Duration
import kotlin.time.Duration.Companion.milliseconds
+import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.stateIn
/** Exposes accessibility-related state. */
interface AccessibilityRepository {
@@ -34,22 +42,39 @@
/** @see [AccessibilityManager.isEnabled] */
val isEnabled: Flow<Boolean>
+ /** Returns whether a filtered set of [AccessibilityServiceInfo]s are enabled. */
+ val isEnabledFiltered: StateFlow<Boolean>
+
fun getRecommendedTimeout(originalTimeout: Duration, uiFlags: Int): Duration
companion object {
- operator fun invoke(a11yManager: AccessibilityManager): AccessibilityRepository =
- AccessibilityRepositoryImpl(a11yManager)
+ operator fun invoke(
+ a11yManager: AccessibilityManager,
+ @Background backgroundExecutor: Executor,
+ @Background backgroundHandler: Handler,
+ @Background backgroundScope: CoroutineScope,
+ ): AccessibilityRepository =
+ AccessibilityRepositoryImpl(
+ a11yManager,
+ backgroundExecutor,
+ backgroundHandler,
+ backgroundScope,
+ )
}
}
private const val TAG = "AccessibilityRepository"
-private class AccessibilityRepositoryImpl(private val manager: AccessibilityManager) :
- AccessibilityRepository {
+private class AccessibilityRepositoryImpl(
+ private val manager: AccessibilityManager,
+ @Background private val bgExecutor: Executor,
+ @Background private val bgHandler: Handler,
+ @Background private val bgScope: CoroutineScope,
+) : AccessibilityRepository {
override val isTouchExplorationEnabled: Flow<Boolean> =
tracedConflatedCallbackFlow(TAG) {
val listener = TouchExplorationStateChangeListener(::trySend)
- manager.addTouchExplorationStateChangeListener(listener)
+ manager.addTouchExplorationStateChangeListener(listener, bgHandler)
trySend(manager.isTouchExplorationEnabled)
tracedAwaitClose(TAG) {
manager.removeTouchExplorationStateChangeListener(listener)
@@ -60,12 +85,36 @@
override val isEnabled: Flow<Boolean> =
tracedConflatedCallbackFlow(TAG) {
val listener = AccessibilityManager.AccessibilityStateChangeListener(::trySend)
- manager.addAccessibilityStateChangeListener(listener)
+ manager.addAccessibilityStateChangeListener(listener, bgHandler)
trySend(manager.isEnabled)
tracedAwaitClose(TAG) { manager.removeAccessibilityStateChangeListener(listener) }
}
.distinctUntilChanged()
+ override val isEnabledFiltered: StateFlow<Boolean> =
+ tracedConflatedCallbackFlow(TAG) {
+ val listener =
+ AccessibilityManager.AccessibilityServicesStateChangeListener {
+ accessibilityManager ->
+ trySend(
+ accessibilityManager
+ .getEnabledAccessibilityServiceList(
+ AccessibilityServiceInfo.FEEDBACK_AUDIBLE or
+ AccessibilityServiceInfo.FEEDBACK_SPOKEN or
+ AccessibilityServiceInfo.FEEDBACK_VISUAL or
+ AccessibilityServiceInfo.FEEDBACK_HAPTIC or
+ AccessibilityServiceInfo.FEEDBACK_BRAILLE
+ )
+ .isNotEmpty()
+ )
+ }
+ manager.addAccessibilityServicesStateChangeListener(bgExecutor, listener)
+ tracedAwaitClose(TAG) {
+ manager.removeAccessibilityServicesStateChangeListener(listener)
+ }
+ }
+ .stateIn(scope = bgScope, started = SharingStarted.Eagerly, initialValue = false)
+
override fun getRecommendedTimeout(originalTimeout: Duration, uiFlags: Int): Duration {
return manager
.getRecommendedTimeoutMillis(originalTimeout.inWholeMilliseconds.toInt(), uiFlags)
@@ -75,5 +124,11 @@
@Module
object AccessibilityRepositoryModule {
- @Provides fun provideRepo(manager: AccessibilityManager) = AccessibilityRepository(manager)
+ @Provides
+ fun provideRepo(
+ manager: AccessibilityManager,
+ @Background backgroundExecutor: Executor,
+ @Background backgroundHandler: Handler,
+ @Background backgroundScope: CoroutineScope,
+ ) = AccessibilityRepository(manager, backgroundExecutor, backgroundHandler, backgroundScope)
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/domain/interactor/AccessibilityInteractor.kt b/packages/SystemUI/src/com/android/systemui/accessibility/domain/interactor/AccessibilityInteractor.kt
index 93b624a..55acd3a 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/domain/interactor/AccessibilityInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/domain/interactor/AccessibilityInteractor.kt
@@ -16,10 +16,12 @@
package com.android.systemui.accessibility.domain.interactor
+import android.accessibilityservice.AccessibilityServiceInfo
import com.android.systemui.accessibility.data.repository.AccessibilityRepository
import com.android.systemui.dagger.SysUISingleton
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.StateFlow
@SysUISingleton
class AccessibilityInteractor
@@ -32,4 +34,7 @@
/** @see [android.view.accessibility.AccessibilityManager.isEnabled] */
val isEnabled: Flow<Boolean> = a11yRepo.isEnabled
+
+ /** This returns whether a filtered set of [AccessibilityServiceInfo]s are enabled. */
+ val isEnabledFiltered: StateFlow<Boolean> = a11yRepo.isEnabledFiltered
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/AmbientVolumeLayout.java b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/AmbientVolumeLayout.java
index 33c9eb1..d23db67 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/AmbientVolumeLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/AmbientVolumeLayout.java
@@ -119,7 +119,7 @@
mExpandIcon = requireViewById(R.id.ambient_expand_icon);
mExpandIcon.setOnClickListener(v -> {
- setExpanded(!mExpanded);
+ setControlExpanded(!mExpanded);
if (mUiEventLogger != null) {
HearingDevicesUiEvent uiEvent = mExpanded
? HearingDevicesUiEvent.HEARING_DEVICES_AMBIENT_EXPAND_CONTROLS
@@ -139,21 +139,21 @@
}
@Override
- public void setExpandable(boolean expandable) {
+ public void setControlExpandable(boolean expandable) {
mExpandable = expandable;
if (!mExpandable) {
- setExpanded(false);
+ setControlExpanded(false);
}
updateExpandIcon();
}
@Override
- public boolean isExpandable() {
+ public boolean isControlExpandable() {
return mExpandable;
}
@Override
- public void setExpanded(boolean expanded) {
+ public void setControlExpanded(boolean expanded) {
if (!mExpandable && expanded) {
return;
}
@@ -163,7 +163,7 @@
}
@Override
- public boolean isExpanded() {
+ public boolean isControlExpanded() {
return mExpanded;
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java
index 680da3a..d7c1a79 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java
@@ -61,6 +61,7 @@
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
import com.android.systemui.Flags;
import com.android.systemui.accessibility.hearingaid.HearingDevicesListAdapter.HearingDeviceItemCallback;
+import com.android.systemui.animation.ActivityTransitionAnimator;
import com.android.systemui.animation.DialogTransitionAnimator;
import com.android.systemui.bluetooth.qsdialog.ActiveHearingDeviceItemFactory;
import com.android.systemui.bluetooth.qsdialog.AvailableHearingDeviceItemFactory;
@@ -206,13 +207,12 @@
@Override
public void onDeviceItemGearClicked(@NonNull DeviceItem deviceItem, @NonNull View view) {
mUiEventLogger.log(HearingDevicesUiEvent.HEARING_DEVICES_GEAR_CLICK, mLaunchSourceId);
- dismissDialogIfExists();
Bundle bundle = new Bundle();
bundle.putString(KEY_BLUETOOTH_ADDRESS, deviceItem.getCachedBluetoothDevice().getAddress());
Intent intent = new Intent(ACTION_BLUETOOTH_DEVICE_DETAILS)
.setPackage(mQSSettingsPackageRepository.getSettingsPackageName())
.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, bundle);
- mActivityStarter.postStartActivityDismissingKeyguard(intent, /* delay= */ 0,
+ startActivityWithTransition(intent,
mDialogTransitionAnimator.createActivityTransitionController(view));
}
@@ -289,12 +289,10 @@
.putExtra(Intent.EXTRA_COMPONENT_NAME,
ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME.flattenToString())
.setPackage(mQSSettingsPackageRepository.getSettingsPackageName());
-
- mActivityStarter.postStartActivityDismissingKeyguard(intent, /* delay= */ 0,
- mDialogTransitionAnimator.createActivityTransitionController(
- dialog));
+ startActivityWithTransition(intent,
+ mDialogTransitionAnimator.createActivityTransitionController(dialog));
},
- /* dismissOnClick = */ true
+ /* dismissOnClick = */ false
);
dialog.setPositiveButton(
R.string.quick_settings_done,
@@ -466,10 +464,9 @@
if (mShowPairNewDevice) {
pairButton.setOnClickListener(v -> {
mUiEventLogger.log(HearingDevicesUiEvent.HEARING_DEVICES_PAIR, mLaunchSourceId);
- dismissDialogIfExists();
final Intent intent = new Intent(Settings.ACTION_HEARING_DEVICE_PAIRING_SETTINGS)
.setPackage(mQSSettingsPackageRepository.getSettingsPackageName());
- mActivityStarter.postStartActivityDismissingKeyguard(intent, /* delay= */ 0,
+ startActivityWithTransition(intent,
mDialogTransitionAnimator.createActivityTransitionController(dialog));
});
}
@@ -599,8 +596,7 @@
if (Flags.stuckHearingDevicesQsTileFix()) {
mDialogTransitionAnimator.disableAllCurrentDialogsExitAnimations();
}
- dismissDialogIfExists();
- mActivityStarter.postStartActivityDismissingKeyguard(intent, /* delay= */ 0,
+ startActivityWithTransition(intent,
mDialogTransitionAnimator.createActivityTransitionController(view));
});
return view;
@@ -627,6 +623,17 @@
}
}
+ private void startActivityWithTransition(Intent intent,
+ ActivityTransitionAnimator.Controller animationController) {
+ if (animationController == null) {
+ // The ActivityTransitionAnimator.Controller should take care of dismissing dialog,
+ // but we can make sure we dismiss the dialog if we don't animate it.
+ dismissDialogIfExists();
+ }
+ mActivityStarter.postStartActivityDismissingKeyguard(intent, /* delay= */ 0,
+ animationController);
+ }
+
private void showErrorToast(int stringResId) {
Toast.makeText(mDialog.getContext(), stringResId, Toast.LENGTH_SHORT).show();
}
diff --git a/packages/SystemUI/src/com/android/systemui/back/domain/interactor/BackActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/back/domain/interactor/BackActionInteractor.kt
index 113df20..b813534 100644
--- a/packages/SystemUI/src/com/android/systemui/back/domain/interactor/BackActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/back/domain/interactor/BackActionInteractor.kt
@@ -28,6 +28,7 @@
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.shade.QuickSettingsController
import com.android.systemui.shade.ShadeController
import com.android.systemui.shade.domain.interactor.ShadeBackActionInteractor
@@ -80,6 +81,10 @@
notificationShadeWindowController.windowRootView?.viewRootImpl?.onBackInvokedDispatcher
override fun start() {
+ if (SceneContainerFlag.isEnabled) {
+ return
+ }
+
scope.launch {
windowRootViewVisibilityInteractor.isLockscreenOrShadeVisibleAndInteractive.collect {
visible ->
@@ -93,12 +98,20 @@
}
fun shouldBackBeHandled(): Boolean {
+ if (SceneContainerFlag.isEnabled) {
+ return false
+ }
+
return statusBarStateController.state != StatusBarState.KEYGUARD &&
statusBarStateController.state != StatusBarState.SHADE_LOCKED &&
!statusBarKeyguardViewManager.isBouncerShowingOverDream
}
fun onBackRequested(): Boolean {
+ if (SceneContainerFlag.isEnabled) {
+ return false
+ }
+
if (statusBarKeyguardViewManager.canHandleBackPressed()) {
statusBarKeyguardViewManager.onBackPressed()
return true
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
index 7bb08ed..38af7e0 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
@@ -741,7 +741,7 @@
final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT,
- WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
+ WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG,
windowFlags,
PixelFormat.TRANSLUCENT);
lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/FaceHelpMessageDeferral.kt b/packages/SystemUI/src/com/android/systemui/biometrics/FaceHelpMessageDeferral.kt
index d382ada..83e1683 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/FaceHelpMessageDeferral.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/FaceHelpMessageDeferral.kt
@@ -20,7 +20,6 @@
import android.os.SystemClock.elapsedRealtime
import com.android.keyguard.logging.BiometricMessageDeferralLogger
import com.android.systemui.Dumpable
-import com.android.systemui.Flags.faceMessageDeferUpdate
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.deviceentry.shared.model.HelpFaceAuthenticationStatus
@@ -94,47 +93,32 @@
private val systemClock: Lazy<SystemClock>,
) : Dumpable {
- private val faceHelpMessageDebouncer: FaceHelpMessageDebouncer? =
- if (faceMessageDeferUpdate()) {
- FaceHelpMessageDebouncer(
- window = windowToAnalyzeLastNFrames,
- startWindow = 0L,
- shownFaceMessageFrequencyBoost = 0,
- threshold = threshold,
- )
- } else {
- null
- }
+ private val faceHelpMessageDebouncer: FaceHelpMessageDebouncer =
+ FaceHelpMessageDebouncer(
+ window = windowToAnalyzeLastNFrames,
+ startWindow = 0L,
+ shownFaceMessageFrequencyBoost = 0,
+ threshold = threshold,
+ )
private val acquiredInfoToFrequency: MutableMap<Int, Int> = HashMap()
private val acquiredInfoToHelpString: MutableMap<Int, String> = HashMap()
private var mostFrequentAcquiredInfoToDefer: Int? = null
private var totalFrames = 0
init {
- dumpManager.registerNormalDumpable(
- "${this.javaClass.name}[$id]",
- this,
- )
+ dumpManager.registerNormalDumpable("${this.javaClass.name}[$id]", this)
}
override fun dump(pw: PrintWriter, args: Array<out String>) {
pw.println("messagesToDefer=$messagesToDefer")
pw.println("totalFrames=$totalFrames")
pw.println("threshold=$threshold")
- pw.println("faceMessageDeferUpdateFlagEnabled=${faceMessageDeferUpdate()}")
- if (faceMessageDeferUpdate()) {
- pw.println("windowToAnalyzeLastNFrames(ms)=$windowToAnalyzeLastNFrames")
- }
+ pw.println("windowToAnalyzeLastNFrames(ms)=$windowToAnalyzeLastNFrames")
}
/** Reset all saved counts. */
fun reset() {
totalFrames = 0
- if (!faceMessageDeferUpdate()) {
- mostFrequentAcquiredInfoToDefer = null
- acquiredInfoToFrequency.clear()
- }
-
acquiredInfoToHelpString.clear()
logBuffer.reset()
}
@@ -170,46 +154,26 @@
}
totalFrames++
- if (faceMessageDeferUpdate()) {
- faceHelpMessageDebouncer?.let {
- val helpFaceAuthStatus =
- HelpFaceAuthenticationStatus(
- msgId = acquiredInfo,
- msg = null,
- systemClock.get().elapsedRealtime()
- )
- if (totalFrames == 1) { // first frame
- it.startNewFaceAuthSession(helpFaceAuthStatus.createdAt)
- }
- it.addMessage(helpFaceAuthStatus)
+ with(faceHelpMessageDebouncer) {
+ val helpFaceAuthStatus =
+ HelpFaceAuthenticationStatus(
+ msgId = acquiredInfo,
+ msg = null,
+ systemClock.get().elapsedRealtime(),
+ )
+ if (totalFrames == 1) { // first frame
+ startNewFaceAuthSession(helpFaceAuthStatus.createdAt)
}
- } else {
- val newAcquiredInfoCount = acquiredInfoToFrequency.getOrDefault(acquiredInfo, 0) + 1
- acquiredInfoToFrequency[acquiredInfo] = newAcquiredInfoCount
- if (
- messagesToDefer.contains(acquiredInfo) &&
- (mostFrequentAcquiredInfoToDefer == null ||
- newAcquiredInfoCount >
- acquiredInfoToFrequency.getOrDefault(
- mostFrequentAcquiredInfoToDefer!!,
- 0
- ))
- ) {
- mostFrequentAcquiredInfoToDefer = acquiredInfo
- }
+ addMessage(helpFaceAuthStatus)
}
logBuffer.logFrameProcessed(
acquiredInfo,
totalFrames,
- if (faceMessageDeferUpdate()) {
- faceHelpMessageDebouncer
- ?.getMessageToShow(systemClock.get().elapsedRealtime())
- ?.msgId
- .toString()
- } else {
- mostFrequentAcquiredInfoToDefer?.toString()
- }
+ faceHelpMessageDebouncer
+ .getMessageToShow(systemClock.get().elapsedRealtime())
+ ?.msgId
+ .toString(),
)
}
@@ -221,17 +185,9 @@
* [threshold] percentage.
*/
fun getDeferredMessage(): CharSequence? {
- if (faceMessageDeferUpdate()) {
- faceHelpMessageDebouncer?.let {
- val helpFaceAuthStatus = it.getMessageToShow(systemClock.get().elapsedRealtime())
- return acquiredInfoToHelpString[helpFaceAuthStatus?.msgId]
- }
- } else {
- mostFrequentAcquiredInfoToDefer?.let {
- if (acquiredInfoToFrequency.getOrDefault(it, 0) > (threshold * totalFrames)) {
- return acquiredInfoToHelpString[it]
- }
- }
+ with(faceHelpMessageDebouncer) {
+ val helpFaceAuthStatus = getMessageToShow(systemClock.get().elapsedRealtime())
+ return acquiredInfoToHelpString[helpFaceAuthStatus?.msgId]
}
return null
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index 9064966..7966f41 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -83,6 +83,7 @@
import com.android.systemui.biometrics.udfps.TouchProcessorResult;
import com.android.systemui.biometrics.ui.viewmodel.DefaultUdfpsTouchOverlayViewModel;
import com.android.systemui.biometrics.ui.viewmodel.DeviceEntryUdfpsTouchOverlayViewModel;
+import com.android.systemui.biometrics.ui.viewmodel.PromptUdfpsTouchOverlayViewModel;
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor;
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor;
import com.android.systemui.dagger.SysUISingleton;
@@ -178,6 +179,8 @@
mDeviceEntryUdfpsTouchOverlayViewModel;
@NonNull private final Lazy<DefaultUdfpsTouchOverlayViewModel>
mDefaultUdfpsTouchOverlayViewModel;
+ @NonNull private final Lazy<PromptUdfpsTouchOverlayViewModel>
+ mPromptUdfpsTouchOverlayViewModel;
@NonNull private final AlternateBouncerInteractor mAlternateBouncerInteractor;
@NonNull private final UdfpsOverlayInteractor mUdfpsOverlayInteractor;
@NonNull private final PowerInteractor mPowerInteractor;
@@ -272,18 +275,11 @@
mUdfpsOverlayInteractor.setRequestId(requestId);
mFgExecutor.execute(() -> UdfpsController.this.showUdfpsOverlay(
new UdfpsControllerOverlay(
- mContext,
mInflater,
mWindowManager,
mAccessibilityManager,
- mStatusBarStateController,
- mKeyguardViewManager,
mKeyguardUpdateMonitor,
- mDialogManager,
- mDumpManager,
- mConfigurationController,
mKeyguardStateController,
- mUnlockedScreenOffAnimationController,
mUdfpsDisplayMode,
requestId,
reason,
@@ -292,14 +288,10 @@
requestId,
event
),
- mActivityTransitionAnimator,
- mPrimaryBouncerInteractor,
- mAlternateBouncerInteractor,
mKeyguardTransitionInteractor,
- mSelectedUserInteractor,
mDeviceEntryUdfpsTouchOverlayViewModel,
mDefaultUdfpsTouchOverlayViewModel,
- mShadeInteractor,
+ mPromptUdfpsTouchOverlayViewModel,
mUdfpsOverlayInteractor,
mPowerInteractor,
mScope
@@ -697,6 +689,7 @@
@NonNull KeyguardTransitionInteractor keyguardTransitionInteractor,
Lazy<DeviceEntryUdfpsTouchOverlayViewModel> deviceEntryUdfpsTouchOverlayViewModel,
Lazy<DefaultUdfpsTouchOverlayViewModel> defaultUdfpsTouchOverlayViewModel,
+ Lazy<PromptUdfpsTouchOverlayViewModel> promptUdfpsTouchOverlayViewModel,
@NonNull UdfpsOverlayInteractor udfpsOverlayInteractor,
@NonNull PowerInteractor powerInteractor,
@Application CoroutineScope scope,
@@ -752,6 +745,7 @@
mSessionTracker = sessionTracker;
mDeviceEntryUdfpsTouchOverlayViewModel = deviceEntryUdfpsTouchOverlayViewModel;
mDefaultUdfpsTouchOverlayViewModel = defaultUdfpsTouchOverlayViewModel;
+ mPromptUdfpsTouchOverlayViewModel = promptUdfpsTouchOverlayViewModel;
mDumpManager.registerDumpable(TAG, this);
@@ -827,7 +821,7 @@
+ " isn't running on keyguard. Skip show.");
return;
}
- if (overlay.show(this, mOverlayParams)) {
+ if (overlay.show(mOverlayParams)) {
Log.d(TAG, "showUdfpsOverlay | adding window reason=" + requestReason);
mOnFingerDown = false;
mAttemptedToDismissKeyguard = false;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
index 61b6707..1e6567e 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
@@ -18,7 +18,6 @@
import android.annotation.SuppressLint
import android.annotation.UiThread
-import android.content.Context
import android.graphics.PixelFormat
import android.graphics.Rect
import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_BP
@@ -27,10 +26,8 @@
import android.hardware.biometrics.BiometricRequestConstants.REASON_ENROLL_FIND_SENSOR
import android.hardware.biometrics.BiometricRequestConstants.RequestReason
import android.hardware.fingerprint.IUdfpsOverlayControllerCallback
-import android.os.Build
import android.os.RemoteException
import android.os.Trace
-import android.provider.Settings
import android.util.Log
import android.util.RotationUtils
import android.view.LayoutInflater
@@ -40,32 +37,21 @@
import android.view.WindowManager
import android.view.accessibility.AccessibilityManager
import android.view.accessibility.AccessibilityManager.TouchExplorationStateChangeListener
-import androidx.annotation.VisibleForTesting
import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.keyguard.KeyguardUpdateMonitor
-import com.android.systemui.animation.ActivityTransitionAnimator
import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor
import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams
import com.android.systemui.biometrics.ui.binder.UdfpsTouchOverlayBinder
import com.android.systemui.biometrics.ui.view.UdfpsTouchOverlay
import com.android.systemui.biometrics.ui.viewmodel.DefaultUdfpsTouchOverlayViewModel
import com.android.systemui.biometrics.ui.viewmodel.DeviceEntryUdfpsTouchOverlayViewModel
-import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
-import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
+import com.android.systemui.biometrics.ui.viewmodel.PromptUdfpsTouchOverlayViewModel
import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.dump.DumpManager
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState
-import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.res.R
-import com.android.systemui.shade.domain.interactor.ShadeInteractor
-import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
-import com.android.systemui.statusbar.phone.SystemUIDialogManager
-import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController
-import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.KeyguardStateController
-import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import dagger.Lazy
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
@@ -75,42 +61,27 @@
private const val TAG = "UdfpsControllerOverlay"
-@VisibleForTesting const val SETTING_REMOVE_ENROLLMENT_UI = "udfps_overlay_remove_enrollment_ui"
-
/**
* Keeps track of the overlay state and UI resources associated with a single FingerprintService
* request. This state can persist across configuration changes via the [show] and [hide] methods.
*/
@UiThread
class UdfpsControllerOverlay
-@JvmOverloads
constructor(
- private val context: Context,
private val inflater: LayoutInflater,
private val windowManager: WindowManager,
private val accessibilityManager: AccessibilityManager,
- private val statusBarStateController: StatusBarStateController,
- private val statusBarKeyguardViewManager: StatusBarKeyguardViewManager,
private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
- private val dialogManager: SystemUIDialogManager,
- private val dumpManager: DumpManager,
- private val configurationController: ConfigurationController,
private val keyguardStateController: KeyguardStateController,
- private val unlockedScreenOffAnimationController: UnlockedScreenOffAnimationController,
private var udfpsDisplayModeProvider: UdfpsDisplayModeProvider,
val requestId: Long,
@RequestReason val requestReason: Int,
private val controllerCallback: IUdfpsOverlayControllerCallback,
private val onTouch: (View, MotionEvent) -> Boolean,
- private val activityTransitionAnimator: ActivityTransitionAnimator,
- private val primaryBouncerInteractor: PrimaryBouncerInteractor,
- private val alternateBouncerInteractor: AlternateBouncerInteractor,
- private val isDebuggable: Boolean = Build.IS_DEBUGGABLE,
- private val transitionInteractor: KeyguardTransitionInteractor,
- private val selectedUserInteractor: SelectedUserInteractor,
+ transitionInteractor: KeyguardTransitionInteractor,
private val deviceEntryUdfpsTouchOverlayViewModel: Lazy<DeviceEntryUdfpsTouchOverlayViewModel>,
private val defaultUdfpsTouchOverlayViewModel: Lazy<DefaultUdfpsTouchOverlayViewModel>,
- private val shadeInteractor: ShadeInteractor,
+ private val promptUdfpsTouchOverlayViewModel: Lazy<PromptUdfpsTouchOverlayViewModel>,
private val udfpsOverlayInteractor: UdfpsOverlayInteractor,
private val powerInteractor: PowerInteractor,
@Application private val scope: CoroutineScope,
@@ -170,20 +141,9 @@
private var touchExplorationEnabled = false
- private fun shouldRemoveEnrollmentUi(): Boolean {
- if (isDebuggable) {
- return Settings.Global.getInt(
- context.contentResolver,
- SETTING_REMOVE_ENROLLMENT_UI,
- 0, /* def */
- ) != 0
- }
- return false
- }
-
/** Show the overlay or return false and do nothing if it is already showing. */
@SuppressLint("ClickableViewAccessibility")
- fun show(controller: UdfpsController, params: UdfpsOverlayParams): Boolean {
+ fun show(params: UdfpsOverlayParams): Boolean {
if (getTouchOverlay() == null) {
overlayParams = params
sensorBounds = Rect(params.sensorBounds)
@@ -206,6 +166,12 @@
viewModel = deviceEntryUdfpsTouchOverlayViewModel.get(),
udfpsOverlayInteractor = udfpsOverlayInteractor,
)
+ REASON_AUTH_BP ->
+ UdfpsTouchOverlayBinder.bind(
+ view = this,
+ viewModel = promptUdfpsTouchOverlayViewModel.get(),
+ udfpsOverlayInteractor = udfpsOverlayInteractor,
+ )
else ->
UdfpsTouchOverlayBinder.bind(
view = this,
@@ -340,7 +306,7 @@
val rot = overlayParams.rotation
if (rot == Surface.ROTATION_90 || rot == Surface.ROTATION_270) {
- if (!shouldRotate(animation)) {
+ if (!shouldRotate()) {
Log.v(
TAG,
"Skip rotating UDFPS bounds " +
@@ -375,7 +341,7 @@
return this
}
- private fun shouldRotate(animation: UdfpsAnimationViewController<*>?): Boolean {
+ private fun shouldRotate(): Boolean {
if (!keyguardStateController.isShowing) {
// always rotate view if we're not on the keyguard
return true
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/BiometricStatusRepository.kt b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/BiometricStatusRepository.kt
index 5a5f34d..da270c0 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/BiometricStatusRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/BiometricStatusRepository.kt
@@ -66,9 +66,6 @@
/** The current status of an acquired fingerprint. */
val fingerprintAcquiredStatus: Flow<FingerprintAuthenticationStatus>
-
- /** The authentication states of all biometric modalities. */
- val authenticationState: Flow<AuthenticationState>
}
@SysUISingleton
@@ -87,7 +84,7 @@
* [FaceManager.FaceDetectionCallback], and onEnrollmentError, onEnrollmentHelp, and
* onAcquired in [FingerprintManager.EnrollmentCallback] and [FaceManager.EnrollmentCallback]
*/
- override val authenticationState: Flow<AuthenticationState> =
+ private val authenticationState: Flow<AuthenticationState> =
callbackFlow {
val updateAuthenticationState = { state: AuthenticationState ->
Log.d(TAG, "authenticationState updated: $state")
@@ -103,7 +100,7 @@
AuthenticationState.Acquired(
authInfo.biometricSourceType,
authInfo.requestReason.toAuthenticationReason(),
- authInfo.acquiredInfo,
+ authInfo.acquiredInfo
)
)
}
@@ -114,7 +111,7 @@
authInfo.biometricSourceType,
authInfo.errString,
authInfo.errCode,
- authInfo.requestReason.toAuthenticationReason(),
+ authInfo.requestReason.toAuthenticationReason()
)
)
}
@@ -124,7 +121,7 @@
AuthenticationState.Failed(
authInfo.biometricSourceType,
authInfo.requestReason.toAuthenticationReason(),
- authInfo.userId,
+ authInfo.userId
)
)
}
@@ -135,7 +132,7 @@
authInfo.biometricSourceType,
authInfo.helpString,
authInfo.helpCode,
- authInfo.requestReason.toAuthenticationReason(),
+ authInfo.requestReason.toAuthenticationReason()
)
)
}
@@ -144,7 +141,7 @@
updateAuthenticationState(
AuthenticationState.Started(
authInfo.biometricSourceType,
- authInfo.requestReason.toAuthenticationReason(),
+ authInfo.requestReason.toAuthenticationReason()
)
)
}
@@ -153,7 +150,7 @@
updateAuthenticationState(
AuthenticationState.Stopped(
authInfo.biometricSourceType,
- AuthenticationReason.NotRunning,
+ AuthenticationReason.NotRunning
)
)
}
@@ -166,7 +163,7 @@
authInfo.biometricSourceType,
authInfo.isIsStrongBiometric,
authInfo.requestReason.toAuthenticationReason(),
- authInfo.userId,
+ authInfo.userId
)
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/BiometricStatusInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/BiometricStatusInteractor.kt
index 579e54c..83aefca 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/BiometricStatusInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/BiometricStatusInteractor.kt
@@ -17,20 +17,16 @@
package com.android.systemui.biometrics.domain.interactor
import android.app.ActivityTaskManager
-import android.hardware.biometrics.BiometricSourceType
import android.util.Log
import com.android.systemui.biometrics.data.repository.BiometricStatusRepository
import com.android.systemui.biometrics.data.repository.FingerprintPropertyRepository
import com.android.systemui.biometrics.shared.model.AuthenticationReason
import com.android.systemui.biometrics.shared.model.AuthenticationReason.SettingsOperations
-import com.android.systemui.biometrics.shared.model.AuthenticationState
import com.android.systemui.keyguard.shared.model.FingerprintAuthenticationStatus
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.filter
-import kotlinx.coroutines.flow.filterIsInstance
import kotlinx.coroutines.flow.onEach
/** Encapsulates business logic for interacting with biometric authentication state. */
@@ -43,9 +39,6 @@
/** The current status of an acquired fingerprint. */
val fingerprintAcquiredStatus: Flow<FingerprintAuthenticationStatus>
-
- /** The events of fingerprint authentication failure while unlocking the device. */
- val fingerprintAuthFailureEventsForDeviceEntry: Flow<AuthenticationState.Failed>
}
class BiometricStatusInteractorImpl
@@ -59,7 +52,7 @@
override val sfpsAuthenticationReason: Flow<AuthenticationReason> =
combine(
biometricStatusRepository.fingerprintAuthenticationReason,
- fingerprintPropertyRepository.sensorType,
+ fingerprintPropertyRepository.sensorType
) { reason: AuthenticationReason, sensorType ->
if (
sensorType.isPowerButton() &&
@@ -76,14 +69,6 @@
override val fingerprintAcquiredStatus: Flow<FingerprintAuthenticationStatus> =
biometricStatusRepository.fingerprintAcquiredStatus
- override val fingerprintAuthFailureEventsForDeviceEntry: Flow<AuthenticationState.Failed> =
- biometricStatusRepository.authenticationState
- .filter {
- it.biometricSourceType == BiometricSourceType.FINGERPRINT &&
- it.requestReason == AuthenticationReason.DeviceEntryAuthentication
- }
- .filterIsInstance<AuthenticationState.Failed>()
-
companion object {
private const val TAG = "BiometricStatusInteractor"
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/FingerprintPropertyInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/FingerprintPropertyInteractor.kt
index 0eedb6c..ae855d1 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/FingerprintPropertyInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/FingerprintPropertyInteractor.kt
@@ -19,20 +19,13 @@
import android.content.Context
import android.graphics.Rect
import android.hardware.biometrics.SensorLocationInternal
-import android.provider.Settings
-import com.android.internal.R
import com.android.systemui.biometrics.data.repository.FingerprintPropertyRepository
import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.shared.customization.data.SensorLocation
-import com.android.systemui.util.kotlin.emitOnStart
-import com.android.systemui.util.settings.SecureSettings
-import com.android.systemui.util.settings.SettingsProxyExt.observerFlow
import javax.inject.Inject
-import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
@@ -40,7 +33,6 @@
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filterNotNull
-import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
@@ -54,8 +46,6 @@
@Main private val configurationInteractor: ConfigurationInteractor,
displayStateInteractor: DisplayStateInteractor,
udfpsOverlayInteractor: UdfpsOverlayInteractor,
- private val secureSettings: SecureSettings,
- @Background private val backgroundDispatcher: CoroutineDispatcher,
) {
val propertiesInitialized: Flow<Boolean> = repository.propertiesInitialized
val isUdfps: StateFlow<Boolean> =
@@ -67,39 +57,6 @@
initialValue = repository.sensorType.value.isUdfps(),
)
- /** True if it is ultrasonic udfps sensor, otherwise false. */
- val isUltrasonic: StateFlow<Boolean> =
- repository.sensorType
- .map { it.isUltrasonic() }
- .stateIn(
- scope = applicationScope,
- started = SharingStarted.Eagerly,
- initialValue = repository.sensorType.value.isUltrasonic(),
- )
-
- /** True if screen-off unlock is both supported and enabled, otherwise false. */
- val screenOffUnlockEnabled: StateFlow<Boolean> =
- secureSettings
- .observerFlow(context.userId, Settings.Secure.SCREEN_OFF_UNLOCK_UDFPS_ENABLED)
- .emitOnStart()
- .map { isScreenOffUnlockEnabled() }
- .distinctUntilChanged()
- .flowOn(backgroundDispatcher)
- .stateIn(
- applicationScope,
- started = SharingStarted.Eagerly,
- initialValue = isScreenOffUnlockEnabled(),
- )
-
- private fun isScreenOffUnlockEnabled(): Boolean =
- context.resources.getBoolean(R.bool.config_screen_off_udfps_enabled) &&
- Settings.Secure.getIntForUser(
- context.contentResolver,
- Settings.Secure.SCREEN_OFF_UNLOCK_UDFPS_ENABLED,
- 0,
- context.userId,
- ) != 0
-
/**
* Devices with multiple physical displays use unique display ids to determine which sensor is
* on the active physical display. This value represents a unique physical display id.
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
index 54c52b5..37ce116 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
@@ -139,7 +139,6 @@
)
}
}
- ViewCompat.setAccessibilityDelegate(backgroundView, cancelDelegate)
ViewCompat.setAccessibilityDelegate(cancelButton, cancelDelegate)
// TODO(b/330788871): temporary workaround for the unsafe callbacks & legacy controllers
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/model/DualShadeEducationalTooltipModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptUdfpsTouchOverlayViewModel.kt
similarity index 61%
copy from packages/SystemUI/src/com/android/systemui/scene/domain/model/DualShadeEducationalTooltipModel.kt
copy to packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptUdfpsTouchOverlayViewModel.kt
index f63e132..d8c659e 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/model/DualShadeEducationalTooltipModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptUdfpsTouchOverlayViewModel.kt
@@ -14,11 +14,13 @@
* limitations under the License.
*/
-package com.android.systemui.scene.domain.model
+package com.android.systemui.biometrics.ui.viewmodel
-/** Enumerates the type of tooltip to show. */
-enum class DualShadeEducationalTooltipModel {
- None,
- ForNotificationsShade,
- ForQuickSettingsShade,
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+
+class PromptUdfpsTouchOverlayViewModel @Inject constructor() : UdfpsTouchOverlayViewModel {
+ // Biometric Prompt should always handle touches
+ override val shouldHandleTouches: Flow<Boolean> = MutableStateFlow(true)
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModel.kt
index a796ecf..9c9ae63 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModel.kt
@@ -38,7 +38,6 @@
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.domain.interactor.DeviceEntrySideFpsOverlayInteractor
import com.android.systemui.res.R
-import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
@@ -153,7 +152,7 @@
/** List of LottieCallbacks use for adding dynamic color to the overlayView */
val lottieCallbacks: Flow<List<LottieCallback>> =
- _lottieBounds.sample(deviceEntrySideFpsOverlayInteractor.showIndicatorForDeviceEntry) {
+ combine(_lottieBounds, deviceEntrySideFpsOverlayInteractor.showIndicatorForDeviceEntry) {
_,
showIndicatorForDeviceEntry: Boolean ->
val callbacks = mutableListOf<LottieCallback>()
diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothDetailsContent.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothDetailsContent.kt
index 26d6566..adda34c 100644
--- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothDetailsContent.kt
+++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothDetailsContent.kt
@@ -32,7 +32,7 @@
// Inflate with the existing dialog xml layout
val view =
LayoutInflater.from(context)
- .inflate(R.layout.bluetooth_tile_dialog, /* root= */ null)
+ .inflate(R.layout.bluetooth_tile_details, /* root= */ null)
detailsContentViewModel.bindDetailsView(view)
view
},
diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothDetailsContentManager.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothDetailsContentManager.kt
index 36f9706..87285ee 100644
--- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothDetailsContentManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothDetailsContentManager.kt
@@ -16,6 +16,8 @@
package com.android.systemui.bluetooth.qsdialog
+import android.content.Intent
+import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.View.AccessibilityDelegate
@@ -39,20 +41,34 @@
import androidx.recyclerview.widget.RecyclerView
import com.android.internal.R as InternalR
import com.android.internal.logging.UiEventLogger
+import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast
+import com.android.systemui.Prefs
+import com.android.systemui.animation.DialogTransitionAnimator
import com.android.systemui.bluetooth.ui.viewModel.BluetoothDetailsContentViewModel
-import com.android.systemui.bluetooth.ui.viewModel.BluetoothTileDialogCallback
import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.qs.flags.QsDetailedView
import com.android.systemui.res.R
+import com.android.systemui.statusbar.phone.SystemUIDialog
import com.android.systemui.util.annotations.DeprecatedSysuiVisibleForTesting
import com.android.systemui.util.time.SystemClock
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.cancel
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.channels.produce
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.isActive
+import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
data class DeviceItemClick(val deviceItem: DeviceItem, val clickedView: View, val target: Target) {
@@ -68,14 +84,15 @@
constructor(
@Assisted private val initialUiProperties: BluetoothDetailsContentViewModel.UiProperties,
@Assisted private val cachedContentHeight: Int,
- @Assisted private val bluetoothTileDialogCallback: BluetoothTileDialogCallback,
@Assisted private val isInDialog: Boolean,
@Assisted private val doneButtonCallback: () -> Unit,
@Main private val mainDispatcher: CoroutineDispatcher,
private val systemClock: SystemClock,
private val uiEventLogger: UiEventLogger,
private val logger: BluetoothTileDialogLogger,
-) {
+ private val dialogTransitionAnimator: DialogTransitionAnimator,
+ private val activityStarter: ActivityStarter,
+) : BluetoothTileDialogCallback {
private val mutableBluetoothStateToggle: MutableStateFlow<Boolean?> = MutableStateFlow(null)
@DeprecatedSysuiVisibleForTesting
@@ -107,11 +124,11 @@
private var lastItemRow: Int = -1
+ private lateinit var coroutineScope: CoroutineScope
+
// UI Components
private lateinit var contentView: View
- private lateinit var doneButton: Button
private lateinit var bluetoothToggle: Switch
- private lateinit var subtitleTextView: TextView
private lateinit var seeAllButton: View
private lateinit var pairNewDeviceButton: View
private lateinit var deviceListView: RecyclerView
@@ -123,23 +140,32 @@
private lateinit var progressBarBackground: View
private lateinit var scrollViewContent: View
+ // UI Components that only exist in dialog, but not tile details view.
+ private var doneButton: Button? = null
+ private var titleTextView: TextView? = null
+ private var subtitleTextView: TextView? = null
+
@AssistedFactory
interface Factory {
fun create(
initialUiProperties: BluetoothDetailsContentViewModel.UiProperties,
cachedContentHeight: Int,
- dialogCallback: BluetoothTileDialogCallback,
isInDialog: Boolean,
doneButtonCallback: () -> Unit,
): BluetoothDetailsContentManager
}
- fun bind(contentView: View) {
- this.contentView = contentView
+ fun bind(
+ contentView: View,
+ dialog: SystemUIDialog?,
+ coroutineScope: CoroutineScope,
+ detailsUIState: BluetoothDetailsContentViewModel.DetailsUIState,
+ ) {
- doneButton = contentView.requireViewById(R.id.done_button)
+ this.contentView = contentView
+ this.coroutineScope = coroutineScope
+
bluetoothToggle = contentView.requireViewById(R.id.bluetooth_toggle)
- subtitleTextView = contentView.requireViewById(R.id.bluetooth_tile_dialog_subtitle)
seeAllButton = contentView.requireViewById(R.id.see_all_button)
pairNewDeviceButton = contentView.requireViewById(R.id.pair_new_device_button)
deviceListView = contentView.requireViewById(R.id.device_list)
@@ -154,17 +180,27 @@
contentView.requireViewById(R.id.bluetooth_tile_dialog_progress_background)
scrollViewContent = contentView.requireViewById(R.id.scroll_view)
+ if (isInDialog) {
+ // If `QsDetailedView` is enabled, it should show the details view.
+ QsDetailedView.assertInLegacyMode()
+
+ // If rendering with tile details view, the title and subtitle will be added in the
+ // `TileDetails`
+ titleTextView = contentView.requireViewById(R.id.bluetooth_tile_dialog_title)
+ subtitleTextView = contentView.requireViewById(R.id.bluetooth_tile_dialog_subtitle)
+ // If rendering with tile details view, done button shouldn't exist.
+ doneButton = contentView.requireViewById(R.id.done_button)
+ }
+
setupToggle()
setupRecyclerView()
- setupDoneButton()
- subtitleTextView.text = contentView.context.getString(initialUiProperties.subTitleResId)
- seeAllButton.setOnClickListener { bluetoothTileDialogCallback.onSeeAllClicked(it) }
- pairNewDeviceButton.setOnClickListener {
- bluetoothTileDialogCallback.onPairNewDeviceClicked(it)
- }
+ doneButton?.setOnClickListener { doneButtonCallback() }
+ subtitleTextView?.text = contentView.context.getString(initialUiProperties.subTitleResId)
+ seeAllButton.setOnClickListener { onSeeAllClicked(it) }
+ pairNewDeviceButton.setOnClickListener { onPairNewDeviceClicked(it) }
audioSharingButton.apply {
- setOnClickListener { bluetoothTileDialogCallback.onAudioSharingButtonClicked(it) }
+ setOnClickListener { onAudioSharingButtonClicked(it) }
accessibilityDelegate =
object : AccessibilityDelegate() {
override fun onInitializeAccessibilityNodeInfo(
@@ -189,6 +225,47 @@
resources.getDimensionPixelSize(initialUiProperties.scrollViewMinHeightResId)
layoutParams.height = maxOf(cachedContentHeight, minimumHeight)
}
+ updateDetailsUI(dialog, detailsUIState)
+ }
+
+ private fun updateDetailsUI(
+ dialog: SystemUIDialog?,
+ detailsUIState: BluetoothDetailsContentViewModel.DetailsUIState,
+ ) {
+ coroutineScope.launch {
+ var updateDialogUiJob: Job? = null
+
+ detailsUIState.deviceItem
+ .filterNotNull()
+ .onEach {
+ updateDialogUiJob?.cancel()
+ updateDialogUiJob = launch {
+ onDeviceItemUpdated(it.deviceItem, it.showSeeAll, it.showPairNewDevice)
+ }
+ }
+ .launchIn(this)
+
+ detailsUIState.shouldAnimateProgressBar
+ .filterNotNull()
+ .onEach { animateProgressBar(it) }
+ .launchIn(this)
+
+ detailsUIState.audioSharingButton
+ .filterNotNull()
+ .onEach { onAudioSharingButtonUpdated(it.visibility, it.label, it.isActive) }
+ .launchIn(this)
+
+ detailsUIState.bluetoothState
+ .filterNotNull()
+ .onEach { onBluetoothStateUpdated(it.isEnabled, it.uiProperties) }
+ .launchIn(this)
+
+ detailsUIState.bluetoothAutoOn
+ .filterNotNull()
+ .onEach { onBluetoothAutoOnUpdated(it.isEnabled, it.infoResId) }
+ .launchIn(this)
+ produce<Unit> { awaitClose { dialog?.cancel() } }
+ }
}
fun start() {
@@ -199,7 +276,31 @@
mutableContentHeight.value = scrollViewContent.measuredHeight
}
- internal suspend fun animateProgressBar(animate: Boolean) {
+ override fun onSeeAllClicked(view: View) {
+ uiEventLogger.log(BluetoothTileDialogUiEvent.SEE_ALL_CLICKED)
+ startSettingsActivity(Intent(ACTION_PREVIOUSLY_CONNECTED_DEVICE), view)
+ }
+
+ override fun onPairNewDeviceClicked(view: View) {
+ uiEventLogger.log(BluetoothTileDialogUiEvent.PAIR_NEW_DEVICE_CLICKED)
+ startSettingsActivity(Intent(ACTION_PAIR_NEW_DEVICE), view)
+ }
+
+ override fun onAudioSharingButtonClicked(view: View) {
+ uiEventLogger.log(BluetoothTileDialogUiEvent.BLUETOOTH_AUDIO_SHARING_BUTTON_CLICKED)
+ val intent =
+ Intent(ACTION_AUDIO_SHARING).apply {
+ putExtra(
+ EXTRA_SHOW_FRAGMENT_ARGUMENTS,
+ Bundle().apply {
+ putBoolean(LocalBluetoothLeBroadcast.EXTRA_START_LE_AUDIO_SHARING, true)
+ },
+ )
+ }
+ startSettingsActivity(intent, view)
+ }
+
+ suspend fun animateProgressBar(animate: Boolean) {
withContext(mainDispatcher) {
if (animate) {
showProgressBar()
@@ -210,7 +311,7 @@
}
}
- internal suspend fun onDeviceItemUpdated(
+ suspend fun onDeviceItemUpdated(
deviceItem: List<DeviceItem>,
showSeeAll: Boolean,
showPairNewDevice: Boolean,
@@ -236,7 +337,7 @@
}
}
- internal fun onBluetoothStateUpdated(
+ fun onBluetoothStateUpdated(
isEnabled: Boolean,
uiProperties: BluetoothDetailsContentViewModel.UiProperties,
) {
@@ -245,16 +346,16 @@
setEnabled(true)
alpha = ENABLED_ALPHA
}
- subtitleTextView.text = contentView.context.getString(uiProperties.subTitleResId)
+ subtitleTextView?.text = contentView.context.getString(uiProperties.subTitleResId)
autoOnToggleLayout.visibility = uiProperties.autoOnToggleVisibility
}
- internal fun onBluetoothAutoOnUpdated(isEnabled: Boolean, @StringRes infoResId: Int) {
+ fun onBluetoothAutoOnUpdated(isEnabled: Boolean, @StringRes infoResId: Int) {
autoOnToggle.isChecked = isEnabled
autoOnToggleInfoTextView.text = contentView.context.getString(infoResId)
}
- internal fun onAudioSharingButtonUpdated(visibility: Int, label: String?, isActive: Boolean) {
+ fun onAudioSharingButtonUpdated(visibility: Int, label: String?, isActive: Boolean) {
audioSharingButton.apply {
this.visibility = visibility
label?.let { text = it }
@@ -262,6 +363,19 @@
}
}
+ fun startSettingsActivity(intent: Intent, view: View) {
+ if (coroutineScope.isActive) {
+ intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
+ val controller = dialogTransitionAnimator.createActivityTransitionController(view)
+ // The controller will be null when the screen is locked and going to show the
+ // primary bouncer. In this case we dismiss the dialog manually.
+ if (controller == null) {
+ coroutineScope.cancel()
+ }
+ activityStarter.postStartActivityDismissingKeyguard(intent, 0, controller)
+ }
+ }
+
private fun setupToggle() {
bluetoothToggle.setOnCheckedChangeListener { view, isChecked ->
mutableBluetoothStateToggle.value = isChecked
@@ -280,14 +394,6 @@
}
}
- private fun setupDoneButton() {
- if (isInDialog) {
- doneButton.setOnClickListener { doneButtonCallback() }
- } else {
- doneButton.visibility = GONE
- }
- }
-
private fun setupRecyclerView() {
deviceListView.apply {
layoutManager = LinearLayoutManager(contentView.context)
@@ -447,6 +553,8 @@
}
internal companion object {
+ private const val EXTRA_SHOW_FRAGMENT_ARGUMENTS = ":settings:show_fragment_args"
+ const val CONTENT_HEIGHT_PREF_KEY = Prefs.Key.BLUETOOTH_TILE_DIALOG_CONTENT_HEIGHT
const val MIN_HEIGHT_CHANGE_INTERVAL_MS = 800L
const val ACTION_BLUETOOTH_DEVICE_DETAILS =
"com.android.settings.BLUETOOTH_DEVICE_DETAIL_SETTINGS"
@@ -463,3 +571,11 @@
}
}
}
+
+interface BluetoothTileDialogCallback {
+ fun onSeeAllClicked(view: View)
+
+ fun onPairNewDeviceClicked(view: View)
+
+ fun onAudioSharingButtonClicked(view: View)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegate.kt
index 79602a8..56e02ae 100644
--- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegate.kt
+++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegate.kt
@@ -20,7 +20,6 @@
import android.view.LayoutInflater
import com.android.internal.logging.UiEventLogger
import com.android.systemui.bluetooth.ui.viewModel.BluetoothDetailsContentViewModel
-import com.android.systemui.bluetooth.ui.viewModel.BluetoothTileDialogCallback
import com.android.systemui.qs.flags.QsDetailedView
import com.android.systemui.res.R
import com.android.systemui.shade.domain.interactor.ShadeDialogContextInteractor
@@ -36,7 +35,6 @@
constructor(
@Assisted private val initialUiProperties: BluetoothDetailsContentViewModel.UiProperties,
@Assisted private val cachedContentHeight: Int,
- @Assisted private val bluetoothTileDialogCallback: BluetoothTileDialogCallback,
@Assisted private val dismissListener: Runnable,
private val uiEventLogger: UiEventLogger,
private val systemuiDialogFactory: SystemUIDialog.Factory,
@@ -52,7 +50,6 @@
fun create(
initialUiProperties: BluetoothDetailsContentViewModel.UiProperties,
cachedContentHeight: Int,
- dialogCallback: BluetoothTileDialogCallback,
dismissListener: Runnable,
): BluetoothTileDialogDelegate
}
@@ -81,13 +78,11 @@
bluetoothDetailsContentManagerFactory.create(
initialUiProperties,
cachedContentHeight,
- bluetoothTileDialogCallback,
/* isInDialog= */ true,
/* doneButtonCallback= */ fun() {
dialog.dismiss()
},
)
- contentManager.bind(dialog.requireViewById(R.id.root))
}
override fun onStart(dialog: SystemUIDialog) {
diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/ui/viewModel/BluetoothDetailsContentViewModel.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/ui/viewModel/BluetoothDetailsContentViewModel.kt
index 293acb5..951e7a9 100644
--- a/packages/SystemUI/src/com/android/systemui/bluetooth/ui/viewModel/BluetoothDetailsContentViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bluetooth/ui/viewModel/BluetoothDetailsContentViewModel.kt
@@ -17,36 +17,30 @@
package com.android.systemui.bluetooth.ui.viewModel
import android.content.Context
-import android.content.Intent
import android.content.SharedPreferences
-import android.os.Bundle
import android.view.View
import android.view.View.GONE
import android.view.View.VISIBLE
import android.view.ViewGroup
import androidx.annotation.DimenRes
import androidx.annotation.StringRes
-import androidx.annotation.VisibleForTesting
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.internal.jank.InteractionJankMonitor
-import com.android.internal.logging.UiEventLogger
-import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast
import com.android.settingslib.volume.domain.interactor.AudioModeInteractor
-import com.android.systemui.Prefs
import com.android.systemui.animation.DialogCuj
import com.android.systemui.animation.DialogTransitionAnimator
import com.android.systemui.animation.Expandable
import com.android.systemui.bluetooth.qsdialog.AudioSharingInteractor
import com.android.systemui.bluetooth.qsdialog.BluetoothAutoOnInteractor
import com.android.systemui.bluetooth.qsdialog.BluetoothDetailsContentManager
-import com.android.systemui.bluetooth.qsdialog.BluetoothDetailsContentManager.Companion.ACTION_AUDIO_SHARING
-import com.android.systemui.bluetooth.qsdialog.BluetoothDetailsContentManager.Companion.ACTION_PAIR_NEW_DEVICE
-import com.android.systemui.bluetooth.qsdialog.BluetoothDetailsContentManager.Companion.ACTION_PREVIOUSLY_CONNECTED_DEVICE
+import com.android.systemui.bluetooth.qsdialog.BluetoothDetailsContentManager.Companion.CONTENT_HEIGHT_PREF_KEY
import com.android.systemui.bluetooth.qsdialog.BluetoothDeviceMetadataInteractor
import com.android.systemui.bluetooth.qsdialog.BluetoothStateInteractor
import com.android.systemui.bluetooth.qsdialog.BluetoothTileDialogDelegate
import com.android.systemui.bluetooth.qsdialog.BluetoothTileDialogLogger
-import com.android.systemui.bluetooth.qsdialog.BluetoothTileDialogUiEvent
import com.android.systemui.bluetooth.qsdialog.DeviceFetchTrigger
import com.android.systemui.bluetooth.qsdialog.DeviceItemActionInteractor
import com.android.systemui.bluetooth.qsdialog.DeviceItemClick
@@ -55,7 +49,6 @@
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.qs.flags.QsDetailedView
import com.android.systemui.res.R
import com.android.systemui.statusbar.phone.SystemUIDialog
@@ -63,9 +56,8 @@
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
-import kotlinx.coroutines.channels.awaitClose
-import kotlinx.coroutines.channels.produce
import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.filterNotNull
@@ -90,8 +82,6 @@
private val audioSharingButtonViewModelFactory: AudioSharingButtonViewModel.Factory,
private val bluetoothDeviceMetadataInteractor: BluetoothDeviceMetadataInteractor,
private val dialogTransitionAnimator: DialogTransitionAnimator,
- private val activityStarter: ActivityStarter,
- private val uiEventLogger: UiEventLogger,
private val logger: BluetoothTileDialogLogger,
@Application private val coroutineScope: CoroutineScope,
@Main private val mainDispatcher: CoroutineDispatcher,
@@ -99,7 +89,13 @@
@Main private val sharedPreferences: SharedPreferences,
private val bluetoothDialogDelegateFactory: BluetoothTileDialogDelegate.Factory,
private val bluetoothDetailsContentManagerFactory: BluetoothDetailsContentManager.Factory,
-) : BluetoothTileDialogCallback {
+) {
+
+ var title by mutableStateOf("")
+ private set
+
+ var subTitle by mutableStateOf("")
+ private set
lateinit var contentManager: BluetoothDetailsContentManager
private var job: Job? = null
@@ -111,16 +107,26 @@
*/
fun bindDetailsView(view: View) {
// If `QsDetailedView` is not enabled, it should show the dialog.
- QsDetailedView.unsafeAssertInNewMode()
+ if (QsDetailedView.isUnexpectedlyInLegacyMode()) return
cancelJob()
job =
coroutineScope.launch(context = mainDispatcher) {
+ val detailsUIState = createInitialDetailsUIState()
contentManager = createContentManager()
- contentManager.bind(view)
+ contentManager.bind(
+ contentView = view,
+ dialog = null,
+ coroutineScope = this,
+ detailsUIState = detailsUIState,
+ )
contentManager.start()
- updateDetailsUI(context = view.context, dialog = null)
+ updateDetailsUIState(
+ context = view.context,
+ detailsUIState = detailsUIState,
+ dialog = null,
+ )
}
}
@@ -133,6 +139,7 @@
job =
coroutineScope.launch(context = mainDispatcher) {
+ val detailsUIState = createInitialDetailsUIState()
val dialogDelegate = createBluetoothTileDialog()
val dialog = dialogDelegate.createDialog()
@@ -148,7 +155,13 @@
} ?: dialog.show()
// contentManager is created after dialog.show
contentManager = dialogDelegate.contentManager
- updateDetailsUI(dialog.context, dialog)
+ contentManager.bind(
+ contentView = dialog.requireViewById(R.id.root),
+ dialog = dialog,
+ coroutineScope = this,
+ detailsUIState = detailsUIState,
+ )
+ updateDetailsUIState(dialog.context, detailsUIState, dialog)
}
}
@@ -158,31 +171,41 @@
contentManager.releaseView()
}
- private suspend fun updateDetailsUI(context: Context, dialog: SystemUIDialog?) {
+ private fun createInitialDetailsUIState(): DetailsUIState =
+ DetailsUIState(
+ deviceItem = MutableStateFlow(null),
+ shouldAnimateProgressBar = MutableStateFlow(null),
+ audioSharingButton = MutableStateFlow(null),
+ bluetoothState = MutableStateFlow(null),
+ bluetoothAutoOn = MutableStateFlow(null),
+ )
+
+ private suspend fun updateDetailsUIState(
+ context: Context,
+ detailsUIState: DetailsUIState,
+ dialog: SystemUIDialog?,
+ ) {
coroutineScope {
var updateDeviceItemJob: Job?
- var updateDialogUiJob: Job? = null
updateDeviceItemJob = launch {
deviceItemInteractor.updateDeviceItems(context, DeviceFetchTrigger.FIRST_LOAD)
}
+ title = context.getString(R.string.quick_settings_bluetooth_label)
+
// deviceItemUpdate is emitted when device item list is done fetching, update UI and
// stop the progress bar.
combine(deviceItemInteractor.deviceItemUpdate, deviceItemInteractor.showSeeAllUpdate) {
deviceItem,
showSeeAll ->
- updateDialogUiJob?.cancel()
- updateDialogUiJob = launch {
- contentManager.apply {
- onDeviceItemUpdated(
- deviceItem,
- showSeeAll,
- showPairNewDevice = bluetoothStateInteractor.isBluetoothEnabled(),
- )
- animateProgressBar(false)
- }
- }
+ detailsUIState.shouldAnimateProgressBar.value = false
+ detailsUIState.deviceItem.value =
+ DeviceItem(
+ deviceItem,
+ showSeeAll,
+ showPairNewDevice = bluetoothStateInteractor.isBluetoothEnabled(),
+ )
}
.launchIn(this)
@@ -202,7 +225,7 @@
},
)
.onEach {
- contentManager.animateProgressBar(true)
+ detailsUIState.shouldAnimateProgressBar.value = true
updateDeviceItemJob?.cancel()
updateDeviceItemJob = launch {
deviceItemInteractor.updateDeviceItems(
@@ -223,18 +246,16 @@
.onEach {
when (it) {
is AudioSharingButtonState.Visible -> {
- contentManager.onAudioSharingButtonUpdated(
- VISIBLE,
- context.getString(it.resId),
- it.isActive,
- )
+ detailsUIState.audioSharingButton.value =
+ AudioSharingButton(
+ VISIBLE,
+ context.getString(it.resId),
+ it.isActive,
+ )
}
is AudioSharingButtonState.Gone -> {
- contentManager.onAudioSharingButtonUpdated(
- GONE,
- label = null,
- isActive = false,
- )
+ detailsUIState.audioSharingButton.value =
+ AudioSharingButton(GONE, label = null, isActive = false)
}
}
}
@@ -247,10 +268,9 @@
// the device item list.
bluetoothStateInteractor.bluetoothStateUpdate
.onEach {
- contentManager.onBluetoothStateUpdated(
- it,
- UiProperties.build(it, isAutoOnToggleFeatureAvailable()),
- )
+ val uiProperties = UiProperties.build(it, isAutoOnToggleFeatureAvailable())
+ subTitle = context.getString(uiProperties.subTitleResId)
+ detailsUIState.bluetoothState.value = BluetoothState(it, uiProperties)
updateDeviceItemJob?.cancel()
updateDeviceItemJob = launch {
deviceItemInteractor.updateDeviceItems(
@@ -266,7 +286,7 @@
contentManager.bluetoothStateToggle
.filterNotNull()
.onEach {
- contentManager.animateProgressBar(true)
+ detailsUIState.shouldAnimateProgressBar.value = true
bluetoothStateInteractor.setBluetoothEnabled(it)
}
.launchIn(this)
@@ -286,7 +306,7 @@
DeviceItemClick.Target.ACTION_ICON -> {
deviceItemActionInteractor.onActionIconClick(it.deviceItem) { intent ->
- startSettingsActivity(intent, it.clickedView)
+ contentManager.startSettingsActivity(intent, it.clickedView)
}
}
}
@@ -308,11 +328,12 @@
// changed.
bluetoothAutoOnInteractor.isEnabled
.onEach {
- contentManager.onBluetoothAutoOnUpdated(
- it,
- if (it) R.string.turn_on_bluetooth_auto_info_enabled
- else R.string.turn_on_bluetooth_auto_info_disabled,
- )
+ detailsUIState.bluetoothAutoOn.value =
+ BluetoothAutoOn(
+ it,
+ if (it) R.string.turn_on_bluetooth_auto_info_enabled
+ else R.string.turn_on_bluetooth_auto_info_disabled,
+ )
}
.launchIn(this)
@@ -323,15 +344,15 @@
.onEach { bluetoothAutoOnInteractor.setEnabled(it) }
.launchIn(this)
}
- produce<Unit> { awaitClose { dialog?.cancel() } }
}
}
+ suspend fun isAutoOnToggleFeatureAvailable() = bluetoothAutoOnInteractor.isAutoOnSupported()
+
private suspend fun createBluetoothTileDialog(): BluetoothTileDialogDelegate {
return bluetoothDialogDelegateFactory.create(
getUiProperties(),
getCachedContentHeight(),
- this@BluetoothDetailsContentViewModel,
{ cancelJob() },
)
}
@@ -340,7 +361,6 @@
return bluetoothDetailsContentManagerFactory.create(
getUiProperties(),
getCachedContentHeight(),
- this@BluetoothDetailsContentViewModel,
/* isInDialog= */ false,
/* doneButtonCallback= */ fun() {},
)
@@ -359,56 +379,13 @@
}
}
- override fun onSeeAllClicked(view: View) {
- uiEventLogger.log(BluetoothTileDialogUiEvent.SEE_ALL_CLICKED)
- startSettingsActivity(Intent(ACTION_PREVIOUSLY_CONNECTED_DEVICE), view)
- }
-
- override fun onPairNewDeviceClicked(view: View) {
- uiEventLogger.log(BluetoothTileDialogUiEvent.PAIR_NEW_DEVICE_CLICKED)
- startSettingsActivity(Intent(ACTION_PAIR_NEW_DEVICE), view)
- }
-
- override fun onAudioSharingButtonClicked(view: View) {
- uiEventLogger.log(BluetoothTileDialogUiEvent.BLUETOOTH_AUDIO_SHARING_BUTTON_CLICKED)
- val intent =
- Intent(ACTION_AUDIO_SHARING).apply {
- putExtra(
- EXTRA_SHOW_FRAGMENT_ARGUMENTS,
- Bundle().apply {
- putBoolean(LocalBluetoothLeBroadcast.EXTRA_START_LE_AUDIO_SHARING, true)
- },
- )
- }
- startSettingsActivity(intent, view)
- }
-
private fun cancelJob() {
job?.cancel()
job = null
}
- private fun startSettingsActivity(intent: Intent, view: View) {
- if (job?.isActive == true) {
- intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
- val controller = dialogTransitionAnimator.createActivityTransitionController(view)
- // The controller will be null when the screen is locked and going to show the
- // primary bouncer. In this case we dismiss the dialog manually.
- if (controller == null) {
- cancelJob()
- }
- activityStarter.postStartActivityDismissingKeyguard(intent, 0, controller)
- }
- }
-
- @VisibleForTesting
- internal suspend fun isAutoOnToggleFeatureAvailable() =
- bluetoothAutoOnInteractor.isAutoOnSupported()
-
companion object {
private const val INTERACTION_JANK_TAG = "bluetooth_tile_dialog"
- private const val CONTENT_HEIGHT_PREF_KEY = Prefs.Key.BLUETOOTH_TILE_DIALOG_CONTENT_HEIGHT
- private const val EXTRA_SHOW_FRAGMENT_ARGUMENTS = ":settings:show_fragment_args"
private fun getSubtitleResId(isBluetoothEnabled: Boolean) =
if (isBluetoothEnabled) R.string.quick_settings_bluetooth_tile_subtitle
@@ -437,12 +414,24 @@
)
}
}
-}
-interface BluetoothTileDialogCallback {
- fun onSeeAllClicked(view: View)
+ data class DeviceItem(
+ val deviceItem: List<com.android.systemui.bluetooth.qsdialog.DeviceItem>,
+ val showSeeAll: Boolean,
+ val showPairNewDevice: Boolean,
+ )
- fun onPairNewDeviceClicked(view: View)
+ data class AudioSharingButton(val visibility: Int, val label: String?, val isActive: Boolean)
- fun onAudioSharingButtonClicked(view: View)
+ data class BluetoothState(val isEnabled: Boolean, val uiProperties: UiProperties)
+
+ data class BluetoothAutoOn(val isEnabled: Boolean, @StringRes val infoResId: Int)
+
+ data class DetailsUIState(
+ val deviceItem: MutableStateFlow<DeviceItem?>,
+ val shouldAnimateProgressBar: MutableStateFlow<Boolean?>,
+ val audioSharingButton: MutableStateFlow<AudioSharingButton?>,
+ val bluetoothState: MutableStateFlow<BluetoothState?>,
+ val bluetoothAutoOn: MutableStateFlow<BluetoothAutoOn?>,
+ )
}
diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/ui/viewModel/BluetoothDetailsViewModel.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/ui/viewModel/BluetoothDetailsViewModel.kt
index eff0aa4..001c86f 100644
--- a/packages/SystemUI/src/com/android/systemui/bluetooth/ui/viewModel/BluetoothDetailsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bluetooth/ui/viewModel/BluetoothDetailsViewModel.kt
@@ -26,9 +26,9 @@
onSettingsClick()
}
- // TODO: b/378513956 Update the placeholder text
- override val title = "Bluetooth"
+ override val title: String
+ get() = detailsContentViewModel.title
- // TODO: b/378513956 Update the placeholder text
- override val subTitle = "Tap to connect or disconnect a device"
+ override val subTitle: String
+ get() = detailsContentViewModel.subTitle
}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/ComposeBouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/ComposeBouncerViewBinder.kt
index 33cebf7..bb4c365 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/ComposeBouncerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/ComposeBouncerViewBinder.kt
@@ -4,6 +4,7 @@
import androidx.activity.OnBackPressedDispatcher
import androidx.activity.OnBackPressedDispatcherOwner
import androidx.activity.setViewTreeOnBackPressedDispatcherOwner
+import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.ComposeView
import androidx.lifecycle.Lifecycle
import com.android.app.tracing.coroutines.launchTraced as launch
@@ -12,6 +13,7 @@
import com.android.systemui.bouncer.ui.composable.BouncerContainer
import com.android.systemui.bouncer.ui.viewmodel.BouncerContainerViewModel
import com.android.systemui.bouncer.ui.viewmodel.BouncerOverlayContentViewModel
+import com.android.systemui.compose.modifiers.sysUiResTagContainer
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.lifecycle.WindowLifecycleState
import com.android.systemui.lifecycle.repeatWhenAttached
@@ -81,7 +83,13 @@
view.addView(
ComposeView(view.context).apply {
- setContent { BouncerContainer(viewModelFactory, dialogFactory) }
+ setContent {
+ BouncerContainer(
+ viewModelFactory,
+ dialogFactory,
+ Modifier.sysUiResTagContainer(),
+ )
+ }
}
)
awaitCancellation()
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/composable/BouncerContainer.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/composable/BouncerContainer.kt
index 204eb3e..09c14d4 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/composable/BouncerContainer.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/composable/BouncerContainer.kt
@@ -33,12 +33,13 @@
fun BouncerContainer(
viewModelFactory: BouncerOverlayContentViewModel.Factory,
dialogFactory: BouncerDialogFactory,
+ modifier: Modifier = Modifier,
) {
PlatformTheme {
val backgroundColor = MaterialTheme.colorScheme.surface
val bouncerViewModel = rememberViewModel("BouncerContainer") { viewModelFactory.create() }
- Box {
+ Box(modifier) {
Canvas(Modifier.fillMaxSize()) { drawRect(color = backgroundColor) }
// Separate the bouncer content into a reusable composable that doesn't have any
diff --git a/packages/SystemUI/src/com/android/systemui/common/ui/binder/TintedIconViewBinder.kt b/packages/SystemUI/src/com/android/systemui/common/ui/binder/TintedIconViewBinder.kt
index 5c5723f..be1a3d6 100644
--- a/packages/SystemUI/src/com/android/systemui/common/ui/binder/TintedIconViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/common/ui/binder/TintedIconViewBinder.kt
@@ -17,7 +17,6 @@
package com.android.systemui.common.ui.binder
import android.widget.ImageView
-import com.android.settingslib.Utils
import com.android.systemui.common.shared.model.TintedIcon
object TintedIconViewBinder {
@@ -27,14 +26,11 @@
* [TintedIcon.tint] will always be applied, meaning that if it is null, then the tint *will* be
* reset to null.
*/
- fun bind(
- tintedIcon: TintedIcon,
- view: ImageView,
- ) {
+ fun bind(tintedIcon: TintedIcon, view: ImageView) {
IconViewBinder.bind(tintedIcon.icon, view)
view.imageTintList =
if (tintedIcon.tint != null) {
- Utils.getColorAttr(view.context, tintedIcon.tint)
+ view.context.getColorStateList(tintedIcon.tint)
} else {
null
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/CommunalDreamStartable.kt b/packages/SystemUI/src/com/android/systemui/communal/CommunalDreamStartable.kt
index 6dc7c97..30ccdd9 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/CommunalDreamStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/CommunalDreamStartable.kt
@@ -32,6 +32,7 @@
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.filterState
import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.util.kotlin.BooleanFlowOperators.allOf
import com.android.systemui.util.kotlin.BooleanFlowOperators.not
@@ -77,7 +78,7 @@
@SuppressLint("MissingPermission")
override fun start() {
- if (!communalSettingsInteractor.isCommunalFlagEnabled()) {
+ if (!communalSettingsInteractor.isCommunalFlagEnabled() || SceneContainerFlag.isEnabled) {
return
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/DeviceInactiveCondition.kt b/packages/SystemUI/src/com/android/systemui/communal/DeviceInactiveCondition.kt
deleted file mode 100644
index 70dce2e..0000000
--- a/packages/SystemUI/src/com/android/systemui/communal/DeviceInactiveCondition.kt
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.systemui.communal
-
-import com.android.keyguard.KeyguardUpdateMonitor
-import com.android.keyguard.KeyguardUpdateMonitorCallback
-import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.keyguard.WakefulnessLifecycle
-import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
-import com.android.systemui.keyguard.shared.model.DozeStateModel.Companion.isDozeOff
-import com.android.systemui.keyguard.shared.model.DozeTransitionModel
-import com.android.systemui.shared.condition.Condition
-import com.android.systemui.statusbar.policy.KeyguardStateController
-import com.android.systemui.util.kotlin.JavaAdapter
-import javax.inject.Inject
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.Job
-import kotlinx.coroutines.launch
-
-/**
- * Condition which estimates device inactivity in order to avoid launching a full-screen activity
- * while the user is actively using the device.
- */
-class DeviceInactiveCondition
-@Inject
-constructor(
- @Application private val applicationScope: CoroutineScope,
- @Background backgroundScope: CoroutineScope,
- private val keyguardStateController: KeyguardStateController,
- private val wakefulnessLifecycle: WakefulnessLifecycle,
- private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
- private val keyguardInteractor: KeyguardInteractor,
- private val javaAdapter: JavaAdapter,
-) : Condition(backgroundScope) {
- private var anyDozeListenerJob: Job? = null
- private var anyDoze = false
- private val keyguardStateCallback: KeyguardStateController.Callback =
- object : KeyguardStateController.Callback {
- override fun onKeyguardShowingChanged() {
- updateState()
- }
- }
- private val wakefulnessObserver: WakefulnessLifecycle.Observer =
- object : WakefulnessLifecycle.Observer {
- override fun onStartedGoingToSleep() {
- updateState()
- }
- }
- private val keyguardUpdateCallback: KeyguardUpdateMonitorCallback =
- object : KeyguardUpdateMonitorCallback() {
- override fun onDreamingStateChanged(dreaming: Boolean) {
- updateState()
- }
- }
-
- override suspend fun start() {
- updateState()
- keyguardStateController.addCallback(keyguardStateCallback)
-
- // Keyguard update monitor callbacks must be registered on the main thread
- applicationScope.launch { keyguardUpdateMonitor.registerCallback(keyguardUpdateCallback) }
- wakefulnessLifecycle.addObserver(wakefulnessObserver)
- anyDozeListenerJob =
- javaAdapter.alwaysCollectFlow(keyguardInteractor.dozeTransitionModel) {
- dozeModel: DozeTransitionModel ->
- anyDoze = !isDozeOff(dozeModel.to)
- updateState()
- }
- }
-
- override fun stop() {
- keyguardStateController.removeCallback(keyguardStateCallback)
- keyguardUpdateMonitor.removeCallback(keyguardUpdateCallback)
- wakefulnessLifecycle.removeObserver(wakefulnessObserver)
- anyDozeListenerJob?.cancel(null)
- }
-
- override val startStrategy: Int
- get() = START_EAGERLY
-
- private fun updateState() {
- val asleep = wakefulnessLifecycle.wakefulness == WakefulnessLifecycle.WAKEFULNESS_ASLEEP
- // Doze/AoD is also a dream, but we should never override it with low light as to the user
- // it's totally unrelated.
- updateCondition(
- !anyDoze &&
- (asleep || keyguardStateController.isShowing || keyguardUpdateMonitor.isDreaming)
- )
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractor.kt
index 60fb6a1..6c9c76f 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractor.kt
@@ -137,7 +137,14 @@
return@launch
}
- if (currentScene.value == newScene) return@launch
+ if (currentScene.value == newScene) {
+ // If the same Blank scene, notify listeners since the next keyguard state might
+ // require an update.
+ if (newScene == CommunalScenes.Blank && keyguardState != null) {
+ notifyListeners(newScene, keyguardState)
+ }
+ return@launch
+ }
logger.logSceneChangeRequested(
from = currentScene.value,
to = newScene,
@@ -224,6 +231,16 @@
)
}
+ /** Target scene requested has changed from the previous transition. */
+ val targetSceneChanged: StateFlow<Boolean> =
+ currentScene
+ .pairwiseBy(initialValue = repository.currentScene.value) { old, new -> old != new }
+ .stateIn(
+ scope = applicationScope,
+ started = SharingStarted.Eagerly,
+ initialValue = false,
+ )
+
private val _editModeState = MutableStateFlow<EditModeState?>(null)
/**
* Current state for glanceable hub edit mode, used to chain the animations when transitioning
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneTransitionInteractor.kt
index 89d738e..6219efb 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneTransitionInteractor.kt
@@ -100,7 +100,7 @@
keyguardInteractor.isKeyguardShowing,
) { asleep, dreaming, occluded, keyguardGoingAway, keyguardShowing ->
if (asleep) {
- KeyguardState.DOZING
+ keyguardInteractor.asleepKeyguardState.value
} else if (keyguardGoingAway) {
KeyguardState.GONE
} else if (occluded && !dreaming) {
@@ -163,6 +163,10 @@
prevTransition: ObservableTransitionState,
idle: ObservableTransitionState.Idle,
) {
+ val isReversedTransition =
+ prevTransition is ObservableTransitionState.Transition &&
+ currentTransitionId != null &&
+ idle.currentScene == prevTransition.fromContent
if (
prevTransition is ObservableTransitionState.Transition &&
currentTransitionId != null &&
@@ -171,13 +175,18 @@
finishCurrentTransition()
} else {
// We may receive an Idle event without a corresponding Transition
- // event, such as when snapping to a scene without an animation.
+ // event, such as when snapping to a scene without an animation, or the previous
+ // is a reversed scene transition.
val targetState =
if (idle.currentScene == CommunalScenes.Communal) {
KeyguardState.GLANCEABLE_HUB
} else if (currentToState == KeyguardState.GLANCEABLE_HUB) {
nextKeyguardState.value
} else {
+ if (isReversedTransition) {
+ // Previous is a reversed transition, finish current ktf transition.
+ finishCurrentTransition()
+ }
// Do nothing as we are no longer in the hub state.
return
}
@@ -225,12 +234,46 @@
transition: ObservableTransitionState.Transition,
) {
if (
- prevTransition.isTransitioning(from = transition.fromContent, to = transition.toContent)
+ prevTransition.isTransitioning(
+ from = transition.fromContent,
+ to = transition.toContent,
+ ) && !sceneInteractor.targetSceneChanged.value
) {
// This is a new transition, but exactly the same as the previous state. Skip resetting
// KTF for this case and just collect the new progress instead.
collectProgress(transition)
- } else if (transition.toContent == CommunalScenes.Communal) {
+ return
+ }
+ if (
+ prevTransition.isTransitioning(from = transition.fromContent, to = transition.toContent)
+ ) {
+ // The transition has the same from and to content as the previous transition, but
+ // different target scene, it may be a reversed transition.
+ val targetScene = sceneInteractor.currentScene.value
+ if (
+ targetScene == CommunalScenes.Blank &&
+ transition.fromContent == CommunalScenes.Blank
+ ) {
+ // Prev transition: Blank->Communal, X->hub running
+ // New transition: Blank->Communal (reversed->Blank), we should reverse->X
+ startReversedTransitionToState(nextKeyguardState.value)
+ collectProgress(transition, isReversed = true)
+ return
+ }
+ if (
+ targetScene == CommunalScenes.Communal &&
+ transition.fromContent == CommunalScenes.Communal
+ ) {
+ // Prev transition: Communal->Blank, hub->X running
+ // New transition: Communal->Blank (reversed->Communal), we should reverse->hub
+ startReversedTransitionToState(KeyguardState.GLANCEABLE_HUB)
+ collectProgress(transition, isReversed = true)
+ return
+ }
+ }
+ // The new transition does not have the same content key with the previous, or it has the
+ // same content key and toContent is just the target scene
+ if (transition.toContent == CommunalScenes.Communal) {
if (currentToState == KeyguardState.GLANCEABLE_HUB) {
transitionKtfTo(transitionInteractor.startedKeyguardTransitionStep.value.from)
}
@@ -257,11 +300,18 @@
}
}
- private fun collectProgress(transition: ObservableTransitionState.Transition) {
+ private fun collectProgress(
+ transition: ObservableTransitionState.Transition,
+ isReversed: Boolean = false,
+ ) {
progressJob?.cancel()
progressJob =
applicationScope.launch("$TAG#collectProgress", mainImmediateDispatcher) {
- transition.progress.collect { updateProgress(it) }
+ transition.progress.collect {
+ // during a reversed scene transition, the progress is from 1 to 0
+ val progress = if (isReversed) 1f - it else it
+ updateProgress(progress)
+ }
}
}
@@ -291,6 +341,19 @@
startTransition(newTransition)
}
+ private suspend fun startReversedTransitionToState(state: KeyguardState) {
+ val currentState = internalTransitionInteractor.currentTransitionInfoInternal().to
+ val newTransition =
+ TransitionInfo(
+ ownerName = this::class.java.simpleName,
+ from = currentState,
+ to = state,
+ animator = null,
+ modeOnCanceled = TransitionModeOnCanceled.REVERSE,
+ )
+ startTransition(newTransition)
+ }
+
private suspend fun startTransition(transitionInfo: TransitionInfo) {
if (currentTransitionId != null) {
resetTransitionData()
diff --git a/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalTransitionKeys.kt b/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalTransitionKeys.kt
index f24b428..cf3c95e 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalTransitionKeys.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalTransitionKeys.kt
@@ -37,4 +37,6 @@
val FromOccluded = TransitionKey("FromOccluded")
/** Swipes out of glanceable hub in landscape orientation */
val SwipeInLandscape = TransitionKey("SwipeInLandscape")
+ /** Transition from AOD state to the glanceable hub */
+ val FromAod = TransitionKey("FromAod")
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/PerDisplayRepositoriesModule.kt b/packages/SystemUI/src/com/android/systemui/dagger/PerDisplayRepositoriesModule.kt
index 4bd275d..280746d 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/PerDisplayRepositoriesModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/dagger/PerDisplayRepositoriesModule.kt
@@ -21,9 +21,12 @@
import com.android.app.displaylib.PerDisplayRepository
import com.android.systemui.display.data.repository.DisplayComponentRepository
import com.android.systemui.display.data.repository.PerDisplayCoroutineScopeRepositoryModule
+import com.android.systemui.model.SceneContainerPlugin
+import com.android.systemui.model.SceneContainerPluginImpl
import com.android.systemui.model.SysUIStateInstanceProvider
import com.android.systemui.model.SysUiState
import com.android.systemui.shade.shared.flag.ShadeWindowGoesAround
+import dagger.Binds
import dagger.Module
import dagger.Provides
@@ -31,19 +34,25 @@
@Module(
includes = [PerDisplayCoroutineScopeRepositoryModule::class, DisplayComponentRepository::class]
)
-class PerDisplayRepositoriesModule {
+interface PerDisplayRepositoriesModule {
+ @Binds
@SysUISingleton
- @Provides
- fun provideSysUiStateRepository(
- repositoryFactory: PerDisplayInstanceRepositoryImpl.Factory<SysUiState>,
- instanceProvider: SysUIStateInstanceProvider,
- ): PerDisplayRepository<SysUiState> {
- val debugName = "SysUiStatePerDisplayRepo"
- return if (ShadeWindowGoesAround.isEnabled) {
- repositoryFactory.create(debugName, instanceProvider)
- } else {
- DefaultDisplayOnlyInstanceRepositoryImpl(debugName, instanceProvider)
+ fun bindSceneContainerPlugin(impl: SceneContainerPluginImpl): SceneContainerPlugin
+
+ companion object {
+ @SysUISingleton
+ @Provides
+ fun provideSysUiStateRepository(
+ repositoryFactory: PerDisplayInstanceRepositoryImpl.Factory<SysUiState>,
+ instanceProvider: SysUIStateInstanceProvider,
+ ): PerDisplayRepository<SysUiState> {
+ val debugName = "SysUiStatePerDisplayRepo"
+ return if (ShadeWindowGoesAround.isEnabled) {
+ repositoryFactory.create(debugName, instanceProvider)
+ } else {
+ DefaultDisplayOnlyInstanceRepositoryImpl(debugName, instanceProvider)
+ }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/display/data/repository/FocusedDisplayRepository.kt b/packages/SystemUI/src/com/android/systemui/display/data/repository/FocusedDisplayRepository.kt
index 6fc08f6..c9a77c6 100644
--- a/packages/SystemUI/src/com/android/systemui/display/data/repository/FocusedDisplayRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/display/data/repository/FocusedDisplayRepository.kt
@@ -72,6 +72,6 @@
)
}
- override val focusedDisplayId: StateFlow<Int>
- get() = focusedTask.stateIn(backgroundScope, SharingStarted.Eagerly, DEFAULT_DISPLAY)
+ override val focusedDisplayId: StateFlow<Int> =
+ focusedTask.stateIn(backgroundScope, SharingStarted.Eagerly, DEFAULT_DISPLAY)
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index f431a7f..7a95a41 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -41,10 +41,7 @@
import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.biometrics.AuthController;
-import com.android.systemui.biometrics.domain.interactor.BiometricStatusInteractor;
-import com.android.systemui.biometrics.domain.interactor.FingerprintPropertyInteractor;
import com.android.systemui.broadcast.BroadcastDispatcher;
-import com.android.systemui.dagger.qualifiers.Application;
import com.android.systemui.dock.DockManager;
import com.android.systemui.doze.DozeMachine.State;
import com.android.systemui.doze.dagger.DozeScope;
@@ -55,19 +52,14 @@
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import com.android.systemui.util.Assert;
-import com.android.systemui.util.kotlin.JavaAdapterKt;
import com.android.systemui.util.sensors.AsyncSensorManager;
import com.android.systemui.util.sensors.ProximityCheck;
import com.android.systemui.util.sensors.ProximitySensor;
import com.android.systemui.util.settings.SecureSettings;
import com.android.systemui.util.wakelock.WakeLock;
-import kotlinx.coroutines.CoroutineScope;
-import kotlinx.coroutines.Job;
-
import java.io.PrintWriter;
import java.util.Optional;
-import java.util.concurrent.CancellationException;
import java.util.function.Consumer;
import javax.inject.Inject;
@@ -112,14 +104,10 @@
private final KeyguardStateController mKeyguardStateController;
private final UserTracker mUserTracker;
private final SelectedUserInteractor mSelectedUserInteractor;
- private final BiometricStatusInteractor mBiometricStatusInteractor;
- private final FingerprintPropertyInteractor mFingerprintPropertyInteractor;
- private final CoroutineScope mScope;
private final UiEventLogger mUiEventLogger;
private long mNotificationPulseTime;
private Runnable mAodInterruptRunnable;
- private Job mScreenOffUnlockResultJob;
/** see {@link #onProximityFar} prox for callback */
private boolean mWantProxSensor;
@@ -216,10 +204,7 @@
KeyguardStateController keyguardStateController,
DevicePostureController devicePostureController,
UserTracker userTracker,
- SelectedUserInteractor selectedUserInteractor,
- BiometricStatusInteractor biometricStatusInteractor,
- FingerprintPropertyInteractor fingerprintPropertyInteractor,
- @Application CoroutineScope scope) {
+ SelectedUserInteractor selectedUserInteractor) {
mContext = context;
mDozeHost = dozeHost;
mConfig = config;
@@ -241,9 +226,6 @@
mKeyguardStateController = keyguardStateController;
mUserTracker = userTracker;
mSelectedUserInteractor = selectedUserInteractor;
- mBiometricStatusInteractor = biometricStatusInteractor;
- mFingerprintPropertyInteractor = fingerprintPropertyInteractor;
- mScope = scope;
}
@Override
@@ -370,11 +352,7 @@
mDozeLog.d("udfpsLongPress - Not sending aodInterrupt. "
+ "Unsupported doze state.");
}
- // We will only request pulse when the authentication is failed while doing
- // screen-off unlock on usudfps, in other cases, immediately request pulse.
- if (mScreenOffUnlockResultJob == null) {
- requestPulse(DozeLog.REASON_SENSOR_UDFPS_LONG_PRESS, true, null);
- }
+ requestPulse(DozeLog.REASON_SENSOR_UDFPS_LONG_PRESS, true, null);
} else {
mDozeHost.extendPulse(pulseReason);
}
@@ -726,35 +704,5 @@
public void onSideFingerprintAcquisitionStarted() {
DozeTriggers.this.onSideFingerprintAcquisitionStarted();
}
-
- @Override
- public void onDozingChanged(boolean isDozing) {
- // No need to handle screen-off unlock when aod is enabled.
- if (mInAod) {
- mScreenOffUnlockResultJob = null;
- return;
- }
- // Only do this for ultrasonic udfps.
- if (!mFingerprintPropertyInteractor.isUltrasonic().getValue()
- || !mFingerprintPropertyInteractor.getScreenOffUnlockEnabled().getValue()) {
- return;
- }
- // Start collecting authentication failure events while entering doze.
- if (isDozing && mScreenOffUnlockResultJob == null) {
- mScreenOffUnlockResultJob = JavaAdapterKt.collectFlow(
- mScope,
- mScope.getCoroutineContext(),
- mBiometricStatusInteractor.getFingerprintAuthFailureEventsForDeviceEntry(),
- state ->
- requestPulse(DozeLog.REASON_SENSOR_UDFPS_LONG_PRESS, true, null)
- );
- }
- // Cancel the coroutine job while exiting doze.
- if (!isDozing && mScreenOffUnlockResultJob != null) {
- mScreenOffUnlockResultJob.cancel(
- new CancellationException("Ask stopping monitoring"));
- mScreenOffUnlockResultJob = null;
- }
- }
};
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/util/BurnInHelper.kt b/packages/SystemUI/src/com/android/systemui/doze/util/BurnInHelper.kt
index a304058..9204017d 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/util/BurnInHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/doze/util/BurnInHelper.kt
@@ -55,9 +55,8 @@
/** Returns a value to scale a view in order to avoid burn in. */
fun getBurnInScale(): Float {
- val multiplier = if (Flags.clockReactiveSmartspaceLayout()) 0.75f else 0.8f
-
- return multiplier +
+ val baseBurninScale = if (Flags.clockReactiveSmartspaceLayout()) 0.75f else 0.8f
+ return baseBurninScale +
zigzag(
System.currentTimeMillis() / MILLIS_PER_MINUTES,
0.2f,
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayRegistrant.kt b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayRegistrant.kt
index c425bee..aa41778 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayRegistrant.kt
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayRegistrant.kt
@@ -23,6 +23,7 @@
import android.content.pm.PackageManager
import android.os.PatternMatcher
import android.os.RemoteException
+import android.service.dreams.Flags
import android.service.dreams.IDreamManager
import android.util.Log
import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
@@ -91,7 +92,7 @@
}
if (
- communalSettingsInteractor.isV2FlagEnabled() &&
+ Flags.dreamsV2() &&
packageManager.getComponentEnabledSetting(overlayServiceComponent) ==
PackageManager.COMPONENT_ENABLED_STATE_ENABLED
) {
@@ -111,8 +112,8 @@
return
}
- // Enable for hub on mobile
- if (communalSettingsInteractor.isV2FlagEnabled()) {
+ // Enable for dreams on mobile.
+ if (Flags.dreamsV2()) {
// Not available on TV or auto
if (
packageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE) ||
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/service/TaskFragmentComponent.kt b/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/service/TaskFragmentComponent.kt
index c815838..ce5f0f8 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/service/TaskFragmentComponent.kt
+++ b/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/service/TaskFragmentComponent.kt
@@ -150,18 +150,6 @@
/** Destroys the task fragment */
fun destroy() {
- organizer.applyTransaction(
- WindowContainerTransaction()
- .addTaskFragmentOperation(
- fragmentToken,
- TaskFragmentOperation.Builder(
- TaskFragmentOperation.OP_TYPE_DELETE_TASK_FRAGMENT
- )
- .build(),
- ),
- TaskFragmentOrganizer.TASK_FRAGMENT_TRANSIT_CLOSE,
- false,
- )
organizer.unregisterOrganizer()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 363f366..1fcf740 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -103,10 +103,6 @@
@JvmField
val WALLPAPER_PICKER_GRID_APPLY_BUTTON = unreleasedFlag("wallpaper_picker_grid_apply_button")
- /** Flag meant to guard the talkback fix for the KeyguardIndicationTextView */
- // TODO(b/286563884): Tracking bug
- @JvmField val KEYGUARD_TALKBACK_FIX = unreleasedFlag("keyguard_talkback_fix")
-
/** Enables preview loading animation in the wallpaper picker. */
// TODO(b/274443705): Tracking Bug
@JvmField
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
index 9c3de4e..920833e 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
@@ -132,6 +132,7 @@
import com.android.systemui.scrim.ScrimDrawable;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.shade.ShadeController;
+import com.android.systemui.shade.ShadeDisplayAware;
import com.android.systemui.shade.shared.flag.ShadeWindowGoesAround;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.VibratorHelper;
@@ -372,7 +373,7 @@
*/
@Inject
public GlobalActionsDialogLite(
- Context context,
+ @ShadeDisplayAware Context context,
GlobalActionsManager windowManagerFuncs,
AudioManager audioManager,
LockPatternUtils lockPatternUtils,
@@ -381,8 +382,8 @@
GlobalSettings globalSettings,
SecureSettings secureSettings,
@NonNull VibratorHelper vibrator,
- @Main Resources resources,
- ConfigurationController configurationController,
+ @ShadeDisplayAware Resources resources,
+ @ShadeDisplayAware ConfigurationController configurationController,
ActivityStarter activityStarter,
UserTracker userTracker,
KeyguardStateController keyguardStateController,
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
index a6255d0..2d81c22 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
@@ -19,7 +19,7 @@
import android.content.Context;
import com.android.systemui.plugins.GlobalActions;
-import com.android.systemui.statusbar.BlurUtils;
+import com.android.systemui.shade.ShadeController;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -31,26 +31,27 @@
private final Context mContext;
private final KeyguardStateController mKeyguardStateController;
private final DeviceProvisionedController mDeviceProvisionedController;
- private final BlurUtils mBlurUtils;
private final CommandQueue mCommandQueue;
private final GlobalActionsDialogLite mGlobalActionsDialog;
private boolean mDisabled;
private ShutdownUi mShutdownUi;
+ private ShadeController mShadeController;
@Inject
public GlobalActionsImpl(Context context, CommandQueue commandQueue,
- GlobalActionsDialogLite globalActionsDialog, BlurUtils blurUtils,
+ GlobalActionsDialogLite globalActionsDialog,
KeyguardStateController keyguardStateController,
DeviceProvisionedController deviceProvisionedController,
+ ShadeController shadeController,
ShutdownUi shutdownUi) {
mContext = context;
mGlobalActionsDialog = globalActionsDialog;
mKeyguardStateController = keyguardStateController;
mDeviceProvisionedController = deviceProvisionedController;
mCommandQueue = commandQueue;
- mBlurUtils = blurUtils;
mCommandQueue.addCallback(this);
mShutdownUi = shutdownUi;
+ mShadeController = shadeController;
}
@Override
@@ -70,6 +71,7 @@
@Override
public void showShutdownUi(boolean isReboot, String reason) {
mShutdownUi.showShutdownUi(isReboot, reason);
+ mShadeController.collapseShade();
}
@Override
public void disable(int displayId, int state1, int state2, boolean animate) {
diff --git a/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderStateTracker.kt b/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderStateTracker.kt
index eef7107..ee7d081 100644
--- a/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderStateTracker.kt
+++ b/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderStateTracker.kt
@@ -17,12 +17,12 @@
package com.android.systemui.haptics.slider
import androidx.annotation.VisibleForTesting
+import com.android.app.tracing.coroutines.launchTraced as launch
import kotlin.math.abs
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive
-import com.android.app.tracing.coroutines.launchTraced as launch
/**
* Slider tracker attached to a slider.
@@ -80,7 +80,10 @@
// This will disambiguate between an imprecise touch that acquires the slider handle,
// and a select and jump operation in the slider track.
setState(SliderState.WAIT)
- } else if (newEventType == SliderEventType.STARTED_TRACKING_PROGRAM) {
+ } else if (
+ newEventType == SliderEventType.STARTED_TRACKING_PROGRAM ||
+ newEventType == SliderEventType.PROGRESS_CHANGE_BY_PROGRAM
+ ) {
val state =
if (bookendReached(currentProgress)) SliderState.ARROW_HANDLE_REACHED_BOOKEND
else SliderState.ARROW_HANDLE_MOVED_ONCE
diff --git a/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderTracker.kt b/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderTracker.kt
index af08f40..3407fa6 100644
--- a/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderTracker.kt
+++ b/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderTracker.kt
@@ -16,10 +16,10 @@
package com.android.systemui.haptics.slider
+import com.android.app.tracing.coroutines.launchTraced as launch
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.cancel
-import com.android.app.tracing.coroutines.launchTraced as launch
/**
* Tracker component for a slider.
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java
index e5bec84..a15d5b9 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java
@@ -16,8 +16,6 @@
package com.android.systemui.keyguard;
-import static com.android.systemui.flags.Flags.KEYGUARD_TALKBACK_FIX;
-
import android.annotation.Nullable;
import android.content.res.ColorStateList;
import android.graphics.Color;
@@ -30,7 +28,6 @@
import com.android.keyguard.logging.KeyguardLogger;
import com.android.systemui.Dumpable;
import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.KeyguardIndicationController;
import com.android.systemui.statusbar.phone.KeyguardIndicationTextView;
@@ -78,7 +75,6 @@
// Executor that will show the next message after a delay
private final DelayableExecutor mExecutor;
- private final FeatureFlags mFeatureFlags;
@VisibleForTesting
@Nullable ShowNextIndication mShowNextIndicationRunnable;
@@ -95,8 +91,7 @@
KeyguardIndicationTextView view,
@Main DelayableExecutor executor,
StatusBarStateController statusBarStateController,
- KeyguardLogger logger,
- FeatureFlags flags
+ KeyguardLogger logger
) {
super(view);
mMaxAlpha = view.getAlpha();
@@ -105,14 +100,12 @@
? mView.getTextColors() : ColorStateList.valueOf(Color.WHITE);
mStatusBarStateController = statusBarStateController;
mLogger = logger;
- mFeatureFlags = flags;
init();
}
@Override
protected void onViewAttached() {
mStatusBarStateController.addCallback(mStatusBarStateListener);
- mView.setAlwaysAnnounceEnabled(mFeatureFlags.isEnabled(KEYGUARD_TALKBACK_FIX));
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
index 604880d..9d02e83 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
@@ -66,7 +66,6 @@
import android.window.TransitionInfo;
import com.android.internal.annotations.GuardedBy;
-import com.android.internal.foldables.FoldGracePeriodProvider;
import com.android.internal.policy.IKeyguardDismissCallback;
import com.android.internal.policy.IKeyguardDrawnCallback;
import com.android.internal.policy.IKeyguardExitCallback;
@@ -326,12 +325,6 @@
private final KeyguardEnabledInteractor mKeyguardEnabledInteractor;
private final KeyguardWakeDirectlyToGoneInteractor mKeyguardWakeDirectlyToGoneInteractor;
private final KeyguardDismissInteractor mKeyguardDismissInteractor;
- private final Lazy<FoldGracePeriodProvider> mFoldGracePeriodProvider = new Lazy<>() {
- @Override
- public FoldGracePeriodProvider get() {
- return new FoldGracePeriodProvider();
- }
- };
private final KeyguardServiceShowLockscreenInteractor mKeyguardServiceShowLockscreenInteractor;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@@ -689,9 +682,8 @@
public void showDismissibleKeyguard() {
trace("showDismissibleKeyguard");
checkPermission();
- if (mFoldGracePeriodProvider.get().isEnabled()) {
- mKeyguardInteractor.showDismissibleKeyguard();
- }
+ mKeyguardInteractor.showDismissibleKeyguard();
+
if (KeyguardWmStateRefactor.isEnabled()) {
mKeyguardServiceShowLockscreenInteractor.onKeyguardServiceShowDismissibleKeyguard();
@@ -699,7 +691,7 @@
mKeyguardViewMediator.showDismissibleKeyguard();
}
- if (SceneContainerFlag.isEnabled() && mFoldGracePeriodProvider.get().isEnabled()) {
+ if (SceneContainerFlag.isEnabled()) {
mMainExecutor.execute(() -> mSceneInteractorLazy.get().changeScene(
Scenes.Lockscreen, "KeyguardService.showDismissibleKeyguard"));
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 099a7f0..d366ffd 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -42,7 +42,6 @@
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE;
import static com.android.systemui.DejankUtils.whitelistIpcs;
import static com.android.systemui.Flags.notifyPowerManagerUserActivityBackground;
-import static com.android.systemui.Flags.relockWithPowerButtonImmediately;
import static com.android.systemui.Flags.simPinBouncerReset;
import static com.android.systemui.Flags.translucentOccludingActivityFix;
import static com.android.systemui.keyguard.ui.viewmodel.LockscreenToDreamingTransitionViewModel.DREAMING_ANIMATION_DURATION_MS;
@@ -112,7 +111,6 @@
import com.android.app.animation.Interpolators;
import com.android.app.tracing.coroutines.TrackTracer;
-import com.android.internal.foldables.FoldGracePeriodProvider;
import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.jank.InteractionJankMonitor.Configuration;
import com.android.internal.logging.UiEventLogger;
@@ -1473,9 +1471,6 @@
private final SelectedUserInteractor mSelectedUserInteractor;
private final KeyguardInteractor mKeyguardInteractor;
private final KeyguardTransitionBootInteractor mTransitionBootInteractor;
- @VisibleForTesting
- protected FoldGracePeriodProvider mFoldGracePeriodProvider =
- new FoldGracePeriodProvider();
private final KeyguardStateController mKeyguardStateController;
private final KeyguardStateController.Callback mKeyguardStateControllerCallback =
new KeyguardStateController.Callback() {
@@ -2375,18 +2370,13 @@
* This must be safe to call from any thread and with any window manager locks held.
*/
public void showDismissibleKeyguard() {
- if (mFoldGracePeriodProvider.isEnabled()) {
- if (!mUpdateMonitor.isDeviceProvisioned()) {
- Log.d(TAG, "Device not provisioned, so ignore request to show keyguard.");
- return;
- }
- Bundle showKeyguardUnlocked = new Bundle();
- showKeyguardUnlocked.putBoolean(OPTION_SHOW_DISMISSIBLE, true);
- showKeyguard(showKeyguardUnlocked);
- } else {
- Log.e(TAG, "fold grace period feature isn't enabled, but showKeyguard() method is"
- + " being called", new Throwable());
+ if (!mUpdateMonitor.isDeviceProvisioned()) {
+ Log.d(TAG, "Device not provisioned, so ignore request to show keyguard.");
+ return;
}
+ Bundle showKeyguardUnlocked = new Bundle();
+ showKeyguardUnlocked.putBoolean(OPTION_SHOW_DISMISSIBLE, true);
+ showKeyguard(showKeyguardUnlocked);
}
/**
@@ -3567,9 +3557,7 @@
// A lock is pending, meaning the keyguard exit animation was cancelled because we're
// re-locking. We should just end the surface-behind animation without exiting the
// keyguard. The pending lock will be handled by onFinishedGoingToSleep().
- if (relockWithPowerButtonImmediately()) {
- mIsKeyguardExitAnimationCanceled = true;
- }
+ mIsKeyguardExitAnimationCanceled = true;
finishSurfaceBehindRemoteAnimation(true /* showKeyguard */);
maybeHandlePendingLock();
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/InWindowLauncherUnlockAnimationRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/InWindowLauncherUnlockAnimationRepository.kt
index d23899b..f0420d6 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/InWindowLauncherUnlockAnimationRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/InWindowLauncherUnlockAnimationRepository.kt
@@ -71,6 +71,8 @@
*/
val launcherSmartspaceState: MutableStateFlow<SmartspaceState?> = MutableStateFlow(null)
+ val isLauncherUnderneath: MutableStateFlow<Boolean> = MutableStateFlow(false)
+
fun setStartedUnlockAnimation(started: Boolean) {
startedUnlockAnimation.value = started
}
@@ -86,4 +88,8 @@
fun setLauncherSmartspaceState(state: SmartspaceState?) {
launcherSmartspaceState.value = state
}
+
+ fun setIsLauncherUnderneath(isUnderneath: Boolean) {
+ isLauncherUnderneath.value = isUnderneath
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardClockRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardClockRepository.kt
index a4d64ad..ccdae32 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardClockRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardClockRepository.kt
@@ -131,7 +131,7 @@
override val currentClock: StateFlow<ClockController?> =
currentClockId
.map {
- clockEventController.clock = clockRegistry.createCurrentClock()
+ clockEventController.clock = clockRegistry.createCurrentClock(context)
clockEventController.clock
}
.stateIn(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardPreviewRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardPreviewRepository.kt
index c83a694..4156888 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardPreviewRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardPreviewRepository.kt
@@ -17,11 +17,14 @@
package com.android.systemui.keyguard.data.repository
import android.app.WallpaperColors
+import android.content.Context
import android.hardware.display.DisplayManager
import android.os.Bundle
import android.os.IBinder
+import android.view.ContextThemeWrapper
import android.view.Display
import android.view.Display.DEFAULT_DISPLAY
+import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.shared.model.ClockSizeSetting
import com.android.systemui.plugins.clocks.ClockController
import com.android.systemui.shared.clocks.ClockRegistry
@@ -42,6 +45,7 @@
class KeyguardPreviewRepository
@AssistedInject
constructor(
+ @Application context: Context,
private val clockRepository: KeyguardClockRepository,
private val clockRegistry: ClockRegistry,
private val displayManager: DisplayManager,
@@ -55,6 +59,9 @@
val displayId = request.getInt(KEY_DISPLAY_ID, DEFAULT_DISPLAY)
val display: Display? = displayManager.getDisplay(displayId)
+ val previewContext: Context =
+ display?.let { ContextThemeWrapper(context.createDisplayContext(it), context.getTheme()) }
+ ?: context
/** [shouldHideClock] here means that we never create and bind the clock views */
val shouldHideClock: Boolean = request.getBoolean(KEY_HIDE_CLOCK, false)
@@ -66,7 +73,7 @@
clockRepository.currentClockId.map {
// We should create a new instance for each collect call cause in preview, the same
// clock will be attached to a different parent view at the same time.
- clockRegistry.createCurrentClock()
+ clockRegistry.createCurrentClock(previewContext)
}
companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
index a0c826a..644e05d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
@@ -16,6 +16,7 @@
package com.android.systemui.keyguard.data.repository
+import android.annotation.SuppressLint
import android.graphics.Point
import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.internal.widget.LockPatternUtils
@@ -49,6 +50,7 @@
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
@@ -108,7 +110,7 @@
"away' is isInTransitionToState(GONE), but consider using more specific flows " +
"whenever possible."
)
- val isKeyguardGoingAway: MutableStateFlow<Boolean>
+ val isKeyguardGoingAway: MutableSharedFlow<Boolean>
/**
* Whether the keyguard is enabled, per [KeyguardService]. If the keyguard is not enabled, the
@@ -373,8 +375,12 @@
override val isKeyguardDismissible: MutableStateFlow<Boolean> =
MutableStateFlow(keyguardStateController.isUnlocked)
- override val isKeyguardGoingAway: MutableStateFlow<Boolean> =
- MutableStateFlow(keyguardStateController.isKeyguardGoingAway)
+ @SuppressLint("SharedFlowCreation")
+ override val isKeyguardGoingAway: MutableSharedFlow<Boolean> =
+ MutableSharedFlow<Boolean>(
+ extraBufferCapacity = 3,
+ onBufferOverflow = BufferOverflow.DROP_OLDEST,
+ )
private val _isKeyguardEnabled =
MutableStateFlow(!lockPatternUtils.isLockScreenDisabled(userTracker.userId))
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepository.kt
index 0871f13..0bc74fe 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepository.kt
@@ -16,12 +16,17 @@
package com.android.systemui.keyguard.data.repository
+import android.app.WallpaperColors
+import android.app.WallpaperManager
import android.content.Context
import android.graphics.Point
import androidx.core.animation.Animator
import androidx.core.animation.ValueAnimator
+import com.android.internal.colorextraction.ColorExtractor
import com.android.keyguard.logging.ScrimLogger
+import com.android.systemui.colorextraction.SysuiColorExtractor
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.keyguard.shared.model.BiometricUnlockMode
import com.android.systemui.keyguard.shared.model.BiometricUnlockSource
import com.android.systemui.power.data.repository.PowerRepository
@@ -35,14 +40,18 @@
import com.android.systemui.statusbar.PowerButtonReveal
import javax.inject.Inject
import kotlin.math.max
+import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
val DEFAULT_REVEAL_EFFECT = LiftReveal
const val DEFAULT_REVEAL_DURATION = 500L
@@ -64,8 +73,11 @@
val isAnimating: Boolean
- /** Limit the max alpha for the scrim to allow for some transparency */
- val maxAlpha: MutableStateFlow<Float>
+ /** Allows for transparency to see the wallpaper on AOD */
+ val wallpaperSupportsAmbientMode: MutableStateFlow<Boolean>
+
+ /** For brighter wallpapers, add a darker scrim to ensure contrast */
+ val useDarkWallpaperScrim: StateFlow<Boolean>
fun startRevealAmountAnimator(reveal: Boolean, duration: Long = DEFAULT_REVEAL_DURATION)
}
@@ -78,6 +90,8 @@
@ShadeDisplayAware val context: Context,
powerRepository: PowerRepository,
private val scrimLogger: ScrimLogger,
+ private val colorExtractor: SysuiColorExtractor,
+ @Background backgroundScope: CoroutineScope,
) : LightRevealScrimRepository {
companion object {
val TAG = LightRevealScrimRepository::class.simpleName!!
@@ -130,7 +144,25 @@
private val revealAmountAnimator = ValueAnimator.ofFloat(0f, 1f)
- override val maxAlpha: MutableStateFlow<Float> = MutableStateFlow(DEFAULT_MAX_ALPHA)
+ override val wallpaperSupportsAmbientMode: MutableStateFlow<Boolean> = MutableStateFlow(false)
+
+ override val useDarkWallpaperScrim: StateFlow<Boolean> =
+ callbackFlow {
+ fun sendLuminanceUpdate() {
+ colorExtractor.getWallpaperColors(WallpaperManager.FLAG_LOCK)?.let {
+ val useDarkScrim =
+ (it.getColorHints() and WallpaperColors.HINT_SUPPORTS_DARK_TEXT) > 0
+ trySend(useDarkScrim)
+ }
+ }
+
+ val listener =
+ ColorExtractor.OnColorsChangedListener { _, _ -> sendLuminanceUpdate() }
+ sendLuminanceUpdate()
+ colorExtractor.addOnColorsChangedListener(listener)
+ awaitClose { colorExtractor.removeOnColorsChangedListener(listener) }
+ }
+ .stateIn(backgroundScope, SharingStarted.Eagerly, false)
override val revealAmount: Flow<Float> = callbackFlow {
val updateListener =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt
index d29b951..67e9479 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt
@@ -23,6 +23,7 @@
import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
import com.android.systemui.communal.shared.model.CommunalScenes
+import com.android.systemui.communal.shared.model.CommunalTransitionKeys
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
@@ -93,11 +94,13 @@
// transition.
scope.launch("$TAG#listenForAodToAwake") {
powerInteractor.detailedWakefulness
- .apply {
+ .let { flow ->
if (!KeyguardWmStateRefactor.isEnabled) {
// This works around some timing issues pre-refactor that are no longer an
// issue (and this causes problems with the flag enabled).
- debounce(50L)
+ flow.debounce(50L)
+ } else {
+ flow
}
}
.filterRelevantKeyguardStateAnd { wakefulness -> wakefulness.isAwake() }
@@ -158,6 +161,7 @@
communalSceneInteractor.changeScene(
CommunalScenes.Communal,
"listen for aod to communal",
+ transitionKey = CommunalTransitionKeys.FromAod,
)
} else if (shouldTransitionToLockscreen) {
val modeOnCanceled =
@@ -253,5 +257,6 @@
val TO_LOCKSCREEN_DURATION = 500.milliseconds
val TO_OCCLUDED_DURATION = 550.milliseconds
val TO_PRIMARY_BOUNCER_DURATION = DEFAULT_DURATION
+ val TO_GLANCEABLE_HUB_DURATION = DEFAULT_DURATION
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt
index 81644a3..a63fa43 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt
@@ -157,7 +157,6 @@
.collect { (detailedWakefulness, isCommunalAvailable, shouldShowCommunal) ->
val isKeyguardOccludedLegacy = keyguardInteractor.isKeyguardOccluded.value
val primaryBouncerShowing = keyguardInteractor.primaryBouncerShowing.value
- val isKeyguardGoingAway = keyguardInteractor.isKeyguardGoingAway.value
if (!deviceEntryInteractor.isLockscreenEnabled()) {
if (!SceneContainerFlag.isEnabled) {
@@ -166,16 +165,11 @@
ownerReason = "lockscreen not enabled",
)
}
- } else if (canDismissLockscreen() || isKeyguardGoingAway) {
+ } else if (canDismissLockscreen()) {
if (!SceneContainerFlag.isEnabled) {
startTransitionTo(
KeyguardState.GONE,
- ownerReason =
- if (canDismissLockscreen()) {
- "canDismissLockscreen()"
- } else {
- "isKeyguardGoingAway"
- },
+ ownerReason = "canDismissLockscreen()",
)
}
} else if (primaryBouncerShowing) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
index ca6a790..a847ab9 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
@@ -136,8 +136,8 @@
private fun listenForGoneToDreaming() {
scope.launch("$TAG#listenForGoneToDreaming") {
- keyguardInteractor.isAbleToDream
- .filterRelevantKeyguardStateAnd { isAbleToDream -> isAbleToDream }
+ keyguardInteractor.isDreaming
+ .filterRelevantKeyguardStateAnd { isDreaming -> isDreaming }
.collect { startTransitionTo(KeyguardState.DREAMING) }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/InWindowLauncherUnlockAnimationInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/InWindowLauncherUnlockAnimationInteractor.kt
index 28d4ba96..5eda0be 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/InWindowLauncherUnlockAnimationInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/InWindowLauncherUnlockAnimationInteractor.kt
@@ -18,6 +18,7 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.keyguard.data.repository.InWindowLauncherUnlockAnimationRepository
import com.android.systemui.keyguard.data.repository.KeyguardSurfaceBehindRepository
import com.android.systemui.keyguard.shared.model.Edge
@@ -31,8 +32,10 @@
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.launch
@SysUISingleton
class InWindowLauncherUnlockAnimationInteractor
@@ -40,6 +43,7 @@
constructor(
private val repository: InWindowLauncherUnlockAnimationRepository,
@Application scope: CoroutineScope,
+ @Background val backgroundScope: CoroutineScope,
transitionInteractor: KeyguardTransitionInteractor,
surfaceBehindRepository: dagger.Lazy<KeyguardSurfaceBehindRepository>,
private val activityManager: ActivityManagerWrapper,
@@ -54,7 +58,7 @@
transitionInteractor
.isInTransition(
edge = Edge.create(to = Scenes.Gone),
- edgeWithoutSceneContainer = Edge.create(to = GONE)
+ edgeWithoutSceneContainer = Edge.create(to = GONE),
)
.map { transitioningToGone -> transitioningToGone && isLauncherUnderneath() }
.stateIn(scope, SharingStarted.Eagerly, false)
@@ -74,6 +78,25 @@
}
.stateIn(scope, SharingStarted.Eagerly, false)
+ init {
+ backgroundScope.launch {
+ // Whenever we're not GONE, update whether Launcher was the app in front. We need to do
+ // this before the next unlock, but it triggers a binder call, so this is the best time
+ // to do it. In edge cases where this changes while we're locked (the foreground app
+ // crashes, etc.) the worst case is that we fall back to the normal unlock animation (or
+ // unnecessarily play the animation on Launcher when there's an app over it), which is
+ // not a big deal.
+ transitionInteractor.currentKeyguardState
+ .map { it != GONE }
+ .distinctUntilChanged()
+ .collect { notOnGone ->
+ if (notOnGone) {
+ updateIsLauncherUnderneath()
+ }
+ }
+ }
+ }
+
/** Sets whether we've started */
fun setStartedUnlockAnimation(started: Boolean) {
repository.setStartedUnlockAnimation(started)
@@ -92,13 +115,24 @@
}
/**
- * Whether the Launcher is currently underneath the lockscreen (it's at the top of the activity
- * task stack).
+ * Whether the Launcher was underneath the lockscreen as of the last time we locked (it's at the
+ * top of the activity task stack).
*/
fun isLauncherUnderneath(): Boolean {
- return repository.launcherActivityClass.value?.let {
- activityManager.runningTask?.topActivity?.className?.equals(it)
+ return repository.isLauncherUnderneath.value
+ }
+
+ /**
+ * Updates whether Launcher is the app underneath the lock screen as of this moment. Triggers a
+ * binder call, so this is run in the background.
+ */
+ private fun updateIsLauncherUnderneath() {
+ backgroundScope.launch {
+ repository.setIsLauncherUnderneath(
+ repository.launcherActivityClass.value?.let {
+ activityManager.runningTask?.topActivity?.className?.equals(it)
+ } ?: false
+ )
}
- ?: false
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
index e846f36..aedce4c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
@@ -59,8 +59,10 @@
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.combine
@@ -238,7 +240,7 @@
/** Whether the keyguard is going away. */
@Deprecated("Use KeyguardTransitionInteractor + KeyguardState.GONE")
- val isKeyguardGoingAway: StateFlow<Boolean> = repository.isKeyguardGoingAway.asStateFlow()
+ val isKeyguardGoingAway: SharedFlow<Boolean> = repository.isKeyguardGoingAway.asSharedFlow()
/** Keyguard can be clipped at the top as the shade is dragged */
val topClippingBounds: Flow<Int?> by lazy {
@@ -267,17 +269,7 @@
@JvmField val primaryBouncerShowing: StateFlow<Boolean> = bouncerRepository.primaryBouncerShow
/** Whether the alternate bouncer is showing or not. */
- val alternateBouncerShowing: Flow<Boolean> =
- bouncerRepository.alternateBouncerVisible.map { alternateBouncerVisible ->
- if (isAbleToDream.value) {
- // If the alternate bouncer will show over a dream, it is likely that the dream has
- // requested a dismissal, which will stop the dream. By delaying this slightly, the
- // DREAMING->LOCKSCREEN transition will now happen first, followed by
- // LOCKSCREEN->ALTERNATE_BOUNCER.
- delay(600L)
- }
- alternateBouncerVisible
- }
+ val alternateBouncerShowing: Flow<Boolean> = bouncerRepository.alternateBouncerVisible
/** Observable for the [StatusBarState] */
val statusBarState: StateFlow<StatusBarState> = repository.statusBarState
@@ -527,7 +519,7 @@
}
fun setIsKeyguardGoingAway(isGoingAway: Boolean) {
- repository.isKeyguardGoingAway.value = isGoingAway
+ repository.isKeyguardGoingAway.tryEmit(isGoingAway)
}
fun setNotificationStackAbsoluteBottom(bottom: Float) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardPreviewInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardPreviewInteractor.kt
index fdda3bd..562ab14 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardPreviewInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardPreviewInteractor.kt
@@ -17,6 +17,7 @@
package com.android.systemui.keyguard.domain.interactor
import android.app.WallpaperColors
+import android.content.Context
import android.os.Bundle
import android.os.IBinder
import android.view.Display
@@ -42,6 +43,9 @@
private val clockInteractor: KeyguardClockInteractor,
private val shadeModeInteractor: ShadeModeInteractor,
) {
+ val previewContext: Context
+ get() = repository.previewContext
+
val previewClock: Flow<ClockController> = repository.previewClock
val previewClockSize: Flow<ClockSizeSetting> =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
index ea51520..01241bf 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
@@ -22,12 +22,12 @@
import android.content.Context
import android.content.Intent
import android.util.Log
-import android.view.accessibility.AccessibilityManager
import com.android.app.tracing.coroutines.withContextTraced as withContext
import com.android.compose.animation.scene.ObservableTransitionState
import com.android.internal.widget.LockPatternUtils
import com.android.keyguard.logging.KeyguardQuickAffordancesLogger
import com.android.systemui.Flags.msdlFeedback
+import com.android.systemui.accessibility.domain.interactor.AccessibilityInteractor
import com.android.systemui.animation.DialogTransitionAnimator
import com.android.systemui.animation.Expandable
import com.android.systemui.dagger.SysUISingleton
@@ -91,7 +91,7 @@
private val devicePolicyManager: DevicePolicyManager,
private val dockManager: DockManager,
private val biometricSettingsRepository: BiometricSettingsRepository,
- private val accessibilityManager: AccessibilityManager,
+ private val accessibilityInteractor: AccessibilityInteractor,
@Background private val backgroundDispatcher: CoroutineDispatcher,
@ShadeDisplayAware private val appContext: Context,
private val sceneInteractor: Lazy<SceneInteractor>,
@@ -109,8 +109,10 @@
* If `false`, the UI goes back to using single taps.
*/
fun useLongPress(): Flow<Boolean> =
- dockManager.retrieveIsDocked().map { isDocked ->
- !isDocked && !accessibilityManager.isEnabled()
+ combine(dockManager.retrieveIsDocked(), accessibilityInteractor.isEnabledFiltered) {
+ isDocked,
+ isAccessibilityEnabled ->
+ !isDocked && !isAccessibilityEnabled
}
/** Returns an observable for the quick affordance at the given position. */
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt
index e673757..fcaf9c0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt
@@ -40,6 +40,7 @@
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
@SysUISingleton
class LightRevealScrimInteractor
@@ -98,23 +99,33 @@
/** Limit the max alpha for the scrim to allow for some transparency */
val maxAlpha: Flow<Float> =
- anyOf(
- transitionInteractor.isInTransition(
- edge = Edge.create(Scenes.Gone, KeyguardState.AOD),
- edgeWithoutSceneContainer = Edge.create(KeyguardState.GONE, KeyguardState.AOD),
- ),
- transitionInteractor.isInTransition(
- Edge.create(KeyguardState.OCCLUDED, KeyguardState.AOD)
- ),
- )
- .flatMapLatest { isInTransition ->
- // During transitions like GONE->AOD, surfaces like the launcher may be visible
- // until WM is told to hide them, which occurs at the end of the animation. Use an
- // opaque scrim until this transition is complete.
- if (isInTransition) {
+ repository.wallpaperSupportsAmbientMode
+ .flatMapLatest { wallpaperSupportsAmbientMode ->
+ if (!wallpaperSupportsAmbientMode) {
flowOf(1f)
} else {
- repository.maxAlpha
+ anyOf(
+ transitionInteractor.isInTransition(
+ edge = Edge.create(Scenes.Gone, KeyguardState.AOD),
+ edgeWithoutSceneContainer =
+ Edge.create(KeyguardState.GONE, KeyguardState.AOD),
+ ),
+ transitionInteractor.isInTransition(
+ Edge.create(KeyguardState.OCCLUDED, KeyguardState.AOD)
+ ),
+ )
+ .flatMapLatest { isInTransition ->
+ // During transitions like GONE->AOD, surfaces like the launcher may be
+ // visible until WM is told to hide them, which occurs at the end of the
+ // animation. Use an opaque scrim until this transition is complete.
+ if (isInTransition) {
+ flowOf(1f)
+ } else {
+ repository.useDarkWallpaperScrim.map { useDarkScrim ->
+ if (useDarkScrim) 0.64f else 0.4f
+ }
+ }
+ }
}
}
.flowOn(backgroundDispatcher)
@@ -137,12 +148,7 @@
/** If the wallpaper supports ambient mode, allow partial transparency */
fun setWallpaperSupportsAmbientMode(supportsAmbientMode: Boolean) {
- repository.maxAlpha.value =
- if (supportsAmbientMode) {
- 0.2f
- } else {
- 1f
- }
+ repository.wallpaperSupportsAmbientMode.value = supportsAmbientMode
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
index b4e9d82..22c08e2 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
@@ -31,6 +31,7 @@
import com.android.systemui.scene.shared.model.Overlays
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.statusbar.notification.domain.interactor.NotificationLaunchAnimationInteractor
+import com.android.systemui.statusbar.policy.domain.interactor.DeviceProvisioningInteractor
import com.android.systemui.util.kotlin.Utils.Companion.toQuad
import com.android.systemui.utils.coroutines.flow.flatMapLatestConflated
import dagger.Lazy
@@ -57,6 +58,7 @@
sceneInteractor: Lazy<SceneInteractor>,
deviceEntryInteractor: Lazy<DeviceEntryInteractor>,
wakeToGoneInteractor: KeyguardWakeDirectlyToGoneInteractor,
+ deviceProvisioningInteractor: Lazy<DeviceProvisioningInteractor>,
) {
private val defaultSurfaceBehindVisibility =
combine(
@@ -197,10 +199,9 @@
}
private val lockscreenVisibilityWithScenes: Flow<Boolean> =
- // The scene container visibility into account as that will be forced to false when the
- // device isn't yet provisioned (e.g. still in the setup wizard).
- sceneInteractor.get().isVisible.flatMapLatestConflated { isVisible ->
- if (isVisible) {
+ deviceProvisioningInteractor.get().isDeviceProvisioned.flatMapLatestConflated {
+ isProvisioned ->
+ if (isProvisioned) {
combine(
sceneInteractor.get().transitionState.flatMapLatestConflated {
when (it) {
@@ -236,7 +237,7 @@
lockscreenVisibilityByTransitionState && !canWakeDirectlyToGone
}
} else {
- // Lockscreen is never visible when the scene container is invisible.
+ // Lockscreen is never visible when the device isn't provisioned.
flowOf(false)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt
index f38a243..9d39c15 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt
@@ -23,7 +23,7 @@
import androidx.lifecycle.repeatOnLifecycle
import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.Flags
-import com.android.systemui.customization.R as customR
+import com.android.systemui.customization.clocks.ClockLogger.Companion.getVisText
import com.android.systemui.keyguard.shared.model.KeyguardBlueprint
import com.android.systemui.keyguard.ui.view.layout.blueprints.transitions.IntraBlueprintTransition
import com.android.systemui.keyguard.ui.view.layout.blueprints.transitions.IntraBlueprintTransition.Config
@@ -34,7 +34,7 @@
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.core.Logger
import com.android.systemui.log.dagger.KeyguardBlueprintLog
-import com.android.systemui.plugins.clocks.ClockLogger.Companion.getVisText
+import com.android.systemui.plugins.clocks.ClockViewIds
import com.android.systemui.shared.R as sharedR
import com.android.systemui.util.kotlin.pairwise
@@ -141,7 +141,7 @@
if (currentClock == null) return
this.i({ "applyCsToSmallClock: vis=${getVisText(int1)}; alpha=$str1; scale=$str2" }) {
- val smallClockViewId = customR.id.lockscreen_clock_view
+ val smallClockViewId = ClockViewIds.LOCKSCREEN_CLOCK_VIEW_SMALL
int1 = cs.getVisibility(smallClockViewId)
str1 = "${cs.getConstraint(smallClockViewId).propertySet.alpha}"
str2 = "${cs.getConstraint(smallClockViewId).transform.scaleX}"
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt
index 84c7686..6674819 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt
@@ -41,7 +41,7 @@
/** Binder for the small clock view, large clock view. */
object KeyguardPreviewClockViewBinder {
- val lockId = View.generateViewId()
+ val lockViewId = View.generateViewId()
@JvmStatic
fun bind(
@@ -85,10 +85,7 @@
.forEach { rootView.removeView(it) }
}
lastClock = currentClock
- updateClockAppearance(
- currentClock,
- clockPreviewConfig.context.resources,
- )
+ updateClockAppearance(currentClock, rootView.context.resources)
if (viewModel.shouldHighlightSelectedAffordance) {
(currentClock.largeClock.layout.views +
@@ -133,8 +130,8 @@
val cs = ConstraintSet().apply { clone(rootView) }
val configWithUpdatedLockId =
- if (rootView.getViewById(lockId) != null) {
- clockPreviewConfig.copy(lockId = lockId)
+ if (rootView.getViewById(lockViewId) != null) {
+ clockPreviewConfig.copy(lockViewId = lockViewId)
} else {
clockPreviewConfig
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewSmartspaceViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewSmartspaceViewBinder.kt
index a716585..248b2bc 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewSmartspaceViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewSmartspaceViewBinder.kt
@@ -74,10 +74,16 @@
val topPadding =
when (it) {
ClockSizeSetting.DYNAMIC ->
- viewModel.getLargeClockSmartspaceTopPadding(clockPreviewConfig)
+ viewModel.getLargeClockSmartspaceTopPadding(
+ smartspace.context,
+ clockPreviewConfig,
+ )
ClockSizeSetting.SMALL ->
- viewModel.getSmallClockSmartspaceTopPadding(clockPreviewConfig)
+ viewModel.getSmallClockSmartspaceTopPadding(
+ smartspace.context,
+ clockPreviewConfig,
+ )
}
smartspace.setTopPadding(topPadding)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt
index 35b6c79..6bde13f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt
@@ -308,7 +308,7 @@
}
if (viewModel.configKey != null) {
- hapticsViewModel.onQuickAffordanceLongPress()
+ hapticsViewModel.onQuickAffordanceLongPress(viewModel.isActivated)
viewModel.onClicked(
KeyguardQuickAffordanceViewModel.OnClickedParameters(
configKey = viewModel.configKey,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
index 1888088..2ef4672 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
@@ -49,7 +49,6 @@
import com.android.systemui.common.ui.view.onApplyWindowInsets
import com.android.systemui.common.ui.view.onLayoutChanged
import com.android.systemui.common.ui.view.onTouchListener
-import com.android.systemui.customization.R as customR
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryHapticsInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.ui.view.layout.sections.AodPromotedNotificationSection
@@ -65,6 +64,7 @@
import com.android.systemui.log.core.Logger
import com.android.systemui.log.dagger.KeyguardBlueprintLog
import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.plugins.clocks.ClockViewIds
import com.android.systemui.res.R
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.shade.domain.interactor.ShadeInteractor
@@ -574,11 +574,11 @@
private val burnInLayerId = R.id.burn_in_layer
private val aodPromotedNotificationId = AodPromotedNotificationSection.viewId
private val aodNotificationIconContainerId = R.id.aod_notification_icon_container
- private val largeClockId = customR.id.lockscreen_clock_view_large
+ private val largeClockId = ClockViewIds.LOCKSCREEN_CLOCK_VIEW_LARGE
private val largeClockDateId = sharedR.id.date_smartspace_view_large
private val largeClockWeatherId = sharedR.id.weather_smartspace_view_large
private val bcSmartspaceId = sharedR.id.bc_smartspace_view
- private val smallClockId = customR.id.lockscreen_clock_view
+ private val smallClockId = ClockViewIds.LOCKSCREEN_CLOCK_VIEW_SMALL
private val indicationArea = R.id.keyguard_indication_area
private val startButton = R.id.start_button
private val endButton = R.id.end_button
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSmartspaceViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSmartspaceViewBinder.kt
index ad47b68..fb3a4ee 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSmartspaceViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSmartspaceViewBinder.kt
@@ -53,11 +53,15 @@
::Pair,
)
.collect {
- updateDateWeatherToBurnInLayer(
- keyguardRootView,
- clockViewModel,
- smartspaceViewModel,
- )
+ if (
+ !com.android.systemui.shared.Flags.clockReactiveSmartspaceLayout()
+ ) {
+ updateDateWeatherToBurnInLayer(
+ keyguardRootView,
+ clockViewModel,
+ smartspaceViewModel,
+ )
+ }
blueprintInteractor.refreshBlueprint(
Config(
Type.SmartspaceVisibility,
@@ -95,20 +99,22 @@
val largeViewId = sharedR.id.date_smartspace_view_large
- launch("$TAG#smartspaceViewModel.burnInLayerVisibility") {
- combine(
- keyguardRootViewModel.burnInLayerVisibility,
- clockViewModel.isLargeClockVisible,
- ::Pair,
- )
- .collect { (visibility, isLargeClock) ->
- if (isLargeClock) {
- // hide small clock date/weather
- keyguardRootView.findViewById<View>(smallViewId)?.let {
- it.visibility = View.GONE
- }
+ launch("$TAG#smartspaceViewModel.isLargeClockVisible") {
+ clockViewModel.isLargeClockVisible.collect { isLargeClock ->
+ if (isLargeClock) {
+ // hide small clock date/weather
+ keyguardRootView.findViewById<View>(smallViewId)?.let {
+ it.visibility = View.GONE
}
+ removeDateWeatherFromBurnInLayer(
+ keyguardRootView,
+ smartspaceViewModel,
+ )
+ } else {
+ addDateWeatherToBurnInLayer(keyguardRootView, smartspaceViewModel)
}
+ clockViewModel.burnInLayer?.updatePostLayout(keyguardRootView)
+ }
}
launch("$TAG#clockEventController.onClockBoundsChanged") {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
index 5ab1aa29..0d13340 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
@@ -28,7 +28,6 @@
import android.os.Handler
import android.provider.Settings
import android.util.Log
-import android.view.ContextThemeWrapper
import android.view.Display.DEFAULT_DISPLAY
import android.view.DisplayInfo
import android.view.LayoutInflater
@@ -48,7 +47,7 @@
import com.android.systemui.animation.view.LaunchableImageView
import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor
import com.android.systemui.broadcast.BroadcastDispatcher
-import com.android.systemui.customization.R as customR
+import com.android.systemui.customization.clocks.R as clocksR
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
@@ -65,12 +64,10 @@
import com.android.systemui.monet.ColorScheme
import com.android.systemui.monet.Style
import com.android.systemui.plugins.clocks.ClockController
-import com.android.systemui.plugins.clocks.ClockPreviewConfig
-import com.android.systemui.plugins.clocks.ContextExt.getId
+import com.android.systemui.plugins.clocks.ClockViewIds
import com.android.systemui.plugins.clocks.ThemeConfig
import com.android.systemui.plugins.clocks.WeatherData
import com.android.systemui.res.R
-import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.shared.clocks.ClockRegistry
import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots
import com.android.systemui.shared.quickaffordance.shared.model.KeyguardPreviewConstants.KEY_INITIALLY_SELECTED_SLOT_ID
@@ -163,11 +160,7 @@
fun render() {
mainHandler.post {
- val previewContext =
- previewViewModel.display?.let {
- ContextThemeWrapper(context.createDisplayContext(it), context.getTheme())
- } ?: context
-
+ val previewContext = previewViewModel.previewContext
val rootView = ConstraintLayout(previewContext)
setupKeyguardRootView(previewContext, rootView)
@@ -302,8 +295,8 @@
val cs = ConstraintSet()
cs.clone(parentView)
cs.apply {
- val largeClockViewId = previewContext.getId("lockscreen_clock_view_large")
- val smallClockViewId = previewContext.getId("lockscreen_clock_view")
+ val largeClockViewId = ClockViewIds.LOCKSCREEN_CLOCK_VIEW_LARGE
+ val smallClockViewId = ClockViewIds.LOCKSCREEN_CLOCK_VIEW_SMALL
largeDateView =
lockscreenSmartspaceController
.buildAndConnectDateView(parentView, true)
@@ -331,7 +324,7 @@
START,
smallClockViewId,
ConstraintSet.END,
- context.resources.getDimensionPixelSize(
+ previewContext.resources.getDimensionPixelSize(
R.dimen.smartspace_padding_horizontal
),
)
@@ -356,11 +349,8 @@
val topPadding: Int =
smartspaceViewModel.getLargeClockSmartspaceTopPadding(
- ClockPreviewConfig(
- previewContext,
- previewViewModel.isShadeLayoutWide,
- SceneContainerFlag.isEnabled,
- )
+ previewContext,
+ previewViewModel.buildPreviewConfig(),
)
val startPadding: Int = smartspaceViewModel.getDateWeatherStartPadding(previewContext)
val endPadding: Int = smartspaceViewModel.getDateWeatherEndPadding(previewContext)
@@ -406,11 +396,7 @@
clockViewModel,
clockRegistry,
::updateClockAppearance,
- ClockPreviewConfig(
- previewContext,
- previewViewModel.isShadeLayoutWide,
- SceneContainerFlag.isEnabled,
- ),
+ previewViewModel.buildPreviewConfig(),
)
}
@@ -420,14 +406,7 @@
KeyguardPreviewSmartspaceViewBinder.bind(
it,
smartspaceViewModel,
- clockPreviewConfig =
- ClockPreviewConfig(
- previewContext,
- previewViewModel.isShadeLayoutWide,
- SceneContainerFlag.isEnabled,
- lockId = null,
- udfpsTop = null,
- ),
+ previewViewModel.buildPreviewConfig(),
)
}
}
@@ -479,18 +458,17 @@
.inflate(R.layout.udfps_keyguard_preview, parentView, false) as View
// Place the UDFPS view in the proper sensor location
- val lockId = KeyguardPreviewClockViewBinder.lockId
- finger.id = lockId
+ val lockViewId = KeyguardPreviewClockViewBinder.lockViewId
+ finger.id = lockViewId
parentView.addView(finger)
- val cs = ConstraintSet()
- cs.clone(parentView as ConstraintLayout)
- cs.apply {
- constrainWidth(lockId, sensorBounds.width())
- constrainHeight(lockId, sensorBounds.height())
- connect(lockId, TOP, PARENT_ID, TOP, sensorBounds.top)
- connect(lockId, START, PARENT_ID, START, sensorBounds.left)
+ ConstraintSet().apply {
+ clone(parentView as ConstraintLayout)
+ constrainWidth(lockViewId, sensorBounds.width())
+ constrainHeight(lockViewId, sensorBounds.height())
+ connect(lockViewId, TOP, PARENT_ID, TOP, sensorBounds.top)
+ connect(lockViewId, START, PARENT_ID, START, sensorBounds.left)
+ applyTo(parentView)
}
- cs.applyTo(parentView)
}
private fun setUpClock(previewContext: Context, parentView: ViewGroup) {
@@ -544,7 +522,7 @@
// When set clock to clockController,it will reset fontsize based on context.resources
// We need to override it with overlaid resources
clock.largeClock.events.onFontSettingChanged(
- resources.getDimensionPixelSize(customR.dimen.large_clock_text_size).toFloat()
+ resources.getDimensionPixelSize(clocksR.dimen.large_clock_text_size).toFloat()
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt
index bca0bed..3063c67 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt
@@ -29,7 +29,7 @@
import androidx.constraintlayout.widget.ConstraintSet.START
import androidx.constraintlayout.widget.ConstraintSet.TOP
import com.android.systemui.common.ui.ConfigurationState
-import com.android.systemui.customization.R as customR
+import com.android.systemui.customization.clocks.R as clocksR
import com.android.systemui.keyguard.shared.model.KeyguardSection
import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
import com.android.systemui.res.R
@@ -96,7 +96,7 @@
val bottomMargin =
context.resources.getDimensionPixelSize(R.dimen.keyguard_status_view_bottom_margin)
val horizontalMargin =
- context.resources.getDimensionPixelSize(customR.dimen.status_view_margin_horizontal)
+ context.resources.getDimensionPixelSize(clocksR.dimen.status_view_margin_horizontal)
val height = context.resources.getDimensionPixelSize(R.dimen.notification_shelf_height)
val isVisible = rootViewModel.isNotifIconContainerVisible.value
val isShadeLayoutWide = shadeModeInteractor.isShadeLayoutWide.value
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt
index 8fbb569..9744a84 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt
@@ -30,7 +30,7 @@
import androidx.constraintlayout.widget.ConstraintSet.TOP
import androidx.constraintlayout.widget.ConstraintSet.VISIBLE
import androidx.constraintlayout.widget.ConstraintSet.WRAP_CONTENT
-import com.android.systemui.customization.R as customR
+import com.android.systemui.customization.clocks.R as clocksR
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor
@@ -42,6 +42,7 @@
import com.android.systemui.keyguard.ui.viewmodel.KeyguardSmartspaceViewModel
import com.android.systemui.plugins.clocks.ClockController
import com.android.systemui.plugins.clocks.ClockFaceLayout
+import com.android.systemui.plugins.clocks.ClockViewIds
import com.android.systemui.res.R
import com.android.systemui.shade.LargeScreenHeaderHelper
import com.android.systemui.shade.ShadeDisplayAware
@@ -132,7 +133,7 @@
connect(
sharedR.id.bc_smartspace_view,
TOP,
- customR.id.lockscreen_clock_view,
+ ClockViewIds.LOCKSCREEN_CLOCK_VIEW_SMALL,
BOTTOM,
)
}
@@ -156,15 +157,15 @@
createBarrier(
R.id.weather_clock_bc_smartspace_bottom,
Barrier.BOTTOM,
- getDimen(ENHANCED_SMARTSPACE_HEIGHT),
- (customR.id.weather_clock_time),
+ context.resources.getDimensionPixelSize(clocksR.dimen.enhanced_smartspace_height),
+ (ClockViewIds.WEATHER_CLOCK_TIME),
)
if (
rootViewModel.isNotifIconContainerVisible.value.value &&
keyguardClockViewModel.hasAodIcons.value
) {
createBarrier(
- R.id.weather_clock_date_and_icons_barrier_bottom,
+ ClockViewIds.WEATHER_CLOCK_DATE_BARRIER_BOTTOM,
Barrier.BOTTOM,
0,
*intArrayOf(
@@ -174,7 +175,7 @@
)
} else {
createBarrier(
- R.id.weather_clock_date_and_icons_barrier_bottom,
+ ClockViewIds.WEATHER_CLOCK_DATE_BARRIER_BOTTOM,
Barrier.BOTTOM,
0,
*intArrayOf(R.id.weather_clock_bc_smartspace_bottom),
@@ -188,10 +189,10 @@
if (keyguardClockViewModel.clockShouldBeCentered.value) PARENT_ID
else R.id.split_shade_guideline
constraints.apply {
- connect(customR.id.lockscreen_clock_view_large, START, PARENT_ID, START)
- connect(customR.id.lockscreen_clock_view_large, END, guideline, END)
+ connect(ClockViewIds.LOCKSCREEN_CLOCK_VIEW_LARGE, START, PARENT_ID, START)
+ connect(ClockViewIds.LOCKSCREEN_CLOCK_VIEW_LARGE, END, guideline, END)
connect(
- customR.id.lockscreen_clock_view_large,
+ ClockViewIds.LOCKSCREEN_CLOCK_VIEW_LARGE,
BOTTOM,
R.id.device_entry_icon_view,
TOP,
@@ -199,51 +200,62 @@
val largeClockTopMargin =
if (com.android.systemui.shared.Flags.clockReactiveSmartspaceLayout()) {
keyguardClockViewModel.getLargeClockTopMargin() +
- getDimen(ENHANCED_SMARTSPACE_HEIGHT)
+ context.resources.getDimensionPixelSize(
+ clocksR.dimen.enhanced_smartspace_height
+ )
} else {
keyguardClockViewModel.getLargeClockTopMargin() +
- getDimen(DATE_WEATHER_VIEW_HEIGHT) +
- getDimen(ENHANCED_SMARTSPACE_HEIGHT)
+ context.resources.getDimensionPixelSize(
+ clocksR.dimen.date_weather_view_height
+ ) +
+ context.resources.getDimensionPixelSize(
+ clocksR.dimen.enhanced_smartspace_height
+ )
}
connect(
- customR.id.lockscreen_clock_view_large,
+ ClockViewIds.LOCKSCREEN_CLOCK_VIEW_LARGE,
TOP,
PARENT_ID,
TOP,
largeClockTopMargin,
)
- constrainWidth(customR.id.lockscreen_clock_view_large, WRAP_CONTENT)
+ constrainWidth(ClockViewIds.LOCKSCREEN_CLOCK_VIEW_LARGE, WRAP_CONTENT)
- // The following two lines make lockscreen_clock_view_large is constrained to available
+ // The following two lines make LOCKSCREEN_CLOCK_VIEW_LARGE is constrained to available
// height when it goes beyond constraints; otherwise, it use WRAP_CONTENT
- constrainHeight(customR.id.lockscreen_clock_view_large, WRAP_CONTENT)
- constrainMaxHeight(customR.id.lockscreen_clock_view_large, 0)
- constrainWidth(customR.id.lockscreen_clock_view, WRAP_CONTENT)
+ constrainHeight(ClockViewIds.LOCKSCREEN_CLOCK_VIEW_LARGE, WRAP_CONTENT)
+ constrainMaxHeight(ClockViewIds.LOCKSCREEN_CLOCK_VIEW_LARGE, 0)
+ constrainWidth(ClockViewIds.LOCKSCREEN_CLOCK_VIEW_SMALL, WRAP_CONTENT)
constrainHeight(
- customR.id.lockscreen_clock_view,
- context.resources.getDimensionPixelSize(customR.dimen.small_clock_height),
+ ClockViewIds.LOCKSCREEN_CLOCK_VIEW_SMALL,
+ context.resources.getDimensionPixelSize(clocksR.dimen.small_clock_height),
)
connect(
- customR.id.lockscreen_clock_view,
+ ClockViewIds.LOCKSCREEN_CLOCK_VIEW_SMALL,
START,
PARENT_ID,
START,
- context.resources.getDimensionPixelSize(customR.dimen.clock_padding_start) +
+ context.resources.getDimensionPixelSize(clocksR.dimen.clock_padding_start) +
context.resources.getDimensionPixelSize(
- customR.dimen.status_view_margin_horizontal
+ clocksR.dimen.status_view_margin_horizontal
),
)
val smallClockTopMargin = keyguardClockViewModel.getSmallClockTopMargin()
create(R.id.small_clock_guideline_top, ConstraintSet.HORIZONTAL_GUIDELINE)
setGuidelineBegin(R.id.small_clock_guideline_top, smallClockTopMargin)
- connect(customR.id.lockscreen_clock_view, TOP, R.id.small_clock_guideline_top, BOTTOM)
+ connect(
+ ClockViewIds.LOCKSCREEN_CLOCK_VIEW_SMALL,
+ TOP,
+ R.id.small_clock_guideline_top,
+ BOTTOM,
+ )
// Explicitly clear pivot to force recalculate pivot instead of using legacy value
- setTransformPivot(customR.id.lockscreen_clock_view_large, Float.NaN, Float.NaN)
+ setTransformPivot(ClockViewIds.LOCKSCREEN_CLOCK_VIEW_LARGE, Float.NaN, Float.NaN)
val smallClockBottom =
keyguardClockViewModel.getSmallClockTopMargin() +
- context.resources.getDimensionPixelSize(customR.dimen.small_clock_height)
+ context.resources.getDimensionPixelSize(clocksR.dimen.small_clock_height)
val marginBetweenSmartspaceAndNotification =
context.resources.getDimensionPixelSize(
R.dimen.keyguard_status_view_bottom_margin
@@ -256,7 +268,9 @@
if (keyguardClockViewModel.shouldDateWeatherBeBelowSmallClock.value) {
val dateWeatherSmartspaceHeight =
- getDimen(context, DATE_WEATHER_VIEW_HEIGHT).toFloat()
+ context.resources
+ .getDimensionPixelSize(clocksR.dimen.date_weather_view_height)
+ .toFloat()
clockInteractor.setNotificationStackDefaultTop(
smallClockBottom +
dateWeatherSmartspaceHeight +
@@ -271,19 +285,4 @@
constrainWeatherClockDateIconsBarrier(constraints)
}
-
- private fun getDimen(name: String): Int {
- return getDimen(context, name)
- }
-
- companion object {
- private const val DATE_WEATHER_VIEW_HEIGHT = "date_weather_view_height"
- private const val ENHANCED_SMARTSPACE_HEIGHT = "enhanced_smartspace_height"
-
- fun getDimen(context: Context, name: String): Int {
- val res = context.packageManager.getResourcesForApplication(context.packageName)
- val id = res.getIdentifier(name, "dimen", context.packageName)
- return if (id == 0) 0 else res.getDimensionPixelSize(id)
- }
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt
index 754b3d7..223006e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt
@@ -26,7 +26,7 @@
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
import com.android.systemui.biometrics.AuthController
-import com.android.systemui.customization.R as customR
+import com.android.systemui.customization.clocks.R as clocksR
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.flags.FeatureFlags
@@ -114,7 +114,7 @@
val scaleFactor: Float = authController.scaleFactor
val mBottomPaddingPx =
- context.resources.getDimensionPixelSize(customR.dimen.lock_icon_margin_bottom)
+ context.resources.getDimensionPixelSize(clocksR.dimen.lock_icon_margin_bottom)
val bounds = windowManager.currentWindowMetrics.bounds
var widthPixels = bounds.right.toFloat()
if (featureFlags.isEnabled(Flags.LOCKSCREEN_ENABLE_LANDSCAPE)) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/KeyguardSliceViewSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/KeyguardSliceViewSection.kt
index 962e608..47c7f02 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/KeyguardSliceViewSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/KeyguardSliceViewSection.kt
@@ -24,12 +24,12 @@
import androidx.constraintlayout.widget.ConstraintSet
import com.android.keyguard.KeyguardSliceView
import com.android.keyguard.KeyguardSliceViewController
-import com.android.systemui.customization.R as customR
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager
import com.android.systemui.keyguard.shared.model.KeyguardSection
import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.plugins.clocks.ClockViewIds
import com.android.systemui.res.R
import com.android.systemui.settings.DisplayTracker
import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController
@@ -95,7 +95,7 @@
connect(
R.id.keyguard_slice_view,
ConstraintSet.TOP,
- customR.id.lockscreen_clock_view,
+ ClockViewIds.LOCKSCREEN_CLOCK_VIEW_SMALL,
ConstraintSet.BOTTOM,
)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt
index bc8d2a6..d7fcaf5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt
@@ -26,7 +26,6 @@
import androidx.constraintlayout.widget.ConstraintSet
import androidx.constraintlayout.widget.ConstraintSet.GONE
import androidx.constraintlayout.widget.ConstraintSet.VISIBLE
-import com.android.systemui.customization.R as customR
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.KeyguardUnlockAnimationController
import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor
@@ -36,8 +35,10 @@
import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardSmartspaceViewModel
+import com.android.systemui.plugins.clocks.ClockViewIds
import com.android.systemui.res.R as R
import com.android.systemui.shade.ShadeDisplayAware
+import com.android.systemui.shared.Flags.clockReactiveSmartspaceLayout
import com.android.systemui.shared.R as sharedR
import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController
import dagger.Lazy
@@ -80,7 +81,7 @@
smartspaceController.buildAndConnectDateView(constraintLayout, false) as? LinearLayout
pastVisibility = smartspaceView?.visibility ?: View.GONE
constraintLayout.addView(smartspaceView)
- if (com.android.systemui.shared.Flags.clockReactiveSmartspaceLayout()) {
+ if (clockReactiveSmartspaceLayout()) {
val weatherViewLargeClock =
smartspaceController.buildAndConnectWeatherView(constraintLayout, true)
dateViewLargeClock =
@@ -136,7 +137,8 @@
KeyguardSmartspaceViewModel.getSmartspaceHorizontalMargin(context)
val dateWeatherBelowSmallClock =
keyguardClockViewModel.shouldDateWeatherBeBelowSmallClock.value
- if (com.android.systemui.shared.Flags.clockReactiveSmartspaceLayout()) {
+ val isLargeClockVisible = keyguardClockViewModel.isLargeClockVisible.value
+ if (clockReactiveSmartspaceLayout()) {
if (dateWeatherBelowSmallClock) {
dateView?.orientation = LinearLayout.HORIZONTAL
} else {
@@ -173,7 +175,7 @@
ConstraintSet.END,
smartspaceHorizontalPadding,
)
- if (keyguardClockViewModel.hasCustomWeatherDataDisplay.value) {
+ if (keyguardClockViewModel.hasCustomWeatherDataDisplay.value && isLargeClockVisible) {
clear(sharedR.id.date_smartspace_view, ConstraintSet.TOP)
connect(
sharedR.id.date_smartspace_view,
@@ -183,12 +185,12 @@
)
} else {
clear(sharedR.id.date_smartspace_view, ConstraintSet.BOTTOM)
- if (com.android.systemui.shared.Flags.clockReactiveSmartspaceLayout()) {
+ if (clockReactiveSmartspaceLayout()) {
if (dateWeatherBelowSmallClock) {
connect(
sharedR.id.date_smartspace_view,
ConstraintSet.TOP,
- customR.id.lockscreen_clock_view,
+ ClockViewIds.LOCKSCREEN_CLOCK_VIEW_SMALL,
ConstraintSet.BOTTOM,
)
connect(
@@ -201,7 +203,7 @@
connect(
sharedR.id.bc_smartspace_view,
ConstraintSet.TOP,
- customR.id.lockscreen_clock_view,
+ ClockViewIds.LOCKSCREEN_CLOCK_VIEW_SMALL,
ConstraintSet.BOTTOM,
)
}
@@ -209,7 +211,7 @@
connect(
sharedR.id.date_smartspace_view,
ConstraintSet.TOP,
- customR.id.lockscreen_clock_view,
+ ClockViewIds.LOCKSCREEN_CLOCK_VIEW_SMALL,
ConstraintSet.BOTTOM,
)
connect(
@@ -221,8 +223,8 @@
}
}
- if (com.android.systemui.shared.Flags.clockReactiveSmartspaceLayout()) {
- if (keyguardClockViewModel.isLargeClockVisible.value) {
+ if (clockReactiveSmartspaceLayout()) {
+ if (isLargeClockVisible) {
setVisibility(sharedR.id.date_smartspace_view, GONE)
constrainHeight(
sharedR.id.date_smartspace_view_large,
@@ -243,7 +245,7 @@
connect(
sharedR.id.date_smartspace_view_large,
ConstraintSet.TOP,
- customR.id.lockscreen_clock_view_large,
+ ClockViewIds.LOCKSCREEN_CLOCK_VIEW_LARGE,
ConstraintSet.BOTTOM,
context.resources.getDimensionPixelSize(R.dimen.smartspace_padding_vertical),
)
@@ -251,13 +253,13 @@
connect(
sharedR.id.date_smartspace_view_large,
ConstraintSet.START,
- customR.id.lockscreen_clock_view_large,
+ ClockViewIds.LOCKSCREEN_CLOCK_VIEW_LARGE,
ConstraintSet.START,
)
connect(
sharedR.id.date_smartspace_view_large,
ConstraintSet.END,
- customR.id.lockscreen_clock_view_large,
+ ClockViewIds.LOCKSCREEN_CLOCK_VIEW_LARGE,
ConstraintSet.END,
)
setHorizontalChainStyle(
@@ -280,7 +282,7 @@
connect(
sharedR.id.date_smartspace_view,
ConstraintSet.START,
- customR.id.lockscreen_clock_view,
+ ClockViewIds.LOCKSCREEN_CLOCK_VIEW_SMALL,
ConstraintSet.END,
context.resources.getDimensionPixelSize(
R.dimen.smartspace_padding_horizontal
@@ -289,20 +291,20 @@
connect(
sharedR.id.date_smartspace_view,
ConstraintSet.TOP,
- customR.id.lockscreen_clock_view,
+ ClockViewIds.LOCKSCREEN_CLOCK_VIEW_SMALL,
ConstraintSet.TOP,
)
connect(
sharedR.id.date_smartspace_view,
ConstraintSet.BOTTOM,
- customR.id.lockscreen_clock_view,
+ ClockViewIds.LOCKSCREEN_CLOCK_VIEW_SMALL,
ConstraintSet.BOTTOM,
)
}
}
}
- if (com.android.systemui.shared.Flags.clockReactiveSmartspaceLayout()) {
+ if (clockReactiveSmartspaceLayout()) {
if (dateWeatherBelowSmallClock) {
createBarrier(
R.id.smart_space_barrier_bottom,
@@ -345,14 +347,14 @@
)
}
}
- updateVisibility(constraintSet)
+ updateVisibility(constraintSet, isLargeClockVisible)
}
override fun removeViews(constraintLayout: ConstraintLayout) {
if (!keyguardSmartspaceViewModel.isSmartspaceEnabled) return
val list =
- if (com.android.systemui.shared.Flags.clockReactiveSmartspaceLayout()) {
+ if (clockReactiveSmartspaceLayout()) {
listOf(smartspaceView, dateView, dateViewLargeClock)
} else {
listOf(smartspaceView, dateView)
@@ -370,16 +372,13 @@
disposableHandle?.dispose()
}
- private fun updateVisibility(constraintSet: ConstraintSet) {
+ private fun updateVisibility(constraintSet: ConstraintSet, isLargeClockVisible: Boolean) {
// This may update the visibility of the smartspace views
smartspaceController.requestSmartspaceUpdate()
val weatherId: Int
val dateId: Int
- if (
- com.android.systemui.shared.Flags.clockReactiveSmartspaceLayout() &&
- keyguardClockViewModel.isLargeClockVisible.value
- ) {
+ if (clockReactiveSmartspaceLayout() && isLargeClockVisible) {
weatherId = sharedR.id.weather_smartspace_view_large
dateId = sharedR.id.date_smartspace_view_large
} else {
@@ -392,12 +391,13 @@
setVisibility(weatherId, if (showWeather) VISIBLE else GONE)
setAlpha(weatherId, if (showWeather) 1f else 0f)
- val showDateView = !keyguardClockViewModel.hasCustomWeatherDataDisplay.value
+ val showDateView =
+ !keyguardClockViewModel.hasCustomWeatherDataDisplay.value || !isLargeClockVisible
setVisibility(dateId, if (showDateView) VISIBLE else GONE)
setAlpha(dateId, if (showDateView) 1f else 0f)
- if (com.android.systemui.shared.Flags.clockReactiveSmartspaceLayout()) {
- if (keyguardClockViewModel.isLargeClockVisible.value) {
+ if (clockReactiveSmartspaceLayout()) {
+ if (isLargeClockVisible) {
setVisibility(sharedR.id.date_smartspace_view, GONE)
} else {
setVisibility(sharedR.id.date_smartspace_view_large, GONE)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeMediaSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeMediaSection.kt
index c91a007..a180639 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeMediaSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeMediaSection.kt
@@ -27,7 +27,7 @@
import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
import androidx.constraintlayout.widget.ConstraintSet.START
import androidx.constraintlayout.widget.ConstraintSet.TOP
-import com.android.systemui.customization.R as customR
+import com.android.systemui.customization.clocks.R as clocksR
import com.android.systemui.keyguard.shared.model.KeyguardSection
import com.android.systemui.media.controls.ui.controller.KeyguardMediaController
import com.android.systemui.res.R
@@ -51,7 +51,7 @@
val horizontalPadding =
padding +
context.resources.getDimensionPixelSize(
- customR.dimen.status_view_margin_horizontal
+ clocksR.dimen.status_view_margin_horizontal
)
setPaddingRelative(horizontalPadding, padding, horizontalPadding, padding)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt
index 3b193ae6..909e838 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt
@@ -28,7 +28,7 @@
import android.view.ViewGroup
import android.view.ViewTreeObserver.OnPreDrawListener
import com.android.app.animation.Interpolators
-import com.android.systemui.customization.R as customR
+import com.android.systemui.customization.clocks.ClockLogger.Companion.getVisText
import com.android.systemui.keyguard.ui.view.layout.blueprints.transitions.IntraBlueprintTransition
import com.android.systemui.keyguard.ui.view.layout.blueprints.transitions.IntraBlueprintTransition.Type
import com.android.systemui.keyguard.ui.view.layout.sections.transitions.ClockSizeTransition.SmartspaceMoveTransition.Companion.STATUS_AREA_MOVE_DOWN_MILLIS
@@ -36,7 +36,7 @@
import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.core.Logger
-import com.android.systemui.plugins.clocks.ClockLogger.Companion.getVisText
+import com.android.systemui.plugins.clocks.ClockViewIds
import com.android.systemui.res.R
import com.android.systemui.shared.R as sharedR
import com.google.android.material.math.MathUtils
@@ -295,14 +295,14 @@
}
?: run {
logger.e("No large clock set, falling back")
- addTarget(customR.id.lockscreen_clock_view_large)
+ addTarget(ClockViewIds.LOCKSCREEN_CLOCK_VIEW_LARGE)
}
if (com.android.systemui.shared.Flags.clockReactiveSmartspaceLayout()) {
addTarget(sharedR.id.date_smartspace_view_large)
}
} else {
logger.i("Adding small clock")
- addTarget(customR.id.lockscreen_clock_view)
+ addTarget(ClockViewIds.LOCKSCREEN_CLOCK_VIEW_SMALL)
if (!viewModel.shouldDateWeatherBeBelowSmallClock.value) {
addTarget(sharedR.id.date_smartspace_view)
}
@@ -408,7 +408,7 @@
override fun mutateTargets(from: Target, to: Target) {
if (to.view.id == sharedR.id.date_smartspace_view) {
- to.isVisible = !viewModel.hasCustomWeatherDataDisplay.value
+ to.isVisible = !viewModel.hasCustomWeatherDataDisplay.value || !isLargeClock
to.visibility = if (to.isVisible) View.VISIBLE else View.GONE
to.alpha = if (to.isVisible) 1f else 0f
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt
index b9996c0..1f8925a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt
@@ -70,6 +70,7 @@
) {
private val TAG = "AodBurnInViewModel"
private val burnInParams = MutableStateFlow(BurnInParameters())
+ private val maxLargeClockScale = if (Flags.clockReactiveSmartspaceLayout()) 0.9f else 1f
fun updateBurnInParams(params: BurnInParameters) {
burnInParams.value =
@@ -192,19 +193,14 @@
val burnInY = MathUtils.lerp(0, burnIn.translationY, interpolated).toInt()
val translationY = max(params.topInset - params.minViewY, burnInY)
- val stopScale = if (Flags.clockReactiveSmartspaceLayout()) MAX_LARGE_CLOCK_SCALE else 1f
BurnInModel(
translationX = MathUtils.lerp(0, burnIn.translationX, interpolated).toInt(),
translationY = translationY,
- scale = MathUtils.lerp(burnIn.scale, stopScale, 1f - interpolated),
+ scale = MathUtils.lerp(burnIn.scale, maxLargeClockScale, 1f - interpolated),
scaleClockOnly = useScaleOnly,
)
}
}
-
- companion object {
- private const val MAX_LARGE_CLOCK_SCALE = 0.9f
- }
}
/** UI-sourced parameters to pass into the various methods of [AodBurnInViewModel]. */
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToGlanceableHubTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToGlanceableHubTransitionViewModel.kt
index 3cf0506..d643902 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToGlanceableHubTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToGlanceableHubTransitionViewModel.kt
@@ -17,9 +17,9 @@
package com.android.systemui.keyguard.ui.viewmodel
import android.util.MathUtils
-import com.android.systemui.communal.ui.compose.TransitionDuration
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.dagger.GlanceableHubBlurComponent
+import com.android.systemui.keyguard.domain.interactor.FromAodTransitionInteractor
import com.android.systemui.keyguard.shared.model.Edge
import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
import com.android.systemui.keyguard.shared.model.KeyguardState.GLANCEABLE_HUB
@@ -42,7 +42,7 @@
private val transitionAnimation =
animationFlow
.setup(
- duration = TransitionDuration.TO_GLANCEABLE_HUB_DURATION_MS.milliseconds,
+ duration = FromAodTransitionInteractor.TO_GLANCEABLE_HUB_DURATION,
edge = Edge.create(AOD, Scenes.Communal),
)
.setupWithoutSceneContainer(edge = Edge.create(AOD, GLANCEABLE_HUB))
@@ -70,6 +70,7 @@
}
},
onStep = { MathUtils.lerp(currentAlpha, 0f, it) },
+ onFinish = { 0f },
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlows.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlows.kt
index 59cc157..8bd0e41 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlows.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlows.kt
@@ -16,9 +16,15 @@
package com.android.systemui.keyguard.ui.viewmodel
+import android.annotation.ColorInt
+import android.content.Context
+import android.util.Log
+import androidx.core.graphics.alpha
import com.android.app.animation.Interpolators.EMPHASIZED_ACCELERATE
+import com.android.systemui.Flags
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
import com.android.systemui.bouncer.shared.flag.ComposeBouncerFlags
+import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.domain.interactor.KeyguardDismissActionInteractor
import com.android.systemui.keyguard.shared.model.Edge
import com.android.systemui.keyguard.shared.model.KeyguardState
@@ -28,7 +34,11 @@
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.shade.ui.ShadeColors.notificationScrim
+import com.android.systemui.shade.ui.ShadeColors.shadePanel
import com.android.systemui.statusbar.SysuiStatusBarStateController
+import com.android.systemui.statusbar.phone.ScrimController
+import com.android.systemui.window.domain.interactor.WindowRootViewBlurInteractor
import dagger.Lazy
import javax.inject.Inject
import kotlin.time.Duration
@@ -41,11 +51,13 @@
class BouncerToGoneFlows
@Inject
constructor(
+ @Application private val context: Context,
private val statusBarStateController: SysuiStatusBarStateController,
private val primaryBouncerInteractor: PrimaryBouncerInteractor,
private val keyguardDismissActionInteractor: Lazy<KeyguardDismissActionInteractor>,
private val shadeInteractor: ShadeInteractor,
private val animationFlow: KeyguardTransitionAnimationFlow,
+ private val windowRootViewBlurInteractor: WindowRootViewBlurInteractor,
) {
/** Common fade for scrim alpha values during *BOUNCER->GONE */
fun scrimAlpha(duration: Duration, fromState: KeyguardState): Flow<ScrimAlpha> {
@@ -58,7 +70,7 @@
createScrimAlphaFlow(
duration,
fromState,
- primaryBouncerInteractor::willRunDismissFromKeyguard
+ primaryBouncerInteractor::willRunDismissFromKeyguard,
)
}
}
@@ -79,11 +91,9 @@
Edge.INVALID
} else {
Edge.create(from = from, to = Scenes.Gone)
- }
+ },
)
- .setupWithoutSceneContainer(
- edge = Edge.create(from = from, to = GONE),
- )
+ .setupWithoutSceneContainer(edge = Edge.create(from = from, to = GONE))
.sharedFlow(
duration = duration,
onStart = { leaveShadeOpen = statusBarStateController.leaveOpenOnKeyguardHide() },
@@ -98,11 +108,9 @@
private fun createScrimAlphaFlow(
duration: Duration,
fromState: KeyguardState,
- willRunAnimationOnKeyguard: () -> Boolean
+ willRunAnimationOnKeyguard: () -> Boolean,
): Flow<ScrimAlpha> {
- var isShadeExpanded = false
- var leaveShadeOpen: Boolean = false
- var willRunDismissFromKeyguard: Boolean = false
+ var startState = StartState()
val transitionAnimation =
animationFlow
.setup(
@@ -112,11 +120,9 @@
Edge.INVALID
} else {
Edge.create(from = fromState, to = Scenes.Gone)
- }
+ },
)
- .setupWithoutSceneContainer(
- edge = Edge.create(from = fromState, to = GONE),
- )
+ .setupWithoutSceneContainer(edge = Edge.create(from = fromState, to = GONE))
return shadeInteractor.anyExpansion
.map { it > 0f }
@@ -127,31 +133,125 @@
duration = duration,
interpolator = EMPHASIZED_ACCELERATE,
onStart = {
- leaveShadeOpen = statusBarStateController.leaveOpenOnKeyguardHide()
- willRunDismissFromKeyguard = willRunAnimationOnKeyguard()
- isShadeExpanded = isAnyExpanded
+ startState =
+ toStartState(
+ leaveShadeOpen =
+ statusBarStateController.leaveOpenOnKeyguardHide(),
+ willRunDismissFromKeyguard = willRunAnimationOnKeyguard(),
+ isShadeExpanded = isAnyExpanded,
+ )
+ Log.d(TAG, "onStart BouncerToGone with $startState")
},
- onStep = { 1f - it },
+ onStep = { it },
)
.map {
- if (willRunDismissFromKeyguard) {
- if (isShadeExpanded) {
- ScrimAlpha(
- behindAlpha = it,
- notificationsAlpha = it,
- )
- } else {
- ScrimAlpha()
- }
- } else if (leaveShadeOpen) {
- ScrimAlpha(
- behindAlpha = 1f,
- notificationsAlpha = 1f,
+ if (Flags.bouncerUiRevamp() || Flags.notificationShadeBlur()) {
+ mapToScrimAlphasWithCustomMaxAlphas(
+ transitionProgress = it,
+ startState = startState,
)
} else {
- ScrimAlpha(behindAlpha = it)
+ mapToScrimAlphas(transitionProgress = it, startState = startState)
}
}
}
}
+
+ private fun mapToScrimAlphasWithCustomMaxAlphas(
+ transitionProgress: Float,
+ startState: StartState,
+ ): ScrimAlpha {
+ // convert to a value that goes from 1 -> 0 and scale down the max allowed alpha with it.
+ val invertedTransitionProgress = 1 - transitionProgress
+ return with(startState) {
+ when {
+ willRunDismissFromKeyguard && isShadeExpanded ->
+ ScrimAlpha(
+ behindAlpha = shadeBehindAlpha * invertedTransitionProgress,
+ notificationsAlpha = shadeNotifAlpha * invertedTransitionProgress,
+ )
+ willRunDismissFromKeyguard && !isShadeExpanded -> ScrimAlpha()
+ leaveShadeOpen ->
+ ScrimAlpha(behindAlpha = shadeBehindAlpha, notificationsAlpha = shadeNotifAlpha)
+ else -> ScrimAlpha(behindAlpha = bouncerBehindAlpha * invertedTransitionProgress)
+ }
+ }
+ }
+
+ private fun toStartState(
+ willRunDismissFromKeyguard: Boolean,
+ isShadeExpanded: Boolean,
+ leaveShadeOpen: Boolean,
+ ): StartState {
+ val isBlurCurrentlySupported = windowRootViewBlurInteractor.isBlurCurrentlySupported.value
+ val defaultValue =
+ StartState(
+ willRunDismissFromKeyguard = willRunDismissFromKeyguard,
+ isShadeExpanded = isShadeExpanded,
+ leaveShadeOpen = leaveShadeOpen,
+ shadeNotifAlpha = 1.0f,
+ shadeBehindAlpha = 1.0f,
+ bouncerBehindAlpha = 1.0f,
+ )
+ val shadeNotifAlpha = colorAlpha(notificationScrim(context, isBlurCurrentlySupported))
+ val shadeBehindAlpha = colorAlpha(shadePanel(context, isBlurCurrentlySupported))
+ val bouncerBehindAlpha =
+ if (isBlurCurrentlySupported) ScrimController.TRANSPARENT_BOUNCER_SCRIM_ALPHA else 1.0f
+ return when {
+ Flags.bouncerUiRevamp() && Flags.notificationShadeBlur() -> {
+ defaultValue.copy(
+ shadeBehindAlpha = shadeBehindAlpha,
+ shadeNotifAlpha = shadeNotifAlpha,
+ bouncerBehindAlpha = bouncerBehindAlpha,
+ )
+ }
+ Flags.bouncerUiRevamp() && !Flags.notificationShadeBlur() -> {
+ defaultValue.copy(bouncerBehindAlpha = bouncerBehindAlpha)
+ }
+ !Flags.bouncerUiRevamp() && Flags.notificationShadeBlur() -> {
+ defaultValue.copy(
+ shadeBehindAlpha = shadeBehindAlpha,
+ shadeNotifAlpha = shadeNotifAlpha,
+ )
+ }
+ else -> defaultValue
+ }
+ }
+
+ private fun colorAlpha(@ColorInt colorInt: Int): Float = colorInt.alpha / 255.0f
+
+ private fun mapToScrimAlphas(transitionProgress: Float, startState: StartState): ScrimAlpha {
+ val willRunDismissFromKeyguard = startState.willRunDismissFromKeyguard
+ val isShadeExpanded: Boolean = startState.isShadeExpanded
+ val leaveShadeOpen: Boolean = startState.leaveShadeOpen
+ val invertedTransitionProgress = 1 - transitionProgress
+ return if (willRunDismissFromKeyguard) {
+ if (isShadeExpanded) {
+ ScrimAlpha(
+ behindAlpha = invertedTransitionProgress,
+ notificationsAlpha = invertedTransitionProgress,
+ )
+ } else {
+ ScrimAlpha()
+ }
+ } else if (leaveShadeOpen) {
+ ScrimAlpha(behindAlpha = 1f, notificationsAlpha = 1f)
+ } else {
+ ScrimAlpha(behindAlpha = invertedTransitionProgress)
+ }
+ }
+
+ private data class StartState(
+ val isShadeExpanded: Boolean = false,
+ val leaveShadeOpen: Boolean = false,
+ val willRunDismissFromKeyguard: Boolean = false,
+ val isBlurCurrentlySupported: Boolean = false,
+ val shadeNotifAlpha: Float = 1.0f,
+ val shadeBehindAlpha: Float = 1.0f,
+ val bouncerBehindAlpha: Float = 1.0f,
+ )
+
+ private companion object {
+ const val TAG = "BouncerToGoneFlows"
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToAodTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToAodTransitionViewModel.kt
index 6a45845..9316618 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToAodTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToAodTransitionViewModel.kt
@@ -58,6 +58,8 @@
startTime = 233.milliseconds,
duration = 250.milliseconds,
onStep = { it },
+ onCancel = { 0f },
+ onFinish = { 1f },
)
override val deviceEntryParentViewAlpha: Flow<Float> =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt
index 61bb245..bd5f708 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt
@@ -21,7 +21,7 @@
import androidx.constraintlayout.helper.widget.Layer
import com.android.keyguard.ClockEventController
import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
-import com.android.systemui.customization.R as customR
+import com.android.systemui.customization.clocks.R as clocksR
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
@@ -32,6 +32,7 @@
import com.android.systemui.log.core.LogLevel
import com.android.systemui.log.dagger.KeyguardSmallClockLog
import com.android.systemui.plugins.clocks.ClockPreviewConfig
+import com.android.systemui.res.R as SysuiR
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.shade.domain.interactor.ShadeModeInteractor
@@ -159,11 +160,17 @@
/** Calculates the top margin for the small clock. */
fun getSmallClockTopMargin(): Int {
return ClockPreviewConfig(
- context,
- shadeModeInteractor.isShadeLayoutWide.value,
- SceneContainerFlag.isEnabled,
+ isShadeLayoutWide = shadeModeInteractor.isShadeLayoutWide.value,
+ isSceneContainerFlagEnabled = SceneContainerFlag.isEnabled,
+ statusBarHeight = systemBarUtils.getStatusBarHeaderHeightKeyguard(),
+ splitShadeTopMargin =
+ context.resources.getDimensionPixelSize(
+ SysuiR.dimen.keyguard_split_shade_top_margin
+ ),
+ clockTopMargin =
+ context.resources.getDimensionPixelSize(SysuiR.dimen.keyguard_clock_top_margin),
)
- .getSmallClockTopPadding(systemBarUtils.getStatusBarHeaderHeightKeyguard())
+ .getSmallClockTopPadding()
}
val smallClockTopMargin =
@@ -178,11 +185,11 @@
fun getLargeClockTopMargin(): Int {
return if (com.android.systemui.shared.Flags.clockReactiveSmartspaceLayout()) {
systemBarUtils.getStatusBarHeight() / 2 +
- resources.getDimensionPixelSize(customR.dimen.keyguard_smartspace_top_offset)
+ resources.getDimensionPixelSize(clocksR.dimen.keyguard_smartspace_top_offset)
} else {
systemBarUtils.getStatusBarHeight() +
- resources.getDimensionPixelSize(customR.dimen.small_clock_padding_top) +
- resources.getDimensionPixelSize(customR.dimen.keyguard_smartspace_top_offset)
+ resources.getDimensionPixelSize(clocksR.dimen.small_clock_padding_top) +
+ resources.getDimensionPixelSize(clocksR.dimen.keyguard_smartspace_top_offset)
}
}
@@ -190,7 +197,7 @@
configurationInteractor.onAnyConfigurationChange.map { getLargeClockTopMargin() }
val largeClockTextSize: Flow<Int> =
- configurationInteractor.dimensionPixelSize(customR.dimen.large_clock_text_size)
+ configurationInteractor.dimensionPixelSize(clocksR.dimen.large_clock_text_size)
val shouldDateWeatherBeBelowSmallClock: StateFlow<Boolean> =
if (com.android.systemui.shared.Flags.clockReactiveSmartspaceLayout()) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardPreviewSmartspaceViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardPreviewSmartspaceViewModel.kt
index e13afa6..18e50df 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardPreviewSmartspaceViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardPreviewSmartspaceViewModel.kt
@@ -17,7 +17,7 @@
package com.android.systemui.keyguard.ui.viewmodel
import android.content.Context
-import com.android.systemui.customization.R as customR
+import com.android.systemui.customization.clocks.R as clocksR
import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardPreviewInteractor
import com.android.systemui.keyguard.shared.model.ClockSizeSetting
@@ -71,17 +71,17 @@
return KeyguardSmartspaceViewModel.getDateWeatherEndMargin(context)
}
- /*
- * SmallClockTopPadding decides the top position of smartspace
- */
- fun getSmallClockSmartspaceTopPadding(config: ClockPreviewConfig): Int {
- return config.getSmallClockTopPadding(systemBarUtils.getStatusBarHeaderHeightKeyguard()) +
- config.context.resources.getDimensionPixelSize(customR.dimen.small_clock_height)
+ /** SmallClockTopPadding decides the top position of smartspace */
+ fun getSmallClockSmartspaceTopPadding(context: Context, config: ClockPreviewConfig): Int {
+ return config.getSmallClockTopPadding(
+ systemBarUtils.getStatusBarHeaderHeightKeyguard(context)
+ ) + context.resources.getDimensionPixelSize(clocksR.dimen.small_clock_height)
}
- fun getLargeClockSmartspaceTopPadding(clockPreviewConfig: ClockPreviewConfig): Int {
- return clockPreviewConfig.getSmallClockTopPadding(
- systemBarUtils.getStatusBarHeaderHeightKeyguard()
+ /** SmallClockTopPadding decides the top position of smartspace */
+ fun getLargeClockSmartspaceTopPadding(context: Context, config: ClockPreviewConfig): Int {
+ return config.getSmallClockTopPadding(
+ systemBarUtils.getStatusBarHeaderHeightKeyguard(context)
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardPreviewViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardPreviewViewModel.kt
index 5f4e9e4..366b39b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardPreviewViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardPreviewViewModel.kt
@@ -17,10 +17,15 @@
package com.android.systemui.keyguard.ui.viewmodel
import android.app.WallpaperColors
+import android.content.Context
import android.os.Bundle
import android.os.IBinder
import android.view.Display
+import com.android.internal.policy.SystemBarUtils
import com.android.systemui.keyguard.domain.interactor.KeyguardPreviewInteractor
+import com.android.systemui.plugins.clocks.ClockPreviewConfig
+import com.android.systemui.res.R as SysuiR
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
@@ -34,6 +39,9 @@
class KeyguardPreviewViewModel
@AssistedInject
constructor(@Assisted private val interactor: KeyguardPreviewInteractor) {
+ val previewContext: Context
+ get() = interactor.previewContext
+
val request: Bundle
get() = interactor.request
@@ -57,4 +65,20 @@
val wallpaperColors: WallpaperColors?
get() = interactor.wallpaperColors
+
+ fun buildPreviewConfig(): ClockPreviewConfig {
+ return ClockPreviewConfig(
+ isShadeLayoutWide = isShadeLayoutWide,
+ isSceneContainerFlagEnabled = SceneContainerFlag.isEnabled,
+ statusBarHeight = SystemBarUtils.getStatusBarHeight(previewContext),
+ splitShadeTopMargin =
+ previewContext.resources.getDimensionPixelSize(
+ SysuiR.dimen.keyguard_split_shade_top_margin
+ ),
+ clockTopMargin =
+ previewContext.resources.getDimensionPixelSize(
+ SysuiR.dimen.keyguard_clock_top_margin
+ ),
+ )
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordanceHapticViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordanceHapticViewModel.kt
index 794c7ca..fd2a1f7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordanceHapticViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordanceHapticViewModel.kt
@@ -40,21 +40,24 @@
if (Flags.msdlFeedback()) {
playMSDLToggleHaptics(toggleOn, toggleOff)
- } else {
- vibratorHelper.vibrate(
+ }
+ }
+
+ fun onQuickAffordanceLongPress(isActivated: Boolean) {
+ longPressed = true
+ if (!Flags.msdlFeedback()) {
+ // Without MSDL, we need to play haptics on long-press instead of when the activated
+ // history updates.
+ val vibration =
if (isActivated) {
KeyguardBottomAreaVibrations.Activated
} else {
KeyguardBottomAreaVibrations.Deactivated
}
- )
+ vibratorHelper.vibrate(vibration)
}
}
- fun onQuickAffordanceLongPress() {
- longPressed = true
- }
-
fun onQuickAffordanceClick() {
if (Flags.msdlFeedback()) {
msdlPlayer.playToken(MSDLToken.FAILURE)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSmartspaceViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSmartspaceViewModel.kt
index 1e8538d..8cd54c9 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSmartspaceViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSmartspaceViewModel.kt
@@ -17,9 +17,7 @@
package com.android.systemui.keyguard.ui.viewmodel
import android.content.Context
-import android.content.res.Configuration
-import android.util.Log
-import com.android.systemui.customization.R as customR
+import com.android.systemui.customization.clocks.R as clocksR
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.domain.interactor.KeyguardSmartspaceInteractor
@@ -31,7 +29,6 @@
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
@SysUISingleton
@@ -54,22 +51,31 @@
/** Whether the date area should be visible. */
val isDateVisible: StateFlow<Boolean> =
- keyguardClockViewModel.hasCustomWeatherDataDisplay
- .map { !it }
+ combine(
+ keyguardClockViewModel.hasCustomWeatherDataDisplay,
+ keyguardClockViewModel.isLargeClockVisible,
+ ) { hasCustomWeatherDataDisplay, isLargeClockVisible ->
+ !hasCustomWeatherDataDisplay || !isLargeClockVisible
+ }
.stateIn(
scope = applicationScope,
started = SharingStarted.WhileSubscribed(),
- initialValue = !keyguardClockViewModel.hasCustomWeatherDataDisplay.value,
+ initialValue =
+ !keyguardClockViewModel.hasCustomWeatherDataDisplay.value ||
+ !keyguardClockViewModel.isLargeClockVisible.value,
)
/** Whether the weather area should be visible. */
val isWeatherVisible: StateFlow<Boolean> =
- combine(isWeatherEnabled, keyguardClockViewModel.hasCustomWeatherDataDisplay) {
+ combine(
isWeatherEnabled,
- clockIncludesCustomWeatherDisplay ->
+ keyguardClockViewModel.hasCustomWeatherDataDisplay,
+ keyguardClockViewModel.isLargeClockVisible,
+ ) { isWeatherEnabled, clockIncludesCustomWeatherDisplay, isLargeClockVisible ->
isWeatherVisible(
clockIncludesCustomWeatherDisplay = clockIncludesCustomWeatherDisplay,
isWeatherEnabled = isWeatherEnabled,
+ isLargeClockVisible = isLargeClockVisible,
)
}
.stateIn(
@@ -80,14 +86,16 @@
clockIncludesCustomWeatherDisplay =
keyguardClockViewModel.hasCustomWeatherDataDisplay.value,
isWeatherEnabled = isWeatherEnabled.value,
+ isLargeClockVisible = keyguardClockViewModel.isLargeClockVisible.value,
),
)
private fun isWeatherVisible(
clockIncludesCustomWeatherDisplay: Boolean,
isWeatherEnabled: Boolean,
+ isLargeClockVisible: Boolean,
): Boolean {
- return !clockIncludesCustomWeatherDisplay && isWeatherEnabled
+ return (!clockIncludesCustomWeatherDisplay || !isLargeClockVisible) && isWeatherEnabled
}
/* trigger clock and smartspace constraints change when smartspace appears */
@@ -98,17 +106,17 @@
companion object {
fun getDateWeatherStartMargin(context: Context): Int {
return context.resources.getDimensionPixelSize(R.dimen.below_clock_padding_start) +
- context.resources.getDimensionPixelSize(customR.dimen.status_view_margin_horizontal)
+ context.resources.getDimensionPixelSize(clocksR.dimen.status_view_margin_horizontal)
}
fun getDateWeatherEndMargin(context: Context): Int {
return context.resources.getDimensionPixelSize(R.dimen.below_clock_padding_end) +
- context.resources.getDimensionPixelSize(customR.dimen.status_view_margin_horizontal)
+ context.resources.getDimensionPixelSize(clocksR.dimen.status_view_margin_horizontal)
}
fun getSmartspaceHorizontalMargin(context: Context): Int {
return context.resources.getDimensionPixelSize(R.dimen.smartspace_padding_horizontal) +
- context.resources.getDimensionPixelSize(customR.dimen.status_view_margin_horizontal)
+ context.resources.getDimensionPixelSize(clocksR.dimen.status_view_margin_horizontal)
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt
index 89584f4..4e6df92 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt
@@ -21,7 +21,7 @@
import androidx.compose.ui.Alignment
import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.biometrics.AuthController
-import com.android.systemui.customization.R as customR
+import com.android.systemui.customization.clocks.R as clocksR
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor
@@ -156,7 +156,7 @@
fun getSmartSpacePaddingTop(resources: Resources): Int {
return if (clockInteractor.clockSize.value == ClockSize.LARGE) {
- resources.getDimensionPixelSize(customR.dimen.keyguard_smartspace_top_offset) +
+ resources.getDimensionPixelSize(clocksR.dimen.keyguard_smartspace_top_offset) +
resources.getDimensionPixelSize(R.dimen.keyguard_clock_top_margin)
} else {
0
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToDreamingTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToDreamingTransitionViewModel.kt
index 9de25fc..46f3fd2 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToDreamingTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToDreamingTransitionViewModel.kt
@@ -16,6 +16,7 @@
package com.android.systemui.keyguard.ui.viewmodel
+import com.android.systemui.Flags
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor
import com.android.systemui.keyguard.shared.model.Edge
@@ -27,7 +28,6 @@
import com.android.systemui.scene.shared.model.Overlays
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.emptyFlow
@SysUISingleton
class PrimaryBouncerToDreamingTransitionViewModel
@@ -43,17 +43,27 @@
.setupWithoutSceneContainer(edge = Edge.create(from = PRIMARY_BOUNCER, to = DREAMING))
override val windowBlurRadius: Flow<Float> =
- transitionAnimation.sharedFlow(
- onStart = { blurConfig.maxBlurRadiusPx },
- onStep = {
- transitionProgressToBlurRadius(
- blurConfig.maxBlurRadiusPx,
- endBlurRadius = blurConfig.minBlurRadiusPx,
- transitionProgress = it,
- )
+ transitionAnimation.sharedFlowWithShade(
+ onStep = { progress, isShadeExpanded ->
+ if (isShadeExpanded && Flags.notificationShadeBlur()) {
+ blurConfig.maxBlurRadiusPx
+ } else {
+ transitionProgressToBlurRadius(
+ blurConfig.maxBlurRadiusPx,
+ endBlurRadius = blurConfig.minBlurRadiusPx,
+ transitionProgress = progress,
+ )
+ }
},
- onFinish = { blurConfig.minBlurRadiusPx },
+ onFinish = { isShadeExpanded ->
+ if (isShadeExpanded && Flags.notificationShadeBlur()) {
+ blurConfig.maxBlurRadiusPx
+ } else {
+ blurConfig.minBlurRadiusPx
+ }
+ },
)
- override val notificationBlurRadius: Flow<Float> = emptyFlow()
+ override val notificationBlurRadius: Flow<Float> =
+ transitionAnimation.immediatelyTransitionTo(blurConfig.minBlurRadiusPx)
}
diff --git a/packages/SystemUI/src/com/android/systemui/log/table/Diffable.kt b/packages/SystemUI/src/com/android/systemui/log/table/Diffable.kt
index a154694..739aaf8 100644
--- a/packages/SystemUI/src/com/android/systemui/log/table/Diffable.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/table/Diffable.kt
@@ -20,7 +20,7 @@
import com.android.systemui.kairos.ExperimentalKairosApi
import com.android.systemui.kairos.State
import com.android.systemui.kairos.changes
-import com.android.systemui.kairos.effect
+import com.android.systemui.kairos.effectSync
import com.android.systemui.util.kotlin.pairwiseBy
import kotlinx.coroutines.flow.Flow
@@ -221,13 +221,13 @@
columnPrefix: String = "",
) {
val initialValue = diffableState.sampleDeferred()
- effect {
+ effectSync {
// Fully log the initial value to the table.
tableLogBuffer.logChange(columnPrefix, isInitial = true) { row ->
initialValue.value.logFull(row)
}
}
- diffableState.changes.observe { newState ->
+ diffableState.changes.observeSync { newState ->
val prevState = diffableState.sample()
tableLogBuffer.logDiffs(columnPrefix, prevVal = prevState, newVal = newState)
}
diff --git a/packages/SystemUI/src/com/android/systemui/lowlightclock/AmbientLightModeMonitor.kt b/packages/SystemUI/src/com/android/systemui/lowlightclock/AmbientLightModeMonitor.kt
index 5e9e930..b4d02ce 100644
--- a/packages/SystemUI/src/com/android/systemui/lowlightclock/AmbientLightModeMonitor.kt
+++ b/packages/SystemUI/src/com/android/systemui/lowlightclock/AmbientLightModeMonitor.kt
@@ -22,34 +22,16 @@
import android.hardware.SensorEventListener
import android.hardware.SensorManager
import android.util.Log
-import com.android.systemui.Dumpable
+import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.lowlightclock.dagger.LowLightModule.Companion.LIGHT_SENSOR
import com.android.systemui.util.sensors.AsyncSensorManager
-import java.io.PrintWriter
import java.util.Optional
import javax.inject.Inject
import javax.inject.Named
import javax.inject.Provider
-/**
- * Monitors ambient light signals, applies a debouncing algorithm, and produces the current ambient
- * light mode.
- *
- * @property algorithm the debounce algorithm which transforms light sensor events into an ambient
- * light mode.
- * @property sensorManager the sensor manager used to register sensor event updates.
- */
-class AmbientLightModeMonitor
-@Inject
-constructor(
- private val algorithm: Optional<DebounceAlgorithm>,
- private val sensorManager: AsyncSensorManager,
- @Named(LIGHT_SENSOR) private val lightSensor: Optional<Provider<Sensor>>,
-) : Dumpable {
+interface AmbientLightModeMonitor {
companion object {
- private const val TAG = "AmbientLightModeMonitor"
- private val DEBUG = Log.isLoggable(TAG, Log.DEBUG)
-
const val AMBIENT_LIGHT_MODE_LIGHT = 0
const val AMBIENT_LIGHT_MODE_DARK = 1
const val AMBIENT_LIGHT_MODE_UNDECIDED = 2
@@ -60,12 +42,54 @@
@IntDef(AMBIENT_LIGHT_MODE_LIGHT, AMBIENT_LIGHT_MODE_DARK, AMBIENT_LIGHT_MODE_UNDECIDED)
annotation class AmbientLightMode
+ /** Interface of the ambient light mode callback, which gets triggered when the mode changes. */
+ fun interface Callback {
+ fun onChange(@AmbientLightMode mode: Int)
+ }
+
+ /** Interface of the algorithm that transforms light sensor events to an ambient light mode. */
+ interface DebounceAlgorithm {
+ // Setting Callback to nullable so mockito can verify without throwing NullPointerException.
+ fun start(callback: Callback?)
+
+ fun stop()
+
+ fun onUpdateLightSensorEvent(value: Float)
+ }
+
/**
* Start monitoring the current ambient light mode.
*
* @param callback callback that gets triggered when the ambient light mode changes.
*/
- fun start(callback: Callback) {
+ fun start(callback: Callback)
+
+ /** Stop monitoring the current ambient light mode. */
+ fun stop()
+}
+
+/**
+ * Monitors ambient light signals, applies a debouncing algorithm, and produces the current ambient
+ * light mode.
+ *
+ * @property algorithm the debounce algorithm which transforms light sensor events into an ambient
+ * light mode.
+ * @property sensorManager the sensor manager used to register sensor event updates.
+ */
+@SysUISingleton
+class AmbientLightModeMonitorImpl
+@Inject
+constructor(
+ private val algorithm: Optional<AmbientLightModeMonitor.DebounceAlgorithm>,
+ private val sensorManager: AsyncSensorManager,
+ @Named(LIGHT_SENSOR) private val lightSensor: Optional<Provider<Sensor>>,
+) : AmbientLightModeMonitor {
+ companion object {
+ private const val TAG = "AmbientLightModeMonitor"
+ private val DEBUG = Log.isLoggable(TAG, Log.DEBUG)
+ }
+
+ override fun start(callback: AmbientLightModeMonitor.Callback) {
if (DEBUG) Log.d(TAG, "start monitoring ambient light mode")
if (lightSensor.isEmpty || lightSensor.get().get() == null) {
@@ -87,7 +111,7 @@
}
/** Stop monitoring the current ambient light mode. */
- fun stop() {
+ override fun stop() {
if (DEBUG) Log.d(TAG, "stop monitoring ambient light mode")
if (algorithm.isPresent) {
@@ -96,13 +120,6 @@
sensorManager.unregisterListener(mSensorEventListener)
}
- override fun dump(pw: PrintWriter, args: Array<out String>) {
- pw.println()
- pw.println("Ambient light mode monitor:")
- pw.println(" lightSensor=$lightSensor")
- pw.println()
- }
-
private val mSensorEventListener: SensorEventListener =
object : SensorEventListener {
override fun onSensorChanged(event: SensorEvent) {
@@ -120,19 +137,4 @@
// Do nothing.
}
}
-
- /** Interface of the ambient light mode callback, which gets triggered when the mode changes. */
- fun interface Callback {
- fun onChange(@AmbientLightMode mode: Int)
- }
-
- /** Interface of the algorithm that transforms light sensor events to an ambient light mode. */
- interface DebounceAlgorithm {
- // Setting Callback to nullable so mockito can verify without throwing NullPointerException.
- fun start(callback: Callback?)
-
- fun stop()
-
- fun onUpdateLightSensorEvent(value: Float)
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/lowlightclock/LowLightCondition.kt b/packages/SystemUI/src/com/android/systemui/lowlightclock/LowLightCondition.kt
deleted file mode 100644
index 34e9a63..0000000
--- a/packages/SystemUI/src/com/android/systemui/lowlightclock/LowLightCondition.kt
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2025 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.systemui.lowlightclock
-
-import com.android.internal.logging.UiEventLogger
-import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.shared.condition.Condition
-import javax.inject.Inject
-import kotlinx.coroutines.CoroutineScope
-
-/** Condition for monitoring when the device enters and exits lowlight mode. */
-class LowLightCondition
-@Inject
-constructor(
- @Background scope: CoroutineScope,
- private val ambientLightModeMonitor: AmbientLightModeMonitor,
- private val uiEventLogger: UiEventLogger,
-) : Condition(scope) {
- override suspend fun start() {
- ambientLightModeMonitor.start { lowLightMode: Int -> onLowLightChanged(lowLightMode) }
- }
-
- override fun stop() {
- ambientLightModeMonitor.stop()
-
- // Reset condition met to false.
- updateCondition(false)
- }
-
- override val startStrategy: Int
- get() = // As this condition keeps the lowlight sensor active, it should only run when
- // needed.
- START_WHEN_NEEDED
-
- private fun onLowLightChanged(lowLightMode: Int) {
- if (lowLightMode == AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_UNDECIDED) {
- // Ignore undecided mode changes.
- return
- }
-
- val isLowLight = lowLightMode == AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK
- if (isLowLight == isConditionMet) {
- // No change in condition, don't do anything.
- return
- }
- uiEventLogger.log(
- if (isLowLight) LowLightDockEvent.AMBIENT_LIGHT_TO_DARK
- else LowLightDockEvent.AMBIENT_LIGHT_TO_LIGHT
- )
- updateCondition(isLowLight)
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/lowlightclock/LowLightMonitor.kt b/packages/SystemUI/src/com/android/systemui/lowlightclock/LowLightMonitor.kt
index 11b1f9d..d4fd20e 100644
--- a/packages/SystemUI/src/com/android/systemui/lowlightclock/LowLightMonitor.kt
+++ b/packages/SystemUI/src/com/android/systemui/lowlightclock/LowLightMonitor.kt
@@ -19,20 +19,21 @@
import android.content.pm.PackageManager
import android.os.UserHandle
import com.android.dream.lowlight.LowLightDreamManager
+import com.android.internal.logging.UiEventLogger
+import com.android.systemui.CoreStartable
import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor
import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.dagger.qualifiers.SystemUser
import com.android.systemui.dreams.dagger.DreamModule
import com.android.systemui.dreams.domain.interactor.DreamSettingsInteractor
import com.android.systemui.dreams.shared.model.WhenToDream
-import com.android.systemui.lowlightclock.dagger.LowLightModule
-import com.android.systemui.shared.condition.Condition
-import com.android.systemui.shared.condition.Monitor
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.keyguard.shared.model.DozeStateModel.Companion.isDozeOff
+import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.statusbar.commandline.Command
import com.android.systemui.statusbar.commandline.CommandRegistry
import com.android.systemui.user.domain.interactor.UserLockedInteractor
-import com.android.systemui.util.condition.ConditionalCoreStartable
import com.android.systemui.util.kotlin.BooleanFlowOperators.allOf
+import com.android.systemui.util.kotlin.BooleanFlowOperators.anyOf
import com.android.systemui.util.kotlin.BooleanFlowOperators.not
import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
import com.android.systemui.utils.coroutines.flow.flatMapLatestConflated
@@ -41,15 +42,16 @@
import javax.inject.Inject
import javax.inject.Named
import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.filterNot
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
@@ -61,9 +63,6 @@
@Inject
constructor(
private val lowLightDreamManager: Lazy<LowLightDreamManager>,
- @param:SystemUser private val conditionsMonitor: Monitor,
- @param:Named(LowLightModule.LOW_LIGHT_PRECONDITIONS)
- private val lowLightConditions: Lazy<Set<Condition>>,
dreamSettingsInteractor: DreamSettingsInteractor,
displayStateInteractor: DisplayStateInteractor,
private val logger: LowLightLogger,
@@ -72,8 +71,12 @@
private val packageManager: PackageManager,
@Background private val scope: CoroutineScope,
private val commandRegistry: CommandRegistry,
- userLockedInteractor: UserLockedInteractor,
-) : ConditionalCoreStartable(conditionsMonitor) {
+ private val userLockedInteractor: UserLockedInteractor,
+ keyguardInteractor: KeyguardInteractor,
+ powerInteractor: PowerInteractor,
+ private val ambientLightModeMonitor: AmbientLightModeMonitor,
+ private val uiEventLogger: UiEventLogger,
+) : CoreStartable {
/** Whether the screen is currently on. */
private val isScreenOn = not(displayStateInteractor.isDefaultDisplayOff).distinctUntilChanged()
@@ -91,35 +94,51 @@
.stateIn(scope = scope, started = SharingStarted.Eagerly, initialValue = null)
/** Whether the device is currently in a low-light environment. */
- private val isLowLightFromSensor = conflatedCallbackFlow {
- val token =
- conditionsMonitor.addSubscription(
- Monitor.Subscription.Builder { trySend(it) }
- .addConditions(lowLightConditions.get())
- .build()
+ private val isLowLightFromSensor =
+ conflatedCallbackFlow {
+ ambientLightModeMonitor.start { lowLightMode: Int -> trySend(lowLightMode) }
+ awaitClose { ambientLightModeMonitor.stop() }
+ }
+ .filterNot { it == AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_UNDECIDED }
+ .map { it == AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK }
+ .distinctUntilChanged()
+ .onEach { isLowLight ->
+ uiEventLogger.log(
+ if (isLowLight) LowLightDockEvent.AMBIENT_LIGHT_TO_DARK
+ else LowLightDockEvent.AMBIENT_LIGHT_TO_LIGHT
+ )
+ }
+ // AmbientLightModeMonitor only supports a single callback, so ensure this is re-used
+ // if there are multiple subscribers.
+ .stateIn(
+ scope,
+ started = SharingStarted.WhileSubscribed(replayExpirationMillis = 0),
+ initialValue = false,
)
- awaitClose { conditionsMonitor.removeSubscription(token) }
- }
-
private val isLowLight: Flow<Boolean> =
- combine(
- not(userLockedInteractor.isUserUnlocked(UserHandle.CURRENT)),
- isLowLightForced,
- isLowLightFromSensor,
- ) { directBoot, forcedValue, sensorValue ->
- if (forcedValue != null) {
- forcedValue
- } else if (directBoot) {
- // If user is locked, normal dreams cannot start so we force lowlight dream.
- true
- } else {
- sensorValue
- }
+ combine(isLowLightForced, isLowLightFromSensor) { forcedValue, sensorValue ->
+ forcedValue ?: sensorValue
}
- @OptIn(ExperimentalCoroutinesApi::class)
- override fun onStart() {
+ private val anyDoze: Flow<Boolean> =
+ keyguardInteractor.dozeTransitionModel.map { !isDozeOff(it.to) }
+
+ /**
+ * Whether the device is idle (lockscreen showing or dreaming or asleep) and not in doze/AOD, as
+ * we do not want to override doze/AOD with lowlight dream.
+ */
+ private val isDeviceIdleAndNotDozing: Flow<Boolean> =
+ allOf(
+ not(anyDoze),
+ anyOf(
+ keyguardInteractor.isDreaming,
+ keyguardInteractor.isKeyguardShowing,
+ powerInteractor.isAsleep,
+ ),
+ )
+
+ override fun start() {
scope.launch {
if (lowLightDreamService != null) {
// Note that the dream service is disabled by default. This prevents the dream from
@@ -139,7 +158,15 @@
allOf(isScreenOn, dreamEnabled)
.flatMapLatestConflated { conditionsMet ->
if (conditionsMet) {
- isLowLight
+ // Force lowlight only if idle and in either direct-boot mode or in
+ // a lowlight environment.
+ allOf(
+ isDeviceIdleAndNotDozing,
+ anyOf(
+ isLowLight,
+ not(userLockedInteractor.isUserUnlocked(UserHandle.CURRENT)),
+ ),
+ )
} else {
flowOf(false)
}
diff --git a/packages/SystemUI/src/com/android/systemui/lowlightclock/dagger/LowLightModule.kt b/packages/SystemUI/src/com/android/systemui/lowlightclock/dagger/LowLightModule.kt
index 2206c62..a86e323 100644
--- a/packages/SystemUI/src/com/android/systemui/lowlightclock/dagger/LowLightModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/lowlightclock/dagger/LowLightModule.kt
@@ -19,44 +19,37 @@
import android.hardware.Sensor
import com.android.dream.lowlight.dagger.LowLightDreamModule
import com.android.systemui.CoreStartable
-import com.android.systemui.communal.DeviceInactiveCondition
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.LogBufferFactory
+import com.android.systemui.lowlightclock.AmbientLightModeMonitor
import com.android.systemui.lowlightclock.AmbientLightModeMonitor.DebounceAlgorithm
-import com.android.systemui.lowlightclock.LowLightCondition
+import com.android.systemui.lowlightclock.AmbientLightModeMonitorImpl
import com.android.systemui.lowlightclock.LowLightDisplayController
import com.android.systemui.lowlightclock.LowLightMonitor
import com.android.systemui.res.R
-import com.android.systemui.shared.condition.Condition
import dagger.Binds
import dagger.BindsOptionalOf
import dagger.Module
import dagger.Provides
import dagger.multibindings.ClassKey
import dagger.multibindings.IntoMap
-import dagger.multibindings.IntoSet
import javax.inject.Named
@Module(includes = [LowLightDreamModule::class])
abstract class LowLightModule {
- @Binds
- @IntoSet
- @Named(LOW_LIGHT_PRECONDITIONS)
- abstract fun bindDeviceInactiveCondition(condition: DeviceInactiveCondition): Condition
-
- @Binds
- @IntoSet
- @Named(LOW_LIGHT_PRECONDITIONS)
- abstract fun bindLowLightCondition(condition: LowLightCondition): Condition
-
@BindsOptionalOf abstract fun bindsLowLightDisplayController(): LowLightDisplayController
@BindsOptionalOf @Named(LIGHT_SENSOR) abstract fun bindsLightSensor(): Sensor
@BindsOptionalOf abstract fun bindsDebounceAlgorithm(): DebounceAlgorithm
+ @Binds
+ abstract fun bindAmbientLightModeMonitor(
+ impl: AmbientLightModeMonitorImpl
+ ): AmbientLightModeMonitor
+
/** Inject into LowLightMonitor. */
@Binds
@IntoMap
@@ -70,7 +63,6 @@
const val ALPHA_ANIMATION_IN_START_DELAY_MILLIS: String =
"alpha_animation_in_start_delay_millis"
const val ALPHA_ANIMATION_DURATION_MILLIS: String = "alpha_animation_duration_millis"
- const val LOW_LIGHT_PRECONDITIONS: String = "low_light_preconditions"
const val LIGHT_SENSOR: String = "low_light_monitor_light_sensor"
/** Provides a [LogBuffer] for logs related to low-light features. */
diff --git a/packages/SystemUI/src/com/android/systemui/media/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/media/NotificationMediaManager.java
index d5461dd..7339d41 100644
--- a/packages/SystemUI/src/com/android/systemui/media/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/media/NotificationMediaManager.java
@@ -15,8 +15,6 @@
*/
package com.android.systemui.media;
-import static com.android.systemui.Flags.mediaControlsUserInitiatedDeleteintent;
-
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.Notification;
@@ -188,7 +186,7 @@
@Override
public void onMediaDataRemoved(@NonNull String key, boolean userInitiated) {
- if (mediaControlsUserInitiatedDeleteintent() && !userInitiated) {
+ if (!userInitiated) {
// Dismissing the notification will send the app's deleteIntent, so ignore if
// this was an automatic removal
Log.d(TAG, "Not dismissing " + key + " because it was removed by the system");
diff --git a/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java b/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java
index b71d8c9..a7ae95c 100644
--- a/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java
+++ b/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java
@@ -119,6 +119,7 @@
throws RemoteException {
playWithVolumeShaping(token, uri, aa, volume, looping, null);
}
+
@Override
public void playWithVolumeShaping(IBinder token, Uri uri, AudioAttributes aa, float volume,
boolean looping, @Nullable VolumeShaper.Configuration volumeShaperConfig)
@@ -127,7 +128,7 @@
Log.d(TAG, "play(token=" + token + ", uri=" + uri + ", uid="
+ Binder.getCallingUid() + ")");
}
- enforceUriUserId(uri);
+ enforceUriUserId(uri, token);
Client client;
synchronized (mClients) {
@@ -210,7 +211,7 @@
@Override
public String getTitle(Uri uri) {
- enforceUriUserId(uri);
+ enforceUriUserId(uri, null /*clientToken*/);
final UserHandle user = Binder.getCallingUserHandle();
return Ringtone.getTitle(getContextForUser(user), uri,
false /*followSettingsUri*/, false /*allowRemote*/);
@@ -218,7 +219,7 @@
@Override
public ParcelFileDescriptor openRingtone(Uri uri) {
- enforceUriUserId(uri);
+ enforceUriUserId(uri, null /*clientToken*/);
final UserHandle user = Binder.getCallingUserHandle();
final ContentResolver resolver = getContextForUser(user).getContentResolver();
@@ -244,29 +245,42 @@
}
throw new SecurityException("Uri is not ringtone, alarm, or notification: " + uri);
}
- };
- /**
- * Must be called from the Binder calling thread.
- * Ensures caller is from the same userId as the content they're trying to access.
- * @param uri the URI to check
- * @throws SecurityException when in a non-system call and userId in uri differs from the
- * caller's userId
- */
- private void enforceUriUserId(Uri uri) throws SecurityException {
- final int uriUserId = ContentProvider.getUserIdFromUri(uri, UserHandle.myUserId());
- // for a non-system call, verify the URI to play belongs to the same user as the caller
- if (UserHandle.isApp(Binder.getCallingUid()) && (UserHandle.myUserId() != uriUserId)) {
- final String errorMessage = "Illegal access to uri=" + uri
- + " content associated with user=" + uriUserId
- + ", current userID: " + UserHandle.myUserId();
- if (android.media.audio.Flags.ringtoneUserUriCheck()) {
- throw new SecurityException(errorMessage);
- } else {
- Log.e(TAG, errorMessage, new Exception());
+ /**
+ * Must be called from the Binder calling thread.
+ * Ensures caller is from the same userId as the content they're trying to access.
+ *
+ * @param uri the URI to check
+ * @param clientToken the Client token used for the current query, null if not available
+ * in the query (expected from calls other than the play* methods)
+ * @throws SecurityException when in a non-system call and userId in uri differs
+ * from the
+ * caller's userId
+ */
+ private void enforceUriUserId(Uri uri, @Nullable IBinder clientToken)
+ throws SecurityException {
+ final int uriUserId = ContentProvider.getUserIdFromUri(uri, UserHandle.myUserId());
+ // for a non-system call, verify the URI to play belongs to the same user as the caller
+ final int uid = Binder.getCallingUid();
+ final int pid = Binder.getCallingPid();
+ if (UserHandle.isApp(uid) && (UserHandle.myUserId() != uriUserId)) {
+ final String errorMessage = "Illegal access by uid:" + uid + " pid:" + pid
+ + " to uri=" + uri
+ + " content associated with user=" + uriUserId
+ + ", current userID=" + UserHandle.myUserId();
+ if (android.media.audio.Flags.ringtoneUserUriCheck()) {
+ if (clientToken != null) {
+ // this client is accessing URIs it shouldn't access, stop it (which also
+ // removes it from mClients in the outer class)
+ stop(clientToken);
+ }
+ throw new SecurityException(errorMessage);
+ } else {
+ Log.e(TAG, errorMessage, new Exception());
+ }
}
}
- }
+ };
private Context getContextForUser(UserHandle user) {
try {
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/data/repository/MediaFilterRepository.kt b/packages/SystemUI/src/com/android/systemui/media/controls/data/repository/MediaFilterRepository.kt
index 099a837..8851bb5 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/data/repository/MediaFilterRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/data/repository/MediaFilterRepository.kt
@@ -22,39 +22,17 @@
import com.android.systemui.media.controls.shared.model.MediaCommonModel
import com.android.systemui.media.controls.shared.model.MediaData
import com.android.systemui.media.controls.shared.model.MediaDataLoadingModel
+import com.android.systemui.media.remedia.data.repository.MediaPipelineRepository
import com.android.systemui.util.time.SystemClock
import java.util.TreeMap
import javax.inject.Inject
import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
/** A repository that holds the state of filtered media data on the device. */
@SysUISingleton
-class MediaFilterRepository @Inject constructor(private val systemClock: SystemClock) {
-
- private val _selectedUserEntries: MutableStateFlow<Map<InstanceId, MediaData>> =
- MutableStateFlow(LinkedHashMap())
- val selectedUserEntries: StateFlow<Map<InstanceId, MediaData>> =
- _selectedUserEntries.asStateFlow()
-
- private val _allUserEntries: MutableStateFlow<Map<String, MediaData>> =
- MutableStateFlow(LinkedHashMap())
- val allUserEntries: StateFlow<Map<String, MediaData>> = _allUserEntries.asStateFlow()
-
- private val comparator =
- compareByDescending<MediaSortKeyModel> {
- it.isPlaying == true && it.playbackLocation == MediaData.PLAYBACK_LOCAL
- }
- .thenByDescending {
- it.isPlaying == true && it.playbackLocation == MediaData.PLAYBACK_CAST_LOCAL
- }
- .thenByDescending { it.active }
- .thenByDescending { !it.isResume }
- .thenByDescending { it.playbackLocation != MediaData.PLAYBACK_CAST_REMOTE }
- .thenByDescending { it.lastActive }
- .thenByDescending { it.updateTime }
- .thenByDescending { it.notificationKey }
+class MediaFilterRepository @Inject constructor(private val systemClock: SystemClock) :
+ MediaPipelineRepository() {
private val _currentMedia: MutableStateFlow<List<MediaCommonModel>> =
MutableStateFlow(mutableListOf())
@@ -62,65 +40,31 @@
private var sortedMedia = TreeMap<MediaSortKeyModel, MediaCommonModel>(comparator)
- fun addMediaEntry(key: String, data: MediaData) {
- val entries = LinkedHashMap<String, MediaData>(_allUserEntries.value)
- entries[key] = data
- _allUserEntries.value = entries
- }
-
- /**
- * Removes the media entry corresponding to the given [key].
- *
- * @return media data if an entry is actually removed, `null` otherwise.
- */
- fun removeMediaEntry(key: String): MediaData? {
- val entries = LinkedHashMap<String, MediaData>(_allUserEntries.value)
- val mediaData = entries.remove(key)
- _allUserEntries.value = entries
- return mediaData
- }
-
- /** @return whether the added media data already exists. */
- fun addSelectedUserMediaEntry(data: MediaData): Boolean {
- val entries = LinkedHashMap<InstanceId, MediaData>(_selectedUserEntries.value)
- val update = _selectedUserEntries.value.containsKey(data.instanceId)
- entries[data.instanceId] = data
- _selectedUserEntries.value = entries
- return update
- }
-
- /**
- * Removes selected user media entry given the corresponding key.
- *
- * @return media data if an entry is actually removed, `null` otherwise.
- */
- fun removeSelectedUserMediaEntry(key: InstanceId): MediaData? {
- val entries = LinkedHashMap<InstanceId, MediaData>(_selectedUserEntries.value)
- val mediaData = entries.remove(key)
- _selectedUserEntries.value = entries
- return mediaData
- }
-
- /**
- * Removes selected user media entry given a key and media data.
- *
- * @return true if media data is removed, false otherwise.
- */
- fun removeSelectedUserMediaEntry(key: InstanceId, data: MediaData): Boolean {
- val entries = LinkedHashMap<InstanceId, MediaData>(_selectedUserEntries.value)
- val succeed = entries.remove(key, data)
- if (!succeed) {
- return false
+ override fun addCurrentUserMediaEntry(data: MediaData): Boolean {
+ return super.addCurrentUserMediaEntry(data).also {
+ addMediaDataLoadingState(MediaDataLoadingModel.Loaded(data.instanceId), it)
}
- _selectedUserEntries.value = entries
- return true
}
- fun clearSelectedUserMedia() {
- _selectedUserEntries.value = LinkedHashMap()
+ override fun removeCurrentUserMediaEntry(key: InstanceId): MediaData? {
+ return super.removeCurrentUserMediaEntry(key)?.also {
+ addMediaDataLoadingState(MediaDataLoadingModel.Removed(key))
+ }
}
- fun addMediaDataLoadingState(
+ override fun removeCurrentUserMediaEntry(key: InstanceId, data: MediaData): Boolean {
+ return super.removeCurrentUserMediaEntry(key, data).also {
+ addMediaDataLoadingState(MediaDataLoadingModel.Removed(key))
+ }
+ }
+
+ override fun clearCurrentUserMedia() {
+ val userEntries = LinkedHashMap<InstanceId, MediaData>(mutableUserEntries.value)
+ mutableUserEntries.value = LinkedHashMap()
+ userEntries.forEach { addMediaDataLoadingState(MediaDataLoadingModel.Removed(it.key)) }
+ }
+
+ private fun addMediaDataLoadingState(
mediaDataLoadingModel: MediaDataLoadingModel,
isUpdate: Boolean = true,
) {
@@ -131,7 +75,7 @@
}
)
- _selectedUserEntries.value[mediaDataLoadingModel.instanceId]?.let {
+ mutableUserEntries.value[mediaDataLoadingModel.instanceId]?.let {
val sortKey =
MediaSortKeyModel(
it.isPlaying,
@@ -194,11 +138,11 @@
}
fun hasActiveMedia(): Boolean {
- return _selectedUserEntries.value.any { it.value.active }
+ return mutableUserEntries.value.any { it.value.active }
}
fun hasAnyMedia(): Boolean {
- return _selectedUserEntries.value.entries.isNotEmpty()
+ return mutableUserEntries.value.entries.isNotEmpty()
}
private fun canBeRemoved(data: MediaData): Boolean {
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataCombineLatest.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataCombineLatest.kt
index 4451dda..da5d96b 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataCombineLatest.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataCombineLatest.kt
@@ -18,7 +18,7 @@
import com.android.systemui.media.controls.shared.model.MediaData
import com.android.systemui.media.controls.shared.model.MediaDeviceData
-import com.android.systemui.media.controls.shared.model.SuggestedMediaDeviceData
+import com.android.systemui.media.controls.shared.model.SuggestionData
import javax.inject.Inject
/** Combines [MediaDataManager.Listener] events with [MediaDeviceManager.Listener] events. */
@@ -26,8 +26,7 @@
MediaDataManager.Listener, MediaDeviceManager.Listener {
private val listeners: MutableSet<MediaDataManager.Listener> = mutableSetOf()
- private val entries:
- MutableMap<String, Triple<MediaData?, MediaDeviceData?, SuggestedMediaDeviceData?>> =
+ private val entries: MutableMap<String, Triple<MediaData?, MediaDeviceData?, SuggestionData?>> =
mutableMapOf()
override fun onMediaDataLoaded(
@@ -38,15 +37,13 @@
) {
if (oldKey != null && oldKey != key && entries.contains(oldKey)) {
val previousEntry = entries.remove(oldKey)
- val (mediaDeviceData, suggestedMediaDeviceData) =
- previousEntry?.second to previousEntry?.third
- entries[key] = Triple(data, mediaDeviceData, suggestedMediaDeviceData)
+ val (mediaDeviceData, suggestionData) = previousEntry?.second to previousEntry?.third
+ entries[key] = Triple(data, mediaDeviceData, suggestionData)
update(key, oldKey)
} else {
val previousEntry = entries[key]
- val (mediaDeviceData, suggestedMediaDeviceData) =
- previousEntry?.second to previousEntry?.third
- entries[key] = Triple(data, mediaDeviceData, suggestedMediaDeviceData)
+ val (mediaDeviceData, suggestionData) = previousEntry?.second to previousEntry?.third
+ entries[key] = Triple(data, mediaDeviceData, suggestionData)
update(key, key)
}
}
@@ -58,22 +55,18 @@
override fun onMediaDeviceChanged(key: String, oldKey: String?, data: MediaDeviceData?) {
if (oldKey != null && oldKey != key && entries.contains(oldKey)) {
val previousEntry = entries.remove(oldKey)
- val (mediaData, suggestedMediaDeviceData) = previousEntry?.first to previousEntry?.third
- entries[key] = Triple(mediaData, data, suggestedMediaDeviceData)
+ val (mediaData, suggestionData) = previousEntry?.first to previousEntry?.third
+ entries[key] = Triple(mediaData, data, suggestionData)
update(key, oldKey)
} else {
val previousEntry = entries[key]
- val (mediaData, suggestedMediaDeviceData) = previousEntry?.first to previousEntry?.third
- entries[key] = Triple(mediaData, data, suggestedMediaDeviceData)
+ val (mediaData, suggestionData) = previousEntry?.first to previousEntry?.third
+ entries[key] = Triple(mediaData, data, suggestionData)
update(key, key)
}
}
- override fun onSuggestedMediaDeviceChanged(
- key: String,
- oldKey: String?,
- data: SuggestedMediaDeviceData?,
- ) {
+ override fun onSuggestionDataChanged(key: String, oldKey: String?, data: SuggestionData?) {
if (oldKey != null && oldKey != key && entries.contains(oldKey)) {
val previousEntry = entries.remove(oldKey)
val (mediaData, mediaDeviceData) = previousEntry?.first to previousEntry?.second
@@ -102,10 +95,9 @@
private fun update(key: String, oldKey: String?) {
val mediaData = entries[key]?.first
val mediaDeviceData = entries[key]?.second
- val suggestedMediaDeviceData = entries[key]?.third
+ val suggestionData = entries[key]?.third
if (mediaData != null && mediaDeviceData != null) {
- val data =
- mediaData.copy(device = mediaDeviceData, suggestedDevice = suggestedMediaDeviceData)
+ val data = mediaData.copy(device = mediaDeviceData, suggestionData = suggestionData)
val listenersCopy = listeners.toSet()
listenersCopy.forEach { it.onMediaDataLoaded(key, oldKey, data) }
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImpl.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImpl.kt
index ed23cad..c2f3bdd 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImpl.kt
@@ -24,14 +24,11 @@
import com.android.internal.logging.InstanceId
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.media.controls.data.repository.MediaFilterRepository
import com.android.systemui.media.controls.shared.MediaLogger
import com.android.systemui.media.controls.shared.model.MediaData
-import com.android.systemui.media.controls.shared.model.MediaDataLoadingModel
+import com.android.systemui.media.remedia.data.repository.MediaPipelineRepository
import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.NotificationLockscreenUserManager
-import com.android.systemui.util.time.SystemClock
-import java.util.SortedMap
import java.util.concurrent.Executor
import javax.inject.Inject
@@ -52,8 +49,7 @@
userTracker: UserTracker,
private val lockscreenUserManager: NotificationLockscreenUserManager,
@Main private val executor: Executor,
- private val systemClock: SystemClock,
- private val mediaFilterRepository: MediaFilterRepository,
+ private val mediaFilterRepository: MediaPipelineRepository,
private val mediaLogger: MediaLogger,
) : MediaDataManager.Listener {
/** Non-UI listeners to media changes. */
@@ -98,13 +94,9 @@
return
}
- val isUpdate = mediaFilterRepository.addSelectedUserMediaEntry(data)
+ mediaFilterRepository.addCurrentUserMediaEntry(data)
mediaLogger.logMediaLoaded(data.instanceId, data.active, "loading media")
- mediaFilterRepository.addMediaDataLoadingState(
- MediaDataLoadingModel.Loaded(data.instanceId),
- isUpdate,
- )
// Notify listeners
listeners.forEach { it.onMediaDataLoaded(key, oldKey, data) }
@@ -113,10 +105,7 @@
override fun onMediaDataRemoved(key: String, userInitiated: Boolean) {
mediaFilterRepository.removeMediaEntry(key)?.let { mediaData ->
val instanceId = mediaData.instanceId
- mediaFilterRepository.removeSelectedUserMediaEntry(instanceId)?.let {
- mediaFilterRepository.addMediaDataLoadingState(
- MediaDataLoadingModel.Removed(instanceId)
- )
+ mediaFilterRepository.removeCurrentUserMediaEntry(instanceId)?.let {
mediaLogger.logMediaRemoved(instanceId, "removing media card")
// Only notify listeners if something actually changed
listeners.forEach { it.onMediaDataRemoved(key, userInitiated) }
@@ -127,13 +116,10 @@
@VisibleForTesting
internal fun handleProfileChanged() {
// TODO(b/317221348) re-add media removed when profile is available.
- mediaFilterRepository.allUserEntries.value.forEach { (key, data) ->
+ mediaFilterRepository.allMediaEntries.value.forEach { (key, data) ->
if (!lockscreenUserManager.isProfileAvailable(data.userId)) {
// Only remove media when the profile is unavailable.
- mediaFilterRepository.removeSelectedUserMediaEntry(data.instanceId, data)
- mediaFilterRepository.addMediaDataLoadingState(
- MediaDataLoadingModel.Removed(data.instanceId)
- )
+ mediaFilterRepository.removeCurrentUserMediaEntry(data.instanceId, data)
mediaLogger.logMediaRemoved(data.instanceId, "Removing $key after profile change")
listeners.forEach { listener -> listener.onMediaDataRemoved(key, false) }
}
@@ -144,26 +130,19 @@
internal fun handleUserSwitched() {
// If the user changes, remove all current MediaData objects.
val listenersCopy = listeners
- val keyCopy = mediaFilterRepository.selectedUserEntries.value.keys.toMutableList()
+ val keyCopy = mediaFilterRepository.currentUserEntries.value.keys.toMutableList()
// Clear the list first and update loading state to remove media from UI.
- mediaFilterRepository.clearSelectedUserMedia()
+ mediaFilterRepository.clearCurrentUserMedia()
keyCopy.forEach { instanceId ->
- mediaFilterRepository.addMediaDataLoadingState(
- MediaDataLoadingModel.Removed(instanceId)
- )
mediaLogger.logMediaRemoved(instanceId, "Removing media after user change")
getKey(instanceId)?.let {
listenersCopy.forEach { listener -> listener.onMediaDataRemoved(it, false) }
}
}
- mediaFilterRepository.allUserEntries.value.forEach { (key, data) ->
+ mediaFilterRepository.allMediaEntries.value.forEach { (key, data) ->
if (lockscreenUserManager.isCurrentProfile(data.userId)) {
- val isUpdate = mediaFilterRepository.addSelectedUserMediaEntry(data)
- mediaFilterRepository.addMediaDataLoadingState(
- MediaDataLoadingModel.Loaded(data.instanceId),
- isUpdate,
- )
+ mediaFilterRepository.addCurrentUserMediaEntry(data)
mediaLogger.logMediaLoaded(
data.instanceId,
data.active,
@@ -177,9 +156,9 @@
/** Invoked when the user has dismissed the media carousel */
fun onSwipeToDismiss() {
if (DEBUG) Log.d(TAG, "Media carousel swiped away")
- val mediaEntries = mediaFilterRepository.allUserEntries.value.entries
+ val mediaEntries = mediaFilterRepository.allMediaEntries.value.entries
mediaEntries.forEach { (key, data) ->
- if (mediaFilterRepository.selectedUserEntries.value.containsKey(data.instanceId)) {
+ if (mediaFilterRepository.currentUserEntries.value.containsKey(data.instanceId)) {
// Force updates to listeners, needed for re-activated card
mediaDataProcessor.setInactive(key, timedOut = true, forceUpdate = true)
}
@@ -192,27 +171,8 @@
/** Remove a listener that was registered with addListener */
fun removeListener(listener: MediaDataProcessor.Listener) = _listeners.remove(listener)
- /**
- * Return the time since last active for the most-recent media.
- *
- * @param sortedEntries selectedUserEntries sorted from the earliest to the most-recent.
- * @return The duration in milliseconds from the most-recent media's last active timestamp to
- * the present. MAX_VALUE will be returned if there is no media.
- */
- private fun timeSinceActiveForMostRecentMedia(
- sortedEntries: SortedMap<InstanceId, MediaData>
- ): Long {
- if (sortedEntries.isEmpty()) {
- return Long.MAX_VALUE
- }
-
- val now = systemClock.elapsedRealtime()
- val lastActiveInstanceId = sortedEntries.lastKey() // most recently active
- return sortedEntries[lastActiveInstanceId]?.let { now - it.lastActive } ?: Long.MAX_VALUE
- }
-
private fun getKey(instanceId: InstanceId): String? {
- val allEntries = mediaFilterRepository.allUserEntries.value
+ val allEntries = mediaFilterRepository.allMediaEntries.value
val filteredEntries = allEntries.filter { (_, data) -> data.instanceId == instanceId }
return if (filteredEntries.isNotEmpty()) {
filteredEntries.keys.first()
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManager.kt
index e21792a..d6a6071 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManager.kt
@@ -30,6 +30,7 @@
import androidx.annotation.MainThread
import androidx.annotation.WorkerThread
import com.android.media.flags.Flags.enableOutputSwitcherPersonalAudioSharing
+import com.android.media.flags.Flags.enableSuggestedDeviceApi
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast
import com.android.settingslib.bluetooth.LocalBluetoothManager
import com.android.settingslib.flags.Flags.enableLeAudioSharing
@@ -46,6 +47,7 @@
import com.android.systemui.media.controls.shared.model.MediaData
import com.android.systemui.media.controls.shared.model.MediaDeviceData
import com.android.systemui.media.controls.shared.model.SuggestedMediaDeviceData
+import com.android.systemui.media.controls.shared.model.SuggestionData
import com.android.systemui.media.controls.util.LocalMediaManagerFactory
import com.android.systemui.media.controls.util.MediaControllerFactory
import com.android.systemui.media.controls.util.MediaDataUtils
@@ -170,12 +172,8 @@
}
@MainThread
- private fun processSuggestedDevice(
- key: String,
- oldKey: String?,
- device: SuggestedMediaDeviceData?,
- ) {
- listeners.forEach { it.onSuggestedMediaDeviceChanged(key, oldKey, device) }
+ private fun processSuggestionData(key: String, oldKey: String?, device: SuggestionData?) {
+ listeners.forEach { it.onSuggestionDataChanged(key, oldKey, device) }
}
interface Listener {
@@ -186,11 +184,7 @@
fun onKeyRemoved(key: String, userInitiated: Boolean)
/** Called when the suggested route has changed for a given notification. */
- fun onSuggestedMediaDeviceChanged(
- key: String,
- oldKey: String?,
- data: SuggestedMediaDeviceData?,
- )
+ fun onSuggestionDataChanged(key: String, oldKey: String?, data: SuggestionData?)
}
private inner class Entry(
@@ -219,11 +213,11 @@
}
}
- private var suggestedDevice: SuggestedMediaDeviceData? = null
+ private var suggestionData: SuggestionData? = null
set(value) {
if (field != value) {
field = value
- fgExecutor.execute { processSuggestedDevice(key, oldKey, value) }
+ fgExecutor.execute { processSuggestionData(key, oldKey, value) }
}
}
@@ -242,6 +236,8 @@
fun start() =
bgExecutor.execute {
if (!started) {
+ // Fetch in case a suggestion already exists before registering for suggestions
+ onSuggestedDeviceUpdated(localMediaManager.getSuggestedDevice())
localMediaManager.registerCallback(this)
if (!Flags.removeUnnecessaryRouteScanning()) {
localMediaManager.startScan()
@@ -310,16 +306,24 @@
}
override fun onSuggestedDeviceUpdated(state: SuggestedDeviceState?) {
+ if (!enableSuggestedDeviceApi()) {
+ return
+ }
bgExecutor.execute {
- suggestedDevice =
- state?.let {
- SuggestedMediaDeviceData(
- name = it.suggestedDeviceInfo.getDeviceDisplayName(),
- icon = it.getIcon(context),
- connectionState = it.connectionState,
- connect = { localMediaManager.connectSuggestedDevice(it) },
- )
- }
+ suggestionData =
+ SuggestionData(
+ suggestedMediaDeviceData =
+ state?.let {
+ SuggestedMediaDeviceData(
+ name = it.suggestedDeviceInfo.getDeviceDisplayName(),
+ icon = it.getIcon(context),
+ connectionState = it.connectionState,
+ connect = { localMediaManager.connectSuggestedDevice(it) },
+ )
+ },
+ onSuggestionSpaceVisible =
+ Runnable { localMediaManager.requestDeviceSuggestion() },
+ )
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaCarouselInteractor.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaCarouselInteractor.kt
index d07ac15..fc901c1 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaCarouselInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaCarouselInteractor.kt
@@ -62,7 +62,7 @@
/** Are there any media notifications active? */
val hasActiveMedia: StateFlow<Boolean> =
- mediaFilterRepository.selectedUserEntries
+ mediaFilterRepository.currentUserEntries
.map { entries -> entries.any { it.value.active } }
.stateIn(
scope = applicationScope,
@@ -72,7 +72,7 @@
/** Are there any media entries, including inactive ones? */
val hasAnyMedia: StateFlow<Boolean> =
- mediaFilterRepository.selectedUserEntries
+ mediaFilterRepository.currentUserEntries
.map { entries -> entries.isNotEmpty() }
.stateIn(
scope = applicationScope,
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaControlInteractor.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaControlInteractor.kt
index f830a56..1eadb8f 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaControlInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaControlInteractor.kt
@@ -63,7 +63,7 @@
) {
val mediaControl: Flow<MediaControlModel?> =
- repository.selectedUserEntries
+ repository.currentUserEntries
.map { entries -> entries[instanceId]?.let { toMediaControlModel(it) } }
.distinctUntilChanged()
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/shared/MediaControlDrawables.kt b/packages/SystemUI/src/com/android/systemui/media/controls/shared/MediaControlDrawables.kt
index ec329d3..a16ef67 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/shared/MediaControlDrawables.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/shared/MediaControlDrawables.kt
@@ -18,7 +18,6 @@
import android.content.Context
import android.graphics.drawable.Drawable
-import com.android.systemui.Flags.mediaControlsDrawablesReuseBugfix
import com.android.systemui.res.R
object MediaControlDrawables {
@@ -34,23 +33,14 @@
private var homeDevices: Drawable? = null
fun getNextIcon(context: Context): Drawable? {
- if (!mediaControlsDrawablesReuseBugfix()) {
- return context.getDrawable(R.drawable.ic_media_next)
- }
return nextIcon ?: context.getDrawable(R.drawable.ic_media_next).also { nextIcon = it }
}
fun getPrevIcon(context: Context): Drawable? {
- if (!mediaControlsDrawablesReuseBugfix()) {
- return context.getDrawable(R.drawable.ic_media_prev)
- }
return prevIcon ?: context.getDrawable(R.drawable.ic_media_prev).also { prevIcon = it }
}
fun getLeAudioSharing(context: Context): Drawable? {
- if (!mediaControlsDrawablesReuseBugfix()) {
- return context.getDrawable(com.android.settingslib.R.drawable.ic_bt_le_audio_sharing)
- }
return leAudioSharing
?: context.getDrawable(com.android.settingslib.R.drawable.ic_bt_le_audio_sharing).also {
leAudioSharing = it
@@ -58,17 +48,11 @@
}
fun getAntenna(context: Context): Drawable? {
- if (!mediaControlsDrawablesReuseBugfix()) {
- return context.getDrawable(R.drawable.settings_input_antenna)
- }
return antenna
?: context.getDrawable(R.drawable.settings_input_antenna).also { antenna = it }
}
fun getGroupDevice(context: Context): Drawable? {
- if (!mediaControlsDrawablesReuseBugfix()) {
- return context.getDrawable(com.android.settingslib.R.drawable.ic_media_group_device)
- }
return groupDevice
?: context.getDrawable(com.android.settingslib.R.drawable.ic_media_group_device).also {
groupDevice = it
@@ -76,9 +60,6 @@
}
fun getHomeDevices(context: Context): Drawable? {
- if (!mediaControlsDrawablesReuseBugfix()) {
- return context.getDrawable(R.drawable.ic_media_home_devices)
- }
return homeDevices
?: context.getDrawable(R.drawable.ic_media_home_devices).also { homeDevices = it }
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/shared/model/MediaData.kt b/packages/SystemUI/src/com/android/systemui/media/controls/shared/model/MediaData.kt
index 57d9631..db291f3 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/shared/model/MediaData.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/shared/model/MediaData.kt
@@ -57,7 +57,7 @@
/** Where the media is playing: phone, headphones, ear buds, remote session. */
val device: MediaDeviceData? = null,
/** Where the media is suggested to be played. */
- val suggestedDevice: SuggestedMediaDeviceData? = null,
+ val suggestionData: SuggestionData? = null,
/**
* When active, a player will be displayed on keyguard and quick-quick settings. This is
* unrelated to the stream being playing or not, a player will not be active if timed out, or in
@@ -222,3 +222,16 @@
/** Action to invoke to transfer media playback to this device. */
val connect: () -> Unit,
)
+
+/** Wrapper for data needed to support suggestions in the media player. */
+data class SuggestionData
+constructor(
+ /** The suggested device for playback. Null if no suggestion exists. */
+ val suggestedMediaDeviceData: SuggestedMediaDeviceData?,
+
+ /**
+ * Callback to be invoked when the area to surface the suggestion becomes visible. Suggestion
+ * providers are notified of the visibility update and can provide suggestions.
+ */
+ val onSuggestionSpaceVisible: Runnable,
+)
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/animation/ColorSchemeTransition.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/animation/ColorSchemeTransition.kt
index e485577..7b5552c 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/animation/ColorSchemeTransition.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/animation/ColorSchemeTransition.kt
@@ -21,14 +21,10 @@
import android.animation.ValueAnimator.AnimatorUpdateListener
import android.content.Context
import android.content.res.ColorStateList
-import android.content.res.Configuration
-import android.content.res.Configuration.UI_MODE_NIGHT_YES
import android.graphics.drawable.RippleDrawable
import com.android.internal.R
import com.android.internal.annotations.VisibleForTesting
import com.android.media.flags.Flags.enableSuggestedDeviceApi
-import com.android.settingslib.Utils
-import com.android.systemui.Flags
import com.android.systemui.media.controls.ui.view.MediaViewHolder
import com.android.systemui.monet.ColorScheme
import com.android.systemui.surfaceeffects.loadingeffect.LoadingEffect
@@ -168,193 +164,39 @@
}
}
- // TODO(media_controls_a11y_colors): remove the below color definitions
- private val bgColor =
- context.getColor(com.google.android.material.R.color.material_dynamic_neutral20)
- private val surfaceColor: AnimatingColorTransition by lazy {
- animatingColorTransitionFactory(bgColor, ::surfaceFromScheme) { surfaceColor ->
- val colorList = ColorStateList.valueOf(surfaceColor)
- mediaViewHolder.seamlessIcon.imageTintList = colorList
- mediaViewHolder.seamlessText.setTextColor(surfaceColor)
- mediaViewHolder.albumView.backgroundTintList = colorList
- mediaViewHolder.gutsViewHolder.setSurfaceColor(surfaceColor)
- }
- }
-
- private val accentPrimary: AnimatingColorTransition by lazy {
- animatingColorTransitionFactory(
- loadDefaultColor(R.attr.textColorPrimary),
- ::accentPrimaryFromScheme,
- ) { accentPrimary ->
- val accentColorList = ColorStateList.valueOf(accentPrimary)
- mediaViewHolder.actionPlayPause.backgroundTintList = accentColorList
- mediaViewHolder.gutsViewHolder.setAccentPrimaryColor(accentPrimary)
- multiRippleController.updateColor(accentPrimary)
- turbulenceNoiseController.updateNoiseColor(accentPrimary)
- loadingEffect?.updateColor(accentPrimary)
- }
- }
-
- private val accentSecondary: AnimatingColorTransition by lazy {
- animatingColorTransitionFactory(
- loadDefaultColor(R.attr.textColorPrimary),
- ::accentSecondaryFromScheme,
- ) { accentSecondary ->
- val colorList = ColorStateList.valueOf(accentSecondary)
- (mediaViewHolder.seamlessButton.background as? RippleDrawable)?.let {
- it.setColor(colorList)
- it.effectColor = colorList
- }
- if (enableSuggestedDeviceApi()) {
- (mediaViewHolder.deviceSuggestionButton.background as? RippleDrawable)?.let {
- it.setColor(colorList)
- it.effectColor = colorList
- }
- }
- }
- }
-
- private val colorSeamlessAndSuggested: AnimatingColorTransition by lazy {
- animatingColorTransitionFactory(
- loadDefaultColor(R.attr.textColorPrimary),
- { colorScheme: ColorScheme ->
- // A1-100 dark in dark theme, A1-200 in light theme
- if (
- context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK ==
- UI_MODE_NIGHT_YES
- )
- colorScheme.accent1.s100
- else colorScheme.accent1.s200
- },
- { seamlessAndSuggestedColor: Int ->
- val accentColorList = ColorStateList.valueOf(seamlessAndSuggestedColor)
- mediaViewHolder.seamlessButton.backgroundTintList = accentColorList
- if (enableSuggestedDeviceApi()) {
- mediaViewHolder.deviceSuggestionButton.backgroundTintList = accentColorList
- }
- },
- )
- }
-
- private val textPrimary: AnimatingColorTransition by lazy {
- animatingColorTransitionFactory(
- loadDefaultColor(R.attr.textColorPrimary),
- ::textPrimaryFromScheme,
- ) { textPrimary ->
- mediaViewHolder.titleText.setTextColor(textPrimary)
- val textColorList = ColorStateList.valueOf(textPrimary)
- mediaViewHolder.seekBar.thumb.setTintList(textColorList)
- mediaViewHolder.seekBar.progressTintList = textColorList
- mediaViewHolder.scrubbingElapsedTimeView.setTextColor(textColorList)
- mediaViewHolder.scrubbingTotalTimeView.setTextColor(textColorList)
- for (button in mediaViewHolder.getTransparentActionButtons()) {
- button.imageTintList = textColorList
- }
- mediaViewHolder.gutsViewHolder.setTextPrimaryColor(textPrimary)
- }
- }
-
- private val textPrimaryInverse: AnimatingColorTransition by lazy {
- animatingColorTransitionFactory(
- loadDefaultColor(R.attr.textColorPrimaryInverse),
- ::textPrimaryInverseFromScheme,
- ) { textPrimaryInverse ->
- mediaViewHolder.actionPlayPause.imageTintList =
- ColorStateList.valueOf(textPrimaryInverse)
- }
- }
-
- private val textSecondary: AnimatingColorTransition by lazy {
- animatingColorTransitionFactory(
- loadDefaultColor(R.attr.textColorSecondary),
- ::textSecondaryFromScheme,
- ) { textSecondary ->
- mediaViewHolder.artistText.setTextColor(textSecondary)
- }
- }
-
- private val textTertiary: AnimatingColorTransition by lazy {
- animatingColorTransitionFactory(
- loadDefaultColor(R.attr.textColorTertiary),
- ::textTertiaryFromScheme,
- ) { textTertiary ->
- mediaViewHolder.seekBar.progressBackgroundTintList =
- ColorStateList.valueOf(textTertiary)
- }
- }
-
fun getDeviceIconColor(): Int {
- if (Flags.mediaControlsA11yColors()) {
- return onPrimaryColor.targetColor
- }
- return surfaceColor.targetColor
+ return onPrimaryColor.targetColor
}
fun getAppIconColor(): Int {
- if (Flags.mediaControlsA11yColors()) {
- return primaryColor.targetColor
- }
- return accentPrimary.targetColor
+ return primaryColor.targetColor
}
fun getSurfaceEffectColor(): Int {
- if (Flags.mediaControlsA11yColors()) {
- return primaryColor.targetColor
- }
- return accentPrimary.targetColor
+ return primaryColor.targetColor
}
fun getGutsTextColor(): Int {
- if (Flags.mediaControlsA11yColors()) {
- return context.getColor(com.android.systemui.res.R.color.media_on_background)
- }
- return textPrimary.targetColor
+ return context.getColor(com.android.systemui.res.R.color.media_on_background)
}
private fun getColorTransitions(): Array<AnimatingColorTransition> {
- return if (Flags.mediaControlsA11yColors()) {
- arrayOf(backgroundColor, primaryColor, onPrimaryColor)
- } else {
- arrayOf(
- surfaceColor,
- colorSeamlessAndSuggested,
- accentPrimary,
- accentSecondary,
- textPrimary,
- textPrimaryInverse,
- textSecondary,
- textTertiary,
- )
- }
- }
-
- private fun loadDefaultColor(id: Int): Int {
- return Utils.getColorAttr(context, id).defaultColor
+ return arrayOf(backgroundColor, primaryColor, onPrimaryColor)
}
fun updateColorScheme(colorScheme: ColorScheme?): Boolean {
var anyChanged = false
getColorTransitions().forEach {
val isChanged = it.updateColorScheme(colorScheme)
-
- // Ignore changes to colorSeamlessAndSuggested, since that is expected when toggling
- // dark mode
- // TODO(media_controls_a11y_colors): remove, not necessary
- if (it == colorSeamlessAndSuggested) return@forEach
-
anyChanged = isChanged || anyChanged
}
- if (Flags.mediaControlsA11yColors()) {
- getSurfaceEffectColor().let {
- multiRippleController.updateColor(it)
- turbulenceNoiseController.updateNoiseColor(it)
- loadingEffect?.updateColor(it)
- }
- mediaViewHolder.gutsViewHolder.setTextColor(getGutsTextColor())
- colorScheme?.let { mediaViewHolder.gutsViewHolder.setColors(it) }
- } else {
- colorScheme?.let { mediaViewHolder.gutsViewHolder.colorScheme = colorScheme }
+ getSurfaceEffectColor().let {
+ multiRippleController.updateColor(it)
+ turbulenceNoiseController.updateNoiseColor(it)
+ loadingEffect?.updateColor(it)
}
+ mediaViewHolder.gutsViewHolder.setTextColor(getGutsTextColor())
+ colorScheme?.let { mediaViewHolder.gutsViewHolder.setColors(it) }
return anyChanged
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/animation/MediaColorSchemes.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/animation/MediaColorSchemes.kt
index 67113a4..c25d1b5 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/animation/MediaColorSchemes.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/animation/MediaColorSchemes.kt
@@ -18,42 +18,6 @@
import com.android.systemui.monet.ColorScheme
-/** Returns the surface color for media controls based on the scheme. */
-@Deprecated("Remove with media_controls_a11y_colors")
-internal fun surfaceFromScheme(scheme: ColorScheme) = scheme.accent2.s800 // A2-800
-
-/** Returns the primary accent color for media controls based on the scheme. */
-@Deprecated("Remove with media_controls_a11y_colors")
-internal fun accentPrimaryFromScheme(scheme: ColorScheme) = scheme.accent1.s100 // A1-100
-
-/** Returns the secondary accent color for media controls based on the scheme. */
-@Deprecated("Remove with media_controls_a11y_colors")
-internal fun accentSecondaryFromScheme(scheme: ColorScheme) = scheme.accent1.s200 // A1-200
-
-/** Returns the primary text color for media controls based on the scheme. */
-@Deprecated("Remove with media_controls_a11y_colors")
-internal fun textPrimaryFromScheme(scheme: ColorScheme) = scheme.neutral1.s50 // N1-50
-
-/** Returns the inverse of the primary text color for media controls based on the scheme. */
-@Deprecated("Remove with media_controls_a11y_colors")
-internal fun textPrimaryInverseFromScheme(scheme: ColorScheme) = scheme.neutral1.s900 // N1-900
-
-/** Returns the secondary text color for media controls based on the scheme. */
-@Deprecated("Remove with media_controls_a11y_colors")
-internal fun textSecondaryFromScheme(scheme: ColorScheme) = scheme.neutral2.s200 // N2-200
-
-/** Returns the tertiary text color for media controls based on the scheme. */
-@Deprecated("Remove with media_controls_a11y_colors")
-internal fun textTertiaryFromScheme(scheme: ColorScheme) = scheme.neutral2.s400 // N2-400
-
-/** Returns the color for the start of the background gradient based on the scheme. */
-@Deprecated("Remove with media_controls_a11y_colors")
-internal fun backgroundStartFromScheme(scheme: ColorScheme) = scheme.accent2.s700 // A2-700
-
-/** Returns the color for the end of the background gradient based on the scheme. */
-@Deprecated("Remove with media_controls_a11y_colors")
-internal fun backgroundEndFromScheme(scheme: ColorScheme) = scheme.accent1.s700 // A1-700
-
internal fun backgroundFromScheme(scheme: ColorScheme) = scheme.materialScheme.getOnSurface()
internal fun primaryFromScheme(scheme: ColorScheme) = scheme.materialScheme.getPrimaryFixed()
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaControlViewBinder.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaControlViewBinder.kt
index 3f53820..95cd349 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaControlViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaControlViewBinder.kt
@@ -36,7 +36,6 @@
import androidx.lifecycle.repeatOnLifecycle
import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.settingslib.widget.AdaptiveIcon
-import com.android.systemui.Flags
import com.android.systemui.animation.Expandable
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.dagger.qualifiers.Background
@@ -50,9 +49,7 @@
import com.android.systemui.media.controls.ui.viewmodel.MediaActionViewModel
import com.android.systemui.media.controls.ui.viewmodel.MediaControlViewModel
import com.android.systemui.media.controls.ui.viewmodel.MediaControlViewModel.Companion.MEDIA_PLAYER_SCRIM_END_ALPHA
-import com.android.systemui.media.controls.ui.viewmodel.MediaControlViewModel.Companion.MEDIA_PLAYER_SCRIM_END_ALPHA_LEGACY
import com.android.systemui.media.controls.ui.viewmodel.MediaControlViewModel.Companion.MEDIA_PLAYER_SCRIM_START_ALPHA
-import com.android.systemui.media.controls.ui.viewmodel.MediaControlViewModel.Companion.MEDIA_PLAYER_SCRIM_START_ALPHA_LEGACY
import com.android.systemui.media.controls.ui.viewmodel.MediaControlViewModel.Companion.SEMANTIC_ACTIONS_ALL
import com.android.systemui.media.controls.ui.viewmodel.MediaControlViewModel.Companion.SEMANTIC_ACTIONS_COMPACT
import com.android.systemui.media.controls.ui.viewmodel.MediaOutputSwitcherViewModel
@@ -436,7 +433,7 @@
TAG,
)
val isArtworkBound = wallpaperColors != null
- val darkTheme = !Flags.mediaControlsA11yColors()
+ val darkTheme = false
val scheme =
wallpaperColors?.let { ColorScheme(it, darkTheme, Style.CONTENT) }
?: let {
@@ -538,18 +535,8 @@
height: Int,
): LayerDrawable {
val albumArt = MediaArtworkHelper.getScaledBackground(context, artworkIcon, width, height)
- val startAlpha =
- if (Flags.mediaControlsA11yColors()) {
- MEDIA_PLAYER_SCRIM_START_ALPHA
- } else {
- MEDIA_PLAYER_SCRIM_START_ALPHA_LEGACY
- }
- val endAlpha =
- if (Flags.mediaControlsA11yColors()) {
- MEDIA_PLAYER_SCRIM_END_ALPHA
- } else {
- MEDIA_PLAYER_SCRIM_END_ALPHA_LEGACY
- }
+ val startAlpha = MEDIA_PLAYER_SCRIM_START_ALPHA
+ val endAlpha = MEDIA_PLAYER_SCRIM_END_ALPHA
return MediaArtworkHelper.setUpGradientColorOnDrawable(
albumArt,
context.getDrawable(R.drawable.qs_media_scrim)?.mutate() as GradientDrawable,
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt
index a4bc959..0fc1263 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt
@@ -43,8 +43,9 @@
import com.android.internal.logging.InstanceId
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitorCallback
+import com.android.media.flags.Flags.enableSuggestedDeviceApi
import com.android.systemui.Dumpable
-import com.android.systemui.Flags.mediaControlsUmoInflationInBackground
+import com.android.systemui.Flags
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
@@ -197,7 +198,7 @@
val mediaFrame: ViewGroup
@VisibleForTesting
- lateinit var settingsButton: ImageView
+ lateinit var settingsButton: View
private set
private val mediaContent: ViewGroup
@@ -352,6 +353,7 @@
this::updateSeekbarListening,
this::closeGuts,
falsingManager,
+ this::onCarouselVisibleToUser,
logger,
)
carouselLocale = context.resources.configuration.locales.get(0)
@@ -452,18 +454,12 @@
immediately: Boolean,
) {
debugLogger.logMediaLoaded(key, data.active)
- val onUiExecutionEnd =
- if (mediaControlsUmoInflationInBackground()) {
- Runnable {
- if (immediately) {
- updateHostVisibility()
- }
- }
- } else {
- null
+ val onUiExecutionEnd = Runnable {
+ if (immediately) {
+ updateHostVisibility()
}
+ }
addOrUpdatePlayer(key, oldKey, data, onUiExecutionEnd)
-
val canRemove = data.isPlaying?.let { !it } ?: data.isClearable && !data.active
if (canRemove && !Utils.useMediaResumption(context)) {
// This media control is both paused and timed out, and the resumption
@@ -490,8 +486,14 @@
private fun inflateSettingsButton() {
val settings =
- LayoutInflater.from(context)
- .inflate(R.layout.media_carousel_settings_button, mediaFrame, false) as ImageView
+ if (Flags.mediaControlsUiUpdate()) {
+ LayoutInflater.from(context)
+ .inflate(R.layout.media_carousel_settings_button, mediaFrame, false)
+ as ViewGroup
+ } else {
+ LayoutInflater.from(context)
+ .inflate(R.layout.media_carousel_settings_button_legacy, mediaFrame, false)
+ }
if (this::settingsButton.isInitialized) {
mediaFrame.removeView(settingsButton)
}
@@ -757,7 +759,7 @@
key: String,
oldKey: String?,
data: MediaData,
- onUiExecutionEnd: Runnable? = null,
+ onUiExecutionEnd: Runnable,
): Boolean =
traceSection("MediaCarouselController#addOrUpdatePlayer") {
MediaPlayerData.moveIfExists(oldKey, key)
@@ -765,47 +767,30 @@
val curVisibleMediaKey =
MediaPlayerData.visiblePlayerKeys()
.elementAtOrNull(mediaCarouselScrollHandler.visibleMediaIndex)
- if (mediaControlsUmoInflationInBackground()) {
- if (existingPlayer == null) {
- bgExecutor.execute {
- val mediaViewHolder = createMediaViewHolderInBg()
- // Add the new player in the main thread.
- uiExecutor.execute {
- setupNewPlayer(key, data, curVisibleMediaKey, mediaViewHolder)
- updatePageIndicator()
- mediaCarouselScrollHandler.onPlayersChanged()
- mediaControlChipInteractor.updateMediaControlChipModelLegacy(
- MediaPlayerData.getFirstActiveMediaData()
- )
- mediaFrame.requiresRemeasuring = true
- onUiExecutionEnd?.run()
- }
+ if (existingPlayer == null) {
+ bgExecutor.execute {
+ val mediaViewHolder = createMediaViewHolderInBg()
+ // Add the new player in the main thread.
+ uiExecutor.execute {
+ setupNewPlayer(key, data, curVisibleMediaKey, mediaViewHolder)
+ updatePageIndicator()
+ mediaCarouselScrollHandler.onPlayersChanged()
+ mediaControlChipInteractor.updateMediaControlChipModelLegacy(
+ MediaPlayerData.getFirstActiveMediaData()
+ )
+ mediaFrame.requiresRemeasuring = true
+ onUiExecutionEnd.run()
}
- } else {
- updatePlayer(key, data, curVisibleMediaKey, existingPlayer)
- updatePageIndicator()
- mediaCarouselScrollHandler.onPlayersChanged()
- mediaControlChipInteractor.updateMediaControlChipModelLegacy(
- MediaPlayerData.getFirstActiveMediaData()
- )
- mediaFrame.requiresRemeasuring = true
- onUiExecutionEnd?.run()
}
} else {
- if (existingPlayer == null) {
- val mediaViewHolder =
- MediaViewHolder.create(LayoutInflater.from(context), mediaContent)
- setupNewPlayer(key, data, curVisibleMediaKey, mediaViewHolder)
- } else {
- updatePlayer(key, data, curVisibleMediaKey, existingPlayer)
- }
+ updatePlayer(key, data, curVisibleMediaKey, existingPlayer)
updatePageIndicator()
mediaCarouselScrollHandler.onPlayersChanged()
mediaControlChipInteractor.updateMediaControlChipModelLegacy(
MediaPlayerData.getFirstActiveMediaData()
)
mediaFrame.requiresRemeasuring = true
- onUiExecutionEnd?.run()
+ onUiExecutionEnd.run()
}
return existingPlayer == null
}
@@ -1135,11 +1120,17 @@
// communal for aesthetic and accessibility purposes since the background of
// Glanceable Hub is a dynamic color.
if (desiredLocation == MediaHierarchyManager.LOCATION_COMMUNAL_HUB) {
- settingsButton.setColorFilter(
- context.getColor(com.android.internal.R.color.materialColorOnPrimary)
- )
+ settingsButton
+ .requireViewById<ImageView>(R.id.settings_cog)
+ .setColorFilter(
+ context.getColor(com.android.internal.R.color.materialColorOnPrimary)
+ )
} else {
- settingsButton.setColorFilter(context.getColor(R.color.notification_gear_color))
+ settingsButton
+ .requireViewById<ImageView>(R.id.settings_cog)
+ .setColorFilter(
+ context.getColor(com.android.internal.R.color.materialColorOnSurface)
+ )
}
val shouldCloseGuts =
@@ -1225,6 +1216,17 @@
}
}
+ fun onCarouselVisibleToUser() {
+ if (!enableSuggestedDeviceApi() || !mediaCarouselScrollHandler.visibleToUser) {
+ return
+ }
+ val visibleMediaIndex = mediaCarouselScrollHandler.visibleMediaIndex
+ if (MediaPlayerData.players().size > visibleMediaIndex) {
+ val mediaControlPanel = MediaPlayerData.getMediaControlPanel(visibleMediaIndex)
+ mediaControlPanel?.onSuggestionSpaceVisible()
+ }
+ }
+
@VisibleForTesting
fun onSwipeToDismiss() {
if (SceneContainerFlag.isEnabled) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java
index 71471ee..e9640d3 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java
@@ -23,9 +23,7 @@
import static com.android.systemui.Flags.mediaLockscreenLaunchAnimation;
import static com.android.systemui.media.controls.domain.pipeline.MediaActionsKt.getNotificationActions;
import static com.android.systemui.media.controls.ui.viewmodel.MediaControlViewModel.MEDIA_PLAYER_SCRIM_END_ALPHA;
-import static com.android.systemui.media.controls.ui.viewmodel.MediaControlViewModel.MEDIA_PLAYER_SCRIM_END_ALPHA_LEGACY;
import static com.android.systemui.media.controls.ui.viewmodel.MediaControlViewModel.MEDIA_PLAYER_SCRIM_START_ALPHA;
-import static com.android.systemui.media.controls.ui.viewmodel.MediaControlViewModel.MEDIA_PLAYER_SCRIM_START_ALPHA_LEGACY;
import android.animation.Animator;
import android.animation.AnimatorInflater;
@@ -97,6 +95,7 @@
import com.android.systemui.media.controls.shared.model.MediaData;
import com.android.systemui.media.controls.shared.model.MediaDeviceData;
import com.android.systemui.media.controls.shared.model.SuggestedMediaDeviceData;
+import com.android.systemui.media.controls.shared.model.SuggestionData;
import com.android.systemui.media.controls.ui.animation.AnimationBindHandler;
import com.android.systemui.media.controls.ui.animation.ColorSchemeTransition;
import com.android.systemui.media.controls.ui.animation.MediaColorSchemesKt;
@@ -241,6 +240,7 @@
private TurbulenceNoiseAnimationConfig mTurbulenceNoiseAnimationConfig;
private boolean mWasPlaying = false;
private boolean mButtonClicked = false;
+ @Nullable private Runnable mOnSuggestionSpaceVisibleRunnable = null;
private final PaintDrawCallback mNoiseDrawCallback =
new PaintDrawCallback() {
@@ -630,24 +630,41 @@
Trace.endSection();
}
+ /**
+ * Should be called when the space that holds device suggestions becomes visible to the user.
+ */
+ public void onSuggestionSpaceVisible() {
+ @Nullable Runnable onSuggestionVisibleRunnable = mOnSuggestionSpaceVisibleRunnable;
+ if (onSuggestionVisibleRunnable != null) {
+ onSuggestionVisibleRunnable.run();
+ }
+ }
+
private void bindDeviceSuggestion(@NonNull MediaData data) {
if (!com.android.media.flags.Flags.enableSuggestedDeviceApi()) {
return;
}
View deviceSuggestionButton = mMediaViewHolder.getDeviceSuggestionButton();
TextView deviceText = mMediaViewHolder.getSeamlessText();
- @Nullable SuggestedMediaDeviceData suggestionData = data.getSuggestedDevice();
- if (suggestionData == null || !isValidSuggestion(suggestionData)) {
- deviceSuggestionButton.setVisibility(View.GONE);
- deviceText.setVisibility(View.VISIBLE);
- return;
+ @Nullable SuggestionData suggestionData = data.getSuggestionData();
+ if (suggestionData != null) {
+ mOnSuggestionSpaceVisibleRunnable = suggestionData.getOnSuggestionSpaceVisible();
+ @Nullable
+ SuggestedMediaDeviceData suggestionDeviceData =
+ suggestionData.getSuggestedMediaDeviceData();
+ if (suggestionDeviceData != null && isValidSuggestion(suggestionDeviceData)) {
+ // Don't show the OSw device text if we have a suggestion: just show the icon
+ deviceText.setVisibility(View.GONE);
+ setSuggestionClickListener(suggestionDeviceData);
+ setSuggestionText(suggestionDeviceData);
+ setSuggestionIcon(suggestionDeviceData);
+ deviceSuggestionButton.setVisibility(View.VISIBLE);
+ return;
+ }
}
- // Don't show the OSw device text if we have a suggestion: just show the icon
- deviceText.setVisibility(View.GONE);
- setSuggestionClickListener(suggestionData);
- setSuggestionText(suggestionData);
- setSuggestionIcon(suggestionData);
- deviceSuggestionButton.setVisibility(View.VISIBLE);
+ deviceSuggestionButton.setVisibility(View.GONE);
+ deviceText.setVisibility(View.VISIBLE);
+ return;
}
private boolean isValidSuggestion(SuggestedMediaDeviceData suggestionData) {
@@ -921,7 +938,7 @@
boolean isArtworkBound;
Icon artworkIcon = data.getArtwork();
WallpaperColors wallpaperColors = getWallpaperColor(artworkIcon);
- boolean darkTheme = !Flags.mediaControlsA11yColors();
+ boolean darkTheme = false;
if (wallpaperColors != null) {
mutableColorScheme = new ColorScheme(wallpaperColors, darkTheme, Style.CONTENT);
artwork = addGradientToPlayerAlbum(artworkIcon, mutableColorScheme, finalWidth,
@@ -1036,32 +1053,16 @@
Drawable albumArt = getScaledBackground(artworkIcon, width, height);
GradientDrawable gradient = (GradientDrawable) mContext.getDrawable(
R.drawable.qs_media_scrim).mutate();
- if (Flags.mediaControlsA11yColors()) {
- return setupGradientColorOnDrawable(albumArt, gradient, mutableColorScheme,
- MEDIA_PLAYER_SCRIM_START_ALPHA, MEDIA_PLAYER_SCRIM_END_ALPHA);
- }
return setupGradientColorOnDrawable(albumArt, gradient, mutableColorScheme,
- MEDIA_PLAYER_SCRIM_START_ALPHA_LEGACY, MEDIA_PLAYER_SCRIM_END_ALPHA_LEGACY);
+ MEDIA_PLAYER_SCRIM_START_ALPHA, MEDIA_PLAYER_SCRIM_END_ALPHA);
}
private LayerDrawable setupGradientColorOnDrawable(Drawable albumArt, GradientDrawable gradient,
ColorScheme mutableColorScheme, float startAlpha, float endAlpha) {
- int startColor;
- int endColor;
- if (Flags.mediaControlsA11yColors()) {
- startColor = MediaColorSchemesKt.backgroundFromScheme(mutableColorScheme);
- endColor = startColor;
- } else {
- startColor = MediaColorSchemesKt.backgroundStartFromScheme(mutableColorScheme);
- endColor = MediaColorSchemesKt.backgroundEndFromScheme(mutableColorScheme);
- }
+ int color = MediaColorSchemesKt.backgroundFromScheme(mutableColorScheme);
gradient.setColors(new int[]{
- ColorUtilKt.getColorWithAlpha(
- startColor,
- startAlpha),
- ColorUtilKt.getColorWithAlpha(
- endColor,
- endAlpha),
+ ColorUtilKt.getColorWithAlpha(color, startAlpha),
+ ColorUtilKt.getColorWithAlpha(color, endAlpha),
});
return new LayerDrawable(new Drawable[]{albumArt, gradient});
}
@@ -1271,9 +1272,7 @@
int width = targetView.getWidth();
int height = targetView.getHeight();
Random random = new Random();
- float luminosity = (Flags.mediaControlsA11yColors())
- ? 0.6f
- : TurbulenceNoiseAnimationConfig.DEFAULT_LUMINOSITY_MULTIPLIER;
+ float luminosity = 0.6f;
return new TurbulenceNoiseAnimationConfig(
/* gridCount= */ 2.14f,
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt
index e22ad09..0358da2 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt
@@ -38,7 +38,6 @@
import com.android.app.tracing.traceSection
import com.android.keyguard.KeyguardViewController
import com.android.systemui.Dumpable
-import com.android.systemui.Flags.mediaControlsLockscreenShadeBugFix
import com.android.systemui.communal.ui.viewmodel.CommunalTransitionViewModel
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
@@ -608,13 +607,11 @@
}
}
- if (mediaControlsLockscreenShadeBugFix()) {
- coroutineScope.launch {
- shadeInteractor.shadeExpansion.collect { expansion ->
- if (expansion >= 1f || expansion <= 0f) {
- // Shade has fully expanded or collapsed: force transition amount update
- setTransitionToFullShadeAmount(expansion)
- }
+ coroutineScope.launch {
+ shadeInteractor.shadeExpansion.collect { expansion ->
+ if (expansion >= 1f || expansion <= 0f) {
+ // Shade has fully expanded or collapsed: force transition amount update
+ setTransitionToFullShadeAmount(expansion)
}
}
}
@@ -1329,8 +1326,11 @@
isGlanceableHubVisibleToUser()
val mediaVisible = qsExpanded || hasActiveMedia
logger.logUserVisibilityChange(shadeVisible, mediaVisible)
- mediaCarouselController.mediaCarouselScrollHandler.visibleToUser =
- shadeVisible && mediaVisible
+ val carouselVisible = shadeVisible && mediaVisible
+ mediaCarouselController.mediaCarouselScrollHandler.visibleToUser = carouselVisible
+ if (carouselVisible) {
+ mediaCarouselController.onCarouselVisibleToUser()
+ }
}
private fun isLockScreenVisibleToUser(): Boolean {
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHostStatesManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHostStatesManager.kt
index 3b8ef6a..ae1bcd4 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHostStatesManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHostStatesManager.kt
@@ -18,7 +18,6 @@
import com.android.app.tracing.traceSection
import com.android.systemui.Dumpable
-import com.android.systemui.Flags.mediaControlsUmoInflationInBackground
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dump.DumpManager
import com.android.systemui.media.controls.ui.view.MediaHostState
@@ -99,13 +98,9 @@
}
}
}
- if (mediaControlsUmoInflationInBackground()) {
- // Set carousel size if result measurements changed. This avoids setting carousel
- // size when this method gets called before the addition of media view controllers
- if (!carouselSizes.contains(location) || changed) {
- carouselSizes[location] = result
- }
- } else {
+ // Set carousel size if result measurements changed. This avoids setting carousel
+ // size when this method gets called before the addition of media view controllers
+ if (!carouselSizes.contains(location) || changed) {
carouselSizes[location] = result
}
return carouselSizes[location] ?: result
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt
index 23aff0f..676e913 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt
@@ -1202,12 +1202,7 @@
val width = targetView.width
val height = targetView.height
val random = Random()
- val luminosity =
- if (Flags.mediaControlsA11yColors()) {
- 0.6f
- } else {
- TurbulenceNoiseAnimationConfig.DEFAULT_LUMINOSITY_MULTIPLIER
- }
+ val luminosity = 0.6f
return TurbulenceNoiseAnimationConfig(
gridCount = 2.14f,
luminosity,
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/util/MediaArtworkHelper.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/util/MediaArtworkHelper.kt
index 5570325..b5b48e4 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/util/MediaArtworkHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/util/MediaArtworkHelper.kt
@@ -25,10 +25,7 @@
import android.graphics.drawable.Icon
import android.graphics.drawable.LayerDrawable
import android.util.Log
-import com.android.systemui.Flags
-import com.android.systemui.media.controls.ui.animation.backgroundEndFromScheme
import com.android.systemui.media.controls.ui.animation.backgroundFromScheme
-import com.android.systemui.media.controls.ui.animation.backgroundStartFromScheme
import com.android.systemui.monet.ColorScheme
import com.android.systemui.monet.Style
import com.android.systemui.util.getColorWithAlpha
@@ -91,23 +88,9 @@
startAlpha: Float,
endAlpha: Float,
): LayerDrawable {
- val startColor =
- if (Flags.mediaControlsA11yColors()) {
- backgroundFromScheme(colorScheme)
- } else {
- backgroundStartFromScheme(colorScheme)
- }
- val endColor =
- if (Flags.mediaControlsA11yColors()) {
- startColor
- } else {
- backgroundEndFromScheme(colorScheme)
- }
+ val color = backgroundFromScheme(colorScheme)
gradient.colors =
- intArrayOf(
- getColorWithAlpha(startColor, startAlpha),
- getColorWithAlpha(endColor, endAlpha),
- )
+ intArrayOf(getColorWithAlpha(color, startAlpha), getColorWithAlpha(color, endAlpha))
return LayerDrawable(arrayOf(albumArt, gradient))
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/GutsViewHolder.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/GutsViewHolder.kt
index 05f2880..b4657ef 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/GutsViewHolder.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/GutsViewHolder.kt
@@ -22,12 +22,8 @@
import android.view.ViewGroup
import android.widget.ImageButton
import android.widget.TextView
-import com.android.systemui.Flags
-import com.android.systemui.media.controls.ui.animation.accentPrimaryFromScheme
import com.android.systemui.media.controls.ui.animation.onPrimaryFromScheme
import com.android.systemui.media.controls.ui.animation.primaryFromScheme
-import com.android.systemui.media.controls.ui.animation.surfaceFromScheme
-import com.android.systemui.media.controls.ui.animation.textPrimaryFromScheme
import com.android.systemui.monet.ColorScheme
import com.android.systemui.res.R
@@ -44,8 +40,7 @@
val settings: ImageButton = itemView.requireViewById(R.id.settings)
private var isDismissible: Boolean = true
- // TODO(media_controls_a11y_colors): make private
- var colorScheme: ColorScheme? = null
+ private var colorScheme: ColorScheme? = null
private var textColorFixed: Int? = null
/** Marquees the main text of the guts menu. */
@@ -70,15 +65,9 @@
fun setColors(scheme: ColorScheme) {
colorScheme = scheme
- if (Flags.mediaControlsA11yColors()) {
- textColorFixed?.let { setTextColor(it) }
- setPrimaryColor(primaryFromScheme(scheme))
- setOnPrimaryColor(onPrimaryFromScheme(scheme))
- } else {
- setSurfaceColor(surfaceFromScheme(scheme))
- setTextPrimaryColor(textPrimaryFromScheme(scheme))
- setAccentPrimaryColor(accentPrimaryFromScheme(scheme))
- }
+ textColorFixed?.let { setTextColor(it) }
+ setPrimaryColor(primaryFromScheme(scheme))
+ setOnPrimaryColor(onPrimaryFromScheme(scheme))
}
private fun setPrimaryColor(color: Int) {
@@ -104,34 +93,6 @@
}
}
- /** Sets the surface color on all guts views that use it. */
- @Deprecated("Remove with media_controls_a11y_colors")
- fun setSurfaceColor(surfaceColor: Int) {
- dismissText.setTextColor(surfaceColor)
- if (!isDismissible) {
- cancelText.setTextColor(surfaceColor)
- }
- }
-
- /** Sets the primary accent color on all guts views that use it. */
- @Deprecated("Remove with media_controls_a11y_colors")
- fun setAccentPrimaryColor(accentPrimary: Int) {
- val accentColorList = ColorStateList.valueOf(accentPrimary)
- settings.imageTintList = accentColorList
- cancelText.backgroundTintList = accentColorList
- dismissText.backgroundTintList = accentColorList
- }
-
- /** Sets the primary text color on all guts views that use it. */
- @Deprecated("Remove with media_controls_a11y_colors")
- fun setTextPrimaryColor(textPrimary: Int) {
- val textColorList = ColorStateList.valueOf(textPrimary)
- gutsText.setTextColor(textColorList)
- if (isDismissible) {
- cancelText.setTextColor(textColorList)
- }
- }
-
companion object {
val ids = setOf(R.id.remove_text, R.id.cancel, R.id.dismiss, R.id.settings)
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaCarouselScrollHandler.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaCarouselScrollHandler.kt
index 68865d6..19a2ce9 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaCarouselScrollHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaCarouselScrollHandler.kt
@@ -64,6 +64,7 @@
private var seekBarUpdateListener: (visibleToUser: Boolean) -> Unit,
private val closeGuts: (immediate: Boolean) -> Unit,
private val falsingManager: FalsingManager,
+ private val onCarouselVisibleToUser: () -> Unit,
private val logger: MediaUiEventLogger,
) {
/** Trace state logger for media carousel visibility */
@@ -165,7 +166,8 @@
}
/** A listener that is invoked when the scrolling changes to update player visibilities */
- private val scrollChangedListener =
+ @VisibleForTesting
+ val scrollChangedListener =
object : View.OnScrollChangeListener {
override fun onScrollChange(
v: View?,
@@ -544,6 +546,7 @@
val visible = (i == visibleMediaIndex) || ((i == (visibleMediaIndex + 1)) && scrolledIn)
view.visibility = if (visible) View.VISIBLE else View.INVISIBLE
}
+ onCarouselVisibleToUser()
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaHost.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaHost.kt
index 7bc37ff..1e06fdc 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaHost.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaHost.kt
@@ -20,9 +20,7 @@
import android.util.ArraySet
import android.view.View
import android.view.View.OnAttachStateChangeListener
-import com.android.systemui.Flags.mediaControlsUmoInflationInBackground
import com.android.systemui.media.controls.domain.pipeline.MediaDataManager
-import com.android.systemui.media.controls.shared.model.MediaData
import com.android.systemui.media.controls.ui.controller.MediaCarouselController
import com.android.systemui.media.controls.ui.controller.MediaCarouselControllerLogger
import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager
@@ -87,19 +85,6 @@
private val listener =
object : MediaDataManager.Listener {
- override fun onMediaDataLoaded(
- key: String,
- oldKey: String?,
- data: MediaData,
- immediately: Boolean,
- ) {
- if (mediaControlsUmoInflationInBackground()) return
-
- if (immediately) {
- updateViewVisibility()
- }
- }
-
override fun onMediaDataRemoved(key: String, userInitiated: Boolean) {
updateViewVisibility()
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModel.kt
index 31cfb84..6f2d43a0 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModel.kt
@@ -406,10 +406,6 @@
)
const val TURBULENCE_NOISE_PLAY_MS_DURATION = 7500L
- @Deprecated("Remove with media_controls_a11y_colors flag")
- const val MEDIA_PLAYER_SCRIM_START_ALPHA_LEGACY = 0.25f
- @Deprecated("Remove with media_controls_a11y_colors flag")
- const val MEDIA_PLAYER_SCRIM_END_ALPHA_LEGACY = 1.0f
const val MEDIA_PLAYER_SCRIM_START_ALPHA = 0.65f
const val MEDIA_PLAYER_SCRIM_END_ALPHA = 0.75f
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
index 662a9c7..2a8f893 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
@@ -27,6 +27,7 @@
import com.android.systemui.media.controls.ui.controller.MediaHostStatesManager;
import com.android.systemui.media.controls.ui.view.MediaHost;
import com.android.systemui.media.dream.dagger.MediaComplicationComponent;
+import com.android.systemui.media.remedia.data.MediaDataModule;
import com.android.systemui.media.taptotransfer.receiver.MediaTttReceiverLogBuffer;
import com.android.systemui.media.taptotransfer.sender.MediaTttSenderLogBuffer;
@@ -38,7 +39,8 @@
/** Dagger module for the media package. */
@Module(
includes = {
- MediaDomainModule.class
+ MediaDomainModule.class,
+ MediaDataModule.class,
},
subcomponents = {
MediaComplicationComponent.class,
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaSwitchingController.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaSwitchingController.java
index 5473136..87e581c 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaSwitchingController.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaSwitchingController.java
@@ -1016,6 +1016,11 @@
return mLocalMediaManager.getSessionName();
}
+ @RoutingSessionInfo.ReleaseType
+ int getSessionReleaseType() {
+ return mLocalMediaManager.getSessionReleaseType();
+ }
+
void releaseSession() {
mMetricLogger.logInteractionStopCasting();
mLocalMediaManager.releaseSession();
diff --git a/packages/SystemUI/src/com/android/systemui/media/remedia/data/MediaDataModule.kt b/packages/SystemUI/src/com/android/systemui/media/remedia/data/MediaDataModule.kt
new file mode 100644
index 0000000..8544776
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/remedia/data/MediaDataModule.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.media.remedia.data
+
+import com.android.systemui.Flags
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.media.controls.data.repository.MediaFilterRepository
+import com.android.systemui.media.remedia.data.repository.MediaPipelineRepository
+import com.android.systemui.media.remedia.data.repository.MediaRepositoryImpl
+import dagger.Module
+import dagger.Provides
+import javax.inject.Provider
+
+/** Dagger module for injecting media controls data interfaces. */
+@Module
+interface MediaDataModule {
+
+ companion object {
+ @Provides
+ @SysUISingleton
+ fun providesMediaPipelineRepository(
+ oldProvider: Provider<MediaFilterRepository>,
+ newProvider: Provider<MediaRepositoryImpl>,
+ ): MediaPipelineRepository {
+ return if (Flags.mediaControlsInCompose()) {
+ newProvider.get()
+ } else {
+ oldProvider.get()
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/remedia/data/model/MediaDataModel.kt b/packages/SystemUI/src/com/android/systemui/media/remedia/data/model/MediaDataModel.kt
index bc11423..04c1e3c 100644
--- a/packages/SystemUI/src/com/android/systemui/media/remedia/data/model/MediaDataModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/remedia/data/model/MediaDataModel.kt
@@ -33,7 +33,7 @@
/** Package name of the app that's posting the media, used for logging. */
val packageName: String,
val appName: String,
- val appIcon: Icon,
+ val appIcon: Icon?,
val background: Icon?,
val title: String,
val subtitle: String,
@@ -43,9 +43,9 @@
* Semantic actions buttons, based on the PlaybackState of the media session. If present, these
* actions will be preferred in the UI over [notificationActions]
*/
- val playbackStateActions: MediaButton,
+ val playbackStateActions: MediaButton?,
/** Where the media is playing: phone, headphones, ear buds, remote session. */
- val outputDevice: MediaDeviceData,
+ val outputDevice: MediaDeviceData?,
/** Action to perform when the media player is tapped. */
val clickIntent: PendingIntent?,
val controller: MediaController,
diff --git a/packages/SystemUI/src/com/android/systemui/media/remedia/data/repository/MediaPipelineRepository.kt b/packages/SystemUI/src/com/android/systemui/media/remedia/data/repository/MediaPipelineRepository.kt
new file mode 100644
index 0000000..52f373a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/remedia/data/repository/MediaPipelineRepository.kt
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.media.remedia.data.repository
+
+import com.android.internal.logging.InstanceId
+import com.android.systemui.media.controls.data.model.MediaSortKeyModel
+import com.android.systemui.media.controls.shared.model.MediaData
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
+
+/** An abstract repository class that holds fields and functions called by media pipeline. */
+abstract class MediaPipelineRepository {
+
+ protected val mutableUserEntries: MutableStateFlow<Map<InstanceId, MediaData>> =
+ MutableStateFlow(LinkedHashMap())
+ val currentUserEntries: StateFlow<Map<InstanceId, MediaData>> = mutableUserEntries.asStateFlow()
+
+ private val mutableAllEntries: MutableStateFlow<Map<String, MediaData>> =
+ MutableStateFlow(LinkedHashMap())
+ val allMediaEntries: StateFlow<Map<String, MediaData>> = mutableAllEntries.asStateFlow()
+
+ protected val comparator =
+ compareByDescending<MediaSortKeyModel> {
+ it.isPlaying == true && it.playbackLocation == MediaData.PLAYBACK_LOCAL
+ }
+ .thenByDescending {
+ it.isPlaying == true && it.playbackLocation == MediaData.PLAYBACK_CAST_LOCAL
+ }
+ .thenByDescending { it.active }
+ .thenByDescending { !it.isResume }
+ .thenByDescending { it.playbackLocation != MediaData.PLAYBACK_CAST_REMOTE }
+ .thenByDescending { it.lastActive }
+ .thenByDescending { it.updateTime }
+ .thenByDescending { it.notificationKey }
+
+ fun addMediaEntry(key: String, data: MediaData) {
+ val entries = LinkedHashMap<String, MediaData>(mutableAllEntries.value)
+ entries[key] = data
+ mutableAllEntries.value = entries
+ }
+
+ /**
+ * Removes the media entry corresponding to the given [key].
+ *
+ * @return media data if an entry is actually removed, `null` otherwise.
+ */
+ fun removeMediaEntry(key: String): MediaData? {
+ val entries = LinkedHashMap<String, MediaData>(mutableAllEntries.value)
+ val mediaData = entries.remove(key)
+ mutableAllEntries.value = entries
+ return mediaData
+ }
+
+ /** @return whether the added media data already exists. */
+ open fun addCurrentUserMediaEntry(data: MediaData): Boolean {
+ val entries = LinkedHashMap<InstanceId, MediaData>(mutableUserEntries.value)
+ val update = mutableUserEntries.value.containsKey(data.instanceId)
+ entries[data.instanceId] = data
+ mutableUserEntries.value = entries
+ return update
+ }
+
+ /**
+ * Removes current user media entry given the corresponding key.
+ *
+ * @return media data if an entry is actually removed, `null` otherwise.
+ */
+ open fun removeCurrentUserMediaEntry(key: InstanceId): MediaData? {
+ val entries = LinkedHashMap<InstanceId, MediaData>(mutableUserEntries.value)
+ val mediaData = entries.remove(key)
+ mutableUserEntries.value = entries
+ return mediaData
+ }
+
+ /**
+ * Removes current user media entry given a key and media data.
+ *
+ * @return true if media data is removed, false otherwise.
+ */
+ open fun removeCurrentUserMediaEntry(key: InstanceId, data: MediaData): Boolean {
+ val entries = LinkedHashMap<InstanceId, MediaData>(mutableUserEntries.value)
+ val succeed = entries.remove(key, data)
+ if (!succeed) {
+ return false
+ }
+ mutableUserEntries.value = entries
+ return true
+ }
+
+ abstract fun clearCurrentUserMedia()
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/remedia/data/repository/MediaRepository.kt b/packages/SystemUI/src/com/android/systemui/media/remedia/data/repository/MediaRepository.kt
index e324545..c270ae1 100644
--- a/packages/SystemUI/src/com/android/systemui/media/remedia/data/repository/MediaRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/remedia/data/repository/MediaRepository.kt
@@ -16,36 +16,164 @@
package com.android.systemui.media.remedia.data.repository
+import android.content.Context
+import android.media.session.MediaController
import com.android.internal.logging.InstanceId
+import com.android.systemui.common.shared.model.ContentDescription
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.media.controls.data.model.MediaSortKeyModel
import com.android.systemui.media.controls.shared.model.MediaData
import com.android.systemui.media.remedia.data.model.MediaDataModel
+import com.android.systemui.util.time.SystemClock
+import java.util.TreeMap
+import javax.inject.Inject
+import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
+/** A repository that holds the state of current media on the device. */
interface MediaRepository {
/** Current sorted media sessions. */
val currentMedia: StateFlow<List<MediaDataModel>>
- fun addMediaEntry(key: String, data: MediaData)
-
- /**
- * Removes the media entry corresponding to the given [key].
- *
- * @return media data if an entry is actually removed, `null` otherwise.
- */
- fun removeMediaEntry(key: String): MediaData?
-
- /** @return whether the added media data already exists. */
- fun addCurrentUserMediaEntry(data: MediaData): Boolean
-
- /**
- * Removes current user media entry given the corresponding [key].
- *
- * @return media data if an entry is actually removed, `null` otherwise.
- */
- fun removeCurrentUserMediaEntry(key: InstanceId): MediaData?
-
- fun clearCurrentUserMedia()
-
/** Seek to [to], in milliseconds on the media session with the given [sessionKey]. */
fun seek(sessionKey: InstanceId, to: Long)
+
+ /** Reorders media list when media is not visible to user */
+ fun reorderMedia()
+}
+
+@SysUISingleton
+class MediaRepositoryImpl
+@Inject
+constructor(@Application private val context: Context, private val systemClock: SystemClock) :
+ MediaRepository, MediaPipelineRepository() {
+
+ private val mutableCurrentMedia: MutableStateFlow<List<MediaDataModel>> =
+ MutableStateFlow(mutableListOf())
+ override val currentMedia: StateFlow<List<MediaDataModel>> = mutableCurrentMedia.asStateFlow()
+
+ private var sortedMedia = TreeMap<MediaSortKeyModel, MediaDataModel>(comparator)
+
+ override fun addCurrentUserMediaEntry(data: MediaData): Boolean {
+ return super.addCurrentUserMediaEntry(data).also { addToSortedMedia(data) }
+ }
+
+ override fun removeCurrentUserMediaEntry(key: InstanceId): MediaData? {
+ return super.removeCurrentUserMediaEntry(key)?.also { removeFromSortedMedia(it) }
+ }
+
+ override fun removeCurrentUserMediaEntry(key: InstanceId, data: MediaData): Boolean {
+ return super.removeCurrentUserMediaEntry(key, data).also {
+ if (it) {
+ removeFromSortedMedia(data)
+ }
+ }
+ }
+
+ override fun clearCurrentUserMedia() {
+ val userEntries = LinkedHashMap<InstanceId, MediaData>(mutableUserEntries.value)
+ mutableUserEntries.value = LinkedHashMap()
+ userEntries.forEach { removeFromSortedMedia(it.value) }
+ }
+
+ override fun seek(sessionKey: InstanceId, to: Long) {
+ mutableCurrentMedia.value
+ .first { sessionKey == it.instanceId }
+ .controller
+ .transportControls
+ .seekTo(to)
+ }
+
+ override fun reorderMedia() {
+ mutableCurrentMedia.value = sortedMedia.values.toList()
+ }
+
+ private fun addToSortedMedia(data: MediaData) {
+ val sortedMap = TreeMap<MediaSortKeyModel, MediaDataModel>(comparator)
+ val currentModel = sortedMedia.values.find { it.instanceId == data.instanceId }
+
+ sortedMap.putAll(
+ sortedMedia.filter { (keyModel, _) -> keyModel.instanceId != data.instanceId }
+ )
+
+ mutableUserEntries.value[data.instanceId]?.let { mediaData ->
+ with(mediaData) {
+ val sortKey =
+ MediaSortKeyModel(
+ isPlaying,
+ playbackLocation,
+ active,
+ resumption,
+ lastActive,
+ notificationKey,
+ systemClock.currentTimeMillis(),
+ instanceId,
+ )
+ val controller =
+ if (currentModel != null && currentModel.controller.sessionToken == token) {
+ currentModel.controller
+ } else {
+ MediaController(context, token!!)
+ }
+ val mediaModel = toDataModel(controller)
+ sortedMap[sortKey] = mediaModel
+
+ var isNewToCurrentMedia = true
+ val currentList =
+ mutableListOf<MediaDataModel>().apply { addAll(mutableCurrentMedia.value) }
+ currentList.forEachIndexed { index, mediaDataModel ->
+ if (mediaDataModel.instanceId == data.instanceId) {
+ // When loading an update for an existing media control.
+ isNewToCurrentMedia = false
+ if (mediaDataModel != mediaModel) {
+ // Update media model if changed.
+ currentList[index] = mediaModel
+ }
+ }
+ }
+ if (isNewToCurrentMedia && active) {
+ mutableCurrentMedia.value = sortedMap.values.toList()
+ } else {
+ mutableCurrentMedia.value = currentList
+ }
+
+ sortedMedia = sortedMap
+ }
+ }
+ }
+
+ private fun removeFromSortedMedia(data: MediaData) {
+ mutableCurrentMedia.value =
+ mutableCurrentMedia.value.filter { model -> data.instanceId != model.instanceId }
+ sortedMedia =
+ TreeMap(sortedMedia.filter { (keyModel, _) -> keyModel.instanceId != data.instanceId })
+ }
+
+ private fun MediaData.toDataModel(controller: MediaController): MediaDataModel {
+ val icon = appIcon?.loadDrawable(context)
+ val background = artwork?.loadDrawable(context)
+ return MediaDataModel(
+ instanceId = instanceId,
+ appUid = appUid,
+ packageName = packageName,
+ appName = app.toString(),
+ appIcon = icon?.let { Icon.Loaded(it, ContentDescription.Loaded(app)) },
+ background = background?.let { Icon.Loaded(background, null) },
+ title = song.toString(),
+ subtitle = artist.toString(),
+ notificationActions = actions,
+ playbackStateActions = semanticActions,
+ outputDevice = device,
+ clickIntent = clickIntent,
+ controller = controller,
+ canBeDismissed = isClearable,
+ isActive = active,
+ isResume = resumption,
+ resumeAction = resumeAction,
+ isExplicit = isExplicit,
+ )
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepository.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepository.kt
index d503fb7..d5d8b89 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepository.kt
@@ -24,6 +24,7 @@
import android.media.projection.StopReason
import android.os.Handler
import android.view.ContentRecordingSession
+import android.view.ContentRecordingSession.RECORD_CONTENT_BELOW_OVERLAY
import android.view.ContentRecordingSession.RECORD_CONTENT_DISPLAY
import com.android.systemui.Flags
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
@@ -158,9 +159,6 @@
.mapLatest {
when (it) {
is CallbackEvent.OnStart -> {
- if (!Flags.statusBarShowAudioOnlyProjectionChip()) {
- return@mapLatest MediaProjectionState.NotProjecting
- }
// It's possible for a projection to be audio-only, in which case `OnStart`
// will occur but `OnRecordingSessionSet` will not. We should still consider
// us to be projecting even if only audio is projecting. See b/373308507.
@@ -213,7 +211,11 @@
displayManager.getDisplay(session.virtualDisplayId)?.name
}
- if (session.contentToRecord == RECORD_CONTENT_DISPLAY || session.tokenToRecord == null) {
+ if (
+ session.contentToRecord == RECORD_CONTENT_DISPLAY ||
+ session.contentToRecord == RECORD_CONTENT_BELOW_OVERLAY ||
+ session.tokenToRecord == null
+ ) {
return MediaProjectionState.Projecting.EntireScreen(hostPackage, hostDeviceName)
}
val matchingTask =
diff --git a/packages/SystemUI/src/com/android/systemui/model/SceneContainerPlugin.kt b/packages/SystemUI/src/com/android/systemui/model/SceneContainerPlugin.kt
index 7b3f4c6..1ad12a1 100644
--- a/packages/SystemUI/src/com/android/systemui/model/SceneContainerPlugin.kt
+++ b/packages/SystemUI/src/com/android/systemui/model/SceneContainerPlugin.kt
@@ -43,22 +43,33 @@
* A plugin for [SysUiState] that provides overrides for certain state flags that must be pulled
* from the scene framework when that framework is enabled.
*/
+interface SceneContainerPlugin {
+ /**
+ * Returns an override value for the given [flag] or `null` if the scene framework isn't enabled
+ * or if the flag value doesn't need to be overridden.
+ */
+ fun flagValueOverride(@SystemUiStateFlags flag: Long, displayId: Int): Boolean?
+
+ data class SceneContainerPluginState(
+ val scene: SceneKey,
+ val overlays: Set<OverlayKey>,
+ val invisibleDueToOcclusion: Boolean,
+ val isVisible: Boolean,
+ )
+}
+
@SysUISingleton
-class SceneContainerPlugin
+class SceneContainerPluginImpl
@Inject
constructor(
private val sceneInteractor: Lazy<SceneInteractor>,
private val occlusionInteractor: Lazy<SceneContainerOcclusionInteractor>,
private val shadeDisplaysRepository: Lazy<ShadeDisplaysRepository>,
-) {
+) : SceneContainerPlugin {
private val shadeDisplayId: StateFlow<Int> by lazy { shadeDisplaysRepository.get().displayId }
- /**
- * Returns an override value for the given [flag] or `null` if the scene framework isn't enabled
- * or if the flag value doesn't need to be overridden.
- */
- fun flagValueOverride(@SystemUiStateFlags flag: Long, displayId: Int): Boolean? {
+ override fun flagValueOverride(@SystemUiStateFlags flag: Long, displayId: Int): Boolean? {
if (!SceneContainerFlag.isEnabled) {
return null
}
@@ -76,7 +87,7 @@
val invisibleDueToOcclusion = occlusionInteractor.get().invisibleDueToOcclusion.value
return idleTransitionStateOrNull?.let { idleState ->
EvaluatorByFlag[flag]?.invoke(
- SceneContainerPluginState(
+ SceneContainerPlugin.SceneContainerPluginState(
scene = idleState.currentScene,
overlays = idleState.currentOverlays,
isVisible = sceneInteractor.get().isVisible.value,
@@ -98,7 +109,7 @@
* to be overridden by the scene framework.
*/
val EvaluatorByFlag =
- mapOf<Long, (SceneContainerPluginState) -> Boolean>(
+ mapOf<Long, (SceneContainerPlugin.SceneContainerPluginState) -> Boolean>(
SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE to
{
when {
@@ -139,11 +150,4 @@
SYSUI_STATE_COMMUNAL_HUB_SHOWING to { it.isVisible && it.scene == Scenes.Communal },
)
}
-
- data class SceneContainerPluginState(
- val scene: SceneKey,
- val overlays: Set<OverlayKey>,
- val invisibleDueToOcclusion: Boolean,
- val isVisible: Boolean,
- )
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
index 6eeeee5..d6f8194 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
@@ -22,11 +22,11 @@
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_EXCLUDE_FROM_SCREEN_MAGNIFICATION;
import static com.android.systemui.Flags.edgebackGestureHandlerGetRunningTasksBackground;
-import static com.android.window.flags.Flags.enableMultidisplayTrackpadBackGesture;
import static com.android.systemui.Flags.predictiveBackDelayWmTransition;
import static com.android.systemui.classifier.Classifier.BACK_GESTURE;
import static com.android.systemui.navigationbar.gestural.Utilities.isTrackpadThreeFingerSwipe;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_TOUCHPAD_GESTURES_DISABLED;
+import static com.android.window.flags.Flags.enableMultidisplayTrackpadBackGesture;
import static com.android.wm.shell.windowdecor.DragResizeWindowGeometry.isEdgeResizePermitted;
import static java.util.stream.Collectors.joining;
@@ -691,9 +691,18 @@
public void onDisplayAddSystemDecorations(int displayId) {
if (enableMultidisplayTrackpadBackGesture() && mIsEnabled) {
mUiThreadContext.runWithScissors(() -> {
+ if (displayId == Display.DEFAULT_DISPLAY) {
+ Log.w(TAG, "onDisplayAddSystemDecorations called for main display");
+ return;
+ }
+ Display display = mDisplayManager.getDisplay(displayId);
+ if (display == null) {
+ Log.w(TAG, "createDisplayBackGestureHandler: can't find display");
+ return;
+ }
removeAndDisposeDisplayResource(displayId);
mDisplayBackGestureHandlers.put(displayId,
- createDisplayBackGestureHandler(displayId));
+ createDisplayBackGestureHandler(display));
});
}
}
@@ -709,12 +718,11 @@
}
}
- private DisplayBackGestureHandler createDisplayBackGestureHandler(int displayId) {
- Display display = mDisplayManager.getDisplay(displayId);
+ private DisplayBackGestureHandler createDisplayBackGestureHandler(Display display) {
Context windowContext = mContext.createWindowContext(display,
WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL, null);
WindowManager displayWindowManager = mDefaultWindowManager;
- if (displayId != mMainDisplayId) {
+ if (display.getDisplayId() != mMainDisplayId) {
displayWindowManager = windowContext.getSystemService(WindowManager.class);
if (displayWindowManager == null) {
displayWindowManager = mDefaultWindowManager;
@@ -815,9 +823,8 @@
if (enableMultidisplayTrackpadBackGesture()) {
// Registers input event receiver and adds a nav bar panel window
for (Display display : mDisplayManager.getDisplays()) {
- int displayId = display.getDisplayId();
- mDisplayBackGestureHandlers.put(displayId,
- createDisplayBackGestureHandler(displayId));
+ mDisplayBackGestureHandlers.put(display.getDisplayId(),
+ createDisplayBackGestureHandler(display));
}
} else {
// Register input event receiver
@@ -1344,7 +1351,8 @@
/* touchX = */ event.getX(),
/* touchY = */ event.getY(),
/* keyAction = */ event.getActionMasked(),
- /* swipeEdge = */ mIsOnLeftEdge ? BackEvent.EDGE_LEFT : BackEvent.EDGE_RIGHT);
+ /* swipeEdge = */ mIsOnLeftEdge ? BackEvent.EDGE_LEFT : BackEvent.EDGE_RIGHT,
+ event.getDisplayId());
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.kt b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.kt
index 9ff0915..a7eaf35 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.kt
@@ -22,12 +22,14 @@
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
+import androidx.compose.ui.Modifier
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.compose.theme.PlatformTheme
+import com.android.systemui.compose.modifiers.sysUiResTagContainer
import com.android.systemui.people.ui.compose.PeopleScreen
import com.android.systemui.people.ui.viewmodel.PeopleViewModel
import javax.inject.Inject
@@ -62,7 +64,15 @@
}
// Set the content of the activity, using either the View or Compose implementation.
- setContent { PlatformTheme { PeopleScreen(viewModel, onResult = { finishActivity(it) }) } }
+ setContent {
+ PlatformTheme {
+ PeopleScreen(
+ viewModel,
+ onResult = { finishActivity(it) },
+ Modifier.sysUiResTagContainer(),
+ )
+ }
+ }
}
private fun finishActivity(result: PeopleViewModel.Result) {
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt
index 7a4be3f..d66ff22 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt
@@ -23,18 +23,23 @@
import android.view.ViewGroup
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
+import android.view.accessibility.AccessibilityNodeInfo
import android.widget.FrameLayout
import android.widget.ImageView
import android.widget.LinearLayout
import com.android.settingslib.Utils
+import com.android.systemui.Flags
import com.android.systemui.res.R
import com.android.systemui.statusbar.events.BackgroundAnimatableView
+import java.time.Duration
-class OngoingPrivacyChip @JvmOverloads constructor(
+class OngoingPrivacyChip
+@JvmOverloads
+constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttrs: Int = 0,
- defStyleRes: Int = 0
+ defStyleRes: Int = 0,
) : FrameLayout(context, attrs, defStyleAttrs, defStyleRes), BackgroundAnimatableView {
private var configuration: Configuration
@@ -64,15 +69,22 @@
}
/**
- * When animating as a chip in the status bar, we want to animate the width for the container
- * of the privacy items. We have to subtract our own top and left offset because the bounds
- * come to us as absolute on-screen bounds, and `iconsContainer` is laid out relative to the
- * frame layout's bounds.
+ * When animating as a chip in the status bar, we want to animate the width for the container of
+ * the privacy items. We have to subtract our own top and left offset because the bounds come to
+ * us as absolute on-screen bounds, and `iconsContainer` is laid out relative to the frame
+ * layout's bounds.
*/
override fun setBoundsForAnimation(l: Int, t: Int, r: Int, b: Int) {
iconsContainer.setLeftTopRightBottom(l - left, t - top, r - left, b - top)
}
+ override fun onInitializeAccessibilityNodeInfo(info: AccessibilityNodeInfo) {
+ super.onInitializeAccessibilityNodeInfo(info)
+ if (Flags.privacyDotLiveRegion()) {
+ info.setMinDurationBetweenContentChanges(Duration.ofSeconds(10L))
+ }
+ }
+
// Should only be called if the builder icons or app changed
private fun updateView(builder: PrivacyChipBuilder) {
fun setIcons(chipBuilder: PrivacyChipBuilder, iconsContainer: ViewGroup) {
@@ -80,10 +92,11 @@
chipBuilder.generateIcons().forEachIndexed { i, it ->
it.mutate()
it.setTint(iconColor)
- val image = ImageView(context).apply {
- setImageDrawable(it)
- scaleType = ImageView.ScaleType.CENTER_INSIDE
- }
+ val image =
+ ImageView(context).apply {
+ setImageDrawable(it)
+ scaleType = ImageView.ScaleType.CENTER_INSIDE
+ }
iconsContainer.addView(image, iconSize, iconSize)
if (i != 0) {
val lp = image.layoutParams as MarginLayoutParams
@@ -92,11 +105,16 @@
}
}
}
-
if (!privacyList.isEmpty()) {
+ if (Flags.privacyDotLiveRegion()) {
+ accessibilityLiveRegion = ACCESSIBILITY_LIVE_REGION_POLITE
+ }
generateContentDescription(builder)
setIcons(builder, iconsContainer)
} else {
+ if (Flags.privacyDotLiveRegion()) {
+ accessibilityLiveRegion = ACCESSIBILITY_LIVE_REGION_NONE
+ }
iconsContainer.removeAllViews()
}
requestLayout()
@@ -104,8 +122,8 @@
private fun generateContentDescription(builder: PrivacyChipBuilder) {
val typesText = builder.joinTypes()
- contentDescription = context.getString(
- R.string.ongoing_privacy_chip_content_multiple_apps, typesText)
+ contentDescription =
+ context.getString(R.string.ongoing_privacy_chip_content_multiple_apps, typesText)
}
override fun onConfigurationChanged(newConfig: Configuration?) {
@@ -120,19 +138,17 @@
}
private fun updateResources() {
- iconMargin = context.resources
- .getDimensionPixelSize(R.dimen.ongoing_appops_chip_icon_margin)
- iconSize = context.resources
- .getDimensionPixelSize(R.dimen.ongoing_appops_chip_icon_size)
+ iconMargin =
+ context.resources.getDimensionPixelSize(R.dimen.ongoing_appops_chip_icon_margin)
+ iconSize = context.resources.getDimensionPixelSize(R.dimen.ongoing_appops_chip_icon_size)
iconColor =
- Utils.getColorAttrDefaultColor(context, com.android.internal.R.attr.colorPrimary)
+ Utils.getColorAttrDefaultColor(context, com.android.internal.R.attr.colorPrimary)
- val height = context.resources
- .getDimensionPixelSize(R.dimen.ongoing_appops_chip_height)
- val padding = context.resources
- .getDimensionPixelSize(R.dimen.ongoing_appops_chip_side_padding)
+ val height = context.resources.getDimensionPixelSize(R.dimen.ongoing_appops_chip_height)
+ val padding =
+ context.resources.getDimensionPixelSize(R.dimen.ongoing_appops_chip_side_padding)
iconsContainer.layoutParams.height = height
iconsContainer.setPaddingRelative(padding, 0, padding, 0)
iconsContainer.background = context.getDrawable(R.drawable.statusbar_privacy_chip_bg)
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt b/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt
index 1e608af1..a3e79bd 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt
@@ -55,7 +55,6 @@
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags.TASK_MANAGER_SHOW_USER_VISIBLE_JOBS
import com.android.internal.jank.InteractionJankMonitor
import com.android.systemui.Dumpable
-import com.android.systemui.Flags
import com.android.systemui.animation.DialogCuj
import com.android.systemui.animation.DialogTransitionAnimator
import com.android.systemui.animation.Expandable
@@ -753,10 +752,8 @@
}
// If the app wants to be a good citizen by being stoppable, even if the category it
// belongs to is exempted for background restriction, let it be stoppable by user.
- if (Flags.stoppableFgsSystemApp()) {
- if (isStoppableApp(packageName)) {
- uiControl = UIControl.NORMAL
- }
+ if (isStoppableApp(packageName)) {
+ uiControl = UIControl.NORMAL
}
uiControlInitialized = true
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSUtils.kt b/packages/SystemUI/src/com/android/systemui/qs/QSUtils.kt
index 5482e6d..e2f947f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSUtils.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSUtils.kt
@@ -1,10 +1,12 @@
package com.android.systemui.qs
import android.content.Context
+import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.ComposeView
import androidx.lifecycle.LifecycleOwner
import com.android.compose.theme.PlatformTheme
import com.android.internal.policy.SystemBarUtils
+import com.android.systemui.compose.modifiers.sysUiResTagContainer
import com.android.systemui.qs.footer.ui.compose.FooterActions
import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel
import com.android.systemui.util.LargeScreenUtils.shouldUseLargeScreenShadeHeader
@@ -33,6 +35,14 @@
viewModel: FooterActionsViewModel,
qsVisibilityLifecycleOwner: LifecycleOwner,
) {
- view.setContent { PlatformTheme { FooterActions(viewModel, qsVisibilityLifecycleOwner) } }
+ view.setContent {
+ PlatformTheme {
+ FooterActions(
+ viewModel,
+ qsVisibilityLifecycleOwner,
+ Modifier.sysUiResTagContainer(),
+ )
+ }
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt b/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt
index bf0165b9..6d75048 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt
@@ -115,6 +115,7 @@
import com.android.systemui.Flags.notificationShadeBlur
import com.android.systemui.brightness.ui.compose.BrightnessSliderContainer
import com.android.systemui.brightness.ui.compose.ContainerColors
+import com.android.systemui.compose.modifiers.sysUiResTagContainer
import com.android.systemui.compose.modifiers.sysuiResTag
import com.android.systemui.dump.DumpManager
import com.android.systemui.keyboard.shortcut.ui.composable.InteractionsConfig
@@ -236,11 +237,26 @@
this@repeatWhenAttached.lifecycle
}
)
- setContent { this@QSFragmentCompose.Content() }
+ setContent {
+ this@QSFragmentCompose.Content(Modifier.sysUiResTagContainer())
+ }
}
}
}
+ val canScrollQs =
+ object : CanScrollQs {
+ override fun forward(): Boolean {
+ return (scrollState.canScrollForward && viewModel.isQsFullyExpanded) ||
+ isCustomizing
+ }
+
+ override fun backward(): Boolean {
+ return (scrollState.canScrollBackward && viewModel.isQsFullyExpanded) ||
+ isCustomizing
+ }
+ }
+
val frame =
FrameLayoutTouchPassthrough(
context,
@@ -248,7 +264,7 @@
snapshotFlow { notificationScrimClippingParams.params },
// Only allow scrolling when we are fully expanded. That way, we don't intercept
// swipes in lockscreen (when somehow QS is receiving touches).
- { (scrollState.canScrollForward && viewModel.isQsFullyExpanded) || isCustomizing },
+ canScrollQs,
viewModel::emitMotionEventForFalsingSwipeNested,
)
frame.addView(
@@ -260,7 +276,7 @@
}
@Composable
- private fun Content() {
+ private fun Content(modifier: Modifier = Modifier) {
PlatformTheme(isDarkTheme = if (notificationShadeBlur()) isSystemInDarkTheme() else true) {
ProvideShortcutHelperIndication(interactionsConfig = interactionsConfig()) {
// TODO(b/389985793): Make sure that there is no coroutine work or recompositions
@@ -268,7 +284,8 @@
if (alwaysCompose || viewModel.isQsVisibleAndAnyShadeExpanded) {
Box(
modifier =
- Modifier.thenIf(alwaysCompose) {
+ modifier
+ .thenIf(alwaysCompose) {
Modifier.layout { measurable, constraints ->
measurable.measure(constraints).run {
layout(width, height) {
@@ -344,11 +361,25 @@
)
LaunchedEffect(Unit) {
- synchronizeQsState(
- sceneState,
- viewModel.containerViewModel.editModeViewModel.isEditing,
- snapshotFlow { viewModel.expansionState }.map { it.progress },
- )
+ launch {
+ synchronizeQsState(
+ sceneState,
+ viewModel.containerViewModel.editModeViewModel.isEditing,
+ snapshotFlow { viewModel.expansionState }.map { it.progress },
+ )
+ }
+ if (alwaysCompose) {
+ // Normally, the Edit mode will stop if the composable leaves, but if the shade
+ // is closed, because we are always composed, we don't stop edit mode.
+ launch {
+ snapshotFlow { viewModel.isQsVisibleAndAnyShadeExpanded }
+ .collect {
+ if (!it) {
+ viewModel.containerViewModel.editModeViewModel.stopEditing()
+ }
+ }
+ }
+ }
}
SceneTransitionLayout(state = sceneState, modifier = Modifier.fillMaxSize()) {
@@ -711,9 +742,21 @@
) {
if (viewModel.isQsEnabled) {
Element(ElementKeys.QuickSettingsContent, modifier = Modifier.weight(1f)) {
- DisposableEffect(Unit) {
- lifecycleScope.launch { scrollState.scrollTo(0) }
- onDispose { lifecycleScope.launch { scrollState.scrollTo(0) } }
+ if (alwaysCompose) {
+ // scrollState never changes
+ LaunchedEffect(Unit) {
+ snapshotFlow { viewModel.isQsFullyCollapsed }
+ .collect { collapsed ->
+ if (collapsed) {
+ scrollState.scrollTo(0)
+ }
+ }
+ }
+ } else {
+ DisposableEffect(Unit) {
+ lifecycleScope.launch { scrollState.scrollTo(0) }
+ onDispose { lifecycleScope.launch { scrollState.scrollTo(0) } }
+ }
}
Column(
modifier =
@@ -852,7 +895,8 @@
modifier =
modifier
.fillMaxWidth()
- .padding(horizontal = { QuickSettingsShade.Dimensions.Padding.roundToPx() }),
+ .padding(horizontal = { QuickSettingsShade.Dimensions.Padding.roundToPx() })
+ .padding(top = { viewModel.qqsHeaderHeight }),
)
}
@@ -1061,7 +1105,7 @@
context: Context,
private val clippingEnabledProvider: () -> Boolean,
private val clippingParams: Flow<NotificationScrimClipParams>,
- private val canScrollForwardQs: () -> Boolean,
+ private val canScrollQs: CanScrollQs,
private val emitMotionEventForFalsing: () -> Unit,
) : FrameLayout(context) {
@@ -1137,6 +1181,7 @@
val touchSlop = ViewConfiguration.get(context).scaledTouchSlop
var downY = 0f
+ var downX = 0f
var preventingIntercept = false
override fun onTouchEvent(event: MotionEvent): Boolean {
@@ -1144,11 +1189,11 @@
when (action) {
MotionEvent.ACTION_DOWN -> {
preventingIntercept = false
- if (canScrollVertically(1)) {
+ if (canScrollQs.forward()) {
// If we can scroll down, make sure we're not intercepted by the parent
preventingIntercept = true
parent?.requestDisallowInterceptTouchEvent(true)
- } else if (!canScrollVertically(-1)) {
+ } else if (!canScrollQs.backward()) {
// Don't pass on the touch to the view, because scrolling will unconditionally
// disallow interception even if we can't scroll.
// if a user can't scroll at all, we should never listen to the touch.
@@ -1172,18 +1217,22 @@
MotionEvent.ACTION_DOWN -> {
preventingIntercept = false
// If we can scroll down, make sure none of our parents intercepts us.
- if (canScrollForwardQs()) {
+ if (canScrollQs.forward()) {
preventingIntercept = true
parent?.requestDisallowInterceptTouchEvent(true)
}
downY = ev.y
+ downX = ev.x
}
MotionEvent.ACTION_MOVE -> {
- val y = ev.y.toInt()
- val yDiff: Float = y - downY
- if (yDiff < -touchSlop && !canScrollForwardQs()) {
- // Intercept touches that are overscrolling.
+ val y = ev.y
+ val x = ev.x
+ val yDiff = y - downY
+ val xDiff = x - downX
+ val collapsing = yDiff < -touchSlop && !canScrollQs.forward()
+ val vertical = Math.abs(xDiff) < Math.abs(yDiff)
+ if (collapsing && vertical) {
return true
}
}
@@ -1192,6 +1241,12 @@
}
}
+private interface CanScrollQs {
+ fun forward(): Boolean
+
+ fun backward(): Boolean
+}
+
private fun Modifier.gesturesDisabled(disabled: Boolean) =
if (disabled) {
pointerInput(Unit) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/dagger/PanelsModuleBase.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/dagger/PanelsModuleBase.kt
index 26619a6..6d762a7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/dagger/PanelsModuleBase.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/dagger/PanelsModuleBase.kt
@@ -20,6 +20,8 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.LogBufferFactory
+import com.android.systemui.qs.panels.data.repository.AppIconRepository
+import com.android.systemui.qs.panels.data.repository.AppIconRepositoryImpl
import com.android.systemui.qs.panels.domain.interactor.EditTilesResetInteractor
import com.android.systemui.qs.panels.domain.interactor.SizedTilesResetInteractor
import com.android.systemui.qs.panels.domain.startable.QSPanelsCoreStartable
@@ -60,6 +62,11 @@
@ClassKey(QSPanelsCoreStartable::class)
fun bindQSPanelsCoreStartable(impl: QSPanelsCoreStartable): CoreStartable
+ @Binds
+ fun bindsAppIconRepositoryFactory(
+ impl: AppIconRepositoryImpl.Factory
+ ): AppIconRepository.Factory
+
companion object {
@Provides
@SysUISingleton
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/AppIconRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/AppIconRepository.kt
new file mode 100644
index 0000000..bf33f0d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/AppIconRepository.kt
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.panels.data.repository
+
+import android.content.Context
+import android.content.pm.ApplicationInfo
+import com.android.launcher3.icons.BaseIconFactory
+import com.android.systemui.common.shared.model.ContentDescription
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.common.shared.model.asIcon
+import com.android.systemui.res.R
+import com.android.systemui.shade.ShadeDisplayAware
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+
+interface AppIconRepository {
+ fun loadIcon(applicationInfo: ApplicationInfo): Icon.Loaded?
+
+ interface Factory {
+ fun create(): AppIconRepository
+ }
+}
+
+class AppIconRepositoryImpl
+@AssistedInject
+constructor(@ShadeDisplayAware private val context: Context) : AppIconRepository {
+ private val appIconFactory: BaseIconFactory = buildAppIconFactory()
+
+ override fun loadIcon(applicationInfo: ApplicationInfo): Icon.Loaded? {
+ return applicationInfo.loadUnbadgedIcon(context.packageManager)?.let {
+ appIconFactory
+ .createBadgedIconBitmap(it)
+ .newIcon(context)
+ .asIcon(ContentDescription.Loaded(null))
+ }
+ }
+
+ private fun buildAppIconFactory(): BaseIconFactory {
+ val res = context.resources
+ val iconSize = res.getDimensionPixelSize(R.dimen.qs_edit_mode_app_icon)
+ return BaseIconFactory(context, res.configuration.densityDpi, iconSize)
+ }
+
+ @AssistedFactory
+ interface Factory : AppIconRepository.Factory {
+ override fun create(): AppIconRepositoryImpl
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/IconAndNameCustomRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/IconAndNameCustomRepository.kt
index 2f054b0a..76c8ae5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/IconAndNameCustomRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/IconAndNameCustomRepository.kt
@@ -37,6 +37,7 @@
private val installedTilesComponentRepository: InstalledTilesComponentRepository,
private val userTracker: UserTracker,
@Background private val backgroundContext: CoroutineContext,
+ private val appIconRepositoryFactory: AppIconRepository.Factory,
) {
/**
* Returns a list of the icon/labels for all available (installed and enabled) tile services.
@@ -48,19 +49,33 @@
val installedTiles =
installedTilesComponentRepository.getInstalledTilesServiceInfos(userTracker.userId)
val packageManager = userTracker.userContext.packageManager
+ val appIconRepository = appIconRepositoryFactory.create()
installedTiles
.map {
val tileSpec = TileSpec.create(it.componentName)
val label = it.loadLabel(packageManager)
val icon = it.loadIcon(packageManager)
val appName = it.applicationInfo.loadLabel(packageManager)
+ val category =
+ if (it.applicationInfo.isSystemApp) {
+ TileCategory.PROVIDED_BY_SYSTEM_APP
+ } else {
+ TileCategory.PROVIDED_BY_APP
+ }
+ val appIcon =
+ if (it.applicationInfo.isSystemApp) {
+ null
+ } else {
+ appIconRepository.loadIcon(it.applicationInfo)
+ }
if (icon != null) {
EditTileData(
tileSpec,
Icon.Loaded(icon, ContentDescription.Loaded(label.toString())),
Text.Loaded(label.toString()),
Text.Loaded(appName.toString()),
- TileCategory.PROVIDED_BY_APP,
+ appIcon,
+ category,
)
} else {
null
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/EditTilesListInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/EditTilesListInteractor.kt
index ec0a754..bd3f503 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/EditTilesListInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/EditTilesListInteractor.kt
@@ -54,6 +54,7 @@
),
Text.Resource(config.uiConfig.labelRes),
null,
+ null,
category = config.category,
)
} else {
@@ -65,6 +66,7 @@
),
Text.Loaded(it.spec),
null,
+ null,
category = TileCategory.UNKNOWN,
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/shared/model/EditTileData.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/shared/model/EditTileData.kt
index b153ef7..00b6634 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/shared/model/EditTileData.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/shared/model/EditTileData.kt
@@ -26,6 +26,7 @@
val icon: Icon,
val label: Text,
val appName: Text?,
+ val appIcon: Icon?,
val category: TileCategory,
) {
init {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/DragAndDropState.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/DragAndDropState.kt
index fddca22..566bd3a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/DragAndDropState.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/DragAndDropState.kt
@@ -92,12 +92,6 @@
true
} ?: false
}
-
- override fun onEntered(event: DragAndDropEvent) {
- if (!dragAndDropState.isDraggedCellRemovable) return
-
- dragAndDropState.movedOutOfBounds()
- }
}
}
return dragAndDropTarget(
@@ -131,6 +125,11 @@
dragAndDropState.onDrop()
}
+ override fun onExited(event: DragAndDropEvent) {
+ if (!dragAndDropState.isDraggedCellRemovable) return
+ dragAndDropState.movedOutOfBounds()
+ }
+
override fun onMoved(event: DragAndDropEvent) {
val offset = event.toOffset()
dragAndDropState.onMoved(offset)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/EditTileListState.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/EditTileListState.kt
index 009601c..1a27a76 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/EditTileListState.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/EditTileListState.kt
@@ -62,8 +62,12 @@
initialTiles.toGridCells(initialLargeTiles).toMutableStateList()
val tiles: List<GridCell> = _tiles
+ var largeTilesSpecs: Set<TileSpec> = initialLargeTiles
+ private set
+
/** Update the grid with this new list of tiles and new set of large tileSpecs. */
fun updateTiles(tiles: List<EditTileViewModel>, largeTiles: Set<TileSpec>) {
+ largeTilesSpecs = largeTiles
tiles.toGridCells(largeTiles).let {
_tiles.apply {
clear()
@@ -186,17 +190,6 @@
}
}
- private fun List<TileGridCell>.updateLargeWidth(
- previousWidth: Int,
- newWidth: Int,
- ): List<TileGridCell> {
- return if (previousWidth != newWidth) {
- map { if (!it.isIcon) it.copy(width = newWidth) else it }
- } else {
- this
- }
- }
-
private fun List<EditTileViewModel>.toGridCells(largeTiles: Set<TileSpec>): List<GridCell> {
return map {
SizedTileImpl(it, if (largeTiles.contains(it.tileSpec)) largeTilesSpan else 1)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/CommonTile.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/CommonTile.kt
index 42be71c..7ffc959 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/CommonTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/CommonTile.kt
@@ -58,6 +58,7 @@
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.draw.drawWithContent
+import androidx.compose.ui.draw.paint
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.BlendMode
@@ -66,8 +67,12 @@
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.graphics.ColorProducer
import androidx.compose.ui.graphics.CompositingStrategy
+import androidx.compose.ui.graphics.DefaultAlpha
import androidx.compose.ui.graphics.drawscope.DrawScope
import androidx.compose.ui.graphics.graphicsLayer
+import androidx.compose.ui.graphics.painter.Painter
+import androidx.compose.ui.layout.ContentScale
+import androidx.compose.ui.layout.Layout
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.Role
@@ -85,6 +90,7 @@
import com.android.compose.modifiers.thenIf
import com.android.compose.ui.graphics.painter.rememberDrawablePainter
import com.android.systemui.Flags
+import com.android.systemui.Flags.iconRefresh2025
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.common.ui.compose.Icon
import com.android.systemui.common.ui.compose.load
@@ -291,12 +297,22 @@
}
}
- Image(
- painter = painter,
- contentDescription = icon.contentDescription?.load(),
- colorFilter = ColorFilter.tint(color = animatedColor),
- modifier = iconModifier,
- )
+ if (iconRefresh2025()) {
+ NonClippedImage(
+ painter = painter,
+ contentDescription = icon.contentDescription?.load(),
+ colorFilter = ColorFilter.tint(color = animatedColor),
+ modifier = iconModifier,
+ contentScale = ContentScale.Crop,
+ )
+ } else {
+ Image(
+ painter = painter,
+ contentDescription = icon.contentDescription?.load(),
+ colorFilter = ColorFilter.tint(color = animatedColor),
+ modifier = iconModifier,
+ )
+ }
} else {
Icon(icon = icon, tint = animatedColor, modifier = iconModifier)
}
@@ -396,3 +412,41 @@
@Composable fun longPressLabel() = stringResource(id = R.string.accessibility_long_click_tile)
}
+
+/** Same as Image, but it doesn't clip its content. */
+@Composable
+private fun NonClippedImage(
+ painter: Painter,
+ contentDescription: String?,
+ modifier: Modifier = Modifier,
+ alignment: Alignment = Alignment.Center,
+ contentScale: ContentScale = ContentScale.Fit,
+ alpha: Float = DefaultAlpha,
+ colorFilter: ColorFilter,
+) {
+ val semantics =
+ if (contentDescription != null) {
+ Modifier.semantics {
+ this.contentDescription = contentDescription
+ this.role = Role.Image
+ }
+ } else {
+ Modifier
+ }
+
+ // Explicitly use a simple Layout implementation here as Spacer squashes any non fixed
+ // constraint with zero
+ Layout(
+ modifier
+ .then(semantics)
+ .paint(
+ painter,
+ alignment = alignment,
+ contentScale = contentScale,
+ alpha = alpha,
+ colorFilter = colorFilter,
+ )
+ ) { _, constraints ->
+ layout(constraints.minWidth, constraints.minHeight) {}
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt
index 0ec41fc..ef43ba7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt
@@ -32,6 +32,7 @@
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.Image
import androidx.compose.foundation.LocalOverscrollFactory
import androidx.compose.foundation.ScrollState
import androidx.compose.foundation.background
@@ -58,6 +59,7 @@
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.requiredHeightIn
import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.statusBarsPadding
import androidx.compose.foundation.layout.systemBars
import androidx.compose.foundation.layout.windowInsetsBottomHeight
import androidx.compose.foundation.layout.wrapContentHeight
@@ -68,6 +70,7 @@
import androidx.compose.foundation.lazy.grid.rememberLazyGridState
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.foundation.text.InlineTextContent
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
@@ -120,21 +123,29 @@
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.CustomAccessibilityAction
+import androidx.compose.ui.semantics.clearAndSetSemantics
import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.customActions
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.semantics.stateDescription
+import androidx.compose.ui.text.AnnotatedString
+import androidx.compose.ui.text.Placeholder
+import androidx.compose.ui.text.PlaceholderVerticalAlign
import androidx.compose.ui.text.style.Hyphens
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.IntOffset
+import androidx.compose.ui.unit.TextUnit
import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
import androidx.compose.ui.util.fastMap
import com.android.compose.gesture.effect.rememberOffsetOverscrollEffectFactory
import com.android.compose.modifiers.height
import com.android.compose.modifiers.thenIf
import com.android.compose.theme.LocalAndroidColorScheme
+import com.android.compose.ui.graphics.painter.rememberDrawablePainter
+import com.android.systemui.common.shared.model.Icon
import com.android.systemui.common.ui.compose.load
import com.android.systemui.qs.panels.shared.model.SizedTileImpl
import com.android.systemui.qs.panels.ui.compose.DragAndDropState
@@ -169,6 +180,7 @@
import com.android.systemui.qs.panels.ui.model.SpacerGridCell
import com.android.systemui.qs.panels.ui.model.TileGridCell
import com.android.systemui.qs.panels.ui.viewmodel.EditTileViewModel
+import com.android.systemui.qs.panels.ui.viewmodel.EditTileViewModelConstants.APP_ICON_INLINE_CONTENT_ID
import com.android.systemui.qs.panels.ui.viewmodel.InfiniteGridSnapshotViewModel
import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.qs.shared.model.TileCategory
@@ -178,7 +190,6 @@
import kotlin.math.roundToInt
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.drop
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
@@ -191,6 +202,7 @@
@Composable
private fun EditModeTopBar(
onStopEditing: () -> Unit,
+ modifier: Modifier = Modifier,
actions: @Composable RowScope.() -> Unit = {},
) {
val surfaceEffect2 = LocalAndroidColorScheme.current.surfaceEffect2
@@ -221,7 +233,8 @@
}
},
actions = actions,
- modifier = Modifier.padding(vertical = 8.dp),
+ modifier = modifier.padding(vertical = 8.dp),
+ windowInsets = WindowInsets(0.dp),
)
}
@@ -263,7 +276,7 @@
Scaffold(
containerColor = Color.Transparent,
topBar = {
- EditModeTopBar(onStopEditing = onStopEditing) {
+ EditModeTopBar(onStopEditing = onStopEditing, modifier = Modifier.statusBarsPadding()) {
AnimatedVisibility(snapshotViewModel.canUndo, enter = fadeIn(), exit = fadeOut()) {
TextButton(
enabled = snapshotViewModel.canUndo,
@@ -569,10 +582,14 @@
currentListState.resizeTile(resizingOperation.spec, resizingOperation.toIcon)
}
is FinalResizeOperation -> {
- // Commit the new size of the tile
- onEditAction(
- EditAction.ResizeTile(resizingOperation.spec, resizingOperation.toIcon)
- )
+ with(resizingOperation) {
+ // Commit the new size of the tile IF the size changed. Do this check before
+ // a snapshot is taken to avoid saving an unnecessary snapshot
+ val isIcon = spec !in listState.largeTilesSpecs
+ if (isIcon != toIcon) {
+ onEditAction(EditAction.ResizeTile(spec, toIcon))
+ }
+ }
}
}
}
@@ -785,11 +802,9 @@
// the tile's size
LaunchedEffect(resizingState) {
snapshotFlow { resizingState.temporaryResizeOperation }
- .drop(1) // Drop the initial state
.onEach { onResize(it) }
.launchIn(this)
snapshotFlow { resizingState.finalResizeOperation }
- .drop(1) // Drop the initial state
.onEach { onResize(it) }
.launchIn(this)
}
@@ -834,7 +849,7 @@
coroutineScope.launch { resizingState.toggleCurrentValue() }
}
},
- onClickLabel = decorationClickLabel,
+ contentDescription = decorationClickLabel,
) {
val placeableColor = MaterialTheme.colorScheme.primary.copy(alpha = .4f)
val backgroundColor by
@@ -857,7 +872,7 @@
Box(
Modifier.fillMaxSize()
- .semantics(mergeDescendants = true) {
+ .clearAndSetSemantics {
this.stateDescription = stateDescription
contentDescription = cell.tile.label.text
customActions =
@@ -922,6 +937,10 @@
val alpha by animateFloatAsState(if (cell.isCurrent) .38f else 1f)
val colors = EditModeTileDefaults.editTileColors()
+ val onClick: () -> Unit = {
+ onAddTile(cell.tileSpec)
+ selectionState.select(cell.tileSpec)
+ }
// Displays the tile as an icon tile with the label underneath
Column(
@@ -930,9 +949,8 @@
modifier =
modifier
.graphicsLayer { this.alpha = alpha }
- .semantics(mergeDescendants = true) {
- stateDescription?.let { this.stateDescription = it }
- },
+ .clickable(enabled = !cell.isCurrent, onClick = onClick)
+ .semantics { stateDescription?.let { this.stateDescription = it } },
) {
Box(Modifier.fillMaxWidth().height(TileHeight)) {
val draggableModifier =
@@ -947,22 +965,13 @@
selectionState.unSelect()
}
}
- val onClick: () -> Unit = {
- onAddTile(cell.tileSpec)
- selectionState.select(cell.tileSpec)
- }
- Box(
- draggableModifier
- .fillMaxSize()
- .clickable(enabled = !cell.isCurrent, onClick = onClick)
- .tileBackground { colors.background }
- ) {
+ Box(draggableModifier.fillMaxSize().tileBackground { colors.background }) {
// Icon
SmallTileContent(
iconProvider = { cell.icon },
color = colors.icon,
animateToEnd = true,
- modifier = Modifier.align(Alignment.Center),
+ modifier = Modifier.align(Alignment.Center).clearAndSetSemantics {},
)
}
@@ -975,20 +984,66 @@
)
}
Box(Modifier.fillMaxSize()) {
- Text(
- cell.label.text,
- maxLines = 2,
- color = colors.label,
- overflow = TextOverflow.Ellipsis,
- textAlign = TextAlign.Center,
- style = MaterialTheme.typography.labelMedium.copy(hyphens = Hyphens.Auto),
- modifier = Modifier.align(Alignment.TopCenter),
- )
+ val icon = cell.appIcon
+ if (icon != null && icon is Icon.Loaded) {
+ AppIconText(icon, cell.label, colors.label)
+ } else {
+ Text(
+ cell.label.text,
+ maxLines = 2,
+ color = colors.label,
+ overflow = TextOverflow.Ellipsis,
+ textAlign = TextAlign.Center,
+ style = MaterialTheme.typography.labelMedium.copy(hyphens = Hyphens.Auto),
+ modifier = Modifier.align(Alignment.TopCenter),
+ )
+ }
}
}
}
@Composable
+private fun BoxScope.AppIconText(
+ icon: Icon.Loaded,
+ label: AnnotatedString,
+ color: Color,
+ modifier: Modifier = Modifier,
+) {
+ val iconSize: TextUnit = dimensionResource(R.dimen.qs_edit_mode_app_icon).value.sp
+ val inlineContent =
+ remember(icon) {
+ mapOf(
+ Pair(
+ APP_ICON_INLINE_CONTENT_ID,
+ InlineTextContent(
+ Placeholder(
+ width = iconSize,
+ height = iconSize,
+ placeholderVerticalAlign = PlaceholderVerticalAlign.Center,
+ )
+ ) {
+ Image(
+ rememberDrawablePainter(icon.drawable),
+ contentDescription = null,
+ Modifier.fillMaxSize(),
+ )
+ },
+ )
+ )
+ }
+ Text(
+ label,
+ maxLines = 2,
+ color = color,
+ overflow = TextOverflow.Ellipsis,
+ textAlign = TextAlign.Center,
+ style = MaterialTheme.typography.labelMedium.copy(hyphens = Hyphens.Auto),
+ inlineContent = inlineContent,
+ modifier = modifier.align(Alignment.TopCenter),
+ )
+}
+
+@Composable
private fun SpacerGridCell(modifier: Modifier = Modifier) {
// By default, spacers are invisible and exist purely to catch drag movements
Box(modifier.height(TileHeight).fillMaxWidth())
@@ -1003,7 +1058,7 @@
colors: TileColors = EditModeTileDefaults.editTileColors(),
) {
val iconSizeDiff = CommonTileDefaults.IconSize - CommonTileDefaults.LargeTileIconSize
- val alpha by animateFloatAsState(if (tileState == TileState.GreyedOut) .4f else 1f)
+ val containerAlpha by animateFloatAsState(if (tileState == TileState.GreyedOut) .4f else 1f)
Row(
horizontalArrangement = spacedBy(6.dp),
verticalAlignment = Alignment.CenterVertically,
@@ -1037,10 +1092,14 @@
}
}
.largeTilePadding()
- .graphicsLayer { this.alpha = alpha },
+ .graphicsLayer { this.alpha = containerAlpha },
) {
// Icon
- Box(Modifier.size(ToggleTargetSize)) {
+ Box(
+ Modifier.size(ToggleTargetSize).thenIf(tile.isDualTarget) {
+ Modifier.drawBehind { drawCircle(colors.iconBackground, alpha = progress()) }
+ }
+ ) {
SmallTileContent(
iconProvider = { tile.icon },
color = colors.icon,
@@ -1083,7 +1142,7 @@
fun editTileColors(): TileColors =
TileColors(
background = LocalAndroidColorScheme.current.surfaceEffect1,
- iconBackground = Color.Transparent,
+ iconBackground = LocalAndroidColorScheme.current.surfaceEffect2,
label = MaterialTheme.colorScheme.onSurface,
secondaryLabel = MaterialTheme.colorScheme.onSurface,
icon = MaterialTheme.colorScheme.onSurface,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt
index 55ca732..102bd2b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt
@@ -23,6 +23,7 @@
import android.os.Trace
import android.service.quicksettings.Tile.STATE_ACTIVE
import android.service.quicksettings.Tile.STATE_INACTIVE
+import android.service.quicksettings.Tile.STATE_UNAVAILABLE
import androidx.compose.animation.animateColorAsState
import androidx.compose.animation.core.animateDpAsState
import androidx.compose.animation.core.animateFloatAsState
@@ -154,6 +155,7 @@
produceState(tile.currentState.toUiState(resources), tile, resources) {
tile.state.collect { value = it.toUiState(resources) }
}
+ val isClickable = uiState.state != STATE_UNAVAILABLE
val icon by
produceState(tile.currentState.toIconProvider(), tile) {
@@ -170,6 +172,7 @@
val tileShape by TileDefaults.animateTileShapeAsState(uiState.state)
val animatedColor by animateColorAsState(colors.background, label = "QSTileBackgroundColor")
val animatedAlpha by animateFloatAsState(colors.alpha, label = "QSTileAlpha")
+ val isDualTarget = uiState.handlesSecondaryClick
TileExpandable(
color = { animatedColor },
@@ -197,32 +200,43 @@
tile.onLongClick(expandable)
}
.takeIf { uiState.handlesLongClick }
+
TileContainer(
- onClick = {
- var hasDetails = false
- if (QsDetailedView.isEnabled) {
- hasDetails = detailsViewModel?.onTileClicked(tile.spec) == true
- }
- if (!hasDetails) {
- // For those tile's who doesn't have a detailed view, process with their
- // `onClick` behavior.
- tile.onClick(expandable)
+ onClick = onClick@{
+ if (!isClickable) return@onClick
+
+ val hasDetails =
+ QsDetailedView.isEnabled &&
+ detailsViewModel?.onTileClicked(tile.spec) == true
+ if (hasDetails) return@onClick
+
+ // For those tile's who doesn't have a detailed view, process with
+ // their `onClick` behavior.
+ if (iconOnly && isDualTarget) {
+ tile.onSecondaryClick()
+ } else {
+ tile.onClick(expandable)
+ }
+
+ // Side effects of the click
hapticsViewModel?.setTileInteractionState(
TileHapticsViewModel.TileInteractionState.CLICKED
)
if (uiState.accessibilityUiState.toggleableState != null) {
+ // Bounce
coroutineScope.launch {
currentBounceableInfo.bounceable.animateBounce()
}
+ // And show footer text feedback for icons
+ if (iconOnly) {
+ requestToggleTextFeedback(tile.spec)
+ }
}
- if (uiState.accessibilityUiState.toggleableState != null && iconOnly) {
- requestToggleTextFeedback(tile.spec)
- }
- }
- },
+ },
onLongClick = longClick,
accessibilityUiState = uiState.accessibilityUiState,
iconOnly = iconOnly,
+ isDualTarget = isDualTarget,
) {
val iconProvider: Context.() -> Icon = { getTileIcon(icon = icon) }
if (iconOnly) {
@@ -240,7 +254,7 @@
)
tile.onSecondaryClick()
}
- .takeIf { uiState.handlesSecondaryClick }
+ .takeIf { isDualTarget }
LargeTileContent(
label = uiState.label,
secondaryLabel = uiState.secondaryLabel,
@@ -282,10 +296,11 @@
@Composable
fun TileContainer(
- onClick: () -> Unit,
+ onClick: (() -> Unit)?,
onLongClick: (() -> Unit)?,
accessibilityUiState: AccessibilityUiState,
iconOnly: Boolean,
+ isDualTarget: Boolean,
content: @Composable BoxScope.() -> Unit,
) {
Box(
@@ -293,10 +308,11 @@
Modifier.height(TileHeight)
.fillMaxWidth()
.tileCombinedClickable(
- onClick = onClick,
+ onClick = onClick ?: {},
onLongClick = onLongClick,
accessibilityUiState = accessibilityUiState,
iconOnly = iconOnly,
+ isDualTarget = isDualTarget,
)
.sysuiResTag(if (iconOnly) TEST_TAG_SMALL else TEST_TAG_LARGE),
content = content,
@@ -349,6 +365,7 @@
onLongClick: (() -> Unit)?,
accessibilityUiState: AccessibilityUiState,
iconOnly: Boolean,
+ isDualTarget: Boolean,
): Modifier {
val longPressLabel = longPressLabel()
return combinedClickable(
@@ -359,10 +376,16 @@
hapticFeedbackEnabled = !Flags.msdlFeedback(),
)
.semantics {
- role = accessibilityUiState.accessibilityRole
- if (accessibilityUiState.accessibilityRole == Role.Switch) {
+ val accessibilityRole =
+ if (iconOnly && isDualTarget) {
+ Role.Switch
+ } else {
+ accessibilityUiState.accessibilityRole
+ }
+ if (accessibilityRole == Role.Switch) {
accessibilityUiState.toggleableState?.let { toggleableState = it }
}
+ role = accessibilityRole
stateDescription = accessibilityUiState.stateDescription
}
.thenIf(iconOnly) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/Selection.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/Selection.kt
index c149b6e..29cee24 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/Selection.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/Selection.kt
@@ -106,7 +106,7 @@
resizingState: ResizingState,
modifier: Modifier = Modifier,
onClick: () -> Unit = {},
- onClickLabel: String? = null,
+ contentDescription: String? = null,
content: @Composable BoxScope.() -> Unit = {},
) {
val transition: Transition<TileState> = updateTransition(tileState)
@@ -150,18 +150,12 @@
state = resizingState.anchoredDraggableState,
orientation = Orientation.Horizontal,
)
- .clickable(
- enabled = tileState != None,
- interactionSource = null,
- indication = null,
- onClickLabel = onClickLabel,
- onClick = onClick,
- )
+ .clickable(enabled = tileState != None, onClick = onClick)
) {
val size = with(LocalDensity.current) { BadgeIconSize.toDp() }
Icon(
Icons.Default.Remove,
- contentDescription = null,
+ contentDescription = contentDescription,
tint = MaterialTheme.colorScheme.onPrimaryContainer,
modifier =
Modifier.size(size).align(Alignment.Center).graphicsLayer {
@@ -217,14 +211,7 @@
Box(
Modifier.fillMaxSize()
.graphicsLayer { this.alpha = alpha }
- .thenIf(enabled) {
- Modifier.clickable(
- interactionSource = null,
- indication = null,
- onClickLabel = contentDescription,
- onClick = onClick,
- )
- }
+ .thenIf(enabled) { Modifier.clickable(onClick = onClick) }
) {
val size = with(LocalDensity.current) { BadgeIconSize.toDp() }
val primaryColor = MaterialTheme.colorScheme.primary
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModel.kt
index 31323c7..0a216c8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModel.kt
@@ -101,8 +101,8 @@
* cannot be created), and they won't be able to be added.
*/
val tiles: Flow<List<EditTileViewModel>> =
- isEditing.flatMapLatest {
- if (it) {
+ isEditing.flatMapLatest { isEditing ->
+ if (isEditing) {
val editTilesData = editTilesListInteractor.getTilesToEdit()
// Query only the non current platform tiles, as any current tile is clearly
// available
@@ -115,6 +115,11 @@
currentTilesInteractor.currentTiles
.map { tiles ->
val currentSpecs = tiles.map { it.spec }
+ val dualTargetSpecs =
+ tiles
+ .filter { it.tile.state.handlesSecondaryClick }
+ .map { it.spec }
+ .toSet()
val canRemoveTiles = currentSpecs.size > minTilesInteractor.minNumberOfTiles
val allTiles = editTilesData.stockTiles + editTilesData.customTiles
val allTilesMap = allTiles.associateBy { it.tileSpec }
@@ -125,6 +130,7 @@
.filterNot { it.tileSpec in unavailable }
.map {
val current = it.tileSpec in currentSpecs
+ val isDualTarget = current && it.tileSpec in dualTargetSpecs
val availableActions = buildSet {
if (current) {
add(AvailableEditActions.MOVE)
@@ -140,7 +146,9 @@
it.icon,
it.label,
it.appName,
+ it.appIcon,
current,
+ isDualTarget,
availableActions,
it.category,
)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditTileViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditTileViewModel.kt
index cf325f5..ef08756 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditTileViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditTileViewModel.kt
@@ -17,11 +17,14 @@
package com.android.systemui.qs.panels.ui.viewmodel
import android.content.Context
+import androidx.compose.foundation.text.appendInlineContent
import androidx.compose.runtime.Immutable
import androidx.compose.ui.text.AnnotatedString
+import androidx.compose.ui.text.buildAnnotatedString
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.common.shared.model.Text
import com.android.systemui.common.ui.compose.toAnnotatedString
+import com.android.systemui.qs.panels.ui.viewmodel.EditTileViewModelConstants.APP_ICON_INLINE_CONTENT_ID
import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.qs.shared.model.CategoryAndName
import com.android.systemui.qs.shared.model.TileCategory
@@ -37,17 +40,32 @@
val icon: Icon,
val label: Text,
val appName: Text?,
+ val appIcon: Icon?,
val isCurrent: Boolean,
+ val isDualTarget: Boolean,
val availableEditActions: Set<AvailableEditActions>,
val category: TileCategory,
) {
fun load(context: Context): EditTileViewModel {
+ val loadedLabel = label.toAnnotatedString(context) ?: AnnotatedString(tileSpec.spec)
+ val inlinedLabel =
+ if (appIcon != null) {
+ buildAnnotatedString {
+ appendInlineContent(APP_ICON_INLINE_CONTENT_ID)
+ append(' ')
+ append(loadedLabel)
+ }
+ } else {
+ loadedLabel
+ }
return EditTileViewModel(
tileSpec = tileSpec,
icon = icon,
- label = label.toAnnotatedString(context) ?: AnnotatedString(tileSpec.spec),
+ label = inlinedLabel,
appName = appName?.toAnnotatedString(context),
+ appIcon = appIcon,
isCurrent = isCurrent,
+ isDualTarget = isDualTarget,
availableEditActions = availableEditActions,
category = category,
)
@@ -60,7 +78,9 @@
val icon: Icon,
val label: AnnotatedString,
val appName: AnnotatedString?,
+ val appIcon: Icon?,
val isCurrent: Boolean,
+ val isDualTarget: Boolean,
val availableEditActions: Set<AvailableEditActions>,
override val category: TileCategory,
) : CategoryAndName {
@@ -76,3 +96,7 @@
REMOVE,
MOVE,
}
+
+object EditTileViewModelConstants {
+ const val APP_ICON_INLINE_CONTENT_ID = "appIcon"
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/TileUiState.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/TileUiState.kt
index 15e71c8..96a0398 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/TileUiState.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/TileUiState.kt
@@ -68,7 +68,7 @@
} else {
""
}
- val secondaryLabel = getSecondaryLabel(stateText)
+ val secondaryLabel = getSecondaryLabel(if (state == Tile.STATE_UNAVAILABLE) stateText else "")
if (!TextUtils.isEmpty(stateText)) {
stateDescription.append(stateText)
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/shared/model/TileCategory.kt b/packages/SystemUI/src/com/android/systemui/qs/shared/model/TileCategory.kt
index c8225e7..d83968c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/shared/model/TileCategory.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/shared/model/TileCategory.kt
@@ -41,6 +41,10 @@
Text.Resource(R.string.qs_edit_mode_category_accessibility),
R.drawable.ic_qs_category_accessibility,
),
+ PROVIDED_BY_SYSTEM_APP(
+ Text.Resource(R.string.qs_edit_mode_category_providedBySystemApps),
+ R.drawable.ic_qs_category_system_apps,
+ ),
PROVIDED_BY_APP(
Text.Resource(R.string.qs_edit_mode_category_providedByApps),
R.drawable.ic_qs_category_provided_by_apps,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ModesTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/ModesTile.kt
index 6a842c31..3148f00 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ModesTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ModesTile.kt
@@ -108,6 +108,7 @@
label = mContext.getString(R.string.quick_settings_modes_label)
icon = ResourceIcon.get(ICON_RES_ID)
state = Tile.STATE_INACTIVE
+ handlesSecondaryClick = true
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java
index b62e858..5701836 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java
@@ -54,9 +54,11 @@
IndividualSensorPrivacyController.Callback {
private final KeyguardStateController mKeyguard;
+ private final SafetyCenterManager mSafetyCenterManager;
protected IndividualSensorPrivacyController mSensorPrivacyController;
- private final Boolean mIsSafetyCenterEnabled;
+ // Set delayed in the background thread.
+ private boolean mIsSafetyCenterEnabled;
/**
* @return Id of the sensor that will be toggled
@@ -90,11 +92,17 @@
statusBarStateController, activityStarter, qsLogger);
mSensorPrivacyController = sensorPrivacyController;
mKeyguard = keyguardStateController;
- mIsSafetyCenterEnabled = safetyCenterManager.isSafetyCenterEnabled();
+ mSafetyCenterManager = safetyCenterManager;
mSensorPrivacyController.observe(getLifecycle(), this);
}
@Override
+ protected void handleInitialize() {
+ super.handleInitialize();
+ mIsSafetyCenterEnabled = mSafetyCenterManager.isSafetyCenterEnabled();
+ }
+
+ @Override
public BooleanState newTileState() {
return new BooleanState();
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/shared/model/QSTileCoroutineScopeFactory.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/shared/model/QSTileCoroutineScopeFactory.kt
index 018a06f..b884668 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/shared/model/QSTileCoroutineScopeFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/shared/model/QSTileCoroutineScopeFactory.kt
@@ -17,6 +17,7 @@
package com.android.systemui.qs.tiles.base.shared.model
import com.android.systemui.coroutines.newTracingContext
+import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
@@ -24,6 +25,7 @@
import kotlinx.coroutines.SupervisorJob
/** Creates a [CoroutineScope] for the [QSTileViewModelImpl]. */
+@SysUISingleton
class QSTileCoroutineScopeFactory
@Inject
constructor(@Background private val bgDispatcher: CoroutineDispatcher) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/CastDetailsContent.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/CastDetailsContent.kt
index cd33c96..21f6613 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/CastDetailsContent.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/CastDetailsContent.kt
@@ -17,19 +17,30 @@
package com.android.systemui.qs.tiles.dialog
import android.view.LayoutInflater
-import androidx.compose.foundation.layout.fillMaxHeight
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.material3.Button
+import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
+import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.testTag
import androidx.compose.ui.viewinterop.AndroidView
+import com.android.compose.ui.graphics.painter.rememberDrawablePainter
import com.android.internal.R
+import com.android.internal.app.MediaRouteChooserContentManager
import com.android.internal.app.MediaRouteControllerContentManager
@Composable
fun CastDetailsContent(castDetailsViewModel: CastDetailsViewModel) {
if (castDetailsViewModel.shouldShowChooserDialog()) {
- // TODO(b/378514236): Show the chooser UI here.
+ val contentManager: MediaRouteChooserContentManager = remember {
+ castDetailsViewModel.createChooserContentManager()
+ }
+ CastChooserView(contentManager)
return
}
@@ -37,8 +48,41 @@
castDetailsViewModel.createControllerContentManager()
}
+ Column(
+ verticalArrangement = Arrangement.Center,
+ horizontalAlignment = Alignment.CenterHorizontally,
+ ) {
+ Image(
+ painter = rememberDrawablePainter(castDetailsViewModel.deviceIcon),
+ // TODO(b/388321032): Replace this string with a string in a translatable xml file.
+ contentDescription = "device icon",
+ )
+ CastControllerView(contentManager)
+ CastControllerDisconnectButton(contentManager)
+ }
+}
+
+@Composable
+fun CastChooserView(contentManager: MediaRouteChooserContentManager) {
AndroidView(
- modifier = Modifier.fillMaxWidth().fillMaxHeight(),
+ modifier = Modifier.fillMaxWidth().testTag(CastDetailsViewModel.CHOOSER_VIEW_TEST_TAG),
+ factory = { context ->
+ // Inflate with the existing dialog xml layout
+ val view =
+ LayoutInflater.from(context).inflate(R.layout.media_route_chooser_dialog, null)
+ contentManager.bindViews(view)
+ contentManager.onAttachedToWindow()
+
+ view
+ },
+ onRelease = { contentManager.onDetachedFromWindow() },
+ )
+}
+
+@Composable
+fun CastControllerView(contentManager: MediaRouteControllerContentManager) {
+ AndroidView(
+ modifier = Modifier.fillMaxWidth().testTag(CastDetailsViewModel.CONTROLLER_VIEW_TEST_TAG),
factory = { context ->
// Inflate with the existing dialog xml layout
val view =
@@ -51,3 +95,14 @@
onRelease = { contentManager.onDetachedFromWindow() },
)
}
+
+@Composable
+fun CastControllerDisconnectButton(contentManager: MediaRouteControllerContentManager) {
+ Button(
+ onClick = { contentManager.onDisconnectButtonClick() },
+ modifier = Modifier.fillMaxWidth(),
+ ) {
+ // TODO(b/388321032): Replace this string with a string in a translatable xml file.
+ Text(text = "Disconnect")
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/CastDetailsViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/CastDetailsViewModel.kt
index 72322eff..9ec5c11 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/CastDetailsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/CastDetailsViewModel.kt
@@ -20,6 +20,10 @@
import android.content.Intent
import android.graphics.drawable.Drawable
import android.provider.Settings
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
+import com.android.internal.app.MediaRouteChooserContentManager
import com.android.internal.app.MediaRouteControllerContentManager
import com.android.internal.app.MediaRouteDialogPresenter
import com.android.systemui.plugins.qs.TileDetailsViewModel
@@ -35,7 +39,14 @@
private val qsTileIntentUserActionHandler: QSTileIntentUserInputHandler,
@Assisted private val context: Context,
@Assisted private val routeTypes: Int,
-) : MediaRouteControllerContentManager.Delegate, TileDetailsViewModel {
+) :
+ MediaRouteChooserContentManager.Delegate,
+ MediaRouteControllerContentManager.Delegate,
+ TileDetailsViewModel {
+ private var detailsViewTitle by mutableStateOf(DEFAULT_TITLE)
+ private val detailsViewSubTitle = if (shouldShowChooserDialog()) DEFAULT_SUBTITLE else ""
+ var deviceIcon: Drawable? by mutableStateOf(null)
+
@AssistedFactory
fun interface Factory {
fun create(context: Context, routeTypes: Int): CastDetailsViewModel
@@ -45,6 +56,10 @@
return MediaRouteDialogPresenter.shouldShowChooserDialog(context, routeTypes)
}
+ fun createChooserContentManager(): MediaRouteChooserContentManager {
+ return MediaRouteChooserContentManager(context, this)
+ }
+
fun createControllerContentManager(): MediaRouteControllerContentManager {
return MediaRouteControllerContentManager(context, this)
}
@@ -56,23 +71,33 @@
)
}
- // TODO(b/388321032): Replace this string with a string in a translatable xml file,
override val title: String
- get() = "Cast screen to device"
+ get() = detailsViewTitle
- // TODO(b/388321032): Replace this string with a string in a translatable xml file,
override val subTitle: String
- get() = "Searching for devices..."
+ get() = detailsViewSubTitle
override fun setMediaRouteDeviceTitle(title: CharSequence?) {
- // TODO(b/378514236): Finish implementing this function.
+ detailsViewTitle = title.toString()
}
override fun setMediaRouteDeviceIcon(icon: Drawable?) {
- // TODO(b/378514236): Finish implementing this function.
+ deviceIcon = icon
}
override fun dismissView() {
// TODO(b/378514236): Finish implementing this function.
}
+
+ override fun showProgressBarWhenEmpty(): Boolean {
+ return false
+ }
+
+ companion object {
+ // TODO(b/388321032): Replace this string with a string in a translatable xml file.
+ const val DEFAULT_TITLE = "Cast screen to device"
+ const val DEFAULT_SUBTITLE = "Searching for devices..."
+ const val CHOOSER_VIEW_TEST_TAG = "CastChooserView"
+ const val CONTROLLER_VIEW_TEST_TAG = "CastControllerView"
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetAdapter.java
index 6236fff..ced263f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetAdapter.java
@@ -56,6 +56,7 @@
private final InternetDetailsContentController mInternetDetailsContentController;
private final CoroutineScope mCoroutineScope;
+ private final Boolean mIsInDetailsView;
@Nullable
private List<WifiEntry> mWifiEntries;
@VisibleForTesting
@@ -68,8 +69,15 @@
public InternetAdapter(InternetDetailsContentController controller,
CoroutineScope coroutineScope) {
+ this(controller, coroutineScope,
+ false);
+ }
+
+ public InternetAdapter(InternetDetailsContentController controller,
+ CoroutineScope coroutineScope, boolean isInDetailsView) {
mInternetDetailsContentController = controller;
mCoroutineScope = coroutineScope;
+ mIsInDetailsView = isInDetailsView;
}
@Override
@@ -79,7 +87,7 @@
mHolderView = LayoutInflater.from(mContext).inflate(R.layout.internet_list_item,
viewGroup, false);
return new InternetViewHolder(mHolderView, mInternetDetailsContentController,
- mCoroutineScope);
+ mCoroutineScope, mIsInDetailsView);
}
@Override
@@ -141,16 +149,18 @@
final Context mContext;
final InternetDetailsContentController mInternetDetailsContentController;
final CoroutineScope mCoroutineScope;
+ final Boolean mIsInDetailsView;
@Nullable
private Job mJob;
InternetViewHolder(View view,
InternetDetailsContentController internetDetailsContentController,
- CoroutineScope coroutineScope) {
+ CoroutineScope coroutineScope, Boolean isInDetailsView) {
super(view);
mContext = view.getContext();
mInternetDetailsContentController = internetDetailsContentController;
mCoroutineScope = coroutineScope;
+ mIsInDetailsView = isInDetailsView;
mContainerLayout = view.requireViewById(R.id.internet_container);
mWifiListLayout = view.requireViewById(R.id.wifi_list);
mWifiNetworkLayout = view.requireViewById(R.id.wifi_network_layout);
@@ -158,6 +168,22 @@
mWifiTitleText = view.requireViewById(R.id.wifi_title);
mWifiSummaryText = view.requireViewById(R.id.wifi_summary);
mWifiEndIcon = view.requireViewById(R.id.wifi_end_icon);
+
+ if (!isInDetailsView){
+ return;
+ }
+
+ // Set the UI styles for details view only.
+ mWifiTitleText.setTextAppearance(R.style.TextAppearance_TileDetailsEntryTitle);
+ mWifiSummaryText.setTextAppearance(R.style.TextAppearance_TileDetailsEntrySubTitle);
+ if (mWifiIcon.getDrawable() != null) {
+ mWifiIcon.setColorFilter(
+ mContext.getColor(com.android.internal.R.color.materialColorOnSurface));
+ }
+ if (mWifiEndIcon.getDrawable() != null){
+ mWifiEndIcon.setColorFilter(
+ mContext.getColor(com.android.internal.R.color.materialColorOnSurface));
+ }
}
void onBind(@NonNull WifiEntry wifiEntry) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentManager.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentManager.kt
index 47326ad..a700aa5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentManager.kt
@@ -19,6 +19,9 @@
import android.app.AlertDialog
import android.content.Context
import android.content.DialogInterface
+import android.content.Intent
+import android.graphics.Canvas
+import android.graphics.Rect
import android.graphics.drawable.Drawable
import android.net.Network
import android.net.NetworkCapabilities
@@ -56,10 +59,12 @@
import com.android.settingslib.satellite.SatelliteDialogUtils.TYPE_IS_WIFI
import com.android.settingslib.satellite.SatelliteDialogUtils.mayStartSatelliteWarningDialog
import com.android.settingslib.wifi.WifiEnterpriseRestrictionUtils
+import com.android.settingslib.wifi.WifiUtils
import com.android.systemui.Prefs
import com.android.systemui.accessibility.floatingmenu.AnnotationLinkSpan
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.qs.flags.QsWifiConfig
import com.android.systemui.res.R
import com.android.systemui.statusbar.phone.SystemUIDialog
import com.android.systemui.statusbar.policy.KeyguardStateController
@@ -112,18 +117,25 @@
private lateinit var wifiRecyclerView: RecyclerView
private lateinit var seeAllLayout: LinearLayout
private lateinit var signalIcon: ImageView
+ private lateinit var turnMobileOnLayout: LinearLayout
+ private lateinit var connectedMobileLayout: LinearLayout
private lateinit var mobileTitleTextView: TextView
private lateinit var mobileSummaryTextView: TextView
private lateinit var airplaneModeSummaryTextView: TextView
private lateinit var mobileDataToggle: MaterialSwitch
private lateinit var wifiToggle: MaterialSwitch
private lateinit var shareWifiButton: LinearLayout
+ private lateinit var addNetworkButton: LinearLayout
private lateinit var airplaneModeButton: Button
private var alertDialog: AlertDialog? = null
private var canChangeWifiState = false
private var wifiNetworkHeight = 0
private var backgroundOn: Drawable? = null
- private var backgroundOff: Drawable? = null
+ private var entryBackgroundActive: Drawable? = null
+ private var entryBackgroundInactive: Drawable? = null
+ private var entryBackgroundStart: Drawable? = null
+ private var entryBackgroundEnd: Drawable? = null
+ private var entryBackgroundMiddle: Drawable? = null
private var clickJob: Job? = null
private var defaultDataSubId = internetDetailsContentController.defaultDataSubscriptionId
@VisibleForTesting internal lateinit var adapter: InternetAdapter
@@ -162,7 +174,12 @@
this.contentView = contentView
context = contentView.context
this.coroutineScope = coroutineScope
- adapter = InternetAdapter(internetDetailsContentController, coroutineScope)
+ adapter =
+ InternetAdapter(
+ internetDetailsContentController,
+ coroutineScope,
+ /* isInDetailsView= */ true,
+ )
canChangeWifiState = WifiEnterpriseRestrictionUtils.isChangeWifiStateAllowed(context)
initializeLifecycle()
@@ -204,6 +221,14 @@
// Network layouts
progressBar = contentView.requireViewById(R.id.wifi_searching_progress)
+ // Background drawables
+ backgroundOn = context.getDrawable(R.drawable.settingslib_switch_bar_bg_on)
+ entryBackgroundActive = context.getDrawable(R.drawable.settingslib_entry_bg_on)
+ entryBackgroundInactive = context.getDrawable(R.drawable.settingslib_entry_bg_off)
+ entryBackgroundStart = context.getDrawable(R.drawable.settingslib_entry_bg_off_start)
+ entryBackgroundEnd = context.getDrawable(R.drawable.settingslib_entry_bg_off_end)
+ entryBackgroundMiddle = context.getDrawable(R.drawable.settingslib_entry_bg_off_middle)
+
// Set wifi, mobile and ethernet layouts
setWifiLayout()
setMobileLayout()
@@ -222,16 +247,25 @@
}
}
+ // Add network
+ addNetworkButton = contentView.requireViewById(R.id.add_network_button)
+ if (QsWifiConfig.isEnabled) {
+ addNetworkButton.setOnClickListener {
+ val intent =
+ WifiUtils.getWifiDialogIntent(null, true /* connectForCaller */).apply {
+ addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT)
+ }
+ internetDetailsContentController.startActivityForDialog(intent)
+ }
+ }
+
// Airplane mode
airplaneModeButton = contentView.requireViewById(R.id.apm_button)
airplaneModeButton.setOnClickListener {
internetDetailsContentController.setAirplaneModeDisabled()
}
airplaneModeSummaryTextView = contentView.requireViewById(R.id.airplane_mode_summary)
-
- // Background drawables
- backgroundOn = context.getDrawable(R.drawable.settingslib_switch_bar_bg_on)
- backgroundOff = context.getDrawable(R.drawable.internet_dialog_selected_effect)
}
private fun setWifiLayout() {
@@ -251,6 +285,54 @@
layoutManager = LinearLayoutManager(context)
adapter = this@InternetDetailsContentManager.adapter
}
+
+ // Add backgrounds for each entry and also add a gap between each item.
+ val verticalSpacing =
+ context.resources.getDimensionPixelSize(R.dimen.tile_details_entry_gap)
+ wifiRecyclerView.addItemDecoration(
+ object : RecyclerView.ItemDecoration() {
+ override fun getItemOffsets(
+ outRect: Rect,
+ view: View,
+ parent: RecyclerView,
+ state: RecyclerView.State,
+ ) {
+ outRect.top = verticalSpacing
+ }
+
+ override fun onDraw(c: Canvas, parent: RecyclerView, state: RecyclerView.State) {
+ // `itemCount` represents the total number of items in your adapter's data set,
+ // regardless of what's visible.
+ val adapter = parent.adapter ?: return
+ val itemCount = adapter.itemCount
+
+ // `parent.childCount` is the number of child views currently visible on screen.
+ // Often less than itemCount since RecyclerView recycles views that scroll
+ // off-screen.
+ for (i in 0 until parent.childCount) {
+ val child = parent.getChildAt(i) ?: continue
+ val adapterPosition = parent.getChildAdapterPosition(child)
+ if (adapterPosition == RecyclerView.NO_POSITION) continue
+ val entryView = child.requireViewById<LinearLayout>(R.id.wifi_list)
+ val background =
+ when {
+ adapterPosition == 0 -> entryBackgroundStart
+ adapterPosition == itemCount - 1 && !hasMoreWifiEntries ->
+ entryBackgroundEnd
+ else -> entryBackgroundMiddle
+ }
+
+ val left = child.left + entryView.left
+ val top = child.top + entryView.top
+ val right = child.left + entryView.right
+ val bottom = child.top + entryView.bottom
+ background?.setBounds(left, top, right, bottom)
+ background?.draw(c)
+ }
+ }
+ }
+ )
+
seeAllLayout = contentView.requireViewById(R.id.see_all_layout)
// Set click listeners for Wi-Fi related views
@@ -258,20 +340,24 @@
val isChecked = wifiToggle.isChecked
handleWifiToggleClicked(isChecked)
}
+
connectedWifiListLayout.setOnClickListener(this::onClickConnectedWifi)
+ connectedWifiListLayout.background = context.getDrawable(R.drawable.settingslib_entry_bg_on)
seeAllLayout.setOnClickListener(this::onClickSeeMoreButton)
}
private fun setMobileLayout() {
// Initialize mobile data related views
mobileNetworkLayout = contentView.requireViewById(R.id.mobile_network_layout)
+ turnMobileOnLayout = contentView.requireViewById(R.id.turn_on_mobile_layout)
+ connectedMobileLayout = contentView.requireViewById(R.id.mobile_connected_layout)
signalIcon = contentView.requireViewById(R.id.signal_icon)
mobileTitleTextView = contentView.requireViewById(R.id.mobile_title)
mobileSummaryTextView = contentView.requireViewById(R.id.mobile_summary)
mobileDataToggle = contentView.requireViewById(R.id.mobile_toggle)
// Set click listeners for mobile data related views
- mobileNetworkLayout.setOnClickListener {
+ connectedMobileLayout.setOnClickListener {
val autoSwitchNonDdsSubId: Int =
internetDetailsContentController.getActiveAutoSwitchNonDdsSubId()
if (autoSwitchNonDdsSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
@@ -363,6 +449,7 @@
wifiRecyclerView.visibility = View.GONE
seeAllLayout.visibility = View.GONE
shareWifiButton.visibility = View.GONE
+ addNetworkButton.visibility = View.GONE
}
private fun setProgressBarVisible(visible: Boolean) {
@@ -384,9 +471,7 @@
}
alertDialog =
AlertDialog.Builder(context)
- .setTitle(
- context.getString(R.string.auto_data_switch_disable_title, carrierName)
- )
+ .setTitle(context.getString(R.string.auto_data_switch_disable_title, carrierName))
.setMessage(R.string.auto_data_switch_disable_message)
.setNegativeButton(R.string.auto_data_switch_dialog_negative_button) { _, _ -> }
.setPositiveButton(R.string.auto_data_switch_dialog_positive_button) { _, _ ->
@@ -424,9 +509,7 @@
alertDialog =
AlertDialog.Builder(context)
.setTitle(R.string.mobile_data_disable_title)
- .setMessage(
- context.getString(R.string.mobile_data_disable_message, carrierName)
- )
+ .setMessage(context.getString(R.string.mobile_data_disable_message, carrierName))
.setNegativeButton(android.R.string.cancel) { _: DialogInterface?, _: Int -> }
.setPositiveButton(
com.android.internal.R.string.alert_windows_notification_turn_off_action
@@ -556,7 +639,8 @@
// non DDS is the currently active sub, set primary visual for it
setNonDDSActive(autoSwitchNonDdsSubId)
} else {
- mobileNetworkLayout.background = if (isNetworkConnected) backgroundOn else backgroundOff
+ connectedMobileLayout.background =
+ if (isNetworkConnected) entryBackgroundActive else entryBackgroundInactive
mobileTitleTextView.setTextAppearance(
if (isNetworkConnected) R.style.TextAppearance_TileDetailsEntryTitle_Active
else R.style.TextAppearance_TileDetailsEntryTitle
@@ -586,7 +670,7 @@
secondaryMobileNetworkLayout?.setOnClickListener { view: View? ->
this.onClickConnectedSecondarySub(view)
}
- secondaryMobileNetworkLayout?.background = backgroundOn
+ secondaryMobileNetworkLayout?.background = entryBackgroundActive
contentView.requireViewById<TextView>(R.id.secondary_mobile_title).apply {
text = getMobileNetworkTitle(autoSwitchNonDdsSubId)
@@ -613,7 +697,7 @@
}
// set secondary visual for default data sub
- mobileNetworkLayout.background = backgroundOff
+ connectedMobileLayout.background = entryBackgroundInactive
mobileTitleTextView.setTextAppearance(R.style.TextAppearance_TileDetailsEntryTitle)
mobileSummaryTextView.setTextAppearance(R.style.TextAppearance_TileDetailsEntrySubTitle)
signalIcon.setColorFilter(context.getColor(R.color.connected_network_secondary_color))
@@ -678,8 +762,12 @@
if (!internetContent.isWifiEnabled || internetContent.isDeviceLocked) {
wifiRecyclerView.visibility = View.GONE
seeAllLayout.visibility = View.GONE
+ addNetworkButton.visibility = View.GONE
return
}
+ if (QsWifiConfig.isEnabled) {
+ addNetworkButton.visibility = View.VISIBLE
+ }
val wifiListMaxCount = getWifiListMaxCount()
if (adapter.itemCount > wifiListMaxCount) {
hasMoreWifiEntries = true
@@ -689,6 +777,8 @@
if (wifiRecyclerView.minimumHeight != wifiListMinHeight) {
wifiRecyclerView.minimumHeight = wifiListMinHeight
}
+
+ wifiRecyclerView.invalidateItemDecorations()
wifiRecyclerView.visibility = View.VISIBLE
seeAllLayout.visibility = if (hasMoreWifiEntries) View.VISIBLE else View.INVISIBLE
}
@@ -765,12 +855,14 @@
Log.d(TAG, "unBind")
}
lifecycleRegistry.currentState = Lifecycle.State.DESTROYED
- mobileNetworkLayout.setOnClickListener(null)
+ connectedMobileLayout.setOnClickListener(null)
connectedWifiListLayout.setOnClickListener(null)
secondaryMobileNetworkLayout?.setOnClickListener(null)
seeAllLayout.setOnClickListener(null)
wifiToggle.setOnCheckedChangeListener(null)
+ mobileDataToggle.setOnCheckedChangeListener(null)
shareWifiButton.setOnClickListener(null)
+ addNetworkButton.setOnClickListener(null)
airplaneModeButton.setOnClickListener(null)
internetDetailsContentController.onStop()
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/airplane/ui/mapper/AirplaneModeTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/airplane/ui/mapper/AirplaneModeTileMapper.kt
index b0b7b4e..6567c66 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/airplane/ui/mapper/AirplaneModeTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/airplane/ui/mapper/AirplaneModeTileMapper.kt
@@ -44,10 +44,8 @@
icon = Icon.Loaded(resources.getDrawable(iconRes, theme), null, iconRes)
if (data.isEnabled) {
activationState = QSTileState.ActivationState.ACTIVE
- secondaryLabel = resources.getStringArray(R.array.tile_states_airplane)[2]
} else {
activationState = QSTileState.ActivationState.INACTIVE
- secondaryLabel = resources.getStringArray(R.array.tile_states_airplane)[1]
}
contentDescription = label
supportedActions =
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/ui/mapper/ColorCorrectionTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/ui/mapper/ColorCorrectionTileMapper.kt
index 93d81f0..054e3a8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/ui/mapper/ColorCorrectionTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/ui/mapper/ColorCorrectionTileMapper.kt
@@ -41,10 +41,8 @@
icon = Icon.Loaded(resources.getDrawable(iconRes, theme), null, iconRes)
if (data.isEnabled) {
activationState = QSTileState.ActivationState.ACTIVE
- secondaryLabel = subtitleArray[2]
} else {
activationState = QSTileState.ActivationState.INACTIVE
- secondaryLabel = subtitleArray[1]
}
contentDescription = label
supportedActions =
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/ui/mapper/FlashlightMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/ui/mapper/FlashlightMapper.kt
index 0be4a9f..e68cee3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/ui/mapper/FlashlightMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/ui/mapper/FlashlightMapper.kt
@@ -55,10 +55,8 @@
return@build
} else if (data is FlashlightTileModel.FlashlightAvailable && data.isEnabled) {
activationState = QSTileState.ActivationState.ACTIVE
- secondaryLabel = resources.getStringArray(R.array.tile_states_flashlight)[2]
} else {
activationState = QSTileState.ActivationState.INACTIVE
- secondaryLabel = resources.getStringArray(R.array.tile_states_flashlight)[1]
}
supportedActions = setOf(QSTileState.UserAction.CLICK)
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/inversion/ui/mapper/ColorInversionTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/inversion/ui/mapper/ColorInversionTileMapper.kt
index 868abc0..b8be63e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/inversion/ui/mapper/ColorInversionTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/inversion/ui/mapper/ColorInversionTileMapper.kt
@@ -34,15 +34,12 @@
QSTileDataToStateMapper<ColorInversionTileModel> {
override fun map(config: QSTileConfig, data: ColorInversionTileModel): QSTileState =
QSTileState.build(resources, theme, config.uiConfig) {
- val subtitleArray = resources.getStringArray(R.array.tile_states_inversion)
val iconRes: Int
if (data.isEnabled) {
activationState = QSTileState.ActivationState.ACTIVE
- secondaryLabel = subtitleArray[2]
iconRes = R.drawable.qs_invert_colors_icon_on
} else {
activationState = QSTileState.ActivationState.INACTIVE
- secondaryLabel = subtitleArray[1]
iconRes = R.drawable.qs_invert_colors_icon_off
}
icon = Icon.Loaded(resources.getDrawable(iconRes, theme), null, iconRes)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/ui/mapper/LocationTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/ui/mapper/LocationTileMapper.kt
index d2c9116..93539e9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/ui/mapper/LocationTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/ui/mapper/LocationTileMapper.kt
@@ -47,10 +47,8 @@
if (data.isEnabled) {
activationState = QSTileState.ActivationState.ACTIVE
- secondaryLabel = resources.getStringArray(R.array.tile_states_location)[2]
} else {
activationState = QSTileState.ActivationState.INACTIVE
- secondaryLabel = resources.getStringArray(R.array.tile_states_location)[1]
}
contentDescription = label
supportedActions =
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/night/ui/mapper/NightDisplayTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/night/ui/mapper/NightDisplayTileMapper.kt
index 3e47875..2ad8a46 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/night/ui/mapper/NightDisplayTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/night/ui/mapper/NightDisplayTileMapper.kt
@@ -17,7 +17,6 @@
package com.android.systemui.qs.tiles.impl.night.ui.mapper
import android.content.res.Resources
-import android.service.quicksettings.Tile
import android.text.TextUtils
import androidx.annotation.StringRes
import com.android.systemui.accessibility.qs.QSAccessibilityModule
@@ -84,9 +83,7 @@
}
}
is NightDisplayTileModel.AutoModeOff -> {
- val subtitleArray = resources.getStringArray(R.array.tile_states_night)
- return subtitleArray[
- if (data.isActivated) Tile.STATE_ACTIVE else Tile.STATE_INACTIVE]
+ return null
}
is NightDisplayTileModel.AutoModeCustom -> {
// User-specified time, approximated to the nearest hour.
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/onehanded/ui/mapper/OneHandedModeTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/onehanded/ui/mapper/OneHandedModeTileMapper.kt
index ace36ce..58db661 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/onehanded/ui/mapper/OneHandedModeTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/onehanded/ui/mapper/OneHandedModeTileMapper.kt
@@ -36,16 +36,13 @@
override fun map(config: QSTileConfig, data: OneHandedModeTileModel): QSTileState =
QSTileState.build(resources, theme, config.uiConfig) {
- val subtitleArray = resources.getStringArray(R.array.tile_states_onehanded)
label = resources.getString(R.string.quick_settings_onehanded_label)
val iconRes = com.android.internal.R.drawable.ic_qs_one_handed_mode
icon = Icon.Loaded(resources.getDrawable(iconRes, theme), null, iconRes)
if (data.isEnabled) {
activationState = QSTileState.ActivationState.ACTIVE
- secondaryLabel = subtitleArray[2]
} else {
activationState = QSTileState.ActivationState.INACTIVE
- secondaryLabel = subtitleArray[1]
}
sideViewIcon = QSTileState.SideViewIcon.None
contentDescription = label
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/reducebrightness/ui/mapper/ReduceBrightColorsTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/reducebrightness/ui/mapper/ReduceBrightColorsTileMapper.kt
index 2e74399..0b0d074 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/reducebrightness/ui/mapper/ReduceBrightColorsTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/reducebrightness/ui/mapper/ReduceBrightColorsTileMapper.kt
@@ -17,7 +17,6 @@
package com.android.systemui.qs.tiles.impl.reducebrightness.ui.mapper
import android.content.res.Resources
-import android.service.quicksettings.Tile
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
import com.android.systemui.qs.tiles.base.shared.model.QSTileState
@@ -41,15 +40,9 @@
if (data.isEnabled) {
activationState = QSTileState.ActivationState.ACTIVE
iconRes = R.drawable.qs_extra_dim_icon_on
- secondaryLabel =
- resources
- .getStringArray(R.array.tile_states_reduce_brightness)[Tile.STATE_ACTIVE]
} else {
activationState = QSTileState.ActivationState.INACTIVE
iconRes = R.drawable.qs_extra_dim_icon_off
- secondaryLabel =
- resources
- .getStringArray(R.array.tile_states_reduce_brightness)[Tile.STATE_INACTIVE]
}
icon = Icon.Loaded(resources.getDrawable(iconRes, theme), null, iconRes)
label =
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/ui/mapper/DataSaverTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/ui/mapper/DataSaverTileMapper.kt
index e15c098..cd138f2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/ui/mapper/DataSaverTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/ui/mapper/DataSaverTileMapper.kt
@@ -40,11 +40,9 @@
if (isEnabled) {
activationState = QSTileState.ActivationState.ACTIVE
iconRes = R.drawable.qs_data_saver_icon_on
- secondaryLabel = resources.getStringArray(R.array.tile_states_saver)[2]
} else {
activationState = QSTileState.ActivationState.INACTIVE
iconRes = R.drawable.qs_data_saver_icon_off
- secondaryLabel = resources.getStringArray(R.array.tile_states_saver)[1]
}
icon = Icon.Loaded(resources.getDrawable(iconRes, theme), null, iconRes)
contentDescription = label
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/uimodenight/ui/mapper/UiModeNightTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/uimodenight/ui/mapper/UiModeNightTileMapper.kt
index 91c0451..ae112f7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/uimodenight/ui/mapper/UiModeNightTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/uimodenight/ui/mapper/UiModeNightTileMapper.kt
@@ -44,8 +44,6 @@
override fun map(config: QSTileConfig, data: UiModeNightTileModel): QSTileState =
with(data) {
QSTileState.build(resources, theme, config.uiConfig) {
- var shouldSetSecondaryLabel = false
-
if (isPowerSave) {
secondaryLabel =
resources.getString(
@@ -89,11 +87,9 @@
)
} else {
secondaryLabel = null // undefined type of nightModeCustomType
- shouldSetSecondaryLabel = true
}
} else {
secondaryLabel = null
- shouldSetSecondaryLabel = true
}
contentDescription =
@@ -101,19 +97,10 @@
else TextUtils.concat(label, ", ", secondaryLabel)
if (isPowerSave) {
activationState = QSTileState.ActivationState.UNAVAILABLE
- if (shouldSetSecondaryLabel)
- secondaryLabel = resources.getStringArray(R.array.tile_states_dark)[0]
} else {
activationState =
if (isNightMode) QSTileState.ActivationState.ACTIVE
else QSTileState.ActivationState.INACTIVE
-
- if (shouldSetSecondaryLabel) {
- secondaryLabel =
- if (activationState == QSTileState.ActivationState.INACTIVE)
- resources.getStringArray(R.array.tile_states_dark)[1]
- else resources.getStringArray(R.array.tile_states_dark)[2]
- }
}
val iconRes =
diff --git a/packages/SystemUI/src/com/android/systemui/scene/data/repository/DualShadeEducationRepository.kt b/packages/SystemUI/src/com/android/systemui/scene/data/repository/DualShadeEducationRepository.kt
index 55c297a..fec7524 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/data/repository/DualShadeEducationRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/data/repository/DualShadeEducationRepository.kt
@@ -18,8 +18,11 @@
import android.annotation.UserIdInt
import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateMapOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
+import androidx.compose.runtime.snapshots.SnapshotStateMap
+import androidx.compose.ui.unit.IntRect
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.stringPreferencesKey
import com.android.systemui.common.data.datastore.DataStoreWrapper
@@ -27,6 +30,7 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.scene.data.model.DualShadeEducationImpressionModel
+import com.android.systemui.scene.shared.model.DualShadeEducationElement
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
@@ -54,6 +58,14 @@
mutableStateOf(DualShadeEducationImpressionModel())
private set
+ private val _elementBounds: SnapshotStateMap<DualShadeEducationElement, IntRect> =
+ mutableStateMapOf(
+ DualShadeEducationElement.Notifications to IntRect.Zero,
+ DualShadeEducationElement.QuickSettings to IntRect.Zero,
+ )
+ val elementBounds: Map<DualShadeEducationElement, IntRect>
+ get() = _elementBounds
+
private var dataStore: DataStoreWrapper? = null
private var hydrationJob: Job? = null
@@ -106,6 +118,10 @@
persist(Keys.EverShownQuickSettingsTooltip, value)
}
+ fun setElementBounds(element: DualShadeEducationElement, bounds: IntRect) {
+ _elementBounds[element] = bounds
+ }
+
/** Each time data store data changes, passes it to the given [receiver]. */
private suspend fun repeatWhenPrefsChange(
dataStore: DataStoreWrapper,
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/DualShadeEducationInteractor.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/DualShadeEducationInteractor.kt
index b3b7700..2a4b3ce 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/DualShadeEducationInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/DualShadeEducationInteractor.kt
@@ -21,14 +21,16 @@
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshotFlow
+import androidx.compose.ui.unit.IntRect
import com.android.compose.animation.scene.OverlayKey
import com.android.systemui.CoreStartable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.lifecycle.ExclusiveActivatable
import com.android.systemui.scene.data.repository.DualShadeEducationRepository
-import com.android.systemui.scene.domain.model.DualShadeEducationalTooltipModel
+import com.android.systemui.scene.domain.model.DualShadeEducationModel
import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.scene.shared.model.DualShadeEducationElement
import com.android.systemui.scene.shared.model.Overlays
import com.android.systemui.shade.domain.interactor.ShadeModeInteractor
import com.android.systemui.shade.shared.model.ShadeMode
@@ -39,6 +41,7 @@
import dagger.multibindings.ClassKey
import dagger.multibindings.IntoMap
import javax.inject.Inject
+import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.awaitCancellation
import kotlinx.coroutines.coroutineScope
@@ -59,11 +62,13 @@
private val repository: DualShadeEducationRepository,
) : ExclusiveActivatable(), CoreStartable {
- /** The tooltip that needs to be shown, if any. */
- var tooltip: DualShadeEducationalTooltipModel by
- mutableStateOf(DualShadeEducationalTooltipModel.None)
+ /** The education that's still needed, regardless of the tooltip that needs to be shown. */
+ var education: DualShadeEducationModel by mutableStateOf(DualShadeEducationModel.None)
private set
+ val elementBounds: Map<DualShadeEducationElement, IntRect>
+ get() = repository.elementBounds
+
override fun start() {
if (!SceneContainerFlag.isEnabled) {
return
@@ -93,8 +98,8 @@
fun dismissNotificationsShadeTooltip() {
logD(TAG) { "marking notification shade tooltip as dismissed" }
backgroundScope.launch {
- if (tooltip == DualShadeEducationalTooltipModel.ForNotificationsShade) {
- tooltip = DualShadeEducationalTooltipModel.None
+ if (education == DualShadeEducationModel.ForNotificationsShade) {
+ education = DualShadeEducationModel.None
}
}
}
@@ -102,12 +107,19 @@
fun dismissQuickSettingsShadeTooltip() {
logD(TAG) { "marking quick settings shade tooltip as dismissed" }
backgroundScope.launch {
- if (tooltip == DualShadeEducationalTooltipModel.ForQuickSettingsShade) {
- tooltip = DualShadeEducationalTooltipModel.None
+ if (education == DualShadeEducationModel.ForQuickSettingsShade) {
+ education = DualShadeEducationModel.None
}
}
}
+ fun onDualShadeEducationElementBoundsChange(
+ element: DualShadeEducationElement,
+ bounds: IntRect,
+ ) {
+ repository.setElementBounds(element, bounds)
+ }
+
/** Keeps the repository data fresh for the selected user. */
private suspend fun hydrateRepository() {
repeatWhenUserSelected { selectedUserId -> repository.activateFor(selectedUserId) }
@@ -132,18 +144,11 @@
launch {
repeatWhenTooltipStillNeedsToBeShown(forOverlay = Overlays.NotificationsShade) {
repeatWhenOverlayShown(Overlays.QuickSettingsShade) {
- logD(TAG) {
- "${Overlays.QuickSettingsShade.debugName} shown, waiting ${TOOLTIP_APPEARANCE_DELAY_MS}ms"
- }
repository.setEverShownQuickSettingsShade(true)
- delay(TOOLTIP_APPEARANCE_DELAY_MS)
- logD(TAG) {
- "Done waiting ${TOOLTIP_APPEARANCE_DELAY_MS}ms after ${Overlays.QuickSettingsShade.debugName} was shown"
- }
- logD(TAG) {
- "Showing tooltip for ${Overlays.NotificationsShade.debugName}"
- }
- tooltip = DualShadeEducationalTooltipModel.ForNotificationsShade
+ showTooltip(
+ shownOverlay = Overlays.QuickSettingsShade,
+ overlayToEducateAbout = Overlays.NotificationsShade,
+ )
}
}
}
@@ -151,18 +156,11 @@
launch {
repeatWhenTooltipStillNeedsToBeShown(forOverlay = Overlays.QuickSettingsShade) {
repeatWhenOverlayShown(Overlays.NotificationsShade) {
- logD(TAG) {
- "${Overlays.NotificationsShade.debugName} shown, waiting ${TOOLTIP_APPEARANCE_DELAY_MS}ms"
- }
repository.setEverShownNotificationsShade(true)
- delay(TOOLTIP_APPEARANCE_DELAY_MS)
- logD(TAG) {
- "Done waiting ${TOOLTIP_APPEARANCE_DELAY_MS}ms after ${Overlays.NotificationsShade.debugName} was shown"
- }
- logD(TAG) {
- "Showing tooltip for ${Overlays.QuickSettingsShade.debugName}"
- }
- tooltip = DualShadeEducationalTooltipModel.ForQuickSettingsShade
+ showTooltip(
+ shownOverlay = Overlays.NotificationsShade,
+ overlayToEducateAbout = Overlays.QuickSettingsShade,
+ )
}
}
}
@@ -260,6 +258,31 @@
}
}
+ private suspend fun showTooltip(shownOverlay: OverlayKey, overlayToEducateAbout: OverlayKey) {
+ try {
+ logD(TAG) {
+ "${shownOverlay.debugName} shown, waiting ${TOOLTIP_APPEARANCE_DELAY_MS}ms before starting to educate about ${overlayToEducateAbout.debugName}"
+ }
+
+ delay(TOOLTIP_APPEARANCE_DELAY_MS)
+ logD(TAG) {
+ "Done waiting ${TOOLTIP_APPEARANCE_DELAY_MS}ms, showing tooltip for ${overlayToEducateAbout.debugName}"
+ }
+ education = DualShadeEducationModel.None
+ education =
+ when (overlayToEducateAbout) {
+ Overlays.NotificationsShade -> DualShadeEducationModel.ForNotificationsShade
+ Overlays.QuickSettingsShade -> DualShadeEducationModel.ForQuickSettingsShade
+ else -> DualShadeEducationModel.None
+ }
+ } catch (e: CancellationException) {
+ logD(TAG) {
+ "Canceled education for ${overlayToEducateAbout.debugName}, resetting state"
+ }
+ education = DualShadeEducationModel.None
+ }
+ }
+
companion object {
private const val TAG = "DualShadeEducation"
@VisibleForTesting const val TOOLTIP_APPEARANCE_DELAY_MS = 5000L
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/model/DualShadeEducationalTooltipModel.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/model/DualShadeEducationModel.kt
similarity index 88%
rename from packages/SystemUI/src/com/android/systemui/scene/domain/model/DualShadeEducationalTooltipModel.kt
rename to packages/SystemUI/src/com/android/systemui/scene/domain/model/DualShadeEducationModel.kt
index f63e132..5aef723 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/model/DualShadeEducationalTooltipModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/model/DualShadeEducationModel.kt
@@ -16,8 +16,8 @@
package com.android.systemui.scene.domain.model
-/** Enumerates the type of tooltip to show. */
-enum class DualShadeEducationalTooltipModel {
+/** Enumerates the type of education still needed. */
+enum class DualShadeEducationModel {
None,
ForNotificationsShade,
ForQuickSettingsShade,
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
index daaa2db..24f334e 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
@@ -47,6 +47,7 @@
import com.android.systemui.keyguard.domain.interactor.WindowManagerLockscreenVisibilityInteractor.Companion.keyguardContent
import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.model.SceneContainerPlugin
+import com.android.systemui.model.SceneContainerPluginImpl
import com.android.systemui.model.SysUiState
import com.android.systemui.model.updateFlags
import com.android.systemui.plugins.FalsingManager
@@ -767,7 +768,7 @@
)
}
.map { sceneContainerPluginState ->
- SceneContainerPlugin.EvaluatorByFlag.map { (flag, evaluator) ->
+ SceneContainerPluginImpl.EvaluatorByFlag.map { (flag, evaluator) ->
flag to evaluator(sceneContainerPluginState)
}
.toMap()
diff --git a/packages/SystemUI/src/com/android/systemui/topwindoweffects/data/entity/SqueezeEffectCornerResourceId.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/model/DualShadeEducationElement.kt
similarity index 82%
rename from packages/SystemUI/src/com/android/systemui/topwindoweffects/data/entity/SqueezeEffectCornerResourceId.kt
rename to packages/SystemUI/src/com/android/systemui/scene/shared/model/DualShadeEducationElement.kt
index a55301e..d07c24f 100644
--- a/packages/SystemUI/src/com/android/systemui/topwindoweffects/data/entity/SqueezeEffectCornerResourceId.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/shared/model/DualShadeEducationElement.kt
@@ -14,6 +14,9 @@
* limitations under the License.
*/
-package com.android.systemui.topwindoweffects.data.entity
+package com.android.systemui.scene.shared.model
-data class SqueezeEffectCornerResourceId(val top: Int, val bottom: Int)
+enum class DualShadeEducationElement {
+ Notifications,
+ QuickSettings,
+}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt
index 602e4fb..e7073d9 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt
@@ -25,6 +25,7 @@
import androidx.activity.OnBackPressedDispatcher
import androidx.activity.OnBackPressedDispatcherOwner
import androidx.activity.setViewTreeOnBackPressedDispatcherOwner
+import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
@@ -37,6 +38,7 @@
import com.android.systemui.common.ui.compose.windowinsets.CutoutLocation
import com.android.systemui.common.ui.compose.windowinsets.DisplayCutout
import com.android.systemui.common.ui.compose.windowinsets.ScreenDecorProvider
+import com.android.systemui.compose.modifiers.sysUiResTagContainer
import com.android.systemui.lifecycle.WindowLifecycleState
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.lifecycle.setSnapshotBinding
@@ -200,6 +202,7 @@
dataSourceDelegator = dataSourceDelegator,
qsSceneAdapter = qsSceneAdapter,
sceneJankMonitorFactory = sceneJankMonitorFactory,
+ modifier = Modifier.sysUiResTagContainer(),
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/DualShadeEducationalTooltipViewModel.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/DualShadeEducationalTooltipViewModel.kt
index d843d90..e712e72 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/DualShadeEducationalTooltipViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/DualShadeEducationalTooltipViewModel.kt
@@ -17,14 +17,13 @@
package com.android.systemui.scene.ui.viewmodel
import androidx.compose.runtime.Stable
+import androidx.compose.ui.unit.IntRect
@Stable
interface DualShadeEducationalTooltipViewModel {
val text: String
- /** Whether the tooltip should be shown on the start-side of the top of the display. */
- val isAlignedToStart: Boolean
- /** The bottom position of the UI element underneath which the tooltip should be anchored. */
- val anchorBottomY: Int
+ /** Bounds of the UI element underneath which the tooltip should be anchored. */
+ val anchorBounds: IntRect
/** Notifies that the tooltip has been shown to the user. The UI should call this. */
val onShown: () -> Unit
/** Notifies that the tooltip has been dismissed by the user. The UI should call this. */
diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/DualShadeEducationalTooltipsViewModel.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/DualShadeEducationalTooltipsViewModel.kt
index d854c94..426dcb9 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/DualShadeEducationalTooltipsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/DualShadeEducationalTooltipsViewModel.kt
@@ -17,37 +17,25 @@
package com.android.systemui.scene.ui.viewmodel
import android.content.Context
-import androidx.compose.runtime.getValue
+import androidx.compose.ui.unit.IntRect
import com.android.systemui.lifecycle.ExclusiveActivatable
-import com.android.systemui.lifecycle.Hydrator
import com.android.systemui.res.R
import com.android.systemui.scene.domain.interactor.DualShadeEducationInteractor
-import com.android.systemui.scene.domain.model.DualShadeEducationalTooltipModel
-import com.android.systemui.statusbar.ui.SystemBarUtilsState
+import com.android.systemui.scene.domain.model.DualShadeEducationModel
+import com.android.systemui.scene.shared.model.DualShadeEducationElement
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlinx.coroutines.awaitCancellation
import kotlinx.coroutines.coroutineScope
-import kotlinx.coroutines.launch
class DualShadeEducationalTooltipsViewModel
@AssistedInject
constructor(
- systemBarUtilsState: SystemBarUtilsState,
private val interactor: DualShadeEducationInteractor,
@Assisted private val context: Context,
) : ExclusiveActivatable() {
- private val hydrator = Hydrator("DualShadeEducationalTooltipsViewModel.hydrator")
-
- private val statusBarHeight: Int by
- hydrator.hydratedStateOf(
- traceName = "statusBarHeight",
- source = systemBarUtilsState.statusBarHeight,
- initialValue = 0,
- )
-
/**
* The tooltip to show, or `null` if none should be shown.
*
@@ -57,27 +45,23 @@
*/
val visibleTooltip: DualShadeEducationalTooltipViewModel?
get() =
- when (interactor.tooltip) {
- DualShadeEducationalTooltipModel.ForNotificationsShade ->
- notificationsTooltip(statusBarHeight)
- DualShadeEducationalTooltipModel.ForQuickSettingsShade ->
- quickSettingsTooltip(statusBarHeight)
- DualShadeEducationalTooltipModel.None -> null
+ when (interactor.education) {
+ DualShadeEducationModel.ForNotificationsShade -> notificationsTooltip()
+ DualShadeEducationModel.ForQuickSettingsShade -> quickSettingsTooltip()
+ else -> null
}
- override suspend fun onActivated(): Nothing = coroutineScope {
- launch { hydrator.activate() }
- awaitCancellation()
- }
+ override suspend fun onActivated(): Nothing = coroutineScope { awaitCancellation() }
- private fun notificationsTooltip(statusBarHeight: Int): DualShadeEducationalTooltipViewModel {
+ private fun notificationsTooltip(): DualShadeEducationalTooltipViewModel? {
+ val bounds =
+ interactor.elementBounds[DualShadeEducationElement.Notifications] ?: return null
+
return object : DualShadeEducationalTooltipViewModel {
override val text: String =
context.getString(R.string.dual_shade_educational_tooltip_notifs)
- override val isAlignedToStart: Boolean = true
-
- override val anchorBottomY: Int = statusBarHeight
+ override val anchorBounds: IntRect = bounds
override val onShown: () -> Unit = interactor::recordNotificationsShadeTooltipImpression
@@ -85,14 +69,15 @@
}
}
- private fun quickSettingsTooltip(statusBarHeight: Int): DualShadeEducationalTooltipViewModel {
+ private fun quickSettingsTooltip(): DualShadeEducationalTooltipViewModel? {
+ val bounds =
+ interactor.elementBounds[DualShadeEducationElement.QuickSettings] ?: return null
+
return object : DualShadeEducationalTooltipViewModel {
override val text: String =
context.getString(R.string.dual_shade_educational_tooltip_qs)
- override val isAlignedToStart: Boolean = false
-
- override val anchorBottomY: Int = statusBarHeight
+ override val anchorBounds: IntRect = bounds
override val onShown: () -> Unit = interactor::recordQuickSettingsShadeTooltipImpression
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/proxy/ScreenshotProxyClient.kt b/packages/SystemUI/src/com/android/systemui/screenshot/proxy/ScreenshotProxyClient.kt
index ca109fd..af48dd2 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/proxy/ScreenshotProxyClient.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/proxy/ScreenshotProxyClient.kt
@@ -19,6 +19,7 @@
import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
+import android.os.UserHandle
import android.util.Log
import android.view.Display
import com.android.internal.infra.ServiceConnector
@@ -38,7 +39,7 @@
context,
Intent(context, ScreenshotProxyService::class.java),
Context.BIND_AUTO_CREATE or Context.BIND_WAIVE_PRIORITY or Context.BIND_NOT_VISIBLE,
- context.userId,
+ UserHandle.USER_SYSTEM,
IScreenshotProxy.Stub::asInterface,
)
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/proxy/ScreenshotProxyService.kt b/packages/SystemUI/src/com/android/systemui/screenshot/proxy/ScreenshotProxyService.kt
index be1c568..c95dde7 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/proxy/ScreenshotProxyService.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/proxy/ScreenshotProxyService.kt
@@ -38,9 +38,11 @@
private val mExpansionMgr: ShadeExpansionStateManager,
@Main private val mMainDispatcher: CoroutineDispatcher,
private val activityStarter: ActivityStarter,
- private val focusedDisplayRepository: FocusedDisplayRepository,
+ focusedDisplayRepository: FocusedDisplayRepository,
) : LifecycleService() {
+ private val focusedDisplayId = focusedDisplayRepository.focusedDisplayId
+
private val mBinder: IBinder =
object : IScreenshotProxy.Stub() {
/** @return true when the notification shade is partially or fully expanded. */
@@ -56,7 +58,7 @@
}
}
- override fun getFocusedDisplay(): Int = focusedDisplayRepository.focusedDisplayId.value
+ override fun getFocusedDisplay(): Int = focusedDisplayId.value
}
private suspend fun executeAfterDismissing(callback: IOnDoneCallback) =
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index f67b100..5a325c9 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -210,7 +210,6 @@
import kotlin.Unit;
import kotlinx.coroutines.CoroutineDispatcher;
-import kotlinx.coroutines.flow.Flow;
import kotlinx.coroutines.flow.MutableStateFlow;
import kotlinx.coroutines.flow.StateFlow;
@@ -3124,11 +3123,6 @@
}
@Override
- public Flow<Float> getLegacyPanelExpansion() {
- return mShadeRepository.getLegacyShadeExpansion();
- }
-
- @Override
public boolean isFullyExpanded() {
return mExpandedHeight >= getMaxPanelTransitionDistance();
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
index f793932..4714b0e 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
@@ -364,7 +364,7 @@
);
}
- if (!SceneContainerFlag.isEnabled() && Flags.useTransitionsForKeyguardOccluded()) {
+ if (!SceneContainerFlag.isEnabled()) {
collectFlow(
mWindowRootView,
mNotificationShadeWindowModel.isKeyguardOccluded(),
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeExpansionStateManager.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeExpansionStateManager.kt
index 5fab889..67fd70a 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeExpansionStateManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeExpansionStateManager.kt
@@ -29,11 +29,7 @@
import java.util.concurrent.CopyOnWriteArrayList
import javax.inject.Inject
-/**
- * A class responsible for managing the notification panel's current state.
- *
- * TODO(b/200063118): Make this class the one source of truth for the state of panel expansion.
- */
+/** A class responsible for managing the notification panel's current state. */
@SysUISingleton
@Deprecated("Use ShadeInteractor instead")
class ShadeExpansionStateManager @Inject constructor() {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt
index 04e812b..a935650 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt
@@ -74,6 +74,7 @@
import com.android.systemui.statusbar.phone.StatusBarLocation
import com.android.systemui.statusbar.phone.StatusIconContainer
import com.android.systemui.statusbar.phone.StatusOverlayHoverListenerFactory
+import com.android.systemui.statusbar.phone.domain.interactor.IsAreaDark
import com.android.systemui.statusbar.phone.ui.StatusBarIconController
import com.android.systemui.statusbar.phone.ui.TintedIconManager
import com.android.systemui.statusbar.pipeline.battery.ui.composable.BatteryWithEstimate
@@ -255,14 +256,10 @@
private val insetListener =
View.OnApplyWindowInsetsListener { view, insets ->
- val windowInsets = WindowInsets(insets)
- if (windowInsets != lastInsets) {
- updateConstraintsForInsets(view as MotionLayout, insets)
- lastInsets = windowInsets
- view.onApplyWindowInsets(insets)
- } else {
- insets
- }
+ updateConstraintsForInsets(view as MotionLayout, insets)
+ lastInsets = WindowInsets(insets)
+
+ view.onApplyWindowInsets(insets)
}
private var singleCarrier = false
@@ -375,7 +372,7 @@
BatteryWithEstimate(
modifier = Modifier.wrapContentSize(),
viewModelFactory = batteryViewModelFactory,
- isDark = { true },
+ isDarkProvider = { IsAreaDark { true } },
showEstimate = showBatteryEstimate,
)
}
@@ -579,9 +576,6 @@
systemIconsHoverContainer.setOnClickListener(null)
systemIconsHoverContainer.isClickable = false
}
-
- lastInsets?.let { updateConstraintsForInsets(header, it) }
-
header.jumpToState(header.startState)
updatePosition()
updateScrollY()
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewControllerEmptyImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewControllerEmptyImpl.kt
index 2976a1f..18f5279 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewControllerEmptyImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewControllerEmptyImpl.kt
@@ -25,7 +25,6 @@
import java.util.function.Consumer
import javax.inject.Inject
import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.flowOf
/** Empty implementation of ShadeViewController for variants with no shade. */
open class ShadeViewControllerEmptyImpl @Inject constructor() :
@@ -111,8 +110,6 @@
override val shadeHeadsUpTracker = ShadeHeadsUpTrackerEmptyImpl()
override val shadeFoldAnimator = ShadeFoldAnimatorEmptyImpl()
- @Deprecated("Use SceneInteractor.currentScene instead.")
- override val legacyPanelExpansion = flowOf(0f)
override val udfpsTransitionToFullShadeProgress = MutableStateFlow(0f)
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/PanelExpansionInteractor.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/PanelExpansionInteractor.kt
index 3520948..d06655d 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/PanelExpansionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/PanelExpansionInteractor.kt
@@ -17,7 +17,6 @@
package com.android.systemui.shade.domain.interactor
import com.android.systemui.dagger.SysUISingleton
-import kotlinx.coroutines.flow.Flow
/**
* Expansion-related methods used throughout SysUI before the addition of the scene container as the
@@ -30,16 +29,6 @@
@Deprecated("Use ShadeInteractor instead.")
interface PanelExpansionInteractor {
/**
- * The amount by which the "panel" has been expanded (`0` when fully collapsed, `1` when fully
- * expanded).
- *
- * This is a legacy concept from the time when the "panel" included the notification/QS shades
- * as well as the keyguard (lockscreen and bouncer). This value is meant only for
- * backwards-compatibility and should not be consumed by newer code.
- */
- @Deprecated("Use SceneInteractor.currentScene instead.") val legacyPanelExpansion: Flow<Float>
-
- /**
* Returns whether the shade height is greater than zero or the shade is expecting a synthesized
* down event.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/PanelExpansionInteractorImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/PanelExpansionInteractorImpl.kt
index c447a19..e7687c9 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/PanelExpansionInteractorImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/PanelExpansionInteractorImpl.kt
@@ -16,18 +16,11 @@
package com.android.systemui.shade.domain.interactor
-import com.android.compose.animation.scene.ContentKey
-import com.android.compose.animation.scene.ObservableTransitionState
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.scene.domain.interactor.SceneInteractor
-import com.android.systemui.scene.shared.model.Overlays
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.statusbar.SysuiStatusBarStateController
import javax.inject.Inject
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.flatMapLatest
-import kotlinx.coroutines.flow.flowOf
-import kotlinx.coroutines.flow.map
@SysUISingleton
class PanelExpansionInteractorImpl
@@ -39,58 +32,6 @@
private val statusBarStateController: SysuiStatusBarStateController,
) : PanelExpansionInteractor {
- /**
- * The amount by which the "panel" has been expanded (`0` when fully collapsed, `1` when fully
- * expanded).
- *
- * This is a legacy concept from the time when the "panel" included the notification/QS shades
- * as well as the keyguard (lockscreen and bouncer). This value is meant only for
- * backwards-compatibility and should not be consumed by newer code.
- */
- @Deprecated("Use SceneInteractor.currentScene instead.")
- override val legacyPanelExpansion: Flow<Float> =
- sceneInteractor.transitionState.flatMapLatest { state ->
- when (state) {
- is ObservableTransitionState.Idle ->
- flowOf(
- if (
- state.currentScene != Scenes.Gone || state.currentOverlays.isNotEmpty()
- ) {
- // When resting on a non-Gone scene, the panel is fully expanded.
- 1f
- } else {
- // When resting on the Gone scene, the panel is considered fully
- // collapsed.
- 0f
- }
- )
- is ObservableTransitionState.Transition ->
- when {
- state.fromContent == Scenes.Gone ->
- if (state.toContent.isExpandable()) {
- // Moving from Gone to a scene that can animate-expand has a
- // panel expansion that tracks with the transition.
- state.progress
- } else {
- // Moving from Gone to a scene that doesn't animate-expand
- // immediately makes the panel fully expanded.
- flowOf(1f)
- }
- state.toContent == Scenes.Gone ->
- if (state.fromContent.isExpandable()) {
- // Moving to Gone from a scene that can animate-expand has a
- // panel expansion that tracks with the transition.
- state.progress.map { 1 - it }
- } else {
- // Moving to Gone from a scene that doesn't animate-expand
- // immediately makes the panel fully collapsed.
- flowOf(0f)
- }
- else -> flowOf(1f)
- }
- }
- }
-
@Deprecated(
"depends on the state you check, use {@link #isShadeFullyExpanded()},\n" +
"{@link #isOnAod()}, {@link #isOnKeyguard()} instead."
@@ -128,14 +69,4 @@
// TODO(b/325936094) if a HUN is showing, return false
return sceneInteractor.currentScene.value == Scenes.Lockscreen
}
-
- private fun ContentKey.isExpandable(): Boolean {
- return when (this) {
- Scenes.Shade,
- Scenes.QuickSettings,
- Overlays.NotificationsShade,
- Overlays.QuickSettingsShade -> true
- else -> false
- }
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractor.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractor.kt
index 479f296..0506d9d 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractor.kt
@@ -17,7 +17,6 @@
package com.android.systemui.shade.domain.interactor
import android.provider.Settings
-import androidx.annotation.FloatRange
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.log.table.TableLogBuffer
@@ -68,20 +67,6 @@
/** Convenience shortcut for querying whether the current [shadeMode] is [ShadeMode.Split]. */
val isSplitShade: Boolean
get() = shadeMode.value is ShadeMode.Split
-
- /**
- * The fraction between [0..1] (i.e., percentage) of screen width to consider the threshold
- * between "top-left" and "top-right" for the purposes of dual-shade invocation.
- *
- * When the dual-shade is not wide, this always returns 0.5 (the top edge is evenly split). On
- * wide layouts however, a larger fraction is returned because only the area of the system
- * status icons is considered top-right.
- *
- * Note that this fraction only determines the split between the absolute left and right
- * directions. In RTL layouts, the "top-start" edge will resolve to "top-right", and "top-end"
- * will resolve to "top-left".
- */
- @FloatRange(from = 0.0, to = 1.0) fun getTopEdgeSplitFraction(): Float
}
class ShadeModeInteractorImpl
@@ -117,8 +102,6 @@
.logDiffsForTable(tableLogBuffer = tableLogBuffer, initialValue = shadeModeInitialValue)
.stateIn(applicationScope, SharingStarted.Eagerly, initialValue = shadeModeInitialValue)
- @FloatRange(from = 0.0, to = 1.0) override fun getTopEdgeSplitFraction(): Float = 0.5f
-
private fun determineShadeMode(
isDualShadeEnabled: Boolean,
isShadeLayoutWide: Boolean,
@@ -141,6 +124,4 @@
override val shadeMode: StateFlow<ShadeMode> = MutableStateFlow(ShadeMode.Single)
override val isShadeLayoutWide: StateFlow<Boolean> = MutableStateFlow(false)
-
- override fun getTopEdgeSplitFraction(): Float = 0.5f
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/startable/ShadeStartable.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/startable/ShadeStartable.kt
index 44f2911..6bafa28 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/startable/ShadeStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/startable/ShadeStartable.kt
@@ -31,8 +31,9 @@
import com.android.systemui.shade.ShadeExpansionStateManager
import com.android.systemui.shade.TouchLogger.Companion.logTouchesTo
import com.android.systemui.shade.data.repository.ShadeRepository
-import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.shade.transition.ScrimShadeTransitionController
+import com.android.systemui.statusbar.NotificationShadeDepthController
import com.android.systemui.statusbar.PulseExpansionHandler
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
import com.android.systemui.statusbar.phone.ScrimController
@@ -53,15 +54,16 @@
@ShadeTouchLog private val touchLog: LogBuffer,
@ShadeDisplayAware private val configurationRepository: ConfigurationRepository,
private val shadeRepository: ShadeRepository,
+ private val shadeInteractorProvider: Provider<ShadeInteractor>,
private val splitShadeStateController: SplitShadeStateController,
private val scrimShadeTransitionController: ScrimShadeTransitionController,
private val sceneInteractorProvider: Provider<SceneInteractor>,
- private val panelExpansionInteractorProvider: Provider<PanelExpansionInteractor>,
private val shadeExpansionStateManager: ShadeExpansionStateManager,
private val pulseExpansionHandler: PulseExpansionHandler,
private val displayStateInteractor: DisplayStateInteractor,
private val nsslc: NotificationStackScrollLayoutController,
private val scrimController: ScrimController,
+ private val depthController: NotificationShadeDepthController,
) : CoreStartable {
override fun start() {
@@ -75,8 +77,10 @@
private fun hydrateShadeExpansionStateManager() {
if (SceneContainerFlag.isEnabled) {
+ val shadeInteractor = shadeInteractorProvider.get()
+
combine(
- panelExpansionInteractorProvider.get().legacyPanelExpansion,
+ shadeInteractor.shadeExpansion,
sceneInteractorProvider.get().isTransitionUserInputOngoing,
) { panelExpansion, tracking ->
shadeExpansionStateManager.onPanelExpansionChanged(
@@ -86,6 +90,16 @@
)
}
.launchIn(applicationScope)
+
+ applicationScope.launch {
+ shadeInteractor.qsExpansion.collect { depthController.qsPanelExpansion = it }
+ }
+
+ applicationScope.launch {
+ shadeInteractor.anyExpansion.collect {
+ depthController.transitionToFullShadeProgress = it
+ }
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/transition/ScrimShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/shade/transition/ScrimShadeTransitionController.kt
index e5349de..1ebaba9 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/transition/ScrimShadeTransitionController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/transition/ScrimShadeTransitionController.kt
@@ -45,7 +45,7 @@
shadeExpansionStateManager.addStateListener(this::onPanelStateChanged)
dumpManager.registerNormalDumpable(
ScrimShadeTransitionController::class.java.simpleName,
- this::dump
+ this::dump,
)
}
@@ -61,15 +61,11 @@
private fun onStateChanged() {
val expansionEvent = lastExpansionEvent ?: return
- val expansionFraction = calculateScrimExpansionFraction(expansionEvent)
+ val expansionFraction = expansionEvent.fraction
scrimController.setRawPanelExpansionFraction(expansionFraction)
lastExpansionFraction = expansionFraction
}
- private fun calculateScrimExpansionFraction(expansionEvent: ShadeExpansionChangeEvent): Float {
- return expansionEvent.fraction
- }
-
private fun dump(printWriter: PrintWriter, args: Array<String>) {
printWriter.println(
"""
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ui/ShadeColors.kt b/packages/SystemUI/src/com/android/systemui/shade/ui/ShadeColors.kt
index 471c8f2..807bf11 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ui/ShadeColors.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ui/ShadeColors.kt
@@ -17,7 +17,6 @@
package com.android.systemui.shade.ui
import android.content.Context
-import android.graphics.Color
import com.android.internal.graphics.ColorUtils
import com.android.systemui.res.R
@@ -25,7 +24,9 @@
@JvmStatic
fun shadePanel(context: Context, blurSupported: Boolean): Int {
return if (blurSupported) {
- shadePanelStandard(context)
+ ColorUtils.compositeColors(
+ shadePanelStandard(context),
+ shadePanelScrimBehind(context))
} else {
shadePanelFallback(context)
}
@@ -42,15 +43,20 @@
@JvmStatic
private fun shadePanelStandard(context: Context): Int {
- val layerAbove = ColorUtils.setAlphaComponent(
- context.getColor(R.color.shade_panel_base),
- (0.4f * 255).toInt()
- )
- val layerBelow = ColorUtils.setAlphaComponent(Color.WHITE, (0.1f * 255).toInt())
+ val layerAbove = context.resources.getColor(
+ com.android.internal.R.color.shade_panel_fg, context.theme)
+ val layerBelow = context.resources.getColor(
+ com.android.internal.R.color.shade_panel_bg, context.theme)
return ColorUtils.compositeColors(layerAbove, layerBelow)
}
@JvmStatic
+ private fun shadePanelScrimBehind(context: Context): Int {
+ return context.resources.getColor(
+ com.android.internal.R.color.shade_panel_scrim, context.theme)
+ }
+
+ @JvmStatic
private fun shadePanelFallback(context: Context): Int {
return context.getColor(R.color.shade_panel_fallback)
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt
index d03f091..71625b3 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt
@@ -27,6 +27,7 @@
import androidx.compose.material3.ColorScheme
import androidx.compose.runtime.getValue
import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.unit.IntRect
import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.compose.animation.scene.OverlayKey
import com.android.systemui.battery.BatteryMeterViewController
@@ -38,7 +39,10 @@
import com.android.systemui.privacy.OngoingPrivacyChip
import com.android.systemui.privacy.PrivacyItem
import com.android.systemui.res.R
+import com.android.systemui.scene.domain.interactor.DualShadeEducationInteractor
import com.android.systemui.scene.domain.interactor.SceneInteractor
+import com.android.systemui.scene.domain.model.DualShadeEducationModel
+import com.android.systemui.scene.shared.model.DualShadeEducationElement
import com.android.systemui.scene.shared.model.Overlays
import com.android.systemui.scene.shared.model.TransitionKeys.SlightlyFasterShadeCollapse
import com.android.systemui.shade.ShadeDisplayAware
@@ -83,6 +87,7 @@
val statusBarIconController: StatusBarIconController,
val kairosNetwork: KairosNetwork,
val mobileIconsViewModelKairos: dagger.Lazy<MobileIconsViewModelKairos>,
+ private val dualShadeEducationInteractor: DualShadeEducationInteractor,
) : ExclusiveActivatable() {
private val hydrator = Hydrator("ShadeHeaderViewModel.hydrator")
@@ -169,6 +174,14 @@
/** Whether or not the privacy chip is enabled in the device privacy config. */
val isPrivacyChipEnabled: StateFlow<Boolean> = privacyChipInteractor.isChipEnabled
+ val animateNotificationsChipBounce: Boolean
+ get() =
+ dualShadeEducationInteractor.education == DualShadeEducationModel.ForNotificationsShade
+
+ val animateSystemIconChipBounce: Boolean
+ get() =
+ dualShadeEducationInteractor.education == DualShadeEducationModel.ForQuickSettingsShade
+
private val longerPattern = context.getString(R.string.abbrev_wday_month_day_no_year_alarm)
private val shorterPattern = context.getString(R.string.abbrev_month_day_no_year)
@@ -261,6 +274,13 @@
)
}
+ fun onDualShadeEducationElementBoundsChange(
+ element: DualShadeEducationElement,
+ bounds: IntRect,
+ ) {
+ dualShadeEducationInteractor.onDualShadeEducationElementBoundsChange(element, bounds)
+ }
+
/** Represents the background highlight of a header icons chip. */
sealed interface HeaderChipHighlight {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt b/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt
index 6d04c27..ace08b1e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt
@@ -198,7 +198,9 @@
* @see android.view.SurfaceControl.Transaction#setBackgroundBlurRadius(SurfaceControl, int)
*/
open fun supportsBlursOnWindows(): Boolean {
- return supportsBlursOnWindowsBase() && crossWindowBlurListeners.isCrossWindowBlurEnabled
+ return supportsBlursOnWindowsBase() &&
+ crossWindowBlurListeners != null &&
+ crossWindowBlurListeners.isCrossWindowBlurEnabled
}
private fun supportsBlursOnWindowsBase(): Boolean {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index bc74a48..8f86d80 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -104,7 +104,6 @@
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor;
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFingerprintAuthInteractor;
import com.android.systemui.dock.DockManager;
-import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.keyguard.KeyguardIndication;
import com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController;
import com.android.systemui.keyguard.ScreenLifecycle;
@@ -220,6 +219,7 @@
private boolean mBatteryDefender;
/** Whether the battery defender is triggered with the device plugged. */
private boolean mEnableBatteryDefender;
+ private boolean mBatteryDead;
private boolean mIncompatibleCharger;
private int mChargingWattage;
private int mBatteryLevel = -1;
@@ -280,7 +280,6 @@
// triggered while the device is asleep
private final AlarmTimeout mHideTransientMessageHandler;
private final AlarmTimeout mHideBiometricMessageHandler;
- private final FeatureFlags mFeatureFlags;
private final IndicationHelper mIndicationHelper;
private final DeviceEntryBiometricSettingsInteractor mDeviceEntryBiometricSettingsInteractor;
@@ -316,7 +315,6 @@
AlarmManager alarmManager,
UserTracker userTracker,
BouncerMessageInteractor bouncerMessageInteractor,
- FeatureFlags flags,
IndicationHelper indicationHelper,
DeviceEntryBiometricSettingsInteractor deviceEntryBiometricSettingsInteractor,
KeyguardInteractor keyguardInteractor,
@@ -349,7 +347,6 @@
mAlternateBouncerInteractor = alternateBouncerInteractor;
mUserTracker = userTracker;
mBouncerMessageInteractor = bouncerMessageInteractor;
- mFeatureFlags = flags;
mIndicationHelper = indicationHelper;
mDeviceEntryBiometricSettingsInteractor = deviceEntryBiometricSettingsInteractor;
mKeyguardInteractor = keyguardInteractor;
@@ -435,8 +432,7 @@
mLockScreenIndicationView,
mExecutor,
mStatusBarStateController,
- mKeyguardLogger,
- mFeatureFlags
+ mKeyguardLogger
);
updateDeviceEntryIndication(false /* animate */);
updateOrganizedOwnedDevice();
@@ -1167,6 +1163,12 @@
if (mPowerCharged) {
return mContext.getResources().getString(R.string.keyguard_charged);
}
+
+ String percentage = NumberFormat.getPercentInstance().format(mBatteryLevel / 100f);
+ if (mBatteryDead) {
+ return mContext.getResources().getString(R.string.keyguard_plugged_in, percentage);
+ }
+
final boolean hasChargingTime = mChargingTimeRemaining > 0;
int chargingId;
if (mPowerPluggedInWired) {
@@ -1201,7 +1203,6 @@
: R.string.keyguard_plugged_in;
}
- String percentage = NumberFormat.getPercentInstance().format(mBatteryLevel / 100f);
if (hasChargingTime) {
String chargingTimeFormatted = Formatter.formatShortElapsedTimeRoundingUpToMinutes(
mContext, mChargingTimeRemaining);
@@ -1361,6 +1362,7 @@
mBatteryLevel = status.level;
mBatteryPresent = status.present;
mBatteryDefender = isBatteryDefender(status);
+ mBatteryDead = status.isDead();
// when the battery is overheated, device doesn't charge so only guard on pluggedIn:
mEnableBatteryDefender = mBatteryDefender && status.isPluggedIn();
mIncompatibleCharger = status.incompatibleCharger.orElse(false);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
index e500b51..9128b35 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
@@ -29,6 +29,7 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.qs.ui.adapter.QSSceneAdapter
import com.android.systemui.res.R
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.shade.data.repository.ShadeRepository
import com.android.systemui.shade.domain.interactor.ShadeInteractor
@@ -412,7 +413,11 @@
set(value) {
if (field != value || forceApplyAmount) {
field = value
- if (!nsslController.isInLockedDownShade() || field == 0f || forceApplyAmount) {
+ if (SceneContainerFlag.isEnabled) {
+ // TODO(b/359957196) do we need to do anything here?
+ } else if (
+ !nsslController.isInLockedDownShade() || field == 0f || forceApplyAmount
+ ) {
fractionToShade =
MathUtils.saturate(dragDownAmount / notificationShelfTransitionDistance)
shadeRepository.setLockscreenShadeExpansion(fractionToShade)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
index e52b42a..55fb66c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
@@ -15,7 +15,6 @@
*/
package com.android.systemui.statusbar;
-import static android.app.Flags.keyguardPrivateNotifications;
import static android.app.StatusBarManager.ACTION_KEYGUARD_PRIVATE_NOTIFICATIONS_CHANGED;
import static android.app.StatusBarManager.EXTRA_KM_PRIVATE_NOTIFS_ALLOWED;
import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED;
@@ -241,14 +240,6 @@
});
} else if (profileAvailabilityActions(action)) {
updateCurrentProfilesCache();
- } else if (Objects.equals(action, Intent.ACTION_USER_UNLOCKED)) {
- if (!keyguardPrivateNotifications()) {
- // Start the overview connection to the launcher service
- // Connect if user hasn't connected yet
- if (mLauncherProxyServiceLazy.get().getProxy() == null) {
- mLauncherProxyServiceLazy.get().startConnectionToCurrentUser();
- }
- }
} else if (Objects.equals(action, NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION)) {
final IntentSender intentSender = intent.getParcelableExtra(
Intent.EXTRA_INTENT);
@@ -382,9 +373,7 @@
dumpManager.registerDumpable(this);
- if (keyguardPrivateNotifications()) {
- init();
- }
+ init();
// To avoid dependency injection cycle, finish constructing this object before using the
// KeyguardInteractor. The CoroutineScope will only be null in tests.
@@ -417,10 +406,6 @@
public void setUpWithPresenter(NotificationPresenter presenter) {
mPresenter = presenter;
-
- if (!keyguardPrivateNotifications()) {
- init();
- }
}
private void init() {
@@ -498,11 +483,10 @@
mBroadcastDispatcher.registerReceiver(mAllUsersReceiver,
new IntentFilter(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED),
mBackgroundExecutor, UserHandle.ALL);
- if (keyguardPrivateNotifications()) {
- mBroadcastDispatcher.registerReceiver(mKeyguardReceiver,
- new IntentFilter(ACTION_KEYGUARD_PRIVATE_NOTIFICATIONS_CHANGED),
- mBackgroundExecutor, UserHandle.ALL);
- }
+
+ mBroadcastDispatcher.registerReceiver(mKeyguardReceiver,
+ new IntentFilter(ACTION_KEYGUARD_PRIVATE_NOTIFICATIONS_CHANGED),
+ mBackgroundExecutor, UserHandle.ALL);
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_USER_ADDED);
@@ -541,9 +525,7 @@
false, mLockScreenUris, 0, UserHandle.of(userId));
updateDpcSettings(userId);
- if (keyguardPrivateNotifications()) {
- updateGlobalKeyguardSettings();
- }
+ updateGlobalKeyguardSettings();
}
public boolean shouldShowLockscreenNotifications() {
@@ -571,12 +553,8 @@
boolean show;
boolean allowedByDpm;
- if (keyguardPrivateNotifications()) {
- show = mUsersUsersAllowingNotifications.get(mCurrentUserId);
- } else {
- show = mUsersUsersAllowingNotifications.get(mCurrentUserId)
- && mKeyguardAllowingNotifications;
- }
+ show = mUsersUsersAllowingNotifications.get(mCurrentUserId);
+
// If DPC never notified us about a user, that means they have no policy for the user,
// and they allow the behavior
allowedByDpm = mUsersDpcAllowingNotifications.get(mCurrentUserId, true);
@@ -610,12 +588,7 @@
userId) != 0;
mUsersUsersAllowingNotifications.put(userId, newAllowLockscreen);
- if (keyguardPrivateNotifications()) {
- return (newAllowLockscreen != originalAllowLockscreen);
- } else {
- boolean keyguardChanged = updateGlobalKeyguardSettings();
- return (newAllowLockscreen != originalAllowLockscreen) || keyguardChanged;
- }
+ return (newAllowLockscreen != originalAllowLockscreen);
}
@WorkerThread
@@ -674,14 +647,9 @@
Log.i(TAG, "Asking for redact notifs dpm override too early", new Throwable());
return false;
}
- if (keyguardPrivateNotifications()) {
- return mUsersUsersAllowingPrivateNotifications.get(userHandle)
- && mUsersDpcAllowingPrivateNotifications.get(userHandle)
- && mKeyguardAllowingNotifications;
- } else {
- return mUsersUsersAllowingPrivateNotifications.get(userHandle)
- && mUsersDpcAllowingPrivateNotifications.get(userHandle);
- }
+ return mUsersUsersAllowingPrivateNotifications.get(userHandle)
+ && mUsersDpcAllowingPrivateNotifications.get(userHandle)
+ && mKeyguardAllowingNotifications;
}
/**
@@ -750,14 +718,8 @@
Log.wtf(TAG, "Asking for show notifs dpm override too early", new Throwable());
updateDpcSettings(userHandle);
}
- if (keyguardPrivateNotifications()) {
- return mUsersUsersAllowingNotifications.get(userHandle)
- && mUsersDpcAllowingNotifications.get(userHandle);
- } else {
- return mUsersUsersAllowingNotifications.get(userHandle)
- && mUsersDpcAllowingNotifications.get(userHandle)
- && mKeyguardAllowingNotifications;
- }
+ return mUsersUsersAllowingNotifications.get(userHandle)
+ && mUsersDpcAllowingNotifications.get(userHandle);
}
/**
@@ -790,7 +752,7 @@
if (notificationRequestsRedaction && isNotifRedacted) {
return REDACTION_TYPE_PUBLIC;
}
- if (keyguardPrivateNotifications() && !mKeyguardAllowingNotifications) {
+ if (!mKeyguardAllowingNotifications) {
return REDACTION_TYPE_PUBLIC;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
index f5ab110..e8fe0d2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
@@ -275,7 +275,13 @@
shadeRadius = 0f
}
+ if (spatialModelAppPushback()) {
+ // Brightness slider removes blur
+ shadeRadius *= (1 - brightnessMirrorSpring.ratio)
+ }
+
var blur = shadeRadius.toInt()
+
// If the blur comes from waking up, we don't want to zoom out the background
val zoomOut =
when {
@@ -297,8 +303,12 @@
blur = 0
}
- // Brightness slider removes blur, but doesn't affect zooms
- blur = (blur * (1f - brightnessMirrorSpring.ratio)).toInt()
+ if (!spatialModelAppPushback()) {
+ // Brightness slider removes blur, but doesn't affect zooms. This is the legacy behavior
+ // that zoom out is only applied to the wallpaper (no homescreen, app or all apps
+ // zoom out). The new behavior is under the same flag when it's on a few lines above.
+ blur = (blur * (1f - brightnessMirrorSpring.ratio)).toInt()
+ }
return Pair(blur, zoomOut)
}
@@ -455,6 +465,10 @@
}
shadeAnimation.setStiffness(SpringForce.STIFFNESS_LOW)
shadeAnimation.setDampingRatio(SpringForce.DAMPING_RATIO_NO_BOUNCY)
+ if (spatialModelAppPushback()) {
+ brightnessMirrorSpring.setStiffness(SpringForce.STIFFNESS_LOW)
+ brightnessMirrorSpring.setDampingRatio(SpringForce.DAMPING_RATIO_NO_BOUNCY)
+ }
applicationScope.launch {
wallpaperInteractor.wallpaperSupportsAmbientMode.collect { supported ->
wallpaperSupportsAmbientMode = supported
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index c2a87cd..2b278a4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -690,6 +690,10 @@
StatusBarIconView icon = NotificationBundleUi.isEnabled()
? row.getEntryAdapter().getIcons().getShelfIcon()
: row.getEntryLegacy().getIcons().getShelfIcon();
+ if (icon == null) {
+ // TODO(b/399736937) remove this when adding bundle icon implementation
+ return;
+ }
boolean needsContinuousClipping = ViewState.isAnimatingY(icon) && !mAmbientState.isDozing();
boolean isContinuousClipping = icon.getTag(TAG_CONTINUOUS_CLIPPING) != null;
if (needsContinuousClipping && !isContinuousClipping) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationUiAdjustment.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationUiAdjustment.java
index 3f74aaf..e398bc3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationUiAdjustment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationUiAdjustment.java
@@ -39,11 +39,12 @@
public final List<Notification.Action> smartActions;
public final List<CharSequence> smartReplies;
public final boolean isConversation;
+ public final boolean isBundled;
@VisibleForTesting
NotificationUiAdjustment(
String key, List<Notification.Action> smartActions, List<CharSequence> smartReplies,
- boolean isConversation) {
+ boolean isConversation, boolean isBundled) {
this.key = key;
this.smartActions = smartActions == null
? Collections.emptyList()
@@ -52,13 +53,15 @@
? Collections.emptyList()
: smartReplies;
this.isConversation = isConversation;
+ this.isBundled = isBundled;
}
public static NotificationUiAdjustment extractFromNotificationEntry(
NotificationEntry entry) {
return new NotificationUiAdjustment(
entry.getKey(), entry.getSmartActions(), entry.getSmartReplies(),
- entry.getRanking().isConversation());
+ entry.getRanking().isConversation(),
+ entry.isBundled());
}
public static boolean needReinflate(
@@ -76,6 +79,9 @@
if (!newAdjustment.smartReplies.equals(oldAdjustment.smartReplies)) {
return true;
}
+ if (oldAdjustment.isBundled != newAdjustment.isBundled) {
+ return true;
+ }
return false;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
index 6bec86a..48f0c1f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
@@ -201,9 +201,7 @@
reloadDimens();
maybeUpdateIconScaleDimens();
- if (Flags.statusBarMonochromeIconsFix()) {
- setCropToPadding(true);
- }
+ setCropToPadding(true);
}
/** Should always be preceded by {@link #reloadDimens()} */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/StatusBarChipToHunAnimation.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/StatusBarChipToHunAnimation.kt
new file mode 100644
index 0000000..13da87b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/StatusBarChipToHunAnimation.kt
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.chips
+
+import com.android.systemui.Flags
+import com.android.systemui.flags.FlagToken
+import com.android.systemui.flags.RefactorFlagUtils
+
+/** Helper for reading or using the status bar chip to hun animation flag state. */
+@Suppress("NOTHING_TO_INLINE")
+object StatusBarChipToHunAnimation {
+ /** The aconfig flag name */
+ const val FLAG_NAME = Flags.FLAG_STATUS_BAR_CHIP_TO_HUN_ANIMATION
+
+ /** A token used for dependency declaration */
+ val token: FlagToken
+ get() = FlagToken(FLAG_NAME, isEnabled)
+
+ /** Is the refactor enabled */
+ @JvmStatic
+ inline val isEnabled
+ get() = Flags.statusBarChipToHunAnimation()
+
+ /**
+ * Called to ensure code is only run when the flag is enabled. This can be used to protect users
+ * from the unintended behaviors caused by accidentally running new logic, while also crashing
+ * on an eng build to ensure that the refactor author catches issues in testing.
+ */
+ @JvmStatic
+ inline fun isUnexpectedlyInLegacyMode() =
+ RefactorFlagUtils.isUnexpectedlyInLegacyMode(isEnabled, FLAG_NAME)
+
+ /**
+ * Called to ensure code is only run when the flag is enabled. This will call Log.wtf if the
+ * flag is not enabled to ensure that the refactor author catches issues in testing.
+ *
+ * NOTE: This can be useful for simple methods, but does not return the flag state, so it cannot
+ * be used to implement a safe exit, and as such it does not support code stripping. If the
+ * calling code will do work that is unsafe when the flag is off, it is recommended to write an
+ * early return with `if (isUnexpectedlyInLegacyMode()) return`.
+ */
+ @JvmStatic
+ inline fun expectInNewMode() {
+ RefactorFlagUtils.isUnexpectedlyInLegacyMode(isEnabled, FLAG_NAME)
+ }
+
+ /**
+ * Called to ensure code is only run when the flag is disabled. This will throw an exception if
+ * the flag is enabled to ensure that the refactor author catches issues in testing.
+ */
+ @JvmStatic
+ inline fun assertInLegacyMode() = RefactorFlagUtils.assertInLegacyMode(isEnabled, FLAG_NAME)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractor.kt
index 49d69f2..ba92dfe 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractor.kt
@@ -19,7 +19,6 @@
import android.content.pm.PackageManager
import android.media.projection.StopReason
import com.android.app.tracing.coroutines.launchTraced as launch
-import com.android.systemui.Flags
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.log.LogBuffer
@@ -73,16 +72,12 @@
ProjectionChipModel.Receiver.ShareToApp
}
val contentType =
- if (Flags.statusBarShowAudioOnlyProjectionChip()) {
- when (state) {
- is MediaProjectionState.Projecting.EntireScreen,
- is MediaProjectionState.Projecting.SingleTask ->
- ProjectionChipModel.ContentType.Screen
- is MediaProjectionState.Projecting.NoScreen ->
- ProjectionChipModel.ContentType.Audio
- }
- } else {
- ProjectionChipModel.ContentType.Screen
+ when (state) {
+ is MediaProjectionState.Projecting.EntireScreen,
+ is MediaProjectionState.Projecting.SingleTask ->
+ ProjectionChipModel.ContentType.Screen
+ is MediaProjectionState.Projecting.NoScreen ->
+ ProjectionChipModel.ContentType.Audio
}
logger.log(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/interactor/SingleNotificationChipInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/interactor/SingleNotificationChipInteractor.kt
index 6e0682c..fa2bfc0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/interactor/SingleNotificationChipInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/interactor/SingleNotificationChipInteractor.kt
@@ -144,6 +144,8 @@
return NotificationChipModel(
key = key,
appName = appName,
+ packageName = packageName,
+ uid = uid,
statusBarChipIconView = statusBarChipIconView,
promotedContent = promotedContent,
creationTime = creationTime,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/model/NotificationChipModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/model/NotificationChipModel.kt
index dad51bac..b2f93d7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/model/NotificationChipModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/model/NotificationChipModel.kt
@@ -25,6 +25,10 @@
val key: String,
/** The user-readable name of the app that posted this notification. */
val appName: String,
+ /** The notifying app's package name. */
+ val packageName: String,
+ /** The notifying app's [packageName]'s uid. */
+ val uid: Int,
val statusBarChipIconView: StatusBarIconView?,
val promotedContent: PromotedNotificationContentModels,
/** The time when the notification first appeared as promoted. */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt
index 7e41812..656ac52 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt
@@ -64,9 +64,17 @@
*/
private val notificationChipsWithPrunedContent: Flow<List<PrunedNotificationChipModel>> =
notifChipsInteractor.allNotificationChips
- .map { chips -> chips.map { it.toPrunedModel() } }
+ .map { chips -> chips.filterByPackagePerUser().map { it.toPrunedModel() } }
.distinctUntilChanged()
+ /**
+ * Filters all the chips down to just the most important chip per package per user so we don't
+ * show multiple chips for the same app.
+ */
+ private fun List<NotificationChipModel>.filterByPackagePerUser(): List<NotificationChipModel> {
+ return this.groupBy { Pair(it.packageName, it.uid) }.map { (_, chips) -> chips[0] }
+ }
+
private fun NotificationChipModel.toPrunedModel(): PrunedNotificationChipModel {
// Chips are never shown when locked, so it's safe to use the version with sensitive content
val content = promotedContent.privateVersion
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractor.kt
index fce428d..76ea5f5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractor.kt
@@ -17,7 +17,6 @@
package com.android.systemui.statusbar.chips.screenrecord.domain.interactor
import android.media.projection.StopReason
-import com.android.systemui.Flags
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.log.LogBuffer
@@ -85,11 +84,7 @@
mediaProjectionRepository.mediaProjectionState,
shouldAssumeIsRecording,
) { screenRecordState, mediaProjectionState, shouldAssumeIsRecording ->
- if (
- Flags.statusBarAutoStartScreenRecordChip() &&
- shouldAssumeIsRecording &&
- screenRecordState is ScreenRecordModel.Starting
- ) {
+ if (shouldAssumeIsRecording && screenRecordState is ScreenRecordModel.Starting) {
logger.log(
TAG,
LogLevel.INFO,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/OngoingActivityChips.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/OngoingActivityChips.kt
index 72b2874..668c813 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/OngoingActivityChips.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/OngoingActivityChips.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.chips.ui.compose
+import android.graphics.RectF
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxHeight
@@ -25,16 +26,26 @@
import androidx.compose.runtime.key
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.toAndroidRectF
+import androidx.compose.ui.layout.boundsInWindow
+import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.unit.dp
import com.android.systemui.compose.modifiers.sysuiResTag
import com.android.systemui.statusbar.chips.StatusBarChipsReturnAnimations
import com.android.systemui.statusbar.chips.ui.model.MultipleOngoingActivityChipsModel
import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerViewBinder
+/**
+ * Composable for all ongoing activity chips shown in the status bar.
+ *
+ * @param onChipBoundsChanged should be invoked each time any chip has their on-screen bounds
+ * changed.
+ */
@Composable
fun OngoingActivityChips(
chips: MultipleOngoingActivityChipsModel,
iconViewStore: NotificationIconContainerViewBinder.IconViewStore?,
+ onChipBoundsChanged: (String, RectF) -> Unit,
modifier: Modifier = Modifier,
) {
if (StatusBarChipsReturnAnimations.isEnabled) {
@@ -63,7 +74,13 @@
OngoingActivityChip(
model = it,
iconViewStore = iconViewStore,
- modifier = Modifier.sysuiResTag(it.key),
+ modifier =
+ Modifier.sysuiResTag(it.key).onGloballyPositioned { coordinates ->
+ onChipBoundsChanged.invoke(
+ it.key,
+ coordinates.boundsInWindow().toAndroidRectF(),
+ )
+ },
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModel.kt
index 8fa1bb2..aa9ba3e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModel.kt
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.chips.ui.viewmodel
import android.content.res.Configuration
+import android.graphics.RectF
import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor
import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
import com.android.systemui.dagger.SysUISingleton
@@ -24,6 +25,7 @@
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.core.LogLevel
import com.android.systemui.statusbar.chips.StatusBarChipLogTags.pad
+import com.android.systemui.statusbar.chips.StatusBarChipToHunAnimation
import com.android.systemui.statusbar.chips.StatusBarChipsLog
import com.android.systemui.statusbar.chips.call.ui.viewmodel.CallChipViewModel
import com.android.systemui.statusbar.chips.casttootherdevice.ui.viewmodel.CastToOtherDeviceChipViewModel
@@ -35,6 +37,7 @@
import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUi
import com.android.systemui.statusbar.phone.ongoingcall.StatusBarChipsModernization
+import com.android.systemui.util.kotlin.filterValuesNotNull
import com.android.systemui.util.kotlin.pairwise
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
@@ -365,10 +368,53 @@
}
}
+ /** Stores the latest on-screen bounds for each of the chips. */
+ // Note: This will also store bounds for chips that have been removed. We may want to clear the
+ // value for removed chips.
+ private val chipBounds = MutableStateFlow<Map<String, RectF>>(emptyMap())
+
+ /** Invoked each time a chip's on-screen bounds have changed. */
+ fun onChipBoundsChanged(key: String, newBounds: RectF) {
+ if (!StatusBarChipToHunAnimation.isEnabled) {
+ return
+ }
+ val map = chipBounds.value.toMutableMap()
+ val currentValue = map[key]
+ if (currentValue != null) {
+ currentValue.set(newBounds)
+ } else {
+ map[key] = newBounds
+ }
+ chipBounds.value = map
+ }
+
/** A flow modeling just the keys for the currently visible chips. */
- val visibleChipKeys: Flow<List<String>> =
+ private val visibleChipKeys: Flow<List<String>> =
activeChips.map { chips -> chips.filter { !it.isHidden }.map { it.key } }
+ /** Placeholder chip bounds to use if {@link StatusBarChipToHunAnimation} is disabled. */
+ private val placeholderChipBounds = RectF()
+
+ /** A flow modeling the keys and on-screen bounds for the currently visible chips. */
+ val visibleChipsWithBounds: Flow<Map<String, RectF>> =
+ if (StatusBarChipToHunAnimation.isEnabled) {
+ combine(visibleChipKeys, chipBounds) { keys, chipBounds ->
+ // TODO: Test chip w/o bounds isn't returned
+ // TODO(b/393369891): Should we provide the placeholder bounds as a backup and
+ // make those bounds public so that [NotificationStackScrollLayout] can do a
+ // good default animation for chips even if we couldn't fetch the bounds for
+ // some reason?
+ keys.associateWith { chipBounds[it] }.filterValuesNotNull()
+ }
+ .distinctUntilChanged()
+ } else {
+ // If the custom chip-to-HUN animation isn't enabled, just provide any non-null
+ // chip bounds so that [NotificationStackScrollLayout] knows there's a status bar chip.
+ visibleChipKeys
+ .map { keys -> keys.associateWith { placeholderChipBounds } }
+ .distinctUntilChanged()
+ }
+
/**
* Sort the given chip [bundle] in order of priority, and divide the chips between active,
* overflow, and inactive (see [MultipleOngoingActivityChipsModel] for a description of each).
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/ConnectivityModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/ConnectivityModule.kt
index 4cf456d..2b828ab 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/ConnectivityModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/ConnectivityModule.kt
@@ -21,6 +21,7 @@
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags.SIGNAL_CALLBACK_DEPRECATION
import com.android.systemui.qs.QsEventLogger
+import com.android.systemui.qs.flags.QsInCompose
import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.qs.shared.model.TileCategory
import com.android.systemui.qs.tileimpl.QSTileImpl
@@ -214,7 +215,12 @@
tileSpec = TileSpec.create(INTERNET_TILE_SPEC),
uiConfig =
QSTileUIConfig.Resource(
- iconRes = R.drawable.ic_qs_no_internet_available,
+ iconRes =
+ if (QsInCompose.isEnabled) {
+ com.android.settingslib.R.drawable.ic_wifi_3
+ } else {
+ R.drawable.ic_qs_no_internet_available
+ },
labelRes = R.string.quick_settings_internet_label,
),
instanceId = uiEventLogger.getNewInstanceId(),
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java
index 321593b..271ec10 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java
@@ -724,7 +724,7 @@
cb.setIsAirplaneMode(
new IconState(
mAirplaneMode,
- TelephonyIcons.FLIGHT_MODE_ICON,
+ R.drawable.stat_sys_airplane_mode,
mContext.getString(R.string.accessibility_airplane_mode)));
cb.setNoSims(mHasNoSubs, mSimDetected);
cb.setConnectivityStatus(mNoDefaultNetwork, !mInetCondition, mNoNetworksAvailable);
@@ -1094,7 +1094,7 @@
mCallbackHandler.setIsAirplaneMode(
new IconState(
mAirplaneMode,
- TelephonyIcons.FLIGHT_MODE_ICON,
+ R.drawable.stat_sys_airplane_mode,
mContext.getString(R.string.accessibility_airplane_mode)));
mCallbackHandler.setNoSims(mHasNoSubs, mSimDetected);
}
@@ -1270,7 +1270,7 @@
mCallbackHandler.setIsAirplaneMode(
new IconState(
show,
- TelephonyIcons.FLIGHT_MODE_ICON,
+ R.drawable.stat_sys_airplane_mode,
mContext.getString(R.string.accessibility_airplane_mode)));
}
String fully = args.getString("fully");
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarConnectedDisplays.kt b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarConnectedDisplays.kt
index a58e00c..78150a5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarConnectedDisplays.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarConnectedDisplays.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.core
+import android.window.DesktopExperienceFlags
import com.android.systemui.flags.FlagToken
import com.android.systemui.flags.RefactorFlagUtils
import com.android.systemui.shared.Flags
@@ -30,10 +31,27 @@
val token: FlagToken
get() = FlagToken(FLAG_NAME, isEnabled)
+ /**
+ * This is defined as [DesktopExperienceFlags] to make it possible to enable it together with
+ * all the other desktop experience flags from the dev settings.
+ *
+ * Alternatively, using adb:
+ * ```bash
+ * adb shell aflags enable com.android.window.flags.show_desktop_experience_dev_option && \
+ * adb shell setprop persist.wm.debug.desktop_experience_devopts 1
+ * ```
+ */
+ val FLAG =
+ DesktopExperienceFlags.DesktopExperienceFlag(
+ Flags::statusBarConnectedDisplays,
+ /* shouldOverrideByDevOption= */ true,
+ FLAG_NAME,
+ )
+
/** Is the refactor enabled */
@JvmStatic
inline val isEnabled
- get() = Flags.statusBarConnectedDisplays()
+ get() = FLAG.isTrue
/**
* Called to ensure code is only run when the flag is enabled. This protects users from the
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventCoordinator.kt
index a73d517..211afd9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventCoordinator.kt
@@ -20,14 +20,14 @@
import android.content.Context
import android.provider.DeviceConfig
import android.provider.DeviceConfig.NAMESPACE_PRIVACY
-import com.android.systemui.res.R
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.display.domain.interactor.ConnectedDisplayInteractor
-import com.android.systemui.display.domain.interactor.ConnectedDisplayInteractor.State
import com.android.systemui.privacy.PrivacyChipBuilder
import com.android.systemui.privacy.PrivacyItem
import com.android.systemui.privacy.PrivacyItemController
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.featurepods.vc.domain.interactor.AvControlsChipInteractor
import com.android.systemui.statusbar.policy.BatteryController
import com.android.systemui.util.time.SystemClock
import javax.inject.Inject
@@ -47,12 +47,12 @@
private val systemClock: SystemClock,
private val batteryController: BatteryController,
private val privacyController: PrivacyItemController,
+ private val avControlsChipInteractor: AvControlsChipInteractor,
private val context: Context,
@Application private val appScope: CoroutineScope,
- connectedDisplayInteractor: ConnectedDisplayInteractor
+ connectedDisplayInteractor: ConnectedDisplayInteractor,
) {
- private val onDisplayConnectedFlow =
- connectedDisplayInteractor.connectedDisplayAddition
+ private val onDisplayConnectedFlow = connectedDisplayInteractor.connectedDisplayAddition
private var connectedDisplayCollectionJob: Job? = null
private lateinit var scheduler: SystemStatusAnimationScheduler
@@ -82,91 +82,102 @@
}
fun notifyPrivacyItemsChanged(showAnimation: Boolean = true) {
- val event = PrivacyEvent(showAnimation)
+ // Disabling animation in case that the privacy indicator is implemented as a status bar
+ // chip
+ val shouldShowAnimation = showAnimation && !avControlsChipInteractor.isEnabled.value
+ val event = PrivacyEvent(shouldShowAnimation)
event.privacyItems = privacyStateListener.currentPrivacyItems
event.contentDescription = run {
val items = PrivacyChipBuilder(context, event.privacyItems).joinTypes()
- context.getString(
- R.string.ongoing_privacy_chip_content_multiple_apps, items)
+ context.getString(R.string.ongoing_privacy_chip_content_multiple_apps, items)
}
scheduler.onStatusEvent(event)
}
private fun startConnectedDisplayCollection() {
- val connectedDisplayEvent = ConnectedDisplayEvent().apply {
- contentDescription = context.getString(R.string.connected_display_icon_desc)
- }
+ val connectedDisplayEvent =
+ ConnectedDisplayEvent().apply {
+ contentDescription = context.getString(R.string.connected_display_icon_desc)
+ }
connectedDisplayCollectionJob =
- onDisplayConnectedFlow
- .onEach { scheduler.onStatusEvent(connectedDisplayEvent) }
- .launchIn(appScope)
+ onDisplayConnectedFlow
+ .onEach { scheduler.onStatusEvent(connectedDisplayEvent) }
+ .launchIn(appScope)
}
- private val batteryStateListener = object : BatteryController.BatteryStateChangeCallback {
- private var plugged = false
- private var stateKnown = false
- override fun onBatteryLevelChanged(level: Int, pluggedIn: Boolean, charging: Boolean) {
- if (!stateKnown) {
- stateKnown = true
- plugged = pluggedIn
- notifyListeners(level)
- return
+ private val batteryStateListener =
+ object : BatteryController.BatteryStateChangeCallback {
+ private var plugged = false
+ private var stateKnown = false
+
+ override fun onBatteryLevelChanged(level: Int, pluggedIn: Boolean, charging: Boolean) {
+ if (!stateKnown) {
+ stateKnown = true
+ plugged = pluggedIn
+ notifyListeners(level)
+ return
+ }
+
+ if (plugged != pluggedIn) {
+ plugged = pluggedIn
+ notifyListeners(level)
+ }
}
- if (plugged != pluggedIn) {
- plugged = pluggedIn
- notifyListeners(level)
+ private fun notifyListeners(@IntRange(from = 0, to = 100) batteryLevel: Int) {
+ // We only care about the plugged in status
+ if (plugged) notifyPluggedIn(batteryLevel)
}
}
- private fun notifyListeners(@IntRange(from = 0, to = 100) batteryLevel: Int) {
- // We only care about the plugged in status
- if (plugged) notifyPluggedIn(batteryLevel)
- }
- }
+ private val privacyStateListener =
+ object : PrivacyItemController.Callback {
+ var currentPrivacyItems = listOf<PrivacyItem>()
+ var previousPrivacyItems = listOf<PrivacyItem>()
+ var timeLastEmpty = systemClock.elapsedRealtime()
- private val privacyStateListener = object : PrivacyItemController.Callback {
- var currentPrivacyItems = listOf<PrivacyItem>()
- var previousPrivacyItems = listOf<PrivacyItem>()
- var timeLastEmpty = systemClock.elapsedRealtime()
+ override fun onPrivacyItemsChanged(privacyItems: List<PrivacyItem>) {
+ if (uniqueItemsMatch(privacyItems, currentPrivacyItems)) {
+ return
+ } else if (privacyItems.isEmpty()) {
+ previousPrivacyItems = currentPrivacyItems
+ timeLastEmpty = systemClock.elapsedRealtime()
+ }
- override fun onPrivacyItemsChanged(privacyItems: List<PrivacyItem>) {
- if (uniqueItemsMatch(privacyItems, currentPrivacyItems)) {
- return
- } else if (privacyItems.isEmpty()) {
- previousPrivacyItems = currentPrivacyItems
- timeLastEmpty = systemClock.elapsedRealtime()
+ currentPrivacyItems = privacyItems
+ notifyListeners()
}
- currentPrivacyItems = privacyItems
- notifyListeners()
- }
+ private fun notifyListeners() {
+ if (currentPrivacyItems.isEmpty()) {
+ notifyPrivacyItemsEmpty()
+ } else {
+ val showAnimation =
+ isChipAnimationEnabled() &&
+ (!uniqueItemsMatch(currentPrivacyItems, previousPrivacyItems) ||
+ systemClock.elapsedRealtime() - timeLastEmpty >= DEBOUNCE_TIME)
+ notifyPrivacyItemsChanged(showAnimation)
+ }
+ }
- private fun notifyListeners() {
- if (currentPrivacyItems.isEmpty()) {
- notifyPrivacyItemsEmpty()
- } else {
- val showAnimation = isChipAnimationEnabled() &&
- (!uniqueItemsMatch(currentPrivacyItems, previousPrivacyItems) ||
- systemClock.elapsedRealtime() - timeLastEmpty >= DEBOUNCE_TIME)
- notifyPrivacyItemsChanged(showAnimation)
+ // Return true if the lists contain the same permission groups, used by the same UIDs
+ private fun uniqueItemsMatch(one: List<PrivacyItem>, two: List<PrivacyItem>): Boolean {
+ return one.map { it.application.uid to it.privacyType.permGroupName }.toSet() ==
+ two.map { it.application.uid to it.privacyType.permGroupName }.toSet()
+ }
+
+ private fun isChipAnimationEnabled(): Boolean {
+ val defaultValue =
+ context.resources.getBoolean(R.bool.config_enablePrivacyChipAnimation)
+ return DeviceConfig.getBoolean(
+ NAMESPACE_PRIVACY,
+ CHIP_ANIMATION_ENABLED,
+ defaultValue,
+ )
}
}
-
- // Return true if the lists contain the same permission groups, used by the same UIDs
- private fun uniqueItemsMatch(one: List<PrivacyItem>, two: List<PrivacyItem>): Boolean {
- return one.map { it.application.uid to it.privacyType.permGroupName }.toSet() ==
- two.map { it.application.uid to it.privacyType.permGroupName }.toSet()
- }
-
- private fun isChipAnimationEnabled(): Boolean {
- val defaultValue =
- context.resources.getBoolean(R.bool.config_enablePrivacyChipAnimation)
- return DeviceConfig.getBoolean(NAMESPACE_PRIVACY, CHIP_ANIMATION_ENABLED, defaultValue)
- }
- }
}
private const val DEBOUNCE_TIME = 3000L
private const val CHIP_ANIMATION_ENABLED = "privacy_chip_animation_enabled"
-private const val TAG = "SystemEventCoordinator"
\ No newline at end of file
+private const val TAG = "SystemEventCoordinator"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/av/domain/interactor/AvControlsChipInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/av/domain/interactor/AvControlsChipInteractor.kt
index 302dbf6..a236b863 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/av/domain/interactor/AvControlsChipInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/av/domain/interactor/AvControlsChipInteractor.kt
@@ -25,6 +25,7 @@
import com.android.systemui.statusbar.featurepods.vc.shared.model.SensorActivityModel
import javax.inject.Inject
import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.combine
/**
@@ -37,8 +38,8 @@
*/
@SysUISingleton
class AvControlsChipInteractor @Inject constructor(privacyChipRepository: PrivacyChipRepository) {
- private val isEnabled = MutableStateFlow(false)
-
+ private val _isEnabled = MutableStateFlow(false)
+ val isEnabled = _isEnabled.asStateFlow()
val model =
combine(isEnabled, privacyChipRepository.privacyItems) { isEnabled, privacyItems ->
if (isEnabled) createModel(privacyItems)
@@ -75,6 +76,6 @@
* factors should initialize the interactor. This must be called from a CoreStartable.
*/
fun initialize() {
- isEnabled.value = Flags.expandedPrivacyIndicatorsOnLargeScreen()
+ _isEnabled.value = Flags.expandedPrivacyIndicatorsOnLargeScreen()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/media/domain/interactor/MediaControlChipInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/media/domain/interactor/MediaControlChipInteractor.kt
index 1bee1ea..5ea7a45 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/media/domain/interactor/MediaControlChipInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/media/domain/interactor/MediaControlChipInteractor.kt
@@ -51,7 +51,7 @@
private val isEnabled = MutableStateFlow(false)
private val mediaControlChipModelForScene: Flow<MediaControlChipModel?> =
- combine(mediaFilterRepository.currentMedia, mediaFilterRepository.selectedUserEntries) {
+ combine(mediaFilterRepository.currentMedia, mediaFilterRepository.currentUserEntries) {
mediaList,
userEntries ->
mediaList
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntry.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntry.kt
index f0f4a8b..d62961c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntry.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntry.kt
@@ -43,6 +43,10 @@
_children.add(child)
}
+ fun removeChild(child: ListEntry) {
+ _children.remove(child)
+ }
+
fun clearChildren() {
_children.clear()
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntryAdapter.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntryAdapter.kt
index 9f72519..c66b190 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntryAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntryAdapter.kt
@@ -97,7 +97,7 @@
override fun getIcons(): IconPack? {
// TODO(b/396446620): implement bundle icons
- return null
+ return IconPack.buildEmptyPack(null)
}
override fun isColorized(): Boolean {
@@ -250,6 +250,10 @@
) {
Log.wtf(TAG, "requestRebind() called")
}
+
+ override fun isBundled(): Boolean {
+ return false
+ }
}
private const val TAG = "BundleEntryAdapter"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryAdapter.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryAdapter.java
index 11a215b..9b39186 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryAdapter.java
@@ -228,5 +228,10 @@
void requestRebind(@NonNull RowContentBindStage stage,
@NonNull NotifBindPipeline.BindCallback callback);
+
+ /**
+ * Returns whether this entry is *within* a bundle. The bundle header will always return false.
+ */
+ boolean isBundled();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index 2b8ce38..f97cf3c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -22,6 +22,7 @@
import static android.app.Notification.CATEGORY_MESSAGE;
import static android.app.Notification.CATEGORY_REMINDER;
import static android.app.Notification.FLAG_BUBBLE;
+import static android.app.NotificationChannel.SYSTEM_RESERVED_IDS;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_BADGE;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT;
@@ -412,6 +413,9 @@
return mRanking.getSmartReplies();
}
+ public boolean isBundled() {
+ return SYSTEM_RESERVED_IDS.contains(getRanking().getChannel().getId());
+ }
/*
* Old methods
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntryAdapter.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntryAdapter.kt
index 3255fad..6cfa60b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntryAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntryAdapter.kt
@@ -267,4 +267,8 @@
) {
stage.requestRebind(entry, callback)
}
+
+ override fun isBundled(): Boolean {
+ return entry.isBundled
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
index 89f73f7..9ae981e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
@@ -144,6 +144,7 @@
private int mConsecutiveReentrantRebuilds = 0;
@VisibleForTesting public static final int MAX_CONSECUTIVE_REENTRANT_REBUILDS = 3;
+ private static final boolean DEBUG_FILTER = false;
@Inject
public ShadeListBuilder(
@@ -430,6 +431,7 @@
private void buildList() {
Trace.beginSection("ShadeListBuilder.buildList");
mPipelineState.requireIsBefore(STATE_BUILD_STARTED);
+ debugLog(mPipelineState.getStateName() + "---------------------");
if (mPendingEntries != null) {
mAllEntries = mPendingEntries;
@@ -451,6 +453,7 @@
// Step 2: Filter out any notifications that shouldn't be shown right now
mPipelineState.incrementTo(STATE_PRE_GROUP_FILTERING);
+ debugList("before filterNotifs");
filterNotifs(mAllEntries, mNotifList, mNotifPreGroupFilters);
// Step 3: Group notifications with the same group key and set summaries
@@ -459,6 +462,13 @@
applyNewNotifList();
pruneIncompleteGroups(mNotifList);
+ // Step 3.5: Bundle notifications according to classification
+ if (NotificationBundleUi.isEnabled()) {
+ bundleNotifs(mNotifList, mNewNotifList);
+ applyNewNotifList();
+ debugList("after bundling");
+ }
+
// Step 4: Group transforming
// Move some notifs out of their groups and up to top-level (mostly used for heads-upping)
dispatchOnBeforeTransformGroups(mReadOnlyNotifList);
@@ -469,6 +479,7 @@
// Step 4.5: Reassign/revert any groups to maintain visual stability
mPipelineState.incrementTo(STATE_GROUP_STABILIZING);
stabilizeGroupingNotifs(mNotifList);
+ debugList("after stabilizeGroupingNotifs");
// Step 5: Section & Sort
// Assign each top-level entry a section, and copy to all of its children
@@ -478,6 +489,7 @@
notifySectionEntriesUpdated();
// Sort the list by section and then within section by our list of custom comparators
sortListAndGroups();
+ debugList("after sortListAndGroups");
// Step 6: Filter out entries after pre-group filtering, grouping, promoting, and sorting
// Now filters can see grouping, sectioning, and order information to determine whether
@@ -486,13 +498,17 @@
mPipelineState.incrementTo(STATE_FINALIZE_FILTERING);
filterNotifs(mNotifList, mNewNotifList, mNotifFinalizeFilters);
applyNewNotifList();
+ debugList("after filterNotifs");
pruneIncompleteGroups(mNotifList);
+ debugList("after pruneIncompleteGroups");
// Step 7: Lock in our group structure and log anything that's changed since the last run
mPipelineState.incrementTo(STATE_FINALIZING);
logChanges();
freeEmptyGroups();
+ debugList("after freeEmptyGroups");
cleanupPluggables();
+ debugList("after cleanupPluggables");
// Step 8: Dispatch the new list, first to any listeners and then to the view layer
dispatchOnBeforeRenderList(mReadOnlyNotifList);
@@ -573,39 +589,65 @@
mNotifList.clear();
}
+ private void applyFilterToGroup(GroupEntry groupEntry, long now, List<NotifFilter> filters) {
+ // apply filter on its summary
+ final NotificationEntry summary = groupEntry.getRepresentativeEntry();
+ if (applyFilters(summary, now, filters)) {
+ groupEntry.setSummary(null);
+ annulAddition(summary);
+ }
+
+ // apply filter on its children
+ final List<NotificationEntry> children = groupEntry.getRawChildren();
+ for (int j = children.size() - 1; j >= 0; j--) {
+ final NotificationEntry child = children.get(j);
+ if (applyFilters(child, now, filters)) {
+ children.remove(child);
+ annulAddition(child);
+ }
+ }
+ }
+
+ private void applyFilterToBundle(BundleEntry bundleEntry, long now, List<NotifFilter> filters) {
+ List<ListEntry> bundleChildren = bundleEntry.getChildren();
+ List<ListEntry> bundleChildrenToRemove = new ArrayList();
+ // TODO(b/399736937) Add tests
+ for (ListEntry listEntry: bundleChildren) {
+ if (listEntry instanceof GroupEntry groupEntry) {
+ applyFilterToGroup(groupEntry, now, filters);
+ } else {
+ if (applyFilters((NotificationEntry) listEntry, now, filters)) {
+ bundleChildrenToRemove.add(listEntry);
+ debugLog("annulled bundle child" + listEntry.getKey()
+ + " bundle size: " + bundleEntry.getChildren().size());
+ }
+ }
+ }
+ for (ListEntry r: bundleChildrenToRemove) {
+ bundleEntry.removeChild(r);
+ annulAddition(r);
+ }
+ }
+
private void filterNotifs(
Collection<? extends PipelineEntry> entries,
List<PipelineEntry> out,
List<NotifFilter> filters) {
Trace.beginSection("ShadeListBuilder.filterNotifs");
final long now = UseElapsedRealtimeForCreationTime.getCurrentTime(mSystemClock);
- for (PipelineEntry entry : entries) {
- if (entry instanceof GroupEntry) {
- final GroupEntry groupEntry = (GroupEntry) entry;
-
- // apply filter on its summary
- final NotificationEntry summary = groupEntry.getRepresentativeEntry();
- if (applyFilters(summary, now, filters)) {
- groupEntry.setSummary(null);
- annulAddition(summary);
- }
-
- // apply filter on its children
- final List<NotificationEntry> children = groupEntry.getRawChildren();
- for (int j = children.size() - 1; j >= 0; j--) {
- final NotificationEntry child = children.get(j);
- if (applyFilters(child, now, filters)) {
- children.remove(child);
- annulAddition(child);
- }
- }
-
+ for (PipelineEntry pipelineEntry : entries) {
+ if (pipelineEntry instanceof BundleEntry bundleEntry) {
+ applyFilterToBundle(bundleEntry, now, filters);
+ // We unconditionally preserve the BundleEntry here, then prune if empty later.
+ out.add(bundleEntry);
+ } else if (pipelineEntry instanceof GroupEntry groupEntry) {
+ applyFilterToGroup(groupEntry, now, filters);
out.add(groupEntry);
} else {
- if (applyFilters((NotificationEntry) entry, now, filters)) {
- annulAddition(entry);
+ if (applyFilters((NotificationEntry) pipelineEntry, now, filters)) {
+ annulAddition(pipelineEntry);
} else {
- out.add(entry);
+ out.add(pipelineEntry);
}
}
}
@@ -614,12 +656,11 @@
private void groupNotifs(List<PipelineEntry> entries, List<PipelineEntry> out) {
Trace.beginSection("ShadeListBuilder.groupNotifs");
- for (PipelineEntry PipelineEntry : entries) {
+ for (PipelineEntry pipelineEntry : entries) {
// since grouping hasn't happened yet, all notifs are NotificationEntries
- NotificationEntry entry = (NotificationEntry) PipelineEntry;
+ NotificationEntry entry = (NotificationEntry) pipelineEntry;
if (entry.getSbn().isGroup()) {
final String topLevelKey = entry.getSbn().getGroupKey();
-
GroupEntry group = mGroups.get(topLevelKey);
if (group == null) {
group = new GroupEntry(topLevelKey,
@@ -668,6 +709,89 @@
Trace.endSection();
}
+ @Nullable
+ private BundleEntry getBundleEntry(String id) {
+ BundleEntry be = mIdToBundleEntry.get(id);
+ // TODO(b/399736937) prefix channel with numbered id for fixed order:
+ // "bundle_01:android.app.news"
+ if (be == null) {
+ debugLog("BundleEntry not found for bundleId: " + id);
+ }
+ return be;
+ }
+
+ private void debugLog(String s) {
+ BundleCoordinator.debugBundleLog(TAG, () -> s);
+ }
+
+ private void debugList(String s) {
+ if (!BundleCoordinator.debugBundleUi) {
+ return;
+ }
+ StringBuilder listStr = new StringBuilder();
+ for (int i = 0; i < mNotifList.size(); i++) {
+ PipelineEntry pipelineEntry = mNotifList.get(i);
+ String className = " Notif:";
+ if (pipelineEntry instanceof GroupEntry) {
+ className = " Group:";
+ listStr.append("i=" + i).append(className).append(pipelineEntry.getKey())
+ .append("\n");
+ } else if (pipelineEntry instanceof BundleEntry bundleEntry) {
+ className = " Bundle:";
+ listStr.append("i=" + i).append(className).append(pipelineEntry.getKey())
+ .append(" size: " + bundleEntry.getChildren().size()).append("\n");
+
+ for (ListEntry listEntry : bundleEntry.getChildren()) {
+
+ if (listEntry instanceof NotificationEntry notifEntry) {
+ listStr.append(" Notif").append(notifEntry.getKey()).append("\n");
+
+ } else if (listEntry instanceof GroupEntry groupEntry) {
+ listStr.append(" Group").append(groupEntry.getKey())
+ .append(" size: " + groupEntry.getChildren().size()).append("\n");
+
+ for (NotificationEntry notifEntry : groupEntry.getChildren()) {
+ listStr.append(" Notif").append(notifEntry.getKey()).append("\n");
+ }
+ }
+ }
+ } else { // Unbundled NotifEntry
+ listStr.append("i=" + i).append(className).append(pipelineEntry.getKey())
+ .append("\n");
+ }
+ }
+ Log.d(TAG, mPipelineState.getStateName() + " " + s + " list ---\n" + listStr + "\n");
+ }
+
+ private void bundleNotifs(List<PipelineEntry> in, List<PipelineEntry> out) {
+ // Bundle NotificationEntry and non-empty GroupEntry
+ for (PipelineEntry pipelineEntry : in) {
+ if (!(pipelineEntry instanceof ListEntry listEntry)) {
+ // This should not happen
+ continue;
+ }
+ String id = getNotifBundler().getBundleIdOrNull(listEntry);
+ if (id == null) {
+ debugLog("bundleNotifs: no bundle id for:" + listEntry.getKey());
+ out.add(listEntry);
+ } else {
+ BundleEntry bundleEntry = getBundleEntry(id);
+ if (bundleEntry == null) {
+ debugLog("bundleNotifs: BundleEntry NULL for: "
+ + listEntry.getKey() + " bundleId:" + id);
+ out.add(listEntry);
+ } else {
+ debugLog("bundleNotifs: ADD listEntry:" + listEntry.getKey()
+ + " to bundle:" + bundleEntry.getKey());
+ bundleEntry.addChild(listEntry);
+ listEntry.setParent(bundleEntry);
+ }
+ }
+ }
+ // Add all BundleEntry to the list. They will be pruned later if they are empty.
+ out.addAll(mIdToBundleEntry.values());
+ }
+
private void stabilizeGroupingNotifs(List<PipelineEntry> topLevelList) {
if (getStabilityManager().isEveryChangeAllowed()) {
return;
@@ -758,11 +882,29 @@
return shouldPromote;
});
- }
+ } // Notifications inside BundleEntry will not be promoted out of BundleEntry.
}
Trace.endSection();
}
+ private void pruneEmptyGroupsFromBundle(BundleEntry bundleEntry) {
+ // TODO(b/399736937) Add tests.
+ List<ListEntry> bundleChildren = bundleEntry.getChildren();
+ List<GroupEntry> emptyGroupsToRemove = new ArrayList<>();
+ for (ListEntry listEntry: bundleChildren) {
+ if (listEntry instanceof GroupEntry groupEntry) {
+ if (groupEntry.getChildren().isEmpty()) {
+ annulAddition(groupEntry);
+ emptyGroupsToRemove.add(groupEntry);
+ }
+ }
+ }
+ for (GroupEntry groupEntry: emptyGroupsToRemove) {
+ bundleEntry.removeChild(groupEntry);
+ }
+ }
+
+
private void pruneIncompleteGroups(List<PipelineEntry> shadeList) {
Trace.beginSection("ShadeListBuilder.pruneIncompleteGroups");
// Any group which lost a child on this run to stability is exempt from being pruned or
@@ -779,60 +921,95 @@
// Iterate backwards, so that we can remove elements without affecting indices of
// yet-to-be-accessed entries.
+ debugLog(mPipelineState.getStateName() + " pruneIncompleteGroups size: "
+ + shadeList.size());
+
for (int i = shadeList.size() - 1; i >= 0; i--) {
- final PipelineEntry tle = shadeList.get(i);
+ final PipelineEntry pipelineEntry = shadeList.get(i);
- if (tle instanceof GroupEntry) {
- final GroupEntry group = (GroupEntry) tle;
- final List<NotificationEntry> children = group.getRawChildren();
- final boolean hasSummary = group.getSummary() != null;
+ if (pipelineEntry instanceof GroupEntry groupEntry) {
+ pruneGroupEntry(groupEntry, i, shadeList, groupsExemptFromSummaryPromotion,
+ groupsWithChildrenLostToStability);
- if (hasSummary && children.size() == 0) {
- if (groupsExemptFromSummaryPromotion.contains(group.getKey())) {
- // This group lost a child on this run to promotion or stability, so it is
- // exempt from having its summary promoted to the top level, so prune it.
- // It has no children, so it will just vanish.
- pruneGroupAtIndexAndPromoteAnyChildren(shadeList, group, i);
- } else {
- // For any other summary with no children, promote the summary.
- pruneGroupAtIndexAndPromoteSummary(shadeList, group, i);
- }
- } else if (!hasSummary) {
- // If the group doesn't provide a summary, ignore it and add
- // any children it may have directly to top-level.
- pruneGroupAtIndexAndPromoteAnyChildren(shadeList, group, i);
- } else if (children.size() < MIN_CHILDREN_FOR_GROUP) {
- // This group has a summary and insufficient, but nonzero children.
- checkState(hasSummary, "group must have summary at this point");
- checkState(!children.isEmpty(), "empty group should have been promoted");
+ } else if (pipelineEntry instanceof BundleEntry bundleEntry) {
+ // TODO(b/399736937) Add tests.
+ // We don't need to prune groups here because groups were already pruned before
+ // being bundled.
+ pruneEmptyGroupsFromBundle(bundleEntry);
- if (groupsWithChildrenLostToStability.contains(group.getKey())) {
- // This group lost a child on this run to stability, so it is exempt from
- // the "min children" requirement; keep it around in case more children are
- // added before changes are allowed again.
- group.getAttachState().getSuppressedChanges().setWasPruneSuppressed(true);
- continue;
- }
- if (group.wasAttachedInPreviousPass()
- && !getStabilityManager().isGroupPruneAllowed(group)) {
- checkState(!children.isEmpty(), "empty group should have been pruned");
- // This group was previously attached and group changes aren't
- // allowed; keep it around until group changes are allowed again.
- group.getAttachState().getSuppressedChanges().setWasPruneSuppressed(true);
- continue;
- }
-
- // The group is too small, ignore it and add
- // its children (if any) directly to top-level.
- pruneGroupAtIndexAndPromoteAnyChildren(shadeList, group, i);
+ if (bundleEntry.getChildren().isEmpty()) {
+ BundleEntry prunedBundle = (BundleEntry) shadeList.remove(i);
+ debugLog(mPipelineState.getStateName()
+ + " pruned empty bundle: "
+ + prunedBundle.getKey());
+ } else {
+ debugLog(mPipelineState.getStateName()
+ + " skip pruning bundle: " + bundleEntry.getKey()
+ + " size: " + bundleEntry.getChildren().size());
}
}
}
Trace.endSection();
}
+ private void pruneGroupEntry(GroupEntry group, int i, List<PipelineEntry> shadeList,
+ ArraySet<String> groupsExemptFromSummaryPromotion,
+ Set<String> groupsWithChildrenLostToStability) {
+ final List<NotificationEntry> children = group.getRawChildren();
+ final boolean hasSummary = group.getSummary() != null;
+ debugLog(mPipelineState.getStateName() + " pruneGroupEntry " + group.getKey()
+ + " hasSummary:" + hasSummary
+ + " childCount:" + children.size()
+ );
+ if (hasSummary && children.isEmpty()) {
+ if (groupsExemptFromSummaryPromotion.contains(group.getKey())) {
+ // This group lost a child on this run to promotion or stability, so it is
+ // exempt from having its summary promoted to the top level, so prune it.
+ // It has no children, so it will just vanish.
+ pruneGroupAtIndexAndPromoteAnyChildren(shadeList, group, i, "no child");
+ } else {
+ // For any other summary with no children, promote the summary.
+ pruneGroupAtIndexAndPromoteSummary(shadeList, group, i);
+ }
+ } else if (!hasSummary) {
+ // If the group doesn't provide a summary, ignore it and add
+ // any children it may have directly to parent entry.
+ pruneGroupAtIndexAndPromoteAnyChildren(shadeList, group, i, "no summary");
+
+ } else if (children.size() < MIN_CHILDREN_FOR_GROUP) {
+ // This group has a summary and insufficient, but nonzero children.
+ checkState(hasSummary, "group must have summary at this point");
+ checkState(!children.isEmpty(), "empty group should have been promoted");
+
+ if (groupsWithChildrenLostToStability.contains(group.getKey())) {
+ // This group lost a child on this run to stability, so it is exempt from
+ // the "min children" requirement; keep it around in case more children are
+ // added before changes are allowed again.
+ group.getAttachState().getSuppressedChanges().setWasPruneSuppressed(true);
+ return;
+ }
+ if (group.wasAttachedInPreviousPass()
+ && !getStabilityManager().isGroupPruneAllowed(group)) {
+ checkState(!children.isEmpty(), "empty group should have been pruned");
+ // This group was previously attached and group changes aren't
+ // allowed; keep it around until group changes are allowed again.
+ group.getAttachState().getSuppressedChanges().setWasPruneSuppressed(true);
+ return;
+ }
+ // The group is too small, ignore it and add
+ // its children (if any) directly to top-level.
+ pruneGroupAtIndexAndPromoteAnyChildren(shadeList, group, i, "too small");
+ } else {
+ debugLog(mPipelineState.getStateName()
+ + " group not pruned: " + group.getKey());
+ }
+ }
+
private void pruneGroupAtIndexAndPromoteSummary(List<PipelineEntry> shadeList,
GroupEntry group, int index) {
+ debugLog(mPipelineState.getStateName() + " promote summary prune group:"
+ + group.getKey());
+
// Validate that the group has no children
checkArgument(group.getChildren().isEmpty(), "group should have no children");
@@ -851,11 +1028,24 @@
}
private void pruneGroupAtIndexAndPromoteAnyChildren(List<PipelineEntry> shadeList,
- GroupEntry group, int index) {
+ GroupEntry group, int index, String reason) {
+
+ final boolean inBundle = group.getAttachState().getParent() instanceof BundleEntry;
+ debugLog(mPipelineState.getStateName()
+ + " " + reason + " => promote child prune group:" + group.getKey()
+ + " parent: " + group.getAttachState().getParent().getKey()
+ + " inBundle:" + inBundle
+ );
+
// REMOVE the GroupEntry at this index
PipelineEntry oldEntry = shadeList.remove(index);
// Validate that the replaced entry was the group entry
+ if (oldEntry != group) {
+ debugLog(mPipelineState.getStateName()
+ + " oldEntry:" + oldEntry.getKey()
+ + " groupToRemove:" + group.getKey());
+ }
checkState(oldEntry == group);
List<NotificationEntry> children = group.getRawChildren();
@@ -1021,13 +1211,26 @@
private void assignSections() {
Trace.beginSection("ShadeListBuilder.assignSections");
// Assign sections to top-level elements and their children
+ // TODO(b/399736937) Add tests.
for (PipelineEntry entry : mNotifList) {
NotifSection section = applySections(entry);
- if (entry instanceof GroupEntry) {
- GroupEntry parent = (GroupEntry) entry;
+
+ if (entry instanceof GroupEntry parent) {
for (NotificationEntry child : parent.getChildren()) {
setEntrySection(child, section);
}
+ } else if (entry instanceof BundleEntry be) {
+ setEntrySection(be, section);
+
+ for (ListEntry le : be.getChildren()) {
+ setEntrySection(le, section);
+
+ if (le instanceof GroupEntry ge) {
+ for (NotificationEntry ne : ge.getChildren()) {
+ setEntrySection(ne, section);
+ }
+ }
+ }
}
}
Trace.endSection();
@@ -1043,9 +1246,16 @@
// Sort each group's children
boolean allSorted = true;
for (PipelineEntry entry : mNotifList) {
- if (entry instanceof GroupEntry) {
- GroupEntry parent = (GroupEntry) entry;
+ if (entry instanceof GroupEntry parent) {
allSorted &= sortGroupChildren(parent.getRawChildren());
+ } else if (entry instanceof BundleEntry bundleEntry) {
+ // Sort children of groups within bundles
+ for (ListEntry le : bundleEntry.getChildren()) {
+ if (le instanceof GroupEntry ge) {
+ // TODO(b/399736937) Add tests.
+ allSorted &= sortGroupChildren(ge.getRawChildren());
+ }
+ }
}
}
// Sort each section within the top level list
@@ -1252,6 +1462,7 @@
@Nullable
private NotifComparator getSectionComparator(
@NonNull PipelineEntry o1, @NonNull PipelineEntry o2) {
+ // Sections should be able to sort any PipelineEntry, including bundles
final NotifSection section = o1.getSection();
if (section != o2.getSection()) {
throw new RuntimeException("Entry ordering should only be done within sections");
@@ -1319,6 +1530,12 @@
return stableIndex == -1 ? null : stableIndex;
}
+ private static void debugFilterLog(String s) {
+ if (DEBUG_FILTER) {
+ android.util.Log.d(TAG, s);
+ }
+ }
+
private boolean applyFilters(NotificationEntry entry, long now, List<NotifFilter> filters) {
final NotifFilter filter = findRejectingFilter(entry, now, filters);
entry.getAttachState().setExcludingFilter(filter);
@@ -1342,8 +1559,12 @@
for (int i = 0; i < size; i++) {
NotifFilter filter = filters.get(i);
if (filter.shouldFilterOut(entry, now)) {
+ debugFilterLog("findRejectingFilter: " + filter.getName() + " rejects: "
+ + entry.getKey());
return filter;
}
+ debugFilterLog("findRejectingFilter: " + filter.getName() + " pass: "
+ + entry.getKey());
}
return null;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BundleCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BundleCoordinator.kt
index d34b96a..ba50206 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BundleCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BundleCoordinator.kt
@@ -20,13 +20,18 @@
import android.app.NotificationChannel.PROMOTIONS_ID
import android.app.NotificationChannel.RECS_ID
import android.app.NotificationChannel.SOCIAL_MEDIA_ID
+import com.android.systemui.statusbar.notification.collection.BundleEntry
import com.android.systemui.statusbar.notification.collection.BundleSpec
+import com.android.systemui.statusbar.notification.collection.GroupEntry
+import com.android.systemui.statusbar.notification.collection.ListEntry
import com.android.systemui.statusbar.notification.collection.NotifPipeline
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.PipelineEntry
import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifBundler
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner
+import com.android.systemui.statusbar.notification.collection.render.BundleBarn
import com.android.systemui.statusbar.notification.collection.render.NodeController
import com.android.systemui.statusbar.notification.dagger.NewsHeader
import com.android.systemui.statusbar.notification.dagger.PromoHeader
@@ -48,6 +53,7 @@
@SocialHeader private val socialHeaderController: NodeController,
@RecsHeader private val recsHeaderController: NodeController,
@PromoHeader private val promoHeaderController: NodeController,
+ private val bundleBarn: BundleBarn
) : Coordinator {
val newsSectioner =
@@ -108,21 +114,80 @@
private val bundleIds = this.bundleSpecs.map { it.key }
- override fun getBundleIdOrNull(entry: NotificationEntry?): String? {
+ /**
+ * Return the id string of the bundle this ListEntry belongs in
+ * Or null if this ListEntry should not be bundled
+ */
+ override fun getBundleIdOrNull(entry: ListEntry): String? {
if (debugBundleUi && entry?.key?.contains("notify") == true) {
return "notify"
}
- return entry?.representativeEntry?.channel?.id?.takeIf { it in bundleIds }
+ if (entry is GroupEntry) {
+ if (entry.children.isEmpty()) return null
+ val summary = entry.summary ?: return null
+ // When the model classifies notifications from the same group into
+ // different bundles, system_server creates new group summaries that we can
+ // check for classification here.
+ return getBundleIdForNotifEntry(summary)
+ }
+ return getBundleIdForNotifEntry(entry as NotificationEntry)
+ }
+
+ private fun getBundleIdForNotifEntry(notifEntry: NotificationEntry) : String? {
+ return notifEntry.representativeEntry?.channel?.id?.takeIf { it in this.bundleIds }
+ }
+ }
+
+ /**
+ * Recursively check parents until finding bundle or null
+ */
+ private fun PipelineEntry.getBundleOrNull(): BundleEntry? {
+ return when (this) {
+ is BundleEntry -> this
+ is ListEntry -> getParent()?.getBundleOrNull()
+ else -> error("unhandled PipelineEntry type: $this")
+ }
+ }
+
+ private fun inflateAllBundleEntries(list: List<PipelineEntry>) {
+ list.filterIsInstance<BundleEntry>()
+ .forEach { bundleBarn.inflateBundleEntry(it) }
+ }
+
+ private val bundleFilter: NotifFilter =
+ object : NotifFilter("BundleInflateFilter") {
+
+ override fun shouldFilterOut(entry: NotificationEntry, now: Long): Boolean {
+ // TODO(b/399736937) Do not hide notifications if we have a bug that means the
+ // bundle isn't inflated yet. It's better that we just show those notifications in
+ // the silent section than fail to show them to the user at all
+ val bundle = entry.getBundleOrNull()
+ if (bundle == null) {
+ debugBundleLog(TAG, { "$name bundle null for notifEntry:${entry.key}" })
+ return false
+ }
+ val isInflated = bundleBarn.isInflated(bundle)
+ debugBundleLog(TAG, { "$name isInflated:$isInflated bundle:${bundle.key}" })
+ return !isInflated
}
}
override fun attach(pipeline: NotifPipeline) {
if (NotificationBundleUi.isEnabled) {
pipeline.setNotifBundler(bundler)
+ pipeline.addOnBeforeFinalizeFilterListener(this::inflateAllBundleEntries)
+ pipeline.addFinalizeFilter(bundleFilter);
}
}
companion object {
- @kotlin.jvm.JvmField var debugBundleUi: Boolean = true
+ @JvmField val TAG: String = "BundleCoordinator"
+ @JvmField var debugBundleUi: Boolean = false
+ @JvmStatic
+ fun debugBundleLog(tag: String, stringLambda: () -> String) {
+ if (debugBundleUi) {
+ android.util.Log.d(tag, stringLambda())
+ }
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DataStoreCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DataStoreCoordinator.kt
index e4ec76c..6e7a657 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DataStoreCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DataStoreCoordinator.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.notification.collection.coordinator
+import com.android.systemui.statusbar.notification.collection.BundleEntry
import com.android.systemui.statusbar.notification.collection.GroupEntry
import com.android.systemui.statusbar.notification.collection.PipelineEntry
import com.android.systemui.statusbar.notification.collection.NotifLiveDataStoreImpl
@@ -44,21 +45,23 @@
}
private fun onAfterRenderList(entries: List<PipelineEntry>) {
- val flatEntryList = flattenedEntryList(entries)
+ val flatEntryList = flattenEntrySequence(entries).toList()
notifLiveDataStoreImpl.setActiveNotifList(flatEntryList)
}
- private fun flattenedEntryList(entries: List<PipelineEntry>) =
- mutableListOf<NotificationEntry>().also { list ->
- entries.forEach { entry ->
- when (entry) {
- is NotificationEntry -> list.add(entry)
- is GroupEntry -> {
- list.add(entry.requireSummary)
- list.addAll(entry.children)
- }
- else -> error("Unexpected entry $entry")
+ private fun flattenEntrySequence(entries: List<PipelineEntry>): Sequence<NotificationEntry> = sequence {
+ entries.forEach { entry ->
+ when (entry) {
+ is NotificationEntry -> yield(entry)
+ is GroupEntry -> {
+ yield(entry.requireSummary)
+ yieldAll(entry.children)
+ }
+ is BundleEntry -> {
+ yieldAll(flattenEntrySequence(entry.children))
}
}
}
+ }
}
+
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt
index 8ccdad5..f82f274 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt
@@ -505,14 +505,17 @@
} else { // shouldHeadsUpEver = false
if (posted.isHeadsUpEntry) {
if (notificationSkipSilentUpdates()) {
- if (posted.isPinnedByUser) {
- // We don't want this to be interrupting anymore, let's remove it
+ if (posted.isPinnedByUser
+ || mHeadsUpManager.canRemoveImmediately(posted.entry.key)) {
+ // We don't want this to be interrupting anymore, let's remove it.
// If the notification is pinned by the user, the only way a user
// can un-pin it by tapping the status bar notification chip. Since
// that's a clear user action, we should remove the HUN immediately
// instead of waiting for any sort of minimum timeout.
// TODO(b/401068530) Ensure that status bar chip HUNs are not
// removed for silent update
+ // If we can remove the notification immediately, let's remove it in
+ // this update.
hunMutator.removeNotification(
posted.key,
/* releaseImmediately= */ true,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
index 20169ef..8d4f4a5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
@@ -310,15 +310,20 @@
PipelineEntry entry = entries.get(i);
if (NotificationBundleUi.isEnabled() && entry instanceof BundleEntry bundleEntry) {
for (ListEntry listEntry : bundleEntry.getChildren()) {
+ BundleCoordinator.debugBundleLog(TAG, () -> " inflate bundle with "
+ + bundleEntry.getChildren().size() + " children");
if (listEntry instanceof GroupEntry groupEntry) {
+ BundleCoordinator.debugBundleLog(TAG,
+ () -> "inflate group: " + groupEntry.getKey());
inflateRequiredGroupViews(groupEntry);
} else {
NotificationEntry notifEntry = (NotificationEntry) listEntry;
+ BundleCoordinator.debugBundleLog(TAG,
+ () -> "inflate notifEntry: " + notifEntry.getKey());
inflateRequiredNotifViews(notifEntry);
}
}
- } else if (entry instanceof GroupEntry) {
- GroupEntry groupEntry = (GroupEntry) entry;
+ } else if (entry instanceof GroupEntry groupEntry) {
inflateRequiredGroupViews(groupEntry);
} else {
NotificationEntry notifEntry = (NotificationEntry) entry;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java
index 661734a..964a668 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java
@@ -27,6 +27,7 @@
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifComparator;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner;
import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
@@ -34,6 +35,7 @@
import com.android.systemui.statusbar.notification.collection.render.SectionHeaderController;
import com.android.systemui.statusbar.notification.dagger.AlertingHeader;
import com.android.systemui.statusbar.notification.dagger.SilentHeader;
+import com.android.systemui.statusbar.notification.shared.NotificationBundleUi;
import com.android.systemui.statusbar.notification.stack.NotificationPriorityBucketKt;
import java.util.List;
@@ -157,6 +159,34 @@
mSilentHeaderController.setClearSectionEnabled(
mHasSilentEntries | mHasMinimizedEntries);
}
+
+ private final NotifComparator mSilentSectionComparator = new NotifComparator(
+ "SilentSectionComparator") {
+ @Override
+ public int compare(@NonNull PipelineEntry o1, @NonNull PipelineEntry o2) {
+ boolean isBundle1 = o1 instanceof BundleEntry;
+ boolean isBundle2 = o2 instanceof BundleEntry;
+ if (isBundle1 && isBundle2) {
+ // When both are bundles, order by bundle id, which is guaranteed to be in
+ // a fixed order
+ // TODO(b/399736937) prefix bundle keys to ensure fixed order
+ // TODO(b/399736937) optimize sort since this is on the main thread
+ return o1.getKey().compareTo(o2.getKey());
+ }
+ // Order bundles before non-bundles
+ return -1 * Boolean.compare(isBundle1, isBundle2);
+ }
+ };
+
+
+ @Nullable
+ @Override
+ public NotifComparator getComparator() {
+ if (NotificationBundleUi.isEnabled()) {
+ return mSilentSectionComparator;
+ }
+ return null;
+ }
};
private final NotifSectioner mMinimizedNotifSectioner = new NotifSectioner("Minimized",
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt
index 53a73f4..2a1bb6e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt
@@ -19,6 +19,7 @@
import com.android.app.tracing.traceSection
import com.android.server.notification.Flags.screenshareNotificationHiding
import com.android.systemui.Flags.screenshareNotificationHidingBugFix
+import com.android.systemui.statusbar.notification.collection.BundleEntry
import com.android.systemui.statusbar.notification.collection.PipelineEntry
import com.android.systemui.statusbar.notification.collection.NotifPipeline
import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope
@@ -67,6 +68,10 @@
screenshareNotificationHidingBugFix() &&
sensitiveNotificationProtectionController.isSensitiveStateActive
entries.forEach {
+ if (it is BundleEntry) {
+ // TODO(b/399736937) calculate based on notifs inside bundle
+ return@forEach
+ }
val section = checkNotNull(it.section) { "Null section for ${it.key}" }
val entry = checkNotNull(it.representativeEntry) { "Null notif entry for ${it.key}" }
val isSilent = section.bucket == BUCKET_SILENT
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustment.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustment.kt
index aa5008b8..fb3ede8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustment.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustment.kt
@@ -41,6 +41,7 @@
val isChildInGroup: Boolean,
val isGroupSummary: Boolean,
val summarization: String?,
+ val isBundled: Boolean,
) {
companion object {
@JvmStatic
@@ -63,6 +64,7 @@
!oldAdjustment.isGroupSummary &&
newAdjustment.isGroupSummary -> true
oldAdjustment.summarization != newAdjustment.summarization -> true
+ oldAdjustment.isBundled != newAdjustment.isBundled -> true
else -> false
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProvider.kt
index adcc3ec..e3a9eed3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProvider.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.notification.collection.inflation
+import android.app.NotificationChannel.SYSTEM_RESERVED_IDS
import android.content.Context
import android.database.ContentObserver
import android.os.Handler
@@ -152,6 +153,7 @@
},
isChildInGroup = entry.hasEverBeenGroupChild(),
isGroupSummary = entry.hasEverBeenGroupSummary(),
- summarization = entry.ranking.summarization
+ summarization = entry.ranking.summarization,
+ isBundled = SYSTEM_RESERVED_IDS.contains(entry.ranking?.channel?.id),
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
index a0b3c17..6a2181d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
@@ -155,7 +155,7 @@
ExpandableNotificationRowComponent component =
mExpandableNotificationRowComponentBuilder
.expandableNotificationRow(row)
- .notificationEntry(entry)
+ .pipelineEntry(entry)
.onExpandClickListener(mPresenter)
.build();
ExpandableNotificationRowController rowController =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt
index 8a9548f..b223f28 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt
@@ -22,6 +22,7 @@
import com.android.systemui.log.core.LogLevel.INFO
import com.android.systemui.log.core.LogLevel.WARNING
import com.android.systemui.statusbar.notification.NotifPipelineFlags
+import com.android.systemui.statusbar.notification.collection.BundleEntry
import com.android.systemui.statusbar.notification.collection.GroupEntry
import com.android.systemui.statusbar.notification.collection.PipelineEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntry
@@ -310,6 +311,13 @@
val logRankInFinalList = Compile.IS_DEBUG && notifPipelineFlags.isDevLoggingEnabled()
+ private fun getRankString(entry: PipelineEntry): String {
+ if (entry is BundleEntry) {
+ return entry.key + ":" + entry.children.getOrNull(0)?.representativeEntry!!.ranking.rank
+ }
+ return entry.representativeEntry!!.ranking.rank.toString()
+ }
+
fun logFinalList(entries: List<PipelineEntry>) {
if (entries.isEmpty()) {
buffer.log(TAG, DEBUG, {}, { "(empty list)" })
@@ -320,11 +328,11 @@
int1 = i
str1 = entry.logKey
bool1 = logRankInFinalList
- int2 = entry.representativeEntry!!.ranking.rank
+ str2 = getRankString(entry)
}, {
- "[$int1] $str1".let { if (bool1) "$it rank=$int2" else it }
+ "[$int1] $str1".let { if (bool1) "$it rank=$str2" else it }
})
-
+ // TODO(b/399736937) rank bundles as -1 and log bundle children rankings
if (entry is GroupEntry) {
entry.summary?.let {
buffer.log(TAG, DEBUG, {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifBundler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifBundler.kt
index 5c217ad..3d2294f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifBundler.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifBundler.kt
@@ -17,12 +17,13 @@
import com.android.systemui.statusbar.notification.collection.BundleSpec
import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.ListEntry
/** Pluggable for bundling notifications according to classification. */
abstract class NotifBundler protected constructor(name: String?) : Pluggable<NotifBundler?>(name) {
abstract val bundleSpecs: List<BundleSpec>
- abstract fun getBundleIdOrNull(entry: NotificationEntry?): String?
+ abstract fun getBundleIdOrNull(entry: ListEntry): String?
}
/** The default, no-op instance of NotifBundler which does not bundle anything. */
@@ -30,7 +31,7 @@
override val bundleSpecs: List<BundleSpec>
get() = listOf()
- override fun getBundleIdOrNull(entry: NotificationEntry?): String? {
+ override fun getBundleIdOrNull(entry: ListEntry): String? {
return null
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/BundleBarn.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/BundleBarn.kt
new file mode 100644
index 0000000..923c9b6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/BundleBarn.kt
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection.render
+
+import android.content.Context
+import android.view.ViewGroup
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.settings.UserTracker
+import com.android.systemui.statusbar.NotificationPresenter
+import com.android.systemui.statusbar.notification.collection.BundleEntry
+import com.android.systemui.statusbar.notification.collection.coordinator.BundleCoordinator.Companion.debugBundleLog
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
+import com.android.systemui.statusbar.notification.row.RowInflaterTask
+import com.android.systemui.statusbar.notification.row.RowInflaterTaskLogger
+import com.android.systemui.statusbar.notification.row.dagger.ExpandableNotificationRowComponent
+import com.android.systemui.statusbar.notification.stack.NotificationListContainer
+import com.android.systemui.util.time.SystemClock
+import dagger.Lazy
+import javax.inject.Inject
+import javax.inject.Provider
+
+/**
+ * Class that handles inflating BundleEntry view and controller, for use by NodeSpecBuilder.
+ * TODO(b/402628023) Make this class dumpable and dump its map so that we can see the
+ * "inflation pending" state per bundle
+ */
+@SysUISingleton
+class BundleBarn @Inject constructor(
+ private val rowComponent: ExpandableNotificationRowComponent.Builder,
+ private val rowInflaterTaskProvider: Provider<RowInflaterTask>,
+ private val listContainer: NotificationListContainer,
+ val context: Context? = null,
+ val systemClock: SystemClock,
+ val logger: RowInflaterTaskLogger,
+ val userTracker: UserTracker,
+ private val presenterLazy: Lazy<NotificationPresenter?>? = null
+) {
+ /**
+ * Map of [BundleEntry] key to [NodeController]:
+ * no key -> not started
+ * key maps to null -> inflating
+ * key maps to controller -> inflated
+ */
+ private val keyToControllerMap = mutableMapOf<String, NotifViewController?>()
+
+ fun debugLog(s: String) {
+ debugBundleLog(TAG, { s })
+ }
+
+ /**
+ * Build view and controller for BundleEntry.
+ */
+ fun inflateBundleEntry(bundleEntry: BundleEntry) {
+ debugLog("inflateBundleEntry: ${bundleEntry.key}")
+ if (keyToControllerMap.containsKey(bundleEntry.key)) {
+ // Skip if bundle is inflating or inflated.
+ debugLog("already in map: ${bundleEntry.key}")
+ return
+ }
+ val parent: ViewGroup = listContainer.getViewParentForNotification()
+ val inflationFinishedListener: (ExpandableNotificationRow) -> Unit = { row ->
+ // A subset of NotificationRowBinderImpl.inflateViews
+ debugLog("finished inflating: ${bundleEntry.key}")
+ val component = rowComponent
+ .expandableNotificationRow(row)
+ .pipelineEntry(bundleEntry)
+ .onExpandClickListener(presenterLazy?.get())
+ .build()
+ val controller =
+ component.expandableNotificationRowController
+ controller.init(bundleEntry)
+ keyToControllerMap[bundleEntry.key] = controller
+ }
+ debugLog("calling inflate: ${bundleEntry.key}")
+ keyToControllerMap[bundleEntry.key] = null
+ rowInflaterTaskProvider.get().inflate(
+ context, parent, bundleEntry, inflationFinishedListener
+ )
+ }
+
+ /**
+ * Return true if finished inflating.
+ */
+ fun isInflated(bundleEntry: BundleEntry): Boolean {
+ return keyToControllerMap[bundleEntry.key] != null
+ }
+
+ /**
+ * Return ExpandableNotificationRowController for BundleEntry.
+ */
+ fun requireNodeController(bundleEntry: BundleEntry): NodeController {
+ debugLog("requireNodeController: ${bundleEntry.key}" +
+ "controller: ${keyToControllerMap[bundleEntry.key]}")
+ return keyToControllerMap[bundleEntry.key]
+ ?: error("No view has been registered for bundle: ${bundleEntry.key}")
+ }
+}
+
+private const val TAG = "BundleBarn"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilder.kt
index e173194..0f3f515 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilder.kt
@@ -23,6 +23,7 @@
import com.android.systemui.statusbar.notification.collection.provider.SectionHeaderVisibilityProvider
import com.android.systemui.util.Compile
import com.android.app.tracing.traceSection
+import com.android.systemui.statusbar.notification.collection.BundleEntry
import com.android.systemui.statusbar.notification.collection.PipelineEntry
/**
@@ -39,6 +40,7 @@
private val sectionsFeatureManager: NotificationSectionsFeatureManager,
private val sectionHeaderVisibilityProvider: SectionHeaderVisibilityProvider,
private val viewBarn: NotifViewBarn,
+ private val bundleBarn: BundleBarn,
private val logger: NodeSpecBuilderLogger
) {
private var lastSections = setOf<NotifSection?>()
@@ -105,6 +107,7 @@
is GroupEntry ->
NodeSpecImpl(parent, viewBarn.requireNodeController(checkNotNull(entry.summary)))
.apply { entry.children.forEach { children.add(buildNotifNode(this, it)) } }
+ is BundleEntry -> NodeSpecImpl(parent, bundleBarn.requireNodeController(entry))
else -> throw RuntimeException("Unexpected entry: $entry")
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/RenderStageManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/RenderStageManager.kt
index 93e844d..22cecbe 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/RenderStageManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/RenderStageManager.kt
@@ -18,6 +18,7 @@
import com.android.app.tracing.traceSection
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.notification.collection.BundleEntry
import com.android.systemui.statusbar.notification.collection.GroupEntry
import com.android.systemui.statusbar.notification.collection.PipelineEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntry
@@ -139,6 +140,16 @@
action(entry.requireSummary)
entry.children.forEach(action)
}
+ is BundleEntry -> {
+ for (bundleChild in entry.children) {
+ if (bundleChild is GroupEntry) {
+ action(bundleChild.requireSummary)
+ bundleChild.children.forEach(action)
+ } else if (bundleChild is NotificationEntry) {
+ action(bundleChild)
+ }
+ }
+ }
else -> error("Unhandled entry: $entry")
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt
index 9532d6d..e2cc477 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt
@@ -47,6 +47,7 @@
nodeSpecBuilderLogger: NodeSpecBuilderLogger,
shadeViewDifferLogger: ShadeViewDifferLogger,
private val viewBarn: NotifViewBarn,
+ private val bundleBarn: BundleBarn,
) : PipelineDumpable {
// We pass a shim view here because the listContainer may not actually have a view associated
// with it and the differ never actually cares about the root node's view.
@@ -57,6 +58,7 @@
featureManager,
sectionHeaderVisibilityProvider,
viewBarn,
+ bundleBarn,
nodeSpecBuilderLogger,
)
private val viewDiffer = ShadeViewDiffer(rootController, shadeViewDifferLogger)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/AsyncRowInflater.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/AsyncRowInflater.kt
index c3b2411..d88b80d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/AsyncRowInflater.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/AsyncRowInflater.kt
@@ -29,10 +29,11 @@
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dagger.qualifiers.NotifInflation
-import javax.inject.Inject
+import com.android.systemui.statusbar.notification.collection.coordinator.BundleCoordinator.Companion.debugBundleLog
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
+import javax.inject.Inject
@SysUISingleton
class AsyncRowInflater
@@ -42,6 +43,8 @@
@Main private val mainCoroutineDispatcher: CoroutineDispatcher,
@NotifInflation private val inflationCoroutineDispatcher: CoroutineDispatcher,
) {
+ val TAG: String = "AsyncRowInflater"
+
/**
* Inflate the layout on the background thread, and invoke the listener on the main thread when
* finished.
@@ -60,11 +63,12 @@
return applicationScope.launchTraced("AsyncRowInflater-bg", inflationCoroutineDispatcher) {
val view =
try {
+ debugLog("inflater.inflate resId: $resId parent: $parent")
inflater.inflate(resId, parent, false)
} catch (ex: RuntimeException) {
// Probably a Looper failure, retry on the UI thread
Log.w(
- "AsyncRowInflater",
+ TAG,
"Failed to inflate resource in the background!" +
" Retrying on the UI thread",
ex,
@@ -74,12 +78,18 @@
withContextTraced("AsyncRowInflater-ui", mainCoroutineDispatcher) {
// If the inflate failed on the inflation thread, try again on the main thread
val finalView = view ?: inflater.inflate(resId, parent, false)
+ debugLog("finalView: $finalView")
+
// Inform the listener of the completion
listener.onInflateFinished(finalView, resId, parent)
}
}
}
+ private inline fun debugLog(s: String) {
+ debugBundleLog(TAG, { s });
+ }
+
/**
* Callback interface (identical to the one from AsyncLayoutInflater) for receiving the inflated
* view
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/BundledNotificationInfo.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/BundledNotificationInfo.kt
new file mode 100644
index 0000000..9ec3c3c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/BundledNotificationInfo.kt
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.statusbar.notification.row
+
+import android.app.INotificationManager
+import android.content.Context
+import android.content.pm.PackageManager
+import android.os.RemoteException
+import android.service.notification.Adjustment
+import android.service.notification.NotificationListenerService
+import android.service.notification.StatusBarNotification
+import android.util.AttributeSet
+import android.view.View
+import android.widget.TextView
+import com.android.internal.logging.MetricsLogger
+import com.android.internal.logging.UiEventLogger
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.notification.AssistantFeedbackController
+import com.android.systemui.statusbar.notification.collection.EntryAdapter
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.promoted.domain.interactor.PackageDemotionInteractor
+import com.android.systemui.statusbar.notification.row.icon.AppIconProvider
+import com.android.systemui.statusbar.notification.row.icon.NotificationIconStyleProvider
+import com.android.systemui.statusbar.notification.shared.NotificationBundleUi
+import com.android.systemui.statusbar.notification.stack.StackStateAnimator
+import com.google.android.material.materialswitch.MaterialSwitch
+
+/**
+ * The guts of a notification revealed when performing a long press, specifically for notifications
+ * that are bundled. Contains controls to allow user to disable the feature for the app that posted
+ * the notification.
+ */
+class BundledNotificationInfo(context: Context?, attrs: AttributeSet?) :
+ NotificationInfo(context, attrs) {
+
+ @Throws(RemoteException::class)
+ override fun bindNotification(
+ pm: PackageManager,
+ iNotificationManager: INotificationManager,
+ appIconProvider: AppIconProvider,
+ iconStyleProvider: NotificationIconStyleProvider,
+ onUserInteractionCallback: OnUserInteractionCallback,
+ channelEditorDialogController: ChannelEditorDialogController,
+ packageDemotionInteractor: PackageDemotionInteractor,
+ pkg: String,
+ ranking: NotificationListenerService.Ranking,
+ sbn: StatusBarNotification,
+ entry: NotificationEntry,
+ entryAdapter: EntryAdapter,
+ onSettingsClick: OnSettingsClickListener?,
+ onAppSettingsClick: OnAppSettingsClickListener,
+ feedbackClickListener: OnFeedbackClickListener,
+ uiEventLogger: UiEventLogger,
+ isDeviceProvisioned: Boolean,
+ isNonblockable: Boolean,
+ isDismissable: Boolean,
+ wasShownHighPriority: Boolean,
+ assistantFeedbackController: AssistantFeedbackController,
+ metricsLogger: MetricsLogger,
+ onCloseClick: OnClickListener,
+ ) {
+ super.bindNotification(
+ pm,
+ iNotificationManager,
+ appIconProvider,
+ iconStyleProvider,
+ onUserInteractionCallback,
+ channelEditorDialogController,
+ null,
+ pkg,
+ ranking,
+ sbn,
+ entry,
+ entryAdapter,
+ onSettingsClick,
+ onAppSettingsClick,
+ feedbackClickListener,
+ uiEventLogger,
+ isDeviceProvisioned,
+ isDismissable,
+ isNonblockable,
+ wasShownHighPriority,
+ assistantFeedbackController,
+ metricsLogger,
+ onCloseClick,
+ )
+ }
+
+ override fun bindInlineControls() {
+ val enabled =
+ mINotificationManager.isAdjustmentSupportedForPackage(
+ Adjustment.KEY_TYPE,
+ mSbn.packageName,
+ )
+ val toggle = findViewById<MaterialSwitch>(R.id.feature_toggle)
+ toggle.isChecked = enabled
+ toggle.setOnCheckedChangeListener { buttonView, isChecked ->
+ val isAChange = isChecked != enabled
+ val done = findViewById<TextView>(R.id.done)
+ done.setText(
+ if (isAChange)
+ R.string.inline_ok_button
+ else
+ R.string.inline_done_button
+ )
+ }
+
+ val done = findViewById<View>(R.id.done)
+ done.setOnClickListener {
+ try {
+ if (NotificationBundleUi.isEnabled) {
+ mEntryAdapter.markForUserTriggeredMovement(true)
+ mEntryAdapter.onImportanceChanged()
+ } else {
+ mEntry.markForUserTriggeredMovement(true)
+ mOnUserInteractionCallback.onImportanceChanged(mEntry)
+ }
+ mINotificationManager.setAdjustmentSupportedForPackage(
+ Adjustment.KEY_TYPE,
+ mSbn.packageName,
+ toggle.isChecked,
+ )
+ mOnDismissSettings.onClick(done)
+ } catch (e: RemoteException) {
+ throw RuntimeException(e)
+ }
+ }
+ done.setAccessibilityDelegate(mGutsContainer.accessibilityDelegate)
+
+ findViewById<TextView>(R.id.feature_summary).setText(
+ resources.getString(R.string.notification_guts_bundle_summary, mAppName));
+
+ val dismissButton = findViewById<View>(R.id.inline_dismiss)
+ dismissButton.setOnClickListener(mOnCloseClickListener)
+ dismissButton.visibility = if (dismissButton.hasOnClickListeners() && mIsDismissable)
+ VISIBLE
+ else
+ GONE
+ }
+
+ companion object {
+ private const val TAG = "BundledNotificationInfo"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 89121e2..582f1ba 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -116,6 +116,7 @@
import com.android.systemui.statusbar.notification.NotificationTransitionAnimatorController;
import com.android.systemui.statusbar.notification.NotificationUtils;
import com.android.systemui.statusbar.notification.SourceType;
+import com.android.systemui.statusbar.notification.collection.BundleEntryAdapter;
import com.android.systemui.statusbar.notification.collection.EntryAdapter;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.PipelineEntry;
@@ -770,6 +771,10 @@
@VisibleForTesting
void updateShelfIconColor() {
StatusBarIconView expandedIcon = getShelfIcon();
+ if (expandedIcon == null) {
+ // TODO(b/399736937) get shelf icon for bundle
+ return;
+ }
boolean isPreL = Boolean.TRUE.equals(expandedIcon.getTag(R.id.icon_is_pre_L));
boolean colorize = !isPreL || NotificationUtils.isGrayscale(expandedIcon,
ContrastColorUtil.getInstance(mContext));
@@ -1394,8 +1399,7 @@
}
if (mExpandedWhenPinned) {
return Math.max(getMaxExpandHeight(), getHeadsUpHeight());
- } else if (android.app.Flags.compactHeadsUpNotification()
- && getShowingLayout().isHUNCompact()) {
+ } else if (getShowingLayout().isHUNCompact()) {
return getHeadsUpHeight();
} else if (atLeastMinHeight) {
return Math.max(getCollapsedHeight(), getHeadsUpHeight());
@@ -1963,14 +1967,8 @@
mOnUserInteractionCallback, REASON_CANCEL)
: mOnUserInteractionCallback.registerFutureDismissal(
getEntryLegacy(), REASON_CANCEL);
- if (Flags.notificationReentrantDismiss()) {
- if (futureDismissal != null) {
- post(futureDismissal);
- }
- } else {
- if (futureDismissal != null) {
- futureDismissal.run();
- }
+ if (futureDismissal != null) {
+ post(futureDismissal);
}
}
}
@@ -2282,16 +2280,19 @@
mFalsingManager = falsingManager;
mStatusBarStateController = statusBarStateController;
mPeopleNotificationIdentifier = peopleNotificationIdentifier;
- for (NotificationContentView l : mLayouts) {
- l.initialize(
- mPeopleNotificationIdentifier,
- rivSubcomponentFactory,
- smartReplyConstants,
- smartReplyController,
- statusBarService,
- uiEventLogger
- );
+ if (rivSubcomponentFactory != null) {
+ for (NotificationContentView l : mLayouts) {
+ l.initialize(
+ mPeopleNotificationIdentifier,
+ rivSubcomponentFactory,
+ smartReplyConstants,
+ smartReplyController,
+ statusBarService,
+ uiEventLogger
+ );
+ }
}
+
mOnUserInteractionCallback = onUserInteractionCallback;
mNotificationGutsManager = gutsManager;
mMetricsLogger = metricsLogger;
@@ -2803,6 +2804,13 @@
if (mMenuRow != null && mMenuRow.getMenuView() != null) {
mMenuRow.onParentTranslationUpdate(translationX);
}
+ // Clipping occurs for children of a NotificationChildrenContainer every time the container
+ // needs to draw a child (see drawChild of the container class). Thus, we invalidate the
+ // container so that the clip path used to clip the children can access the latest
+ // translation of this child.
+ if (getParent() instanceof NotificationChildrenContainer childrenContainer) {
+ childrenContainer.invalidate();
+ }
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
index 49d715c..db2bac4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
@@ -21,10 +21,14 @@
import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
import static com.android.systemui.statusbar.notification.NotificationUtils.logKey;
import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.UserHandle;
import android.provider.Settings;
+import android.service.notification.StatusBarNotification;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
@@ -47,6 +51,7 @@
import com.android.systemui.statusbar.notification.ColorUpdateLogger;
import com.android.systemui.statusbar.notification.FeedbackIcon;
import com.android.systemui.statusbar.notification.collection.EntryAdapterFactory;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.PipelineEntry;
import com.android.systemui.statusbar.notification.collection.provider.NotificationDismissibilityProvider;
import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
@@ -55,13 +60,12 @@
import com.android.systemui.statusbar.notification.collection.render.NotifViewController;
import com.android.systemui.statusbar.notification.headsup.HeadsUpManager;
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
-import com.android.systemui.statusbar.notification.row.dagger.AppName;
-import com.android.systemui.statusbar.notification.row.dagger.NotificationKey;
import com.android.systemui.statusbar.notification.row.dagger.NotificationRowScope;
import com.android.systemui.statusbar.notification.shared.NotificationBundleUi;
import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainerLogger;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.notification.stack.ui.view.NotificationRowStatsLogger;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.policy.SmartReplyConstants;
import com.android.systemui.statusbar.policy.dagger.RemoteInputViewSubcomponent;
@@ -92,8 +96,6 @@
private final ActivatableNotificationViewController mActivatableNotificationViewController;
private final PluginManager mPluginManager;
private final SystemClock mClock;
- private final String mAppName;
- private final String mNotificationKey;
private final ColorUpdateLogger mColorUpdateLogger;
private final KeyguardBypassController mKeyguardBypassController;
private final GroupMembershipManager mGroupMembershipManager;
@@ -124,6 +126,7 @@
private final NotificationSettingsController mSettingsController;
private final EntryAdapterFactory mEntryAdapterFactory;
private final WindowRootViewBlurInteractor mWindowRootViewBlurInteractor;
+ private final Context mContext;
@VisibleForTesting
final NotificationSettingsController.Listener mSettingsListener =
@@ -252,7 +255,6 @@
}
};
-
@Inject
public ExpandableNotificationRowController(
ExpandableNotificationRow view,
@@ -267,8 +269,7 @@
SmartReplyController smartReplyController,
PluginManager pluginManager,
SystemClock clock,
- @AppName String appName,
- @NotificationKey String notificationKey,
+ Context context,
KeyguardBypassController keyguardBypassController,
GroupMembershipManager groupMembershipManager,
GroupExpansionManager groupExpansionManager,
@@ -293,13 +294,12 @@
EntryAdapterFactory entryAdapterFactory,
WindowRootViewBlurInteractor windowRootViewBlurInteractor) {
mView = view;
+ mContext = context;
mListContainer = listContainer;
mRemoteInputViewSubcomponentFactory = rivSubcomponentFactory;
mActivatableNotificationViewController = activatableNotificationViewController;
mPluginManager = pluginManager;
mClock = clock;
- mAppName = appName;
- mNotificationKey = notificationKey;
mKeyguardBypassController = keyguardBypassController;
mGroupMembershipManager = groupMembershipManager;
mGroupExpansionManager = groupExpansionManager;
@@ -332,6 +332,35 @@
mWindowRootViewBlurInteractor = windowRootViewBlurInteractor;
}
+ String loadsGutsAppName(Context context, PipelineEntry pipelineEntry) {
+ if (pipelineEntry instanceof NotificationEntry notificationEntry) {
+ StatusBarNotification statusBarNotification = notificationEntry.getSbn();
+ if (statusBarNotification == null) {
+ return "";
+ }
+ // Get the app name.
+ // Note that Notification.Builder#bindHeaderAppName has similar logic
+ // but since this field is used in the guts, it must be accurate.
+ // Therefore we will only show the application label, or, failing that, the
+ // package name. No substitutions.
+ PackageManager pmUser = CentralSurfaces.getPackageManagerForUser(
+ context, statusBarNotification.getUser().getIdentifier());
+ final String pkg = statusBarNotification.getPackageName();
+ try {
+ final ApplicationInfo info = pmUser.getApplicationInfo(pkg,
+ PackageManager.MATCH_UNINSTALLED_PACKAGES
+ | PackageManager.MATCH_DISABLED_COMPONENTS);
+ if (info != null) {
+ return String.valueOf(pmUser.getApplicationLabel(info));
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ // Do nothing
+ }
+ return pkg;
+ }
+ return "";
+ }
+
/**
* Initialize the controller.
*/
@@ -341,8 +370,8 @@
mEntryAdapterFactory.create(entry),
entry,
mRemoteInputViewSubcomponentFactory,
- mAppName,
- mNotificationKey,
+ loadsGutsAppName(mContext, entry),
+ entry.getKey(),
mLoggerCallback,
mKeyguardBypassController,
mGroupMembershipManager,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
index f36a0cf..8038ae8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
@@ -19,8 +19,6 @@
import static com.android.systemui.Flags.notificationColorUpdateLogger;
import static com.android.systemui.Flags.physicalNotificationMovement;
-import static java.lang.Math.abs;
-
import android.animation.AnimatorListenerAdapter;
import android.content.Context;
import android.content.res.Configuration;
@@ -31,7 +29,6 @@
import android.util.IndentingPrintWriter;
import android.util.Log;
import android.view.View;
-import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.widget.FrameLayout;
@@ -113,25 +110,12 @@
protected SpringAnimation mMagneticAnimator = new SpringAnimation(
this /* object */, DynamicAnimation.TRANSLATION_X);
- private int mTouchSlop;
-
protected MagneticRowListener mMagneticRowListener = new MagneticRowListener() {
@Override
- public void setMagneticTranslation(float translation, boolean trackEagerly) {
+ public void setMagneticTranslation(float translation) {
if (!mMagneticAnimator.isRunning()) {
setTranslation(translation);
- return;
- }
-
- if (trackEagerly) {
- float delta = abs(getTranslation() - translation);
- if (delta > mTouchSlop) {
- mMagneticAnimator.animateToFinalPosition(translation);
- } else {
- mMagneticAnimator.cancel();
- setTranslation(translation);
- }
} else {
mMagneticAnimator.animateToFinalPosition(translation);
}
@@ -199,7 +183,6 @@
private void initDimens() {
mContentShift = getResources().getDimensionPixelSize(
R.dimen.shelf_transform_content_shift);
- mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HeadsUpStyleProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HeadsUpStyleProvider.kt
index 86f125b..d567a94 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HeadsUpStyleProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HeadsUpStyleProvider.kt
@@ -16,10 +16,7 @@
package com.android.systemui.statusbar.notification.row
-import android.app.Flags
-import android.os.SystemProperties
import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryStore
-import com.android.systemui.util.Compile
import javax.inject.Inject
/**
@@ -36,7 +33,7 @@
HeadsUpStyleProvider {
override fun shouldApplyCompactStyle(): Boolean {
- return Flags.compactHeadsUpNotification() && isInImmersiveMode()
+ return isInImmersiveMode()
}
private fun isInImmersiveMode() =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
index e6e0014..84af566 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
@@ -20,7 +20,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.app.Flags;
import android.app.Notification;
import android.app.PendingIntent;
import android.content.Context;
@@ -58,7 +57,6 @@
import com.android.systemui.statusbar.notification.FeedbackIcon;
import com.android.systemui.statusbar.notification.NotificationFadeAware;
import com.android.systemui.statusbar.notification.NotificationUtils;
-import com.android.systemui.statusbar.notification.collection.EntryAdapter;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.RemoteInputEntryAdapter;
import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
@@ -570,8 +568,7 @@
mHeadsUpWrapper = NotificationViewWrapper.wrap(getContext(), child,
mContainingNotification);
- mIsHUNCompact = Flags.compactHeadsUpNotification()
- && mHeadsUpWrapper instanceof NotificationCompactHeadsUpTemplateViewWrapper;
+ mIsHUNCompact = mHeadsUpWrapper instanceof NotificationCompactHeadsUpTemplateViewWrapper;
if (mIsHUNCompact) {
logCompactHUNShownEvent();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
index dcbc15e..2ef2219 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
@@ -242,6 +242,13 @@
* channel.
*/
public static final String EXTRA_SHOW_FRAGMENT_ARGUMENTS = ":settings:show_fragment_args";
+
+ private void startBundleSettingsActivity(final int appUid,
+ ExpandableNotificationRow row) {
+ final Intent intent = new Intent(Settings.ACTION_NOTIFICATION_BUNDLES);
+ mNotificationActivityStarter.startNotificationGutsIntent(intent, appUid, row);
+ }
+
private void startAppNotificationSettingsActivity(String packageName, final int appUid,
final NotificationChannel channel, ExpandableNotificationRow row) {
final Intent intent = new Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS);
@@ -348,6 +355,9 @@
initializeFeedbackInfo(row, sbn, ranking, (FeedbackInfo) gutsView);
} else if (gutsView instanceof PromotedPermissionGutsContent) {
initializeDemoteView(sbn, (PromotedPermissionGutsContent) gutsView);
+ } else if (gutsView instanceof BundledNotificationInfo) {
+ initializeBundledNotificationInfo(
+ row, sbn, ranking, (BundledNotificationInfo) gutsView);
}
return true;
} catch (Exception e) {
@@ -380,7 +390,6 @@
/**
* Sets up the {@link NotificationSnooze} inside the notification row's guts.
*
- * @param row view to set up the guts for
* @param demoteGuts view to set up/bind within {@code row}
*/
private void initializeDemoteView(
@@ -421,6 +430,66 @@
}
/**
+ * Sets up the {@link BundledNotificationInfo} inside the notification row's guts.
+ * @param row view to set up the guts for
+ * @param notificationInfoView view to set up/bind within {@code row}
+ */
+ @VisibleForTesting
+ void initializeBundledNotificationInfo(
+ final ExpandableNotificationRow row,
+ final StatusBarNotification sbn,
+ final NotificationListenerService.Ranking ranking,
+ NotificationInfo notificationInfoView) throws Exception {
+ NotificationGuts guts = row.getGuts();
+ String packageName = sbn.getPackageName();
+ UserHandle userHandle = sbn.getUser();
+ PackageManager pmUser = CentralSurfaces.getPackageManagerForUser(
+ mContext, userHandle.getIdentifier());
+
+ NotificationInfo.OnSettingsClickListener onSettingsClick =
+ (View v, NotificationChannel channel, int appUid) -> {
+ mMetricsLogger.action(MetricsProto.MetricsEvent.ACTION_NOTE_INFO);
+ guts.resetFalsingCheck();
+ mOnSettingsClickListener.onSettingsClick(sbn.getKey());
+ startBundleSettingsActivity(appUid, row);
+ };
+
+ NotificationInfo.OnFeedbackClickListener onNasFeedbackClick = (View v, Intent intent) -> {
+ guts.resetFalsingCheck();
+ mNotificationActivityStarter.startNotificationGutsIntent(intent, sbn.getUid(), row);
+ };
+
+ notificationInfoView.bindNotification(
+ pmUser,
+ mNotificationManager,
+ mAppIconProvider,
+ mIconStyleProvider,
+ mOnUserInteractionCallback,
+ mChannelEditorDialogController,
+ mPackageDemotionInteractor,
+ packageName,
+ ranking,
+ sbn,
+ NotificationBundleUi.isEnabled() ? null : row.getEntryLegacy(),
+ NotificationBundleUi.isEnabled() ? row.getEntryAdapter() : null,
+ onSettingsClick,
+ null,
+ onNasFeedbackClick,
+ mUiEventLogger,
+ mDeviceProvisionedController.isDeviceProvisioned(),
+ NotificationBundleUi.isEnabled()
+ ? !row.getEntryAdapter().isBlockable()
+ : row.getIsNonblockable(),
+ row.canViewBeDismissed(),
+ NotificationBundleUi.isEnabled()
+ ? row.getEntryAdapter().isHighPriority()
+ : mHighPriorityProvider.isHighPriority(row.getEntryLegacy()),
+ mAssistantFeedbackController,
+ mMetricsLogger,
+ row.getCloseButtonOnClickListener(row));
+ }
+
+ /**
* Sets up the {@link NotificationInfo} inside the notification row's guts.
* @param row view to set up the guts for
* @param notificationInfoView view to set up/bind within {@code row}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
index c3d65a2..eaef331 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
@@ -95,17 +95,17 @@
private TextView mSilentDescriptionView;
private TextView mAutomaticDescriptionView;
- private INotificationManager mINotificationManager;
+ protected INotificationManager mINotificationManager;
private AppIconProvider mAppIconProvider;
private NotificationIconStyleProvider mIconStyleProvider;
- private OnUserInteractionCallback mOnUserInteractionCallback;
+ protected OnUserInteractionCallback mOnUserInteractionCallback;
private PackageManager mPm;
private MetricsLogger mMetricsLogger;
private ChannelEditorDialogController mChannelEditorDialogController;
private AssistantFeedbackController mAssistantFeedbackController;
private String mPackageName;
- private String mAppName;
+ protected String mAppName;
private int mAppUid;
private String mDelegatePkg;
private NotificationChannel mSingleNotificationChannel;
@@ -124,18 +124,18 @@
private boolean mIsAutomaticChosen;
private boolean mIsSingleDefaultChannel;
private boolean mIsNonblockable;
- private boolean mIsDismissable;
- private NotificationEntry mEntry;
- private StatusBarNotification mSbn;
+ protected boolean mIsDismissable;
+ protected NotificationEntry mEntry;
+ protected StatusBarNotification mSbn;
private NotificationListenerService.Ranking mRanking;
- private EntryAdapter mEntryAdapter;
+ protected EntryAdapter mEntryAdapter;
private boolean mIsDeviceProvisioned;
private boolean mIsSystemRegisteredCall;
private OnSettingsClickListener mOnSettingsClickListener;
private OnAppSettingsClickListener mAppSettingsClickListener;
private OnFeedbackClickListener mFeedbackClickListener;
- private NotificationGuts mGutsContainer;
+ protected NotificationGuts mGutsContainer;
private Drawable mPkgIcon;
private UiEventLogger mUiEventLogger;
@@ -163,11 +163,11 @@
};
// used by standard ui
- private final OnClickListener mOnDismissSettings = v -> {
+ protected final OnClickListener mOnDismissSettings = v -> {
mPressedApply = true;
mGutsContainer.closeControls(v, /* save= */ true);
};
- private OnClickListener mOnCloseClickListener;
+ protected OnClickListener mOnCloseClickListener;
public NotificationInfo(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -266,7 +266,7 @@
mMetricsLogger.write(notificationControlsLogMaker());
}
- private void bindInlineControls() {
+ protected void bindInlineControls() {
if (mIsSystemRegisteredCall) {
findViewById(R.id.non_configurable_call_text).setVisibility(VISIBLE);
findViewById(R.id.non_configurable_text).setVisibility(GONE);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
index 8cc5248..8ec8c1d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
@@ -32,6 +32,7 @@
import android.os.Handler;
import android.os.Looper;
import android.provider.Settings;
+import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import android.util.ArrayMap;
import android.view.LayoutInflater;
@@ -268,6 +269,12 @@
StatusBarNotification sbn = NotificationBundleUi.isEnabled()
? mParent.getEntryAdapter().getSbn()
: mParent.getEntryLegacy().getSbn();
+ NotificationListenerService.Ranking ranking = NotificationBundleUi.isEnabled()
+ ? mParent.getEntryAdapter().getRanking()
+ : mParent.getEntryLegacy().getRanking();
+ boolean isBundled = NotificationBundleUi.isEnabled()
+ ? mParent.getEntryAdapter().isBundled()
+ : mParent.getEntryLegacy().isBundled();
if (personNotifType == PeopleNotificationIdentifier.TYPE_PERSON) {
mInfoItem = createPartialConversationItem(mContext);
} else if (personNotifType >= PeopleNotificationIdentifier.TYPE_FULL_PERSON) {
@@ -277,6 +284,8 @@
&& Flags.permissionHelperUiRichOngoing()
&& sbn.getNotification().isPromotedOngoing()) {
mInfoItem = createPromotedItem(mContext);
+ } else if (android.app.Flags.notificationClassificationUi() && isBundled) {
+ mInfoItem = createBundledInfoItem(mContext);
} else {
mInfoItem = createInfoItem(mContext);
}
@@ -751,6 +760,17 @@
NotificationMenuItem.OMIT_FROM_SWIPE_MENU);
}
+ static NotificationMenuItem createBundledInfoItem(Context context) {
+ Resources res = context.getResources();
+ String infoDescription = res.getString(R.string.notification_menu_gear_description);
+ int layoutId = R.layout.bundled_notification_info;
+ BundledNotificationInfo infoContent =
+ (BundledNotificationInfo) LayoutInflater.from(context).inflate(
+ layoutId, null, false);
+ return new NotificationMenuItem(context, infoDescription, infoContent,
+ NotificationMenuItem.OMIT_FROM_SWIPE_MENU);
+ }
+
static NotificationMenuItem createInfoItem(Context context) {
Resources res = context.getResources();
String infoDescription = res.getString(R.string.notification_menu_gear_description);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PromotedNotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PromotedNotificationInfo.java
index 2cb2089..b03d366 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PromotedNotificationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PromotedNotificationInfo.java
@@ -46,7 +46,6 @@
private static final String TAG = "PromotedNotifInfoGuts";
private INotificationManager mNotificationManager;
private PackageDemotionInteractor mPackageDemotionInteractor;
- private NotificationGuts mGutsContainer;
public PromotedNotificationInfo(Context context, AttributeSet attrs) {
super(context, attrs);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowInflaterTask.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowInflaterTask.java
index 3971661..fb7155a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowInflaterTask.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowInflaterTask.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.notification.row;
+import static com.android.systemui.statusbar.notification.collection.coordinator.BundleCoordinator.debugBundleLog;
+
import android.content.Context;
import android.os.UserHandle;
import android.util.AttributeSet;
@@ -34,7 +36,9 @@
import com.android.systemui.res.R;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.InflationTask;
+import com.android.systemui.statusbar.notification.collection.BundleEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.PipelineEntry;
import com.android.systemui.statusbar.notification.shared.NotificationBundleUi;
import com.android.systemui.util.time.SystemClock;
@@ -50,9 +54,9 @@
private static final String TAG = "RowInflaterTask";
private static final boolean TRACE_ORIGIN = true;
-
private RowInflationFinishedListener mListener;
private NotificationEntry mEntry;
+ private BundleEntry mBundleEntry;
private boolean mCancelled;
private Throwable mInflateOrigin;
private final SystemClock mSystemClock;
@@ -79,6 +83,36 @@
inflate(context, parent, entry, null, listener);
}
+ public void inflate(Context context, ViewGroup parent, BundleEntry entry,
+ RowInflationFinishedListener listener) {
+ inflate(context, parent, entry, null, listener);
+ }
+
+ private void debugLog(String s) {
+ // TODO(b/399736937) update debug logs for bundles to using lambda
+ debugBundleLog(TAG, () -> s);
+ }
+
+ public void inflate(Context context, ViewGroup parent, BundleEntry entry,
+ @Nullable Executor listenerExecutor, RowInflationFinishedListener listener) {
+ if (TRACE_ORIGIN) {
+ mInflateOrigin = new Throwable("inflate requested here");
+ }
+ mBundleEntry = entry;
+ mListener = listener;
+ RowAsyncLayoutInflater asyncLayoutFactory = new RowAsyncLayoutInflater(
+ entry, mSystemClock, mLogger, mUserTracker.getUserHandle());
+ if (Flags.useNotifInflationThreadForRow()) {
+ debugLog("mAsyncRowInflater.inflate bundle: " + entry.getKey());
+ mAsyncRowInflater.inflate(context, asyncLayoutFactory,
+ R.layout.status_bar_notification_row, parent, this);
+ } else {
+ debugLog("AsyncLayoutInflater.inflate bundle: " + entry.getKey());
+ AsyncLayoutInflater inflater = new AsyncLayoutInflater(context, asyncLayoutFactory);
+ inflater.inflate(R.layout.status_bar_notification_row, parent, listenerExecutor, this);
+ }
+ }
+
/**
* Inflates a new notificationView asynchronously, calling the {@code listener} on the supplied
* {@code listenerExecutor} (or the main thread if null) when done. This should not be called
@@ -129,7 +163,8 @@
@VisibleForTesting
public static class RowAsyncLayoutInflater implements AsyncLayoutFactory {
- private final NotificationEntry mEntry;
+ private NotificationEntry mEntry = null;
+ private BundleEntry mBundleEntry = null;
private final SystemClock mSystemClock;
private final RowInflaterTaskLogger mLogger;
private final UserHandle mTargetUser;
@@ -142,6 +177,14 @@
mTargetUser = targetUser;
}
+ public RowAsyncLayoutInflater(BundleEntry entry, SystemClock systemClock,
+ RowInflaterTaskLogger logger, UserHandle targetUser) {
+ mBundleEntry = entry;
+ mSystemClock = systemClock;
+ mLogger = logger;
+ mTargetUser = targetUser;
+ }
+
@Nullable
@Override
public View onCreateView(@Nullable View parent, @NonNull String name,
@@ -159,8 +202,9 @@
}
final long elapsedMs = mSystemClock.elapsedRealtime() - startMs;
- mLogger.logCreatedRow(mEntry, elapsedMs);
-
+ if (mEntry != null) {
+ mLogger.logCreatedRow(mEntry, elapsedMs);
+ }
return row;
}
@@ -179,7 +223,23 @@
@Override
public void onInflateFinished(View view, int resid, ViewGroup parent) {
+ debugLog("mAsyncRowInflater.inflate onInflateFinished: " + view
+ + " parent: " + parent
+ + " mEntry: " + mEntry
+ + " mBundleEntry: " + mBundleEntry
+ + " mCancelled: " + mCancelled
+ );
final long elapsedMs = mSystemClock.elapsedRealtime() - mInflateStartTimeMs;
+ if (mEntry == null) {
+ if (mBundleEntry != null) {
+ if (!mCancelled) {
+ debugLog( "mListener.onInflationFinished for bundle:"
+ + mBundleEntry.getKey());
+ mListener.onInflationFinished((ExpandableNotificationRow) view);
+ }
+ }
+ return;
+ }
mLogger.logInflateFinish(mEntry, elapsedMs, mCancelled);
if (!mCancelled) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/ExpandableNotificationRowComponent.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/ExpandableNotificationRowComponent.java
index 0a6a2c8..053d361 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/ExpandableNotificationRowComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/ExpandableNotificationRowComponent.java
@@ -16,24 +16,19 @@
package com.android.systemui.statusbar.notification.row.dagger;
-import android.content.Context;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.service.notification.StatusBarNotification;
-
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.PipelineEntry;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
import com.android.systemui.statusbar.notification.row.BigPictureIconManager;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRowController;
-import com.android.systemui.statusbar.phone.CentralSurfaces;
-import dagger.Binds;
import dagger.BindsInstance;
import dagger.Module;
import dagger.Provides;
import dagger.Subcomponent;
+
/**
* Dagger Component for a {@link ExpandableNotificationRow}.
*/
@@ -44,7 +39,6 @@
})
@NotificationRowScope
public interface ExpandableNotificationRowComponent {
-
/**
* Builder for {@link NotificationRowComponent}.
*/
@@ -56,7 +50,7 @@
@BindsInstance
Builder expandableNotificationRow(ExpandableNotificationRow view);
@BindsInstance
- Builder notificationEntry(NotificationEntry entry);
+ Builder pipelineEntry(PipelineEntry pipelineEntry);
@BindsInstance
Builder onExpandClickListener(ExpandableNotificationRow.OnExpandClickListener presenter);
ExpandableNotificationRowComponent build();
@@ -78,47 +72,16 @@
* Dagger Module that extracts interesting properties from an ExpandableNotificationRow.
*/
@Module
- abstract class ExpandableNotificationRowModule {
-
+ class ExpandableNotificationRowModule {
/** ExpandableNotificationRow is provided as an instance of ActivatableNotificationView. */
- @Binds
- abstract ActivatableNotificationView bindExpandableView(ExpandableNotificationRow view);
-
@Provides
- static StatusBarNotification provideStatusBarNotification(
- NotificationEntry notificationEntry) {
- return notificationEntry.getSbn();
+ ActivatableNotificationView bindExpandableView(ExpandableNotificationRow view) {
+ return view;
}
@Provides
- @NotificationKey
- static String provideNotificationKey(StatusBarNotification statusBarNotification) {
- return statusBarNotification.getKey();
- }
-
- @Provides
- @AppName
- static String provideAppName(Context context, StatusBarNotification statusBarNotification) {
- // Get the app name.
- // Note that Notification.Builder#bindHeaderAppName has similar logic
- // but since this field is used in the guts, it must be accurate.
- // Therefore we will only show the application label, or, failing that, the
- // package name. No substitutions.
- PackageManager pmUser = CentralSurfaces.getPackageManagerForUser(
- context, statusBarNotification.getUser().getIdentifier());
- final String pkg = statusBarNotification.getPackageName();
- try {
- final ApplicationInfo info = pmUser.getApplicationInfo(pkg,
- PackageManager.MATCH_UNINSTALLED_PACKAGES
- | PackageManager.MATCH_DISABLED_COMPONENTS);
- if (info != null) {
- return String.valueOf(pmUser.getApplicationLabel(info));
- }
- } catch (PackageManager.NameNotFoundException e) {
- // Do nothing
- }
-
- return pkg;
+ NotificationEntry provideNotificationEntry(PipelineEntry pipelineEntry) {
+ return (NotificationEntry) pipelineEntry;
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/AppIconProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/AppIconProvider.kt
index 33d9433..bb15c81 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/AppIconProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/AppIconProvider.kt
@@ -62,6 +62,14 @@
): Drawable
/**
+ * Loads the skeleton (black and white)-themed icon corresponding to [packageName] into cache,
+ * or fetches it from there if already present. This should only be called from the background.
+ */
+ @Throws(NameNotFoundException::class)
+ @WorkerThread
+ fun getOrFetchSkeletonAppIcon(packageName: String, context: Context): Drawable
+
+ /**
* Mark all the entries in the cache that are NOT in [wantedPackages] to be cleared. If they're
* still not needed on the next call of this method (made after a timeout of 1s, in case they
* happen more frequently than that), they will be purged. This can be done from any thread.
@@ -78,6 +86,21 @@
dumpManager.registerNormalDumpable(TAG, this)
}
+ // TODO - b/406484337: Rename non-skeleton members to something like "standardWhatever".
+
+ private val iconSize: Int
+ get() =
+ sysuiContext.resources.getDimensionPixelSize(
+ if (ActivityManager.isLowRamDeviceStatic()) {
+ R.dimen.notification_small_icon_size_low_ram
+ } else {
+ R.dimen.notification_small_icon_size
+ }
+ )
+
+ private val densityDpi: Int
+ get() = sysuiContext.resources.configuration.densityDpi
+
private class NotificationIcons(context: Context?, fillResIconDpi: Int, iconBitmapSize: Int) :
BaseIconFactory(context, fillResIconDpi, iconBitmapSize) {
@@ -98,20 +121,31 @@
}
}
- private val iconFactory: BaseIconFactory
- get() {
- val isLowRam = ActivityManager.isLowRamDeviceStatic()
- val res = sysuiContext.resources
- val iconSize: Int =
- res.getDimensionPixelSize(
- if (isLowRam) R.dimen.notification_small_icon_size_low_ram
- else R.dimen.notification_small_icon_size
+ private class SkeletonNotificationIcons(
+ context: Context?,
+ fillResIconDpi: Int,
+ iconBitmapSize: Int,
+ ) : BaseIconFactory(context, fillResIconDpi, iconBitmapSize) {
+ init {
+ mThemeController =
+ MonoIconThemeController(
+ colorProvider = { _ ->
+ intArrayOf(/* background */ Color.BLACK, /* icon */ Color.WHITE)
+ }
)
- return NotificationIcons(sysuiContext, res.configuration.densityDpi, iconSize)
}
+ }
+
+ private val iconFactory: BaseIconFactory
+ get() = NotificationIcons(sysuiContext, densityDpi, iconSize)
+
+ private val skeletonIconFactory: BaseIconFactory
+ get() = SkeletonNotificationIcons(sysuiContext, densityDpi, iconSize)
private val cache = NotifCollectionCache<Drawable>()
+ private val skeletonCache = NotifCollectionCache<Drawable>()
+
override fun getOrFetchAppIcon(
packageName: String,
context: Context,
@@ -127,6 +161,10 @@
}
}
+ override fun getOrFetchSkeletonAppIcon(packageName: String, context: Context): Drawable {
+ return skeletonCache.getOrFetch(packageName) { fetchSkeletonAppIcon(packageName, context) }
+ }
+
@WorkerThread
private fun fetchAppIcon(
packageName: String,
@@ -137,18 +175,31 @@
val pm = context.packageManager
val icon = pm.getApplicationInfo(packageName, 0).loadUnbadgedIcon(pm)
- val options =
- IconOptions().apply {
- setUser(userIconInfo(context, withWorkProfileBadge))
- setBitmapGenerationMode(BaseIconFactory.MODE_HARDWARE)
- // This color will not be used, but we're just setting it so that the icon factory
- // doesn't try to extract colors from our bitmap (since it won't work, given it's a
- // hardware bitmap).
- setExtractedColor(Color.BLUE)
- }
+ val options = iconOptions(context, withWorkProfileBadge)
val badgedIcon = iconFactory.createBadgedIconBitmap(icon, options)
val creationFlags = if (themed) BitmapInfo.FLAG_THEMED else 0
- return badgedIcon.newIcon(sysuiContext, creationFlags)
+ return badgedIcon.newIcon(sysuiContext, creationFlags).apply { isAnimationEnabled = false }
+ }
+
+ @WorkerThread
+ private fun fetchSkeletonAppIcon(packageName: String, context: Context): Drawable {
+ val pm = context.packageManager
+ val icon = pm.getApplicationInfo(packageName, 0).loadUnbadgedIcon(pm)
+
+ val options = iconOptions(context, withWorkProfileBadge = false)
+ val badgedIcon = skeletonIconFactory.createBadgedIconBitmap(icon, options)
+ return badgedIcon.newIcon(sysuiContext, BitmapInfo.FLAG_THEMED)
+ }
+
+ private fun iconOptions(context: Context, withWorkProfileBadge: Boolean): IconOptions {
+ return IconOptions().apply {
+ setUser(userIconInfo(context, withWorkProfileBadge = false))
+ setBitmapGenerationMode(BaseIconFactory.MODE_HARDWARE)
+ // This color will not be used, but we're just setting it so that the icon factory
+ // doesn't try to extract colors from our bitmap (since it won't work, given it's a
+ // hardware bitmap).
+ setExtractedColor(Color.BLUE)
+ }
}
private fun userIconInfo(context: Context, withWorkProfileBadge: Boolean): UserIconInfo {
@@ -163,6 +214,10 @@
// We don't know from the packages if it's the work profile app or not, so let's just keep
// both if they're present in the cache.
cache.purge(wantedPackages.flatMap { listOf(it, "$it$WORK_SUFFIX") })
+
+ // Skeleton icons don't include profile badges, so we don't need to handle the work profile
+ // suffixes.
+ skeletonCache.purge(wantedPackages)
}
override fun dump(pwOrig: PrintWriter, args: Array<out String>) {
@@ -171,6 +226,9 @@
pw.println("cache information:")
pw.withIncreasedIndent { cache.dump(pw, args) }
+ pw.println("skeleton cache information:")
+ pw.withIncreasedIndent { skeletonCache.dump(pw, args) }
+
val iconFactory = iconFactory
pw.println("icon factory information:")
pw.withIncreasedIndent {
@@ -200,6 +258,11 @@
return ColorDrawable(Color.WHITE)
}
+ override fun getOrFetchSkeletonAppIcon(packageName: String, context: Context): Drawable {
+ Log.wtf(TAG, "NoOpIconProvider should not be used anywhere.")
+ return ColorDrawable(Color.BLACK)
+ }
+
override fun purgeCache(wantedPackages: Collection<String>) {
Log.wtf(TAG, "NoOpIconProvider should not be used anywhere.")
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MagneticNotificationRowManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MagneticNotificationRowManager.kt
index 9bd9a0b..0086c97 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MagneticNotificationRowManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MagneticNotificationRowManager.kt
@@ -32,6 +32,9 @@
*/
interface MagneticNotificationRowManager {
+ /** Set a new [SwipeInfoProvider]. */
+ fun setInfoProvider(swipeInfoProvider: SwipeInfoProvider?)
+
/**
* Notifies a change in the device density. The density can be used to compute the values of
* thresholds in pixels.
@@ -105,18 +108,27 @@
*/
fun reset()
+ /** A provider for the [MagneticNotificationRowManager] to query swipe information. */
+ interface SwipeInfoProvider {
+
+ /* Get the current velocity of a swipe */
+ fun getCurrentSwipeVelocity(): Float
+ }
+
companion object {
/** Detaching threshold in dp */
- const val MAGNETIC_DETACH_THRESHOLD_DP = 56
+ const val MAGNETIC_DETACH_THRESHOLD_DP = 72
/** Re-attaching threshold in dp */
- const val MAGNETIC_ATTACH_THRESHOLD_DP = 40
+ const val MAGNETIC_ATTACH_THRESHOLD_DP = 56
/* An empty implementation of a manager */
@JvmStatic
val Empty: MagneticNotificationRowManager
get() =
object : MagneticNotificationRowManager {
+ override fun setInfoProvider(swipeInfoProvider: SwipeInfoProvider?) {}
+
override fun onDensityChange(density: Float) {}
override fun setMagneticAndRoundableTargets(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MagneticNotificationRowManagerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MagneticNotificationRowManagerImpl.kt
index 03ede2f..418adcf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MagneticNotificationRowManagerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MagneticNotificationRowManagerImpl.kt
@@ -17,10 +17,14 @@
package com.android.systemui.statusbar.notification.stack
import android.os.VibrationAttributes
+import android.os.VibrationEffect
import androidx.dynamicanimation.animation.SpringForce
+import com.android.systemui.Flags
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.VibratorHelper
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.row.NotificationRowLogger
+import com.android.systemui.util.time.SystemClock
import com.google.android.msdl.data.model.MSDLToken
import com.google.android.msdl.domain.InteractionProperties
import com.google.android.msdl.domain.MSDLPlayer
@@ -35,9 +39,11 @@
@Inject
constructor(
private val msdlPlayer: MSDLPlayer,
+ private val vibratorHelper: VibratorHelper,
private val notificationTargetsHelper: NotificationTargetsHelper,
private val notificationRoundnessManager: NotificationRoundnessManager,
private val logger: NotificationRowLogger,
+ private val systemClock: SystemClock,
) : MagneticNotificationRowManager {
var currentState = State.IDLE
@@ -66,17 +72,22 @@
val swipedRowMultiplier =
MAGNETIC_TRANSLATION_MULTIPLIERS[MAGNETIC_TRANSLATION_MULTIPLIERS.size / 2]
- /**
- * An offset applied to input translation that increases on subsequent re-attachments of a
- * detached magnetic view. This helps keep computations consistent when the drag gesture input
- * and the swiped notification don't share the same origin point after a re-attaching animation.
- */
- private var translationOffset = 0f
-
private var dismissVelocity = 0f
private val detachDirectionEstimator = DirectionEstimator()
+ private var magneticSwipeInfoProvider: MagneticNotificationRowManager.SwipeInfoProvider? = null
+
+ // Last time pulling haptics played, in milliseconds since boot
+ // (see SystemClock.elapsedRealtime)
+ private var lastVibrationTime = 0L
+
+ override fun setInfoProvider(
+ swipeInfoProvider: MagneticNotificationRowManager.SwipeInfoProvider?
+ ) {
+ magneticSwipeInfoProvider = swipeInfoProvider
+ }
+
override fun onDensityChange(density: Float) {
magneticDetachThreshold =
density * MagneticNotificationRowManager.MAGNETIC_DETACH_THRESHOLD_DP
@@ -91,7 +102,6 @@
sectionsManager: NotificationSectionsManager,
) {
if (currentState == State.IDLE) {
- translationOffset = 0f
detachDirectionEstimator.reset()
updateMagneticAndRoundableTargets(swipingRow, stackScrollLayout, sectionsManager)
currentState = State.TARGETS_SET
@@ -142,29 +152,28 @@
val canTargetBeDismissed =
currentMagneticListeners.swipedListener()?.canRowBeDismissed() ?: false
- val correctedTranslation = translation - translationOffset
when (currentState) {
State.IDLE -> {
logger.logMagneticRowTranslationNotSet(currentState, row.getLoggingKey())
return false
}
State.TARGETS_SET -> {
- detachDirectionEstimator.recordTranslation(correctedTranslation)
- pullTargets(correctedTranslation, canTargetBeDismissed)
+ detachDirectionEstimator.recordTranslation(translation)
+ pullTargets(translation, canTargetBeDismissed)
currentState = State.PULLING
}
State.PULLING -> {
- detachDirectionEstimator.recordTranslation(correctedTranslation)
- updateRoundness(correctedTranslation)
+ detachDirectionEstimator.recordTranslation(translation)
+ updateRoundness(translation)
if (canTargetBeDismissed) {
- pullDismissibleRow(correctedTranslation)
+ pullDismissibleRow(translation)
} else {
- pullTargets(correctedTranslation, canSwipedBeDismissed = false)
+ pullTargets(translation, canSwipedBeDismissed = false)
}
}
State.DETACHED -> {
- detachDirectionEstimator.recordTranslation(correctedTranslation)
- translateDetachedRow(correctedTranslation)
+ detachDirectionEstimator.recordTranslation(translation)
+ translateDetachedRow(translation)
}
}
return true
@@ -205,10 +214,13 @@
it.setMagneticTranslation(targetTranslation)
}
}
- // TODO(b/399633875): Enable pull haptics after we have a clear and polished haptics design
+ playPullHaptics(mappedTranslation = translation * swipedRowMultiplier, canSwipedBeDismissed)
}
private fun playPullHaptics(mappedTranslation: Float, canSwipedBeDismissed: Boolean) {
+ val currentTime = systemClock.elapsedRealtime()
+ if ((currentTime - lastVibrationTime) < VIBRATION_TIME_THRESHOLD) return
+ lastVibrationTime = currentTime
val normalizedTranslation = abs(mappedTranslation) / magneticDetachThreshold
val scaleFactor =
if (canSwipedBeDismissed) {
@@ -216,14 +228,40 @@
} else {
STRONG_VIBRATION_SCALE
}
- val vibrationScale = scaleFactor * normalizedTranslation
- msdlPlayer.playToken(
- MSDLToken.DRAG_INDICATOR_CONTINUOUS,
- InteractionProperties.DynamicVibrationScale(
- scale = vibrationScale.pow(VIBRATION_PERCEPTION_EXPONENT),
- vibrationAttributes = VIBRATION_ATTRIBUTES_PIPELINING,
- ),
- )
+ val vibrationScale = scaleFactor * normalizedTranslation.pow(VIBRATION_SCALE_EXPONENT)
+ val compensatedScale = vibrationScale.pow(VIBRATION_PERCEPTION_EXPONENT)
+ if (Flags.msdlFeedback()) {
+ msdlPlayer.playToken(
+ MSDLToken.DRAG_INDICATOR_CONTINUOUS,
+ InteractionProperties.DynamicVibrationScale(
+ scale = compensatedScale,
+ vibrationAttributes = VIBRATION_ATTRIBUTES_PIPELINING,
+ ),
+ )
+ } else {
+ val composition =
+ VibrationEffect.startComposition().apply {
+ repeat(N_LOW_TICKS) {
+ addPrimitive(
+ VibrationEffect.Composition.PRIMITIVE_LOW_TICK,
+ compensatedScale,
+ )
+ }
+ }
+ vibratorHelper.vibrate(composition.compose(), VIBRATION_ATTRIBUTES_PIPELINING)
+ }
+ }
+
+ private fun playThresholdHaptics() {
+ if (Flags.msdlFeedback()) {
+ msdlPlayer.playToken(MSDLToken.SWIPE_THRESHOLD_INDICATOR)
+ } else {
+ val composition =
+ VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 0.7f)
+ .compose()
+ vibratorHelper.vibrate(composition)
+ }
}
private fun snapNeighborsBack(velocity: Float? = null) {
@@ -238,13 +276,19 @@
}
private fun detach(listener: MagneticRowListener, toPosition: Float) {
+ val direction = detachDirectionEstimator.direction
+ val velocity = magneticSwipeInfoProvider?.getCurrentSwipeVelocity() ?: 0f
listener.cancelMagneticAnimations()
- listener.triggerMagneticForce(toPosition, detachForce)
+ listener.triggerMagneticForce(
+ toPosition,
+ detachForce,
+ startVelocity = direction * abs(velocity),
+ )
notificationRoundnessManager.setRoundnessForAffectedViews(
/* roundness */ 1f,
/* animate */ true,
)
- msdlPlayer.playToken(MSDLToken.SWIPE_THRESHOLD_INDICATOR)
+ playThresholdHaptics()
}
private fun snapBack(listener: MagneticRowListener, velocity: Float?) {
@@ -259,25 +303,40 @@
private fun translateDetachedRow(translation: Float) {
val crossedThreshold = abs(translation) <= magneticAttachThreshold
if (crossedThreshold) {
- translationOffset += translation
- detachDirectionEstimator.reset()
- updateRoundness(translation = 0f, animate = true)
- currentMagneticListeners.swipedListener()?.let { attach(it) }
+ updateRoundness(translation, animate = true)
+ attach(translation)
currentState = State.PULLING
} else {
val swiped = currentMagneticListeners.swipedListener()
- swiped?.setMagneticTranslation(translation, trackEagerly = false)
+ swiped?.setMagneticTranslation(translation)
}
}
- private fun attach(listener: MagneticRowListener) {
- listener.cancelMagneticAnimations()
- listener.triggerMagneticForce(endTranslation = 0f, attachForce)
- msdlPlayer.playToken(MSDLToken.SWIPE_THRESHOLD_INDICATOR)
+ private fun attach(translation: Float) {
+ val detachDirection = detachDirectionEstimator.direction
+ val swipeVelocity = magneticSwipeInfoProvider?.getCurrentSwipeVelocity() ?: 0f
+ playThresholdHaptics()
+ currentMagneticListeners.forEachIndexed { i, listener ->
+ val targetTranslation = MAGNETIC_TRANSLATION_MULTIPLIERS[i] * translation
+ val attachForce =
+ SpringForce().setStiffness(ATTACH_STIFFNESS).setDampingRatio(ATTACH_DAMPING_RATIO)
+ val velocity =
+ if (i == currentMagneticListeners.size / 2) {
+ detachDirection * abs(swipeVelocity)
+ } else {
+ 0f
+ }
+ listener?.cancelMagneticAnimations()
+ listener?.triggerMagneticForce(
+ endTranslation = targetTranslation,
+ springForce = attachForce,
+ startVelocity = velocity,
+ )
+ }
+ detachDirectionEstimator.reset()
}
override fun onMagneticInteractionEnd(row: ExpandableNotificationRow, velocity: Float?) {
- translationOffset = 0f
detachDirectionEstimator.reset()
if (row.isSwipedTarget()) {
when (currentState) {
@@ -321,7 +380,6 @@
override fun resetRoundness() = notificationRoundnessManager.clear()
override fun reset() {
- translationOffset = 0f
detachDirectionEstimator.reset()
currentMagneticListeners.forEach {
it?.cancelMagneticAnimations()
@@ -429,7 +487,7 @@
private const val DETACH_DAMPING_RATIO = 0.95f
private const val SNAP_BACK_STIFFNESS = 550f
private const val SNAP_BACK_DAMPING_RATIO = 0.6f
- private const val ATTACH_STIFFNESS = 800f
+ private const val ATTACH_STIFFNESS = 850f
private const val ATTACH_DAMPING_RATIO = 0.95f
private const val DISMISS_VELOCITY = 500 // in dp/sec
@@ -442,8 +500,15 @@
.setUsage(VibrationAttributes.USAGE_TOUCH)
.setFlags(VibrationAttributes.FLAG_PIPELINED_EFFECT)
.build()
- private const val VIBRATION_PERCEPTION_EXPONENT = 1 / 0.89f
private const val WEAK_VIBRATION_SCALE = 0.2f
- private const val STRONG_VIBRATION_SCALE = 0.45f
+ private const val STRONG_VIBRATION_SCALE = 0.4f
+ // Exponent applied to a normalized translation to make the linear translation exponential
+ private const val VIBRATION_SCALE_EXPONENT = 1.27f
+ // Exponent applied to a vibration scale to compensate for human vibration perception
+ private const val VIBRATION_PERCEPTION_EXPONENT = 1 / 0.89f
+ // How much time we wait (in milliseconds) before we play a new pulling vibration
+ private const val VIBRATION_TIME_THRESHOLD = 60
+ // The number of LOW_TICK primitives in a pulling vibration
+ private const val N_LOW_TICKS = 5
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MagneticRowListener.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MagneticRowListener.kt
index 344dab4..a52ccd3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MagneticRowListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MagneticRowListener.kt
@@ -24,14 +24,11 @@
/**
* Set a translation due to a magnetic attachment.
*
- * If a magnetic animation is running, [trackEagerly] decides if the new translation is applied
- * immediately or if the animation finishes first. When applying the translation immediately,
- * the change in translation must be greater than a touch slop threshold.
+ * If a magnetic animation is running, the animation animates to a new position.
*
* @param[translation] Incoming gesture translation.
- * @param[trackEagerly] Whether we eagerly track the incoming translation or not.
*/
- fun setMagneticTranslation(translation: Float, trackEagerly: Boolean = true)
+ fun setMagneticTranslation(translation: Float)
/**
* Trigger the magnetic behavior when the row detaches or snaps back from its magnetic
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaContainerView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaContainerView.kt
index 4d10a52..3876378 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaContainerView.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaContainerView.kt
@@ -23,7 +23,6 @@
import android.graphics.RectF
import android.util.AttributeSet
import android.util.Log
-import com.android.systemui.Flags
import com.android.systemui.res.R
import com.android.systemui.statusbar.notification.row.ExpandableView
@@ -94,11 +93,7 @@
}
override fun setVisibility(visibility: Int) {
- if (Flags.bindKeyguardMediaVisibility()) {
- if (isVisibilityValid(visibility)) {
- super.setVisibility(visibility)
- }
- } else {
+ if (isVisibilityValid(visibility)) {
super.setVisibility(visibility)
}
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 0420369..7efb14e 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
@@ -53,6 +53,7 @@
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
+import android.graphics.RectF;
import android.graphics.RenderEffect;
import android.graphics.RenderNode;
import android.graphics.Shader;
@@ -3654,19 +3655,10 @@
public boolean onTouchEvent(MotionEvent ev) {
if (mTouchHandler != null) {
boolean touchHandled = mTouchHandler.onTouchEvent(ev);
- if (SceneContainerFlag.isEnabled()) {
- if (getChildAtPosition(
- mInitialTouchX, mInitialTouchY, true, true, false) == null) {
- // If scene container is enabled, any touch that we are handling that is not on
- // a child view should be handled by scene container instead.
- return false;
- } else {
- // If scene container is enabled, any touch that we are handling that is not on
- // a child view should be handled by scene container instead.
- return touchHandled;
- }
- } else if (touchHandled) {
- return true;
+ if (SceneContainerFlag.isEnabled() || touchHandled) {
+ // If scene container is enabled, let the TouchHandlers decide if this event is
+ // handled by the NSSL.
+ return touchHandled;
}
}
@@ -3707,13 +3699,10 @@
mScrollViewFields.sendCurrentGestureInGuts(false);
mScrollViewFields.sendCurrentGestureOverscroll(false);
setIsBeingDragged(false);
- // dispatch to touchHandlers, so they can still finalize a previously started
- // motion, while the shade is being dragged
- return super.dispatchTouchEvent(ev);
}
- return false;
}
}
+ // dispatch all touches to TouchHandlers, so they can decide whether they want to handle it.
return TouchLogger.logDispatchTouch(TAG, ev, super.dispatchTouchEvent(ev));
}
@@ -5098,10 +5087,10 @@
}
public void generateHeadsUpAnimation(
- NotificationEntry entry, boolean isHeadsUp, boolean hasStatusBarChip) {
+ NotificationEntry entry, boolean isHeadsUp, @Nullable RectF statusBarChipBounds) {
SceneContainerFlag.assertInLegacyMode();
ExpandableNotificationRow row = entry.getHeadsUpAnimationView();
- generateHeadsUpAnimation(row, isHeadsUp, hasStatusBarChip);
+ generateHeadsUpAnimation(row, isHeadsUp, statusBarChipBounds);
}
/**
@@ -5110,11 +5099,15 @@
*
* @param row to animate
* @param isHeadsUp true for appear, false for disappear animations
- * @param hasStatusBarChip true if the status bar is currently displaying a chip for the given
- * notification
+ * @param statusBarChipBounds the on-screen bounds of the status bar chip currently displayed
+ * for the given notification, or null if there is no status bar chip
*/
public void generateHeadsUpAnimation(
- ExpandableNotificationRow row, boolean isHeadsUp, boolean hasStatusBarChip) {
+ ExpandableNotificationRow row,
+ boolean isHeadsUp,
+ @Nullable RectF statusBarChipBounds) {
+ // TODO(b/393369891): Use the chip bounds to animate the heads up animation from the chip.
+ boolean hasStatusBarChip = statusBarChipBounds != null;
boolean addAnimation =
mAnimationsEnabled && (isHeadsUp || mHeadsUpGoingAwayAnimationsAllowed);
boolean hasBackingData = NotificationBundleUi.isEnabled()
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 c593de2..c392642 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
@@ -24,7 +24,6 @@
import static com.android.server.notification.Flags.screenshareNotificationHiding;
import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME;
import static com.android.systemui.Flags.confineNotificationTouchToViewWidth;
-import static com.android.systemui.Flags.ignoreTouchesNextToNotificationShelf;
import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.OnEmptySpaceClickListener;
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.OnOverscrollTopChangedListener;
@@ -35,6 +34,7 @@
import android.animation.ObjectAnimator;
import android.content.res.Configuration;
import android.graphics.Point;
+import android.graphics.RectF;
import android.graphics.RenderEffect;
import android.graphics.Shader;
import android.os.Trace;
@@ -85,6 +85,7 @@
import com.android.systemui.shade.ShadeDisplayAware;
import com.android.systemui.shade.ShadeViewController;
import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.DragDownHelper;
import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener;
@@ -140,7 +141,9 @@
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
@@ -154,7 +157,7 @@
@SysUISingleton
public class NotificationStackScrollLayoutController implements Dumpable {
private static final String TAG = "StackScrollerController";
- private static final boolean DEBUG = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.DEBUG);
+ private static final boolean DEBUG = Compile.IS_DEBUG || Log.isLoggable(TAG, Log.DEBUG);
private static final String HIGH_PRIORITY = "high_priority";
/** Delay in milli-seconds before shade closes for clear all. */
private static final int DELAY_BEFORE_SHADE_CLOSE = 200;
@@ -329,12 +332,12 @@
private float mMaxAlphaForGlanceableHub = 1.0f;
/**
- * A list of keys for the visible status bar chips.
+ * A list of visible status bar chips with their key and their absolute on-screen bounds.
*
* Note that this list can contain both notification keys, as well as keys for other types of
* chips like screen recording.
*/
- private List<String> mVisibleStatusBarChipKeys = new ArrayList<>();
+ private Map<String, RectF> mVisibleStatusBarChips = new HashMap<>();
private final NotificationListViewBinder mViewBinder;
@@ -480,6 +483,14 @@
}
};
+ final MagneticNotificationRowManager.SwipeInfoProvider mMagneticSwipeInfoProvider =
+ new MagneticNotificationRowManager.SwipeInfoProvider() {
+ @Override
+ public float getCurrentSwipeVelocity() {
+ return mSwipeHelper.getCurrentVelocity();
+ }
+ };
+
@VisibleForTesting
final NotificationSwipeHelper.NotificationCallback mNotificationCallback =
new NotificationSwipeHelper.NotificationCallback() {
@@ -616,11 +627,9 @@
// Verify the MotionEvent x,y are actually inside the touch area of the shelf,
// since the shelf may be animated down to a collapsed size on keyguard.
- if (ignoreTouchesNextToNotificationShelf()) {
- if (child instanceof NotificationShelf shelf) {
- if (!NotificationSwipeHelper.isTouchInView(ev, shelf)) {
- return null;
- }
+ if (child instanceof NotificationShelf shelf) {
+ if (!NotificationSwipeHelper.isTouchInView(ev, shelf)) {
+ return null;
}
}
if (child instanceof ExpandableNotificationRow row) {
@@ -880,6 +889,8 @@
.setOnMenuEventListener(mMenuEventListener)
.build();
+ mMagneticNotificationRowManager.setInfoProvider(mMagneticSwipeInfoProvider);
+
mNotifPipeline.addCollectionListener(new NotifCollectionListener() {
@Override
public void onEntryUpdated(NotificationEntry entry) {
@@ -1625,16 +1636,20 @@
return mView.getFirstChildNotGone();
}
- /** Sets the list of keys that have currently visible status bar chips. */
- public void updateStatusBarChipKeys(List<String> visibleStatusBarChipKeys) {
- mVisibleStatusBarChipKeys = visibleStatusBarChipKeys;
+ /** Sets the list of visible status bar chips. */
+ public void updateVisibleStatusBarChips(Map<String, RectF> visibleStatusBarChips) {
+ mVisibleStatusBarChips = visibleStatusBarChips;
}
public void generateHeadsUpAnimation(NotificationEntry entry, boolean isHeadsUp) {
- boolean hasStatusBarChip =
- PromotedNotificationUi.isEnabled()
- && mVisibleStatusBarChipKeys.contains(entry.getKey());
- mView.generateHeadsUpAnimation(entry, isHeadsUp, hasStatusBarChip);
+ RectF chipBounds;
+ if (PromotedNotificationUi.isEnabled()) {
+ chipBounds = mVisibleStatusBarChips.getOrDefault(entry.getKey(), null);
+ } else {
+ chipBounds = null;
+ }
+
+ mView.generateHeadsUpAnimation(entry, isHeadsUp, chipBounds);
}
public void setMaxTopPadding(int padding) {
@@ -2064,20 +2079,31 @@
mView.initDownStates(ev);
mView.handleEmptySpaceClick(ev);
+ boolean skipForDragging = SceneContainerFlag.isEnabled() && mView.isBeingDragged()
+ && ev.getAction() == MotionEvent.ACTION_MOVE;
+
NotificationGuts guts = mNotificationGutsManager.getExposedGuts();
boolean longPressWantsIt = false;
- if (mLongPressedView != null) {
+ if (mLongPressedView != null && !skipForDragging) {
longPressWantsIt = mSwipeHelper.onInterceptTouchEvent(ev);
}
boolean expandWantsIt = false;
if (mLongPressedView == null && !mSwipeHelper.isSwiping()
- && !mView.getOnlyScrollingInThisMotion() && guts == null) {
+ && !mView.getOnlyScrollingInThisMotion() && guts == null && !skipForDragging) {
expandWantsIt = mView.getExpandHelper().onInterceptTouchEvent(ev);
}
+ boolean lockscreenExpandWantsIt = false;
+ if (shouldLockscreenExpandHandleTouch()) {
+ lockscreenExpandWantsIt =
+ getLockscreenExpandTouchHelper().onInterceptTouchEvent(ev);
+ }
boolean scrollWantsIt = false;
- if (mLongPressedView == null && !mSwipeHelper.isSwiping()
- && !mView.isExpandingNotification()) {
+ if (mLongPressedView == null
+ && !mSwipeHelper.isSwiping() // horizontal swipe to dismiss
+ && !mView.isExpandingNotification() // vertical swipe to expand
+ && !lockscreenExpandWantsIt // vertical swipe to expand over lockscreen
+ && !skipForDragging) {
scrollWantsIt = mView.onInterceptTouchEventScroll(ev);
}
boolean hunWantsIt = false;
@@ -2088,8 +2114,10 @@
if (mLongPressedView == null && !mView.isBeingDragged()
&& !mView.isExpandingNotification()
&& !mView.getExpandedInThisMotion()
+ && !lockscreenExpandWantsIt
&& !mView.getOnlyScrollingInThisMotion()
- && !mView.getDisallowDismissInThisMotion()) {
+ && !mView.getDisallowDismissInThisMotion()
+ && !skipForDragging) {
swipeWantsIt = mSwipeHelper.onInterceptTouchEvent(ev);
}
// Check if we need to clear any snooze leavebehinds
@@ -2113,7 +2141,8 @@
&& ev.getActionMasked() != MotionEvent.ACTION_DOWN) {
mJankMonitor.begin(mView, CUJ_NOTIFICATION_SHADE_SCROLL_FLING);
}
- return swipeWantsIt || scrollWantsIt || expandWantsIt || longPressWantsIt || hunWantsIt;
+ return swipeWantsIt || scrollWantsIt || expandWantsIt || longPressWantsIt || hunWantsIt
+ || lockscreenExpandWantsIt;
}
@Override
@@ -2149,19 +2178,26 @@
}
}
}
+ // true when a notification is being dragged to expand over the lockscreen
+ boolean lockscreenExpandWantsIt = false;
+ if (shouldLockscreenExpandHandleTouch()) {
+ lockscreenExpandWantsIt = getLockscreenExpandTouchHelper().onTouchEvent(ev);
+ }
boolean horizontalSwipeWantsIt = false;
boolean scrollerWantsIt = false;
// NOTE: the order of these is important. If reversed, onScrollTouch will reset on an
// UP event, causing horizontalSwipeWantsIt to be set to true on vertical swipes.
if (mLongPressedView == null && !mView.isBeingDragged()
&& !expandingNotification
+ && !lockscreenExpandWantsIt
&& !mView.getExpandedInThisMotion()
&& !onlyScrollingInThisMotion
&& !mView.getDisallowDismissInThisMotion()) {
horizontalSwipeWantsIt = mSwipeHelper.onTouchEvent(ev);
}
if (mLongPressedView == null && mView.isExpanded() && !mSwipeHelper.isSwiping()
- && !expandingNotification && !mView.getDisallowScrollingInThisMotion()) {
+ && !expandingNotification && !lockscreenExpandWantsIt
+ && !mView.getDisallowScrollingInThisMotion()) {
scrollerWantsIt = mView.onScrollTouch(ev);
}
boolean hunWantsIt = false;
@@ -2194,9 +2230,15 @@
traceJankOnTouchEvent(ev.getActionMasked(), scrollerWantsIt);
}
return horizontalSwipeWantsIt || scrollerWantsIt || expandWantsIt || longPressWantsIt
- || hunWantsIt;
+ || hunWantsIt || lockscreenExpandWantsIt;
}
+ @NonNull
+ private DragDownHelper getLockscreenExpandTouchHelper() {
+ return mLockscreenShadeTransitionController.getTouchHelper();
+ }
+
+
private void traceJankOnTouchEvent(int action, boolean scrollerWantsIt) {
if (mJankMonitor == null) {
Log.w(TAG, "traceJankOnTouchEvent, mJankMonitor is null");
@@ -2222,6 +2264,11 @@
}
}
+ private boolean shouldLockscreenExpandHandleTouch() {
+ return SceneContainerFlag.isEnabled() && mLongPressedView == null
+ && !mSwipeHelper.isSwiping();
+ }
+
private boolean shouldHeadsUpHandleTouch() {
return SceneContainerFlag.isEnabled() && mLongPressedView == null
&& !mSwipeHelper.isSwiping();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt
index 84b1df1..cd23dc7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt
@@ -24,6 +24,7 @@
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.media.controls.domain.pipeline.MediaDataManager
import com.android.systemui.res.R
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.statusbar.LockscreenShadeTransitionController
import com.android.systemui.statusbar.StatusBarState.KEYGUARD
@@ -421,6 +422,7 @@
"\tcomputeHeightPerNotificationLimit i=$i notifs=$notifications " +
"notifsHeightSavingSpace=$notifsWithCollapsedHun" +
" shelfWithSpaceBefore=$shelfWithSpaceBefore" +
+ " isOnLockScreen=$onLockscreen" +
" limitLockScreenToOneImportant: $limitLockScreenToOneImportant"
}
yield(
@@ -476,7 +478,9 @@
if (onLockscreen) {
if (
view is ExpandableNotificationRow &&
- (canPeek || view.isPromotedOngoing)
+ (canPeek ||
+ view.isPromotedOngoing ||
+ (SceneContainerFlag.isEnabled && view.isUserLocked))
) {
height
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
index d0096da..74b62e0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
@@ -18,7 +18,6 @@
package com.android.systemui.statusbar.notification.stack;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_ROW_SWIPE;
-import static com.android.systemui.Flags.ignoreTouchesNextToNotificationShelf;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -510,14 +509,9 @@
final int height = (view instanceof ExpandableView)
? ((ExpandableView) view).getActualHeight()
: view.getHeight();
- final int width;
- if (ignoreTouchesNextToNotificationShelf()) {
- width = (view instanceof NotificationShelf)
+ final int width = (view instanceof NotificationShelf)
? ((NotificationShelf) view).getActualWidth()
: view.getWidth();
- } else {
- width = view.getWidth();
- }
final int rx = (int) ev.getRawX();
final int ry = (int) ev.getRawY();
int[] temp = new int[2];
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt
index eddf3f2..e724b82 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt
@@ -136,8 +136,8 @@
if (PromotedNotificationUi.isEnabled) {
launch {
- viewModel.visibleStatusBarChipKeys.collect { keys ->
- viewController.updateStatusBarChipKeys(keys)
+ viewModel.visibleStatusBarChips.collect { chips ->
+ viewController.updateVisibleStatusBarChips(chips)
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt
index 653344a..32ee33c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt
@@ -19,6 +19,7 @@
import android.util.Log
import com.android.app.tracing.coroutines.flow.collectTraced
import com.android.app.tracing.coroutines.launchTraced as launch
+import com.android.systemui.Flags
import com.android.systemui.common.ui.ConfigurationState
import com.android.systemui.common.ui.view.onLayoutChanged
import com.android.systemui.dagger.SysUISingleton
@@ -90,13 +91,12 @@
launch { viewModel.maxAlpha.collectTraced { view.setMaxAlpha(it) } }
launch { viewModel.shadeScrollState.collect { view.setScrollState(it) } }
- launch {
- viewModel.expandFraction.collectTraced {
- view.setExpandFraction(it.coerceIn(0f, 1f))
- }
- }
+ launch { viewModel.expandFraction.collectTraced { view.setExpandFraction(it) } }
launch { viewModel.qsExpandFraction.collectTraced { view.setQsExpandFraction(it) } }
- launch { viewModel.blurRadius(maxBlurRadius).collect(view::setBlurRadius) }
+ if (Flags.notificationShadeBlur()) {
+ launch { viewModel.blurRadius(maxBlurRadius).collect(view::setBlurRadius) }
+ }
+
launch {
viewModel.isShowingStackOnLockscreen.collectTraced {
view.setShowingStackOnLockscreen(it)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModel.kt
index c1eb70e..357a0c8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModel.kt
@@ -16,10 +16,13 @@
package com.android.systemui.statusbar.notification.stack.ui.viewmodel
+import android.graphics.RectF
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dump.DumpManager
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.shade.domain.interactor.ShadeModeInteractor
+import com.android.systemui.shade.shared.model.ShadeMode
import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipsViewModel
import com.android.systemui.statusbar.domain.interactor.RemoteInputInteractor
import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor
@@ -66,6 +69,7 @@
private val headsUpNotificationInteractor: HeadsUpNotificationInteractor,
remoteInputInteractor: RemoteInputInteractor,
shadeInteractor: ShadeInteractor,
+ shadeModeInteractor: ShadeModeInteractor,
userSetupInteractor: UserSetupInteractor,
@Background bgDispatcher: CoroutineDispatcher,
dumpManager: DumpManager,
@@ -242,6 +246,7 @@
flowOf(AnimatedValue.NotAnimating(false))
} else {
combine(
+ shadeModeInteractor.shadeMode,
activeNotificationsInteractor.areAnyNotificationsPresent,
userSetupInteractor.isUserSetUp,
notificationStackInteractor.isShowingOnLockscreen,
@@ -249,6 +254,7 @@
remoteInputInteractor.isRemoteInputActive,
shadeInteractor.shadeExpansion.map { it < 0.5f }.distinctUntilChanged(),
) {
+ shadeMode,
hasNotifications,
isUserSetUp,
isShowingOnLockscreen,
@@ -256,14 +262,16 @@
isRemoteInputActive,
shadeLessThanHalfwayExpanded ->
when {
- !hasNotifications -> VisibilityChange.DISAPPEAR_WITH_ANIMATION
+ // Hide the footer when there are no notifications, unless it's Dual Shade.
+ shadeMode != ShadeMode.Dual && !hasNotifications ->
+ VisibilityChange.DISAPPEAR_WITH_ANIMATION
// Hide the footer until the user setup is complete, to prevent access
// to settings (b/193149550).
!isUserSetUp -> VisibilityChange.DISAPPEAR_WITH_ANIMATION
// Do not show the footer if the lockscreen is visible (incl. AOD),
// except if the shade is opened on top. See also b/219680200.
// Do not animate, as that makes the footer appear briefly when
- // transitioning between the shade and keyguard.
+ // transitioning between the shade and lockscreen.
isShowingOnLockscreen -> VisibilityChange.DISAPPEAR_WITHOUT_ANIMATION
// Do not show the footer if quick settings are fully expanded (except
// for the foldable split shade view). See b/201427195 && b/222699879.
@@ -367,12 +375,13 @@
}
/**
- * A list of keys for the visible status bar chips.
+ * A list of keys and on-screen bounds for the visible status bar chips.
*
* Note that this list can contain both notification keys, as well as keys for other types of
* chips like screen recording.
*/
- val visibleStatusBarChipKeys = ongoingActivityChipsViewModel.visibleChipKeys
+ val visibleStatusBarChips: Flow<Map<String, RectF>> =
+ ongoingActivityChipsViewModel.visibleChipsWithBounds
// TODO(b/325936094) use it for the text displayed in the StatusBar
fun headsUpRow(key: HeadsUpRowKey): HeadsUpRowViewModel =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
index af8ac62..53c9fba 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
@@ -412,8 +412,8 @@
.dumpValue("isDreamingWithoutShade")
/**
- * Fade in if the user swipes the shade back up, not if collapsed by going to AOD. This is
- * needed due to the lack of a SHADE state with existing keyguard transitions.
+ * Fade in if the user swipes the shade back up, not if collapsed by going to AOD or DREAMING.
+ * This is needed due to the lack of a SHADE state with existing keyguard transitions.
*/
private fun awaitCollapse(): Flow<Boolean> {
var aodTransitionIsComplete = true
@@ -422,9 +422,13 @@
keyguardTransitionInteractor.isInTransition(
edge = Edge.create(from = LOCKSCREEN, to = AOD)
),
- ::Pair,
+ keyguardTransitionInteractor.isInTransition(
+ edge = Edge.create(from = LOCKSCREEN, to = DREAMING)
+ ),
+ ::Triple,
)
- .transformWhile { (isOnLockscreenWithoutShade, aodTransitionIsRunning) ->
+ .transformWhile {
+ (isOnLockscreenWithoutShade, aodTransitionIsRunning, dreamTransitionIsRunning) ->
// Wait until the AOD transition is complete before terminating
if (!aodTransitionIsComplete && !aodTransitionIsRunning) {
aodTransitionIsComplete = true
@@ -437,6 +441,9 @@
// Shade is closed, fade in and terminate
emit(true)
false
+ } else if (dreamTransitionIsRunning) {
+ emit(false)
+ false
} else {
true
}
@@ -452,8 +459,9 @@
emit(false)
// Wait for shade to be fully expanded
isShadeLocked.first { it }
- // ... and then for it to be collapsed OR a transition to AOD begins.
- // If AOD, do not fade in (a fade out occurs instead).
+ // ... and then for it to be collapsed OR a transition to AOD or DREAMING
+ // begins.
+ // If AOD or DREAMING, do not fade in (a fade out occurs instead).
awaitCollapse().collect { doFadeIn ->
if (doFadeIn) {
emit(true)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ui/viewbinder/HeadsUpNotificationViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ui/viewbinder/HeadsUpNotificationViewBinder.kt
index afe7971..57e6665 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ui/viewbinder/HeadsUpNotificationViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ui/viewbinder/HeadsUpNotificationViewBinder.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.notification.ui.viewbinder
+import android.graphics.RectF
import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipsViewModel
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
@@ -43,14 +44,14 @@
combine(
viewModel.pinnedHeadsUpRowKeys,
viewModel.activeHeadsUpRowKeys,
- ongoingActivityChipsViewModel.visibleChipKeys,
+ ongoingActivityChipsViewModel.visibleChipsWithBounds,
::Triple,
)
.sample(viewModel.headsUpAnimationsEnabled, ::Pair)
.collect { (newKeys, animationsEnabled) ->
val pinned = newKeys.first
val all = newKeys.second
- val statusBarChips: List<String> = newKeys.third
+ val statusBarChips: Map<String, RectF> = newKeys.third
val added = all.union(pinned) - previousKeys
val removed = previousKeys - pinned
@@ -60,21 +61,21 @@
if (animationsEnabled) {
added.forEach { key ->
val row = obtainView(key)
- val hasStatusBarChip = statusBarChips.contains(row.key)
+ val statusBarChipBounds: RectF? = statusBarChips[row.key]
parentView.generateHeadsUpAnimation(
row,
/* isHeadsUp = */ true,
- hasStatusBarChip,
+ statusBarChipBounds,
)
}
removed.forEach { key ->
val row = obtainView(key)
- val hasStatusBarChip = statusBarChips.contains(row.key)
+ val statusBarChipBounds: RectF? = statusBarChips[row.key]
if (!parentView.isBeingDragged()) {
parentView.generateHeadsUpAnimation(
row,
/* isHeadsUp= */ false,
- hasStatusBarChip,
+ statusBarChipBounds,
)
}
row.markHeadsUpSeen()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternalImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternalImpl.kt
index 6e82d7f..ef3901b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternalImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternalImpl.kt
@@ -42,7 +42,6 @@
import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.DisplayId
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
import com.android.systemui.keyguard.KeyguardViewMediator
@@ -54,6 +53,7 @@
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.shade.ShadeController
import com.android.systemui.shade.domain.interactor.ShadeAnimationInteractor
+import com.android.systemui.shade.domain.interactor.ShadeDialogContextInteractor
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.NotificationLockscreenUserManager
import com.android.systemui.statusbar.NotificationShadeWindowController
@@ -80,12 +80,11 @@
private val statusBarKeyguardViewManagerLazy: Lazy<StatusBarKeyguardViewManager>,
private val keyguardInteractor: KeyguardInteractor,
private val centralSurfacesOptLazy: Lazy<Optional<CentralSurfaces>>,
- private val context: Context,
+ private val contextInteractor: ShadeDialogContextInteractor,
@Main private val resources: Resources,
private val selectedUserInteractor: SelectedUserInteractor,
private val deviceEntryInteractor: DeviceEntryInteractor,
private val activityTransitionAnimator: ActivityTransitionAnimator,
- @DisplayId private val displayId: Int,
private val deviceProvisioningInteractor: DeviceProvisioningInteractor,
private val activityIntentHelper: ActivityIntentHelper,
private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
@@ -103,6 +102,12 @@
private val centralSurfaces: CentralSurfaces?
get() = centralSurfacesOptLazy.get().getOrNull()
+ private val context: Context
+ get() = contextInteractor.context
+
+ private val displayId: Int
+ get() = context.displayId
+
override fun registerTransition(
cookie: ActivityTransitionAnimator.TransitionCookie,
controllerFactory: ActivityTransitionAnimator.ControllerFactory,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index fe367a3..7894a5b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -31,7 +31,6 @@
import static com.android.systemui.Dependency.TIME_TICK_HANDLER_NAME;
import static com.android.systemui.Flags.keyboardShortcutHelperRewrite;
-import static com.android.systemui.Flags.relockWithPowerButtonImmediately;
import static com.android.systemui.Flags.statusBarSignalPolicyRefactor;
import static com.android.systemui.charging.WirelessChargingAnimation.UNKNOWN_BATTERY_LEVEL;
import static com.android.systemui.flags.Flags.SHORTCUT_LIST_SEARCH_LAYOUT;
@@ -2399,12 +2398,8 @@
mStatusBarKeyguardViewManager.reset(true);
} else if (mState == StatusBarState.KEYGUARD
&& !mStatusBarKeyguardViewManager.primaryBouncerIsOrWillBeShowing()) {
- boolean needsBouncer = mStatusBarKeyguardViewManager.isSecure();
- if (relockWithPowerButtonImmediately()) {
- // Only request if SIM bouncer is needed
- needsBouncer = mStatusBarKeyguardViewManager.needsFullscreenBouncer();
- }
-
+ // Only request if SIM bouncer is needed
+ boolean needsBouncer = mStatusBarKeyguardViewManager.needsFullscreenBouncer();
if (needsBouncer) {
var reason = "CentralSurfacesImpl#showBouncerOrLockScreenIfKeyguard";
if (SceneContainerFlag.isEnabled()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java
index a2f1ded..e169364 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java
@@ -58,7 +58,6 @@
private KeyguardIndication mKeyguardIndicationInfo;
private Animator mLastAnimator;
- private boolean mAlwaysAnnounceText;
public KeyguardIndicationTextView(Context context) {
super(context);
@@ -112,19 +111,6 @@
}
/**
- * Controls whether the text displayed in the indication area will be announced always.
- */
- public void setAlwaysAnnounceEnabled(boolean enabled) {
- this.mAlwaysAnnounceText = enabled;
- if (mAlwaysAnnounceText) {
- // We will announce the text programmatically anyway.
- setAccessibilityLiveRegion(ACCESSIBILITY_LIVE_REGION_NONE);
- } else {
- setAccessibilityLiveRegion(ACCESSIBILITY_LIVE_REGION_POLITE);
- }
- }
-
- /**
* Updates the text with an optional animation.
*
* @param text The text to show.
@@ -264,9 +250,6 @@
if (forceAssertiveAccessibilityLiveRegion) {
setAccessibilityLiveRegion(ACCESSIBILITY_LIVE_REGION_ASSERTIVE);
}
- if (mAlwaysAnnounceText) {
- announceForAccessibility(mMessage);
- }
}
private AnimatorSet getInAnimator() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImpl.kt
index 76f67dc..6dc5cb8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImpl.kt
@@ -42,7 +42,6 @@
import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.DisplayId
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.keyguard.KeyguardViewMediator
import com.android.systemui.keyguard.WakefulnessLifecycle
@@ -51,6 +50,7 @@
import com.android.systemui.settings.UserTracker
import com.android.systemui.shade.ShadeController
import com.android.systemui.shade.domain.interactor.ShadeAnimationInteractor
+import com.android.systemui.shade.domain.interactor.ShadeDialogContextInteractor
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.NotificationLockscreenUserManager
import com.android.systemui.statusbar.NotificationShadeWindowController
@@ -84,8 +84,7 @@
private val statusBarKeyguardViewManagerLazy: Lazy<StatusBarKeyguardViewManager>,
private val notifShadeWindowControllerLazy: Lazy<NotificationShadeWindowController>,
private val activityTransitionAnimator: ActivityTransitionAnimator,
- private val context: Context,
- @DisplayId private val displayId: Int,
+ private val contextInteractor: ShadeDialogContextInteractor,
private val lockScreenUserManager: NotificationLockscreenUserManager,
private val statusBarWindowControllerStore: StatusBarWindowControllerStore,
private val wakefulnessLifecycle: WakefulnessLifecycle,
@@ -100,6 +99,12 @@
private val centralSurfaces: CentralSurfaces?
get() = centralSurfacesOptLazy.get().getOrNull()
+ private val context: Context
+ get() = contextInteractor.context
+
+ private val displayId: Int
+ get() = context.displayId
+
override fun registerTransition(
cookie: ActivityTransitionAnimator.TransitionCookie,
controllerFactory: ActivityTransitionAnimator.ControllerFactory,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 8a9d708..236383b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -29,6 +29,7 @@
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.annotation.IntDef;
+import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Color;
import android.os.Handler;
@@ -41,6 +42,7 @@
import android.view.animation.Interpolator;
import androidx.annotation.FloatRange;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.app.tracing.coroutines.TrackTracer;
@@ -74,13 +76,13 @@
import com.android.systemui.scene.shared.flag.SceneContainerFlag;
import com.android.systemui.scene.shared.model.Scenes;
import com.android.systemui.scrim.ScrimView;
-import com.android.systemui.shade.ShadeViewController;
+import com.android.systemui.shade.NotificationPanelViewController;
import com.android.systemui.shade.transition.LargeScreenShadeInterpolator;
+import com.android.systemui.shade.transition.ScrimShadeTransitionController;
import com.android.systemui.shade.ui.ShadeColors;
import com.android.systemui.statusbar.notification.stack.ViewState;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.util.kotlin.JavaAdapter;
import com.android.systemui.util.wakelock.DelayedWakeLock;
import com.android.systemui.util.wakelock.WakeLock;
import com.android.systemui.window.domain.interactor.WindowRootViewBlurInteractor;
@@ -88,11 +90,11 @@
import dagger.Lazy;
import kotlinx.coroutines.CoroutineDispatcher;
+import kotlinx.coroutines.ExperimentalCoroutinesApi;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import java.util.concurrent.Executor;
import java.util.function.Consumer;
import javax.inject.Inject;
@@ -101,7 +103,9 @@
* Controls both the scrim behind the notifications and in front of the notifications (when a
* security method gets shown).
*/
+@SuppressLint("DumpableNotRegistered") // CentralSurfaces dumps ScrimController
@SysUISingleton
+@ExperimentalCoroutinesApi
public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dumpable {
static final String TAG = "ScrimController";
@@ -152,7 +156,7 @@
/**
* Same as {@link #mTransitionToFullShadeProgress}, but specifically for the notifications scrim
* on the lock screen.
- *
+ * <p>
* On split shade lock screen we want the different scrims to fade in at different times and
* rates.
*/
@@ -203,10 +207,6 @@
* Default alpha value for most scrims.
*/
protected static final float KEYGUARD_SCRIM_ALPHA = 0.2f;
- /**
- * Scrim opacity when the phone is about to wake-up.
- */
- public static final float WAKE_SENSOR_SCRIM_ALPHA = 0.6f;
/**
* The default scrim under the shade and dialogs.
@@ -214,11 +214,6 @@
*/
public static final float BUSY_SCRIM_ALPHA = 1f;
- /**
- * Scrim opacity that can have text on top.
- */
- public static final float GAR_SCRIM_ALPHA = 0.6f;
-
static final int TAG_KEY_ANIM = R.id.scrim;
private static final int TAG_START_ALPHA = R.id.scrim_alpha_start;
private static final int TAG_END_ALPHA = R.id.scrim_alpha_end;
@@ -226,7 +221,7 @@
private ScrimState mState = ScrimState.UNINITIALIZED;
- private Context mContext;
+ private final Context mContext;
private ScrimView mScrimInFront;
private ScrimView mNotificationsScrim;
@@ -238,21 +233,19 @@
private final DockManager mDockManager;
private final KeyguardVisibilityCallback mKeyguardVisibilityCallback;
private final Handler mHandler;
- private final Executor mMainExecutor;
- private final JavaAdapter mJavaAdapter;
private final ScreenOffAnimationController mScreenOffAnimationController;
private final KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
private final KeyguardInteractor mKeyguardInteractor;
- private GradientColors mColors;
+ private final GradientColors mColors;
private boolean mNeedsDrawableColorUpdate;
private float mAdditionalScrimBehindAlphaKeyguard = 0f;
// Combined scrim behind keyguard alpha of default scrim + additional scrim
private float mScrimBehindAlphaKeyguard = KEYGUARD_SCRIM_ALPHA;
- static final float TRANSPARENT_BOUNCER_SCRIM_ALPHA = 0.54f;
+ public static final float TRANSPARENT_BOUNCER_SCRIM_ALPHA = 0.54f;
private float mRawPanelExpansionFraction;
private float mPanelScrimMinFraction;
@@ -299,12 +292,13 @@
private boolean mWakeLockHeld;
private boolean mKeyguardOccluded;
- private KeyguardTransitionInteractor mKeyguardTransitionInteractor;
- private CoroutineDispatcher mMainDispatcher;
+ private final KeyguardTransitionInteractor mKeyguardTransitionInteractor;
+ private final CoroutineDispatcher mMainDispatcher;
private boolean mIsBouncerToGoneTransitionRunning = false;
- private PrimaryBouncerToGoneTransitionViewModel mPrimaryBouncerToGoneTransitionViewModel;
- private AlternateBouncerToGoneTransitionViewModel mAlternateBouncerToGoneTransitionViewModel;
- private final Consumer<ScrimAlpha> mScrimAlphaConsumer =
+ private final PrimaryBouncerToGoneTransitionViewModel mPrimaryBouncerToGoneTransitionViewModel;
+ private final AlternateBouncerToGoneTransitionViewModel
+ mAlternateBouncerToGoneTransitionViewModel;
+ private final Consumer<ScrimAlpha> mBouncerToGoneScrimAlphaConsumer =
(ScrimAlpha alphas) -> {
mInFrontAlpha = alphas.getFrontAlpha();
mScrimInFront.setViewAlpha(mInFrontAlpha);
@@ -319,11 +313,11 @@
/**
* Consumer that fades the behind scrim in and out during the transition between the lock screen
* and the glanceable hub.
- *
+ * <p>
* While the lock screen is showing, the behind scrim is used to slightly darken the lock screen
* wallpaper underneath. Since the glanceable hub is under all of the scrims, we want to fade
* out the scrim so that the glanceable hub isn't darkened when it opens.
- *
+ * <p>
* {@link #applyState()} handles the scrim alphas once on the glanceable hub, this is only
* responsible for setting the behind alpha during the transition.
*/
@@ -355,8 +349,6 @@
KeyguardUpdateMonitor keyguardUpdateMonitor,
DockManager dockManager,
ConfigurationController configurationController,
- @Main Executor mainExecutor,
- JavaAdapter javaAdapter,
ScreenOffAnimationController screenOffAnimationController,
KeyguardUnlockAnimationController keyguardUnlockAnimationController,
StatusBarKeyguardViewManager statusBarKeyguardViewManager,
@@ -380,8 +372,6 @@
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mKeyguardVisibilityCallback = new KeyguardVisibilityCallback();
mHandler = handler;
- mMainExecutor = mainExecutor;
- mJavaAdapter = javaAdapter;
mScreenOffAnimationController = screenOffAnimationController;
mWakeLock = delayedWakeLockFactory.create("Scrims");
// Scrim alpha is initially set to the value on the resource but might be changed
@@ -433,10 +423,10 @@
mNotificationsScrim.enableRoundedCorners(true);
final ScrimState[] states = ScrimState.values();
- for (int i = 0; i < states.length; i++) {
- states[i].init(mScrimInFront, mScrimBehind, mDozeParameters, mDockManager);
- states[i].setScrimBehindAlphaKeyguard(mScrimBehindAlphaKeyguard);
- states[i].setDefaultScrimAlpha(getDefaultScrimAlpha());
+ for (ScrimState scrimState : states) {
+ scrimState.init(mScrimInFront, mScrimBehind, mDozeParameters, mDockManager);
+ scrimState.setScrimBehindAlphaKeyguard(mScrimBehindAlphaKeyguard);
+ scrimState.setDefaultScrimAlpha(getDefaultScrimAlpha());
}
mTransparentScrimBackground = notificationsScrim.getResources()
@@ -489,7 +479,7 @@
Edge.Companion.create(PRIMARY_BOUNCER, GONE)),
mBouncerToGoneTransition, mMainDispatcher);
collectFlow(behindScrim, mPrimaryBouncerToGoneTransitionViewModel.getScrimAlpha(),
- mScrimAlphaConsumer, mMainDispatcher);
+ mBouncerToGoneScrimAlphaConsumer, mMainDispatcher);
// ALTERNATE_BOUNCER->GONE
collectFlow(behindScrim, mKeyguardTransitionInteractor.transition(
@@ -497,7 +487,7 @@
Edge.Companion.create(ALTERNATE_BOUNCER, GONE)),
mBouncerToGoneTransition, mMainDispatcher);
collectFlow(behindScrim, mAlternateBouncerToGoneTransitionViewModel.getScrimAlpha(),
- mScrimAlphaConsumer, mMainDispatcher);
+ mBouncerToGoneScrimAlphaConsumer, mMainDispatcher);
// LOCKSCREEN<->GLANCEABLE_HUB
collectFlow(
@@ -732,8 +722,8 @@
private void setScrimBehindValues(float scrimBehindAlphaKeyguard) {
mScrimBehindAlphaKeyguard = scrimBehindAlphaKeyguard;
ScrimState[] states = ScrimState.values();
- for (int i = 0; i < states.length; i++) {
- states[i].setScrimBehindAlphaKeyguard(scrimBehindAlphaKeyguard);
+ for (ScrimState state : states) {
+ state.setScrimBehindAlphaKeyguard(scrimBehindAlphaKeyguard);
}
scheduleUpdate();
}
@@ -769,9 +759,9 @@
/**
* Current state of the shade expansion when pulling it from the top.
* This value is 1 when on top of the keyguard and goes to 0 as the user drags up.
- *
+ * <p>
* The expansion fraction is tied to the scrim opacity.
- *
+ * <p>
* See {@link ScrimShadeTransitionController#onPanelExpansionChanged}.
*
* @param rawPanelExpansionFraction From 0 to 1 where 0 means collapsed and 1 expanded.
@@ -785,7 +775,7 @@
calculateAndUpdatePanelExpansion();
}
- /** See {@link ShadeViewController#setPanelScrimMinFraction(float)}. */
+ /** See {@link NotificationPanelViewController#setPanelScrimMinFraction(float)}. */
public void setPanelScrimMinFraction(float minFraction) {
if (isNaN(minFraction)) {
throw new IllegalArgumentException("minFraction should not be NaN");
@@ -1014,13 +1004,17 @@
assertAlphasValid();
if (!mExpansionAffectsAlpha) {
+ debugLog("Early return in applyState");
+ if (Flags.notificationShadeBlur() && mState == ScrimState.UNLOCKED) {
+ mBehindAlpha = 0.0f;
+ mNotificationsAlpha = 0.0f;
+ }
return;
}
if (mState == ScrimState.UNLOCKED || mState == ScrimState.DREAMING
|| mState == ScrimState.GLANCEABLE_HUB_OVER_DREAM) {
- final boolean occluding =
- mOccludeAnimationPlaying || mState.mLaunchingAffordanceWithPreview;
+ final boolean occluding = mOccludeAnimationPlaying;
// Darken scrim as it's pulled down while unlocked. If we're unlocked but playing the
// screen off/occlusion animations, ignore expansion changes while those animations
// play.
@@ -1220,8 +1214,8 @@
updateScrims();
}
- mState.AOD.setAodFrontScrimAlpha(alpha);
- mState.PULSING.setAodFrontScrimAlpha(alpha);
+ ScrimState.AOD.setAodFrontScrimAlpha(alpha);
+ ScrimState.PULSING.setAodFrontScrimAlpha(alpha);
}
private boolean shouldUpdateFrontScrimAlpha() {
@@ -1371,8 +1365,7 @@
private void updateScrimColor(View scrim, float alpha, int tint) {
alpha = Math.max(0, Math.min(1.0f, alpha));
- if (scrim instanceof ScrimView) {
- ScrimView scrimView = (ScrimView) scrim;
+ if (scrim instanceof ScrimView scrimView) {
if (DEBUG_MODE) {
tint = getDebugScrimTint(scrimView);
}
@@ -1663,7 +1656,7 @@
}
@Override
- public void dump(PrintWriter pw, String[] args) {
+ public void dump(PrintWriter pw, @NonNull String[] args) {
pw.println(" ScrimController: ");
pw.print(" state: ");
pw.println(mState);
@@ -1741,12 +1734,6 @@
}
}
- public void setLaunchingAffordanceWithPreview(boolean launchingAffordanceWithPreview) {
- for (ScrimState state : ScrimState.values()) {
- state.setLaunchingAffordanceWithPreview(launchingAffordanceWithPreview);
- }
- }
-
public interface Callback {
default void onStart() {
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
index 21710fc..5fb5a8f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
@@ -326,9 +326,7 @@
// If launch/occlude animations were playing, they already animated the scrim
// alpha to 0f as part of the animation. If we animate it now, we'll set it back
// to 1f and animate it back to 0f, causing an unwanted scrim flash.
- mAnimateChange = !mLaunchingAffordanceWithPreview
- && !mOccludeAnimationPlaying
- && !fromAod;
+ mAnimateChange = !mOccludeAnimationPlaying && !fromAod;
mFrontTint = Color.TRANSPARENT;
mBehindTint = mBackgroundColor;
@@ -435,7 +433,7 @@
// Scrim parameters should match KEYGUARD as we're showing on top of keyguard.
mBehindTint = mBackgroundColor;
mNotifTint = mNotificationScrimColor;
- mBehindAlpha = mScrimBehindAlphaKeyguard;
+ mBehindAlpha = 0.0f;
mNotifAlpha = 0.0f;
mFrontAlpha = 0.0f;
} else {
@@ -463,7 +461,7 @@
* Device is dreaming and user has swiped from the right edge to enter the glanceable hub UI.
* From this state, the user can swipe from the left edge to go back to the dream, as well as
* swipe down for the notifications and up for the bouncer.
- *
+ * <p>
* This is a separate state from {@link #GLANCEABLE_HUB} because the scrims behave differently
* when the dream is running.
*/
@@ -532,7 +530,6 @@
DozeParameters mDozeParameters;
DockManager mDockManager;
boolean mDisplayRequiresBlanking;
- boolean mLaunchingAffordanceWithPreview;
boolean mOccludeAnimationPlaying;
boolean mWakeLockScreenSensorActive;
boolean mKeyguardFadingAway;
@@ -643,10 +640,6 @@
mNotificationScrimColor = notificationScrimColor;
}
- public void setLaunchingAffordanceWithPreview(boolean launchingAffordanceWithPreview) {
- mLaunchingAffordanceWithPreview = launchingAffordanceWithPreview;
- }
-
public void setOccludeAnimationPlaying(boolean occludeAnimationPlaying) {
mOccludeAnimationPlaying = occludeAnimationPlaying;
}
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 5123409..f4505f9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -1146,9 +1146,6 @@
SysUiStatsLog.KEYGUARD_STATE_CHANGED__STATE__OCCLUDED);
if (mCentralSurfaces.isLaunchingActivityOverLockscreen()) {
final Runnable postCollapseAction = () -> {
- if (!Flags.useTransitionsForKeyguardOccluded()) {
- mNotificationShadeWindowController.setKeyguardOccluded(isOccluded);
- }
reset(true /* hideBouncerWhenShowing */);
};
if (mCentralSurfaces.isDismissingShadeForActivityLaunch()) {
@@ -1164,9 +1161,6 @@
SysUiStatsLog.write(SysUiStatsLog.KEYGUARD_STATE_CHANGED,
SysUiStatsLog.KEYGUARD_STATE_CHANGED__STATE__SHOWN);
}
- if (!Flags.useTransitionsForKeyguardOccluded()) {
- mNotificationShadeWindowController.setKeyguardOccluded(isOccluded);
- }
// setDozing(false) will call reset once we stop dozing. Also, if we're going away, there's
// no need to reset the keyguard views as we'll be gone shortly. Resetting now could cause
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
index 30dc9b9..5d30174 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
@@ -25,7 +25,6 @@
import android.util.ArraySet;
import android.util.Log;
-import com.android.settingslib.mobile.TelephonyIcons;
import com.android.systemui.CoreStartable;
import com.android.systemui.common.shared.model.Icon;
import com.android.systemui.dagger.SysUISingleton;
@@ -262,7 +261,7 @@
if (isAirplaneMode) {
mIconController.setIcon(
mSlotAirplane,
- TelephonyIcons.FLIGHT_MODE_ICON,
+ R.drawable.stat_sys_airplane_mode,
mContext.getString(R.string.accessibility_airplane_mode));
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/battery/ui/binder/UnifiedBatteryViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/battery/ui/binder/UnifiedBatteryViewBinder.kt
index 42d8efb..460ede7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/battery/ui/binder/UnifiedBatteryViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/battery/ui/binder/UnifiedBatteryViewBinder.kt
@@ -27,6 +27,7 @@
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.repeatOnLifecycle
+import com.android.systemui.compose.modifiers.sysuiResTag
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.statusbar.phone.domain.interactor.IsAreaDark
import com.android.systemui.statusbar.pipeline.battery.ui.composable.UnifiedBattery
@@ -55,9 +56,11 @@
val height = with(LocalDensity.current) { STATUS_BAR_BATTERY_HEIGHT.toDp() }
UnifiedBattery(
modifier =
- Modifier.height(height).aspectRatio(BatteryViewModel.ASPECT_RATIO),
+ Modifier.height(height)
+ .aspectRatio(BatteryViewModel.ASPECT_RATIO)
+ .sysuiResTag(BatteryViewModel.TEST_TAG),
viewModelFactory = viewModelFactory,
- isDark = isDark,
+ isDarkProvider = { isDark },
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/battery/ui/composable/BatteryWithEstimate.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/battery/ui/composable/BatteryWithEstimate.kt
index 6e2cdd6..038a5ba 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/battery/ui/composable/BatteryWithEstimate.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/battery/ui/composable/BatteryWithEstimate.kt
@@ -38,7 +38,7 @@
@Composable
fun BatteryWithEstimate(
viewModelFactory: BatteryViewModel.Factory,
- isDark: IsAreaDark,
+ isDarkProvider: () -> IsAreaDark,
showEstimate: Boolean,
modifier: Modifier = Modifier,
) {
@@ -51,7 +51,7 @@
Row(modifier = modifier, verticalAlignment = Alignment.CenterVertically) {
UnifiedBattery(
viewModelFactory = viewModelFactory,
- isDark = isDark,
+ isDarkProvider = isDarkProvider,
modifier =
Modifier.height(batteryHeight)
.align(Alignment.CenterVertically)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/battery/ui/composable/UnifiedBattery.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/battery/ui/composable/UnifiedBattery.kt
index 8e0e92a..9998111 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/battery/ui/composable/UnifiedBattery.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/battery/ui/composable/UnifiedBattery.kt
@@ -34,6 +34,7 @@
import androidx.compose.ui.graphics.drawscope.scale
import androidx.compose.ui.layout.onLayoutRectChanged
import com.android.systemui.common.ui.compose.load
+import com.android.systemui.compose.modifiers.sysuiResTag
import com.android.systemui.lifecycle.rememberViewModel
import com.android.systemui.statusbar.phone.domain.interactor.IsAreaDark
import com.android.systemui.statusbar.pipeline.battery.shared.ui.BatteryColors
@@ -74,7 +75,10 @@
)
}
- Canvas(modifier = modifier.fillMaxSize(), contentDescription = contentDescription) {
+ Canvas(
+ modifier = modifier.fillMaxSize().sysuiResTag(BatteryViewModel.TEST_TAG),
+ contentDescription = contentDescription,
+ ) {
val scale = path.scaleTo(size.width, size.height)
val colors = colorsProvider()
@@ -127,7 +131,7 @@
@Composable
fun UnifiedBattery(
viewModelFactory: BatteryViewModel.Factory,
- isDark: IsAreaDark,
+ isDarkProvider: () -> IsAreaDark,
modifier: Modifier = Modifier,
) {
val viewModel = rememberViewModel(traceName = "UnifiedBattery") { viewModelFactory.create() }
@@ -136,7 +140,7 @@
var bounds by remember { mutableStateOf(Rect()) }
val colorProvider = {
- if (isDark.isDark(bounds)) {
+ if (isDarkProvider().isDark(bounds)) {
viewModel.colorProfile.dark
} else {
viewModel.colorProfile.light
@@ -152,7 +156,8 @@
isFull = viewModel.isFull,
colorsProvider = colorProvider,
modifier =
- modifier.onLayoutRectChanged { relativeLayoutBounds ->
+ modifier.sysuiResTag(BatteryViewModel.TEST_TAG).onLayoutRectChanged {
+ relativeLayoutBounds ->
bounds =
with(relativeLayoutBounds.boundsInScreen) { Rect(left, top, right, bottom) }
},
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/battery/ui/viewmodel/BatteryViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/battery/ui/viewmodel/BatteryViewModel.kt
index 0c2ab50..711c170 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/battery/ui/viewmodel/BatteryViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/battery/ui/viewmodel/BatteryViewModel.kt
@@ -236,6 +236,9 @@
val ASPECT_RATIO = STATUS_BAR_BATTERY_WIDTH.value / STATUS_BAR_BATTERY_HEIGHT.value
+ /** Resource id used to identify battery composable view in SysUI tests */
+ const val TEST_TAG = "battery"
+
fun Int.glyphRepresentation(): List<BatteryGlyph> = toString().map { it.toGlyph() }
private fun Char.toGlyph(): BatteryGlyph =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinderKairos.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinderKairos.kt
index a41120e..47f775d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinderKairos.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinderKairos.kt
@@ -136,8 +136,9 @@
val roamingSpace = view.requireViewById<Space>(R.id.mobile_roaming_space)
val dotView = view.requireViewById<StatusBarIconView>(R.id.status_bar_dot)
+ val isVisible = viewModel.isVisible.sample()
effect {
- view.isVisible = viewModel.isVisible.sample()
+ view.isVisible = isVisible
iconView.isVisible = true
launch {
view.repeatWhenAttachedToWindow {
@@ -182,6 +183,7 @@
oldIcon is SignalIconModel.Cellular &&
newIcon is SignalIconModel.Cellular ->
oldIcon.numberOfLevels != newIcon.numberOfLevels
+
else -> false
}
viewModel.verboseLogger?.logBinderReceivedSignalIcon(
@@ -222,20 +224,23 @@
}
// Set the network type background
- viewModel.networkTypeBackground.observe { background ->
- networkTypeContainer.setBackgroundResource(background?.res ?: 0)
- // Tint will invert when this bit changes
- if (background?.res != null) {
- networkTypeContainer.backgroundTintList =
- ColorStateList.valueOf(binding.iconTint.sample().tint)
- networkTypeView.imageTintList =
- ColorStateList.valueOf(binding.iconTint.sample().contrast)
- } else {
- networkTypeView.imageTintList =
- ColorStateList.valueOf(binding.iconTint.sample().tint)
+ viewModel.networkTypeIcon
+ .mapTransactionally { it to binding.iconTint.sample() }
+ .observe { (background, iconTintColors) ->
+ networkTypeContainer.setBackgroundResource(background?.res ?: 0)
+
+ // Tint will invert when this bit changes
+ if (background?.res != null) {
+ networkTypeContainer.backgroundTintList =
+ ColorStateList.valueOf(iconTintColors.tint)
+ networkTypeView.imageTintList =
+ ColorStateList.valueOf(iconTintColors.contrast)
+ } else {
+ networkTypeView.imageTintList =
+ ColorStateList.valueOf(iconTintColors.tint)
+ }
}
- }
// Set the roaming indicator
viewModel.roaming.observe { isRoaming ->
@@ -266,26 +271,28 @@
}
// Set the tint
- binding.iconTint.observe { colors ->
- val tint = ColorStateList.valueOf(colors.tint)
- val contrast = ColorStateList.valueOf(colors.contrast)
+ binding.iconTint
+ .mapTransactionally { it to viewModel.networkTypeBackground.sample() }
+ .observe { (colors, networkTypeBackground) ->
+ val tint = ColorStateList.valueOf(colors.tint)
+ val contrast = ColorStateList.valueOf(colors.contrast)
- iconView.imageTintList = tint
+ iconView.imageTintList = tint
- // If the bg is visible, tint it and use the contrast for the fg
- if (viewModel.networkTypeBackground.sample() != null) {
- networkTypeContainer.backgroundTintList = tint
- networkTypeView.imageTintList = contrast
- } else {
- networkTypeView.imageTintList = tint
+ // If the bg is visible, tint it and use the contrast for the fg
+ if (networkTypeBackground != null) {
+ networkTypeContainer.backgroundTintList = tint
+ networkTypeView.imageTintList = contrast
+ } else {
+ networkTypeView.imageTintList = tint
+ }
+
+ roamingView.imageTintList = tint
+ activityIn.imageTintList = tint
+ activityOut.imageTintList = tint
+ dotView.setDecorColor(colors.tint)
}
- roamingView.imageTintList = tint
- activityIn.imageTintList = tint
- activityOut.imageTintList = tint
- dotView.setDecorColor(colors.tint)
- }
-
binding.decorTint.observe { tint -> dotView.setDecorColor(tint) }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt
index 58cfd86..1f39886 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt
@@ -39,6 +39,7 @@
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.compose.theme.PlatformTheme
import com.android.keyguard.AlphaOptimizedLinearLayout
+import com.android.systemui.compose.modifiers.sysUiResTagContainer
import com.android.systemui.lifecycle.rememberViewModel
import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager
import com.android.systemui.media.controls.ui.view.MediaHost
@@ -115,6 +116,7 @@
mediaHierarchyManager = mediaHierarchyManager,
mediaHost = mediaHost,
onViewCreated = andThen,
+ modifier = Modifier.sysUiResTagContainer(),
)
}
}
@@ -148,6 +150,7 @@
mediaHierarchyManager: MediaHierarchyManager,
mediaHost: MediaHost,
onViewCreated: (ViewGroup) -> Unit,
+ modifier: Modifier = Modifier,
) {
val displayId = parent.context.displayId
val statusBarViewModel =
@@ -161,7 +164,7 @@
null
}
- Box(Modifier.fillMaxSize()) {
+ Box(modifier.fillMaxSize()) {
// TODO(b/364360986): remove this before rolling the flag forward
if (StatusBarRootModernization.SHOW_DISAMBIGUATION) {
Disambiguation(viewModel = statusBarViewModel)
@@ -208,6 +211,8 @@
OngoingActivityChips(
chips = chipsVisibilityModel.chips,
iconViewStore = iconViewStore,
+ onChipBoundsChanged =
+ statusBarViewModel::onChipBoundsChanged,
)
}
}
@@ -339,9 +344,12 @@
setContent {
val height = with(LocalDensity.current) { STATUS_BAR_BATTERY_HEIGHT.toDp() }
UnifiedBattery(
- modifier = Modifier.height(height).aspectRatio(BatteryViewModel.ASPECT_RATIO),
+ modifier =
+ Modifier.sysUiResTagContainer()
+ .height(height)
+ .aspectRatio(BatteryViewModel.ASPECT_RATIO),
viewModelFactory = statusBarViewModel.batteryViewModelFactory,
- isDark = statusBarViewModel.areaDark,
+ isDarkProvider = { statusBarViewModel.areaDark },
)
}
}
@@ -372,9 +380,11 @@
val height = with(LocalDensity.current) { STATUS_BAR_BATTERY_HEIGHT.toDp() }
UnifiedBattery(
modifier =
- Modifier.height(height).aspectRatio(BatteryViewModel.ASPECT_RATIO),
+ Modifier.sysUiResTagContainer()
+ .height(height)
+ .aspectRatio(BatteryViewModel.ASPECT_RATIO),
viewModelFactory = statusBarViewModel.batteryViewModelFactory,
- isDark = statusBarViewModel.areaDark,
+ isDarkProvider = { statusBarViewModel.areaDark },
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt
index f8535c0..27fff89 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt
@@ -18,6 +18,7 @@
import android.annotation.ColorInt
import android.graphics.Rect
+import android.graphics.RectF
import android.view.Display
import android.view.View
import androidx.compose.runtime.getValue
@@ -139,6 +140,9 @@
/** All supported activity chips, whether they are currently active or not. */
val ongoingActivityChips: ChipsVisibilityModel
+ /** Invoked each time a chip's on-screen bounds have changed. */
+ fun onChipBoundsChanged(key: String, bounds: RectF)
+
/**
* The multiple ongoing activity chips that should be shown on the left-hand side of the status
* bar.
@@ -227,7 +231,7 @@
sceneContainerOcclusionInteractor: SceneContainerOcclusionInteractor,
shadeInteractor: ShadeInteractor,
shareToAppChipViewModel: ShareToAppChipViewModel,
- ongoingActivityChipsViewModel: OngoingActivityChipsViewModel,
+ private val ongoingActivityChipsViewModel: OngoingActivityChipsViewModel,
statusBarPopupChipsViewModelFactory: StatusBarPopupChipsViewModel.Factory,
animations: SystemStatusEventAnimationInteractor,
statusBarContentInsetsViewModelStore: StatusBarContentInsetsViewModelStore,
@@ -496,6 +500,10 @@
source = chipsVisibilityModel,
)
+ override fun onChipBoundsChanged(key: String, bounds: RectF) {
+ ongoingActivityChipsViewModel.onChipBoundsChanged(key, bounds)
+ }
+
private val hasOngoingActivityChips =
if (StatusBarChipsModernization.isEnabled) {
chipsVisibilityModel.map { it.chips.active.any { chip -> !chip.isHidden } }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java
index 0c98323..d851d65 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java
@@ -123,10 +123,12 @@
@Override
public void setListening(boolean listening) {
if (listening) {
- mRotationPolicy.registerRotationPolicyListener(mRotationPolicyListener,
- UserHandle.USER_ALL);
+ mBgExecutor.execute(
+ () -> mRotationPolicy.registerRotationPolicyListener(mRotationPolicyListener,
+ UserHandle.USER_ALL));
} else {
- mRotationPolicy.unregisterRotationPolicyListener(mRotationPolicyListener);
+ mBgExecutor.execute(() -> mRotationPolicy.unregisterRotationPolicyListener(
+ mRotationPolicyListener));
}
if (mIsPerDeviceStateRotationLockEnabled
&& mDeviceStateRotationLockSettingController.isPresent()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/systemstatusicons/ethernet/ui/viewmodel/EthernetIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/systemstatusicons/ethernet/ui/viewmodel/EthernetIconViewModel.kt
new file mode 100644
index 0000000..52c7121
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/systemstatusicons/ethernet/ui/viewmodel/EthernetIconViewModel.kt
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.systemstatusicons.ethernet.ui.viewmodel
+
+import androidx.compose.runtime.getValue
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.lifecycle.ExclusiveActivatable
+import com.android.systemui.lifecycle.Hydrator
+import com.android.systemui.statusbar.pipeline.ethernet.domain.EthernetInteractor
+import com.android.systemui.statusbar.systemstatusicons.SystemStatusIconsInCompose
+import com.android.systemui.statusbar.systemstatusicons.ui.viewmodel.SystemStatusIconViewModel
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+
+/**
+ * View model for the ethernet system status icon. Emits an icon when ethernet is connected and the
+ * default connection. Null icon otherwise.
+ */
+class EthernetIconViewModel @AssistedInject constructor(interactor: EthernetInteractor) :
+ SystemStatusIconViewModel, ExclusiveActivatable() {
+
+ init {
+ /* check if */ SystemStatusIconsInCompose.isUnexpectedlyInLegacyMode()
+ }
+
+ private val hydrator = Hydrator("EthernetIconViewModel.hydrator")
+
+ override val icon: Icon? by
+ hydrator.hydratedStateOf(traceName = null, initialValue = null, source = interactor.icon)
+
+ override suspend fun onActivated(): Nothing {
+ hydrator.activate()
+ }
+
+ @AssistedFactory
+ interface Factory {
+ fun create(): EthernetIconViewModel
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/systemstatusicons/ui/viewmodel/SystemStatusIconsViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/systemstatusicons/ui/viewmodel/SystemStatusIconsViewModel.kt
index dabe5ef..407c0a1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/systemstatusicons/ui/viewmodel/SystemStatusIconsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/systemstatusicons/ui/viewmodel/SystemStatusIconsViewModel.kt
@@ -20,8 +20,12 @@
import com.android.systemui.lifecycle.ExclusiveActivatable
import com.android.systemui.statusbar.systemstatusicons.SystemStatusIconsInCompose
import com.android.systemui.statusbar.systemstatusicons.airplane.ui.viewmodel.AirplaneModeIconViewModel
+import com.android.systemui.statusbar.systemstatusicons.ethernet.ui.viewmodel.EthernetIconViewModel
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
+import kotlinx.coroutines.awaitCancellation
+import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.launch
/**
* ViewModel for managing and displaying a list of system status icons.
@@ -31,21 +35,30 @@
*/
class SystemStatusIconsViewModel
@AssistedInject
-constructor(airplaneModeIconViewModelFactory: AirplaneModeIconViewModel.Factory) :
- ExclusiveActivatable() {
+constructor(
+ airplaneModeIconViewModelFactory: AirplaneModeIconViewModel.Factory,
+ ethernetIconViewModelFactory: EthernetIconViewModel.Factory,
+) : ExclusiveActivatable() {
init {
/* check if */ SystemStatusIconsInCompose.isUnexpectedlyInLegacyMode()
}
private val airplaneModeIcon by lazy { airplaneModeIconViewModelFactory.create() }
- private val iconViewModels by lazy { listOf(airplaneModeIcon) }
+ private val ethernetIcon by lazy { ethernetIconViewModelFactory.create() }
+ private val iconViewModels: List<SystemStatusIconViewModel> by lazy {
+ listOf(ethernetIcon, airplaneModeIcon)
+ }
val icons: List<Icon>
get() = iconViewModels.mapNotNull { viewModel -> viewModel.icon }
override suspend fun onActivated(): Nothing {
- airplaneModeIcon.activate()
+ coroutineScope {
+ launch { ethernetIcon.activate() }
+ launch { airplaneModeIcon.activate() }
+ }
+ awaitCancellation()
}
@AssistedFactory
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ui/SystemBarUtilsProxy.kt b/packages/SystemUI/src/com/android/systemui/statusbar/ui/SystemBarUtilsProxy.kt
index 68bb6c3..e802c45 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ui/SystemBarUtilsProxy.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ui/SystemBarUtilsProxy.kt
@@ -30,19 +30,20 @@
* Developers should almost always prefer [SystemBarUtilsState] instead.
*/
interface SystemBarUtilsProxy {
- fun getStatusBarHeight(): Int
- fun getStatusBarHeaderHeightKeyguard(): Int
+ fun getStatusBarHeight(context: Context? = null): Int
+
+ fun getStatusBarHeaderHeightKeyguard(context: Context? = null): Int
}
-class SystemBarUtilsProxyImpl
-@Inject
-constructor(
- @Application private val context: Context,
-) : SystemBarUtilsProxy {
- override fun getStatusBarHeight(): Int = SystemBarUtils.getStatusBarHeight(context)
- override fun getStatusBarHeaderHeightKeyguard(): Int {
- val cutout = context.display.cutout
- val waterfallInsetTop = if (cutout == null) 0 else cutout.waterfallInsets.top
+class SystemBarUtilsProxyImpl @Inject constructor(@Application private val appContext: Context) :
+ SystemBarUtilsProxy {
+ override fun getStatusBarHeight(context: Context?): Int {
+ return SystemBarUtils.getStatusBarHeight(context ?: appContext)
+ }
+
+ override fun getStatusBarHeaderHeightKeyguard(context: Context?): Int {
+ val context = context ?: appContext
+ val waterfallInsetTop = context.display.cutout?.waterfallInsets?.top ?: 0
val statusBarHeaderHeightKeyguard =
context.resources.getDimensionPixelSize(R.dimen.status_bar_header_height_keyguard)
return max(getStatusBarHeight(), statusBarHeaderHeightKeyguard + waterfallInsetTop)
diff --git a/packages/SystemUI/src/com/android/systemui/topwindoweffects/TopLevelWindowEffects.kt b/packages/SystemUI/src/com/android/systemui/topwindoweffects/TopLevelWindowEffects.kt
index af3a12f..ed8829d 100644
--- a/packages/SystemUI/src/com/android/systemui/topwindoweffects/TopLevelWindowEffects.kt
+++ b/packages/SystemUI/src/com/android/systemui/topwindoweffects/TopLevelWindowEffects.kt
@@ -21,6 +21,7 @@
import android.view.Gravity
import android.view.WindowInsets
import android.view.WindowManager
+import androidx.annotation.DrawableRes
import com.android.systemui.CoreStartable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
@@ -29,6 +30,8 @@
import com.android.systemui.topwindoweffects.domain.interactor.SqueezeEffectInteractor
import com.android.systemui.topwindoweffects.ui.compose.EffectsWindowRoot
import com.android.systemui.topwindoweffects.ui.viewmodel.SqueezeEffectViewModel
+import com.android.wm.shell.appzoomout.AppZoomOut
+import java.util.Optional
import javax.inject.Inject
import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.CoroutineScope
@@ -49,45 +52,37 @@
private val squeezeEffectInteractor: SqueezeEffectInteractor,
private val keyEventInteractor: KeyEventInteractor,
private val viewModelFactory: SqueezeEffectViewModel.Factory,
+ //TODO(b/409930584): make AppZoomOut non-optional
+ private val appZoomOutOptional: Optional<AppZoomOut>,
) : CoreStartable {
+ private var root: EffectsWindowRoot? = null
+
override fun start() {
applicationScope.launch {
- var root: EffectsWindowRoot? = null
var launchWindowEffect: Job? = null
squeezeEffectInteractor.isSqueezeEffectEnabled.collectLatest { enabled ->
if (enabled) {
keyEventInteractor.isPowerButtonDown.collectLatest { down ->
// cancel creating effects window if UP event is received within timeout
- // threshold of 100 milliseconds
+ // threshold of initial delay
launchWindowEffect?.cancel()
if (down) {
- val roundedCornerId =
+ val roundedCornerInfo =
async(context = bgContext) {
squeezeEffectInteractor.getRoundedCornersResourceId()
}
- launchWindowEffect = launch {
- delay(100) // delay to invoke the squeeze effect
- if (root == null) {
- root =
- EffectsWindowRoot(
- context = context,
- viewModelFactory = viewModelFactory,
- topRoundedCornerResourceId =
- roundedCornerId.await().top,
- bottomRoundedCornerResourceId =
- roundedCornerId.await().bottom,
- onEffectFinished = {
- if (root?.isAttachedToWindow == true) {
- windowManager.removeView(root)
- root = null
- }
- },
- )
- root?.let {
- windowManager.addView(it, getWindowManagerLayoutParams())
- }
+ val initialDelay =
+ async(context = bgContext) {
+ squeezeEffectInteractor.getInvocationEffectInitialDelayMs()
}
+ launchWindowEffect = launch {
+ delay(initialDelay.await())
+ addWindow(
+ roundedCornerInfo.await().topResourceId,
+ roundedCornerInfo.await().bottomResourceId,
+ roundedCornerInfo.await().physicalPixelDisplaySizeRatio,
+ )
}
} else {
launchWindowEffect = null
@@ -98,6 +93,33 @@
}
}
+ private fun addWindow(
+ @DrawableRes topRoundedCornerId: Int,
+ @DrawableRes bottomRoundedCornerId: Int,
+ physicalPixelDisplaySizeRatio: Float,
+ ) {
+ if (root == null) {
+ root =
+ EffectsWindowRoot(
+ context = context,
+ viewModelFactory = viewModelFactory,
+ topRoundedCornerResourceId = topRoundedCornerId,
+ bottomRoundedCornerResourceId = bottomRoundedCornerId,
+ physicalPixelDisplaySizeRatio = physicalPixelDisplaySizeRatio,
+ onEffectFinished = {
+ if (root?.isAttachedToWindow == true) {
+ windowManager.removeView(root)
+ root = null
+ }
+ },
+ appZoomOutOptional = appZoomOutOptional,
+ )
+ root?.let { rootView ->
+ windowManager.addView(rootView, getWindowManagerLayoutParams())
+ }
+ }
+ }
+
private fun getWindowManagerLayoutParams(): WindowManager.LayoutParams {
val lp =
WindowManager.LayoutParams(
diff --git a/packages/SystemUI/src/com/android/systemui/topwindoweffects/data/entity/SqueezeEffectCornerResourceId.kt b/packages/SystemUI/src/com/android/systemui/topwindoweffects/data/entity/SqueezeEffectCornersInfo.kt
similarity index 76%
copy from packages/SystemUI/src/com/android/systemui/topwindoweffects/data/entity/SqueezeEffectCornerResourceId.kt
copy to packages/SystemUI/src/com/android/systemui/topwindoweffects/data/entity/SqueezeEffectCornersInfo.kt
index a55301e..1d8da38 100644
--- a/packages/SystemUI/src/com/android/systemui/topwindoweffects/data/entity/SqueezeEffectCornerResourceId.kt
+++ b/packages/SystemUI/src/com/android/systemui/topwindoweffects/data/entity/SqueezeEffectCornersInfo.kt
@@ -16,4 +16,10 @@
package com.android.systemui.topwindoweffects.data.entity
-data class SqueezeEffectCornerResourceId(val top: Int, val bottom: Int)
+import androidx.annotation.DrawableRes
+
+data class SqueezeEffectCornersInfo(
+ @DrawableRes val topResourceId: Int,
+ @DrawableRes val bottomResourceId: Int,
+ val physicalPixelDisplaySizeRatio: Float,
+)
diff --git a/packages/SystemUI/src/com/android/systemui/topwindoweffects/data/repository/SqueezeEffectRepository.kt b/packages/SystemUI/src/com/android/systemui/topwindoweffects/data/repository/SqueezeEffectRepository.kt
index 95bfdbf..f05b1a6 100644
--- a/packages/SystemUI/src/com/android/systemui/topwindoweffects/data/repository/SqueezeEffectRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/topwindoweffects/data/repository/SqueezeEffectRepository.kt
@@ -16,11 +16,13 @@
package com.android.systemui.topwindoweffects.data.repository
-import com.android.systemui.topwindoweffects.data.entity.SqueezeEffectCornerResourceId
+import com.android.systemui.topwindoweffects.data.entity.SqueezeEffectCornersInfo
import kotlinx.coroutines.flow.Flow
interface SqueezeEffectRepository {
val isSqueezeEffectEnabled: Flow<Boolean>
- suspend fun getRoundedCornersResourceId(): SqueezeEffectCornerResourceId
+ suspend fun getInvocationEffectInitialDelayMs(): Long
+
+ suspend fun getRoundedCornersInfo(): SqueezeEffectCornersInfo
}
diff --git a/packages/SystemUI/src/com/android/systemui/topwindoweffects/data/repository/SqueezeEffectRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/topwindoweffects/data/repository/SqueezeEffectRepositoryImpl.kt
index 6f9e38d..57e562d 100644
--- a/packages/SystemUI/src/com/android/systemui/topwindoweffects/data/repository/SqueezeEffectRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/topwindoweffects/data/repository/SqueezeEffectRepositoryImpl.kt
@@ -21,6 +21,7 @@
import android.os.Bundle
import android.os.Handler
import android.provider.Settings.Global.POWER_BUTTON_LONG_PRESS
+import android.provider.Settings.Global.POWER_BUTTON_LONG_PRESS_DURATION_MS
import android.util.DisplayUtils
import android.view.DisplayInfo
import androidx.annotation.ArrayRes
@@ -33,7 +34,7 @@
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.res.R
import com.android.systemui.shared.Flags
-import com.android.systemui.topwindoweffects.data.entity.SqueezeEffectCornerResourceId
+import com.android.systemui.topwindoweffects.data.entity.SqueezeEffectCornersInfo
import com.android.systemui.util.settings.GlobalSettings
import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
import javax.inject.Inject
@@ -47,6 +48,8 @@
@VisibleForTesting
const val SET_INVOCATION_EFFECT_PARAMETERS_ACTION = "set_invocation_effect_parameters"
@VisibleForTesting const val IS_INVOCATION_EFFECT_ENABLED_KEY = "is_invocation_effect_enabled"
+@VisibleForTesting const val DEFAULT_INITIAL_DELAY_MILLIS = 100L
+@VisibleForTesting const val DEFAULT_LONG_PRESS_POWER_DURATION_MILLIS = 500L
@SysUISingleton
class SqueezeEffectRepositoryImpl
@@ -80,24 +83,48 @@
}
.flowOn(bgCoroutineContext)
- override suspend fun getRoundedCornersResourceId(): SqueezeEffectCornerResourceId {
+ override suspend fun getInvocationEffectInitialDelayMs(): Long {
+ val duration = getLongPressPowerDurationFromSettings()
+ // TODO(b/408363187): adjust this difference for values lower than 500ms
+ return if (duration > DEFAULT_LONG_PRESS_POWER_DURATION_MILLIS) {
+ DEFAULT_INITIAL_DELAY_MILLIS + (duration - DEFAULT_LONG_PRESS_POWER_DURATION_MILLIS)
+ } else {
+ DEFAULT_INITIAL_DELAY_MILLIS
+ }
+ }
+
+ override suspend fun getRoundedCornersInfo(): SqueezeEffectCornersInfo {
val displayInfo = DisplayInfo()
context.display.getDisplayInfo(displayInfo)
val displayIndex =
DisplayUtils.getDisplayUniqueIdConfigIndex(context.resources, displayInfo.uniqueId)
- return SqueezeEffectCornerResourceId(
- top =
+ val maxResDisplayMode =
+ DisplayUtils.getMaximumResolutionDisplayMode(displayInfo.supportedModes)
+ val ratio =
+ if (maxResDisplayMode == null) {
+ 1f
+ } else {
+ DisplayUtils.getPhysicalPixelDisplaySizeRatio(
+ /*physicalWidth = */ maxResDisplayMode.physicalWidth,
+ /*physicalHeight = */ maxResDisplayMode.physicalHeight,
+ /*currentWidth = */ displayInfo.naturalWidth,
+ /*currentHeight = */ displayInfo.naturalHeight,
+ )
+ }
+ return SqueezeEffectCornersInfo(
+ topResourceId =
getDrawableResource(
displayIndex = displayIndex,
arrayResId = R.array.config_roundedCornerTopDrawableArray,
backupDrawableId = R.drawable.rounded_corner_top,
),
- bottom =
+ bottomResourceId =
getDrawableResource(
displayIndex = displayIndex,
arrayResId = R.array.config_roundedCornerBottomDrawableArray,
backupDrawableId = R.drawable.rounded_corner_bottom,
),
+ physicalPixelDisplaySizeRatio = ratio,
)
}
@@ -138,6 +165,16 @@
),
) == 5 // 5 corresponds to launch assistant in PhoneWindowManager.java
+ private fun getLongPressPowerDurationFromSettings() =
+ globalSettings
+ .getInt(
+ POWER_BUTTON_LONG_PRESS_DURATION_MS,
+ context.resources.getInteger(
+ com.android.internal.R.integer.config_longPressOnPowerDurationMs
+ ),
+ )
+ .toLong()
+
override fun tryHandleSetUiHints(hints: Bundle): Boolean {
return when (hints.getString(AssistManager.ACTION_KEY)) {
SET_INVOCATION_EFFECT_PARAMETERS_ACTION -> {
diff --git a/packages/SystemUI/src/com/android/systemui/topwindoweffects/domain/interactor/SqueezeEffectInteractor.kt b/packages/SystemUI/src/com/android/systemui/topwindoweffects/domain/interactor/SqueezeEffectInteractor.kt
index f55a2b7..9a88bca 100644
--- a/packages/SystemUI/src/com/android/systemui/topwindoweffects/domain/interactor/SqueezeEffectInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/topwindoweffects/domain/interactor/SqueezeEffectInteractor.kt
@@ -26,6 +26,8 @@
constructor(private val squeezeEffectRepository: SqueezeEffectRepository) {
val isSqueezeEffectEnabled = squeezeEffectRepository.isSqueezeEffectEnabled
- suspend fun getRoundedCornersResourceId() =
- squeezeEffectRepository.getRoundedCornersResourceId()
+ suspend fun getInvocationEffectInitialDelayMs() =
+ squeezeEffectRepository.getInvocationEffectInitialDelayMs()
+
+ suspend fun getRoundedCornersResourceId() = squeezeEffectRepository.getRoundedCornersInfo()
}
diff --git a/packages/SystemUI/src/com/android/systemui/topwindoweffects/ui/compose/EffectsWindowRoot.kt b/packages/SystemUI/src/com/android/systemui/topwindoweffects/ui/compose/EffectsWindowRoot.kt
index 6a81f61..1a79fc0 100644
--- a/packages/SystemUI/src/com/android/systemui/topwindoweffects/ui/compose/EffectsWindowRoot.kt
+++ b/packages/SystemUI/src/com/android/systemui/topwindoweffects/ui/compose/EffectsWindowRoot.kt
@@ -23,6 +23,8 @@
import androidx.compose.ui.platform.AbstractComposeView
import com.android.systemui.compose.ComposeInitializer
import com.android.systemui.topwindoweffects.ui.viewmodel.SqueezeEffectViewModel
+import com.android.wm.shell.appzoomout.AppZoomOut
+import java.util.Optional
@SuppressLint("ViewConstructor")
class EffectsWindowRoot(
@@ -31,6 +33,8 @@
private val viewModelFactory: SqueezeEffectViewModel.Factory,
@DrawableRes private val topRoundedCornerResourceId: Int,
@DrawableRes private val bottomRoundedCornerResourceId: Int,
+ private val physicalPixelDisplaySizeRatio: Float,
+ private val appZoomOutOptional: Optional<AppZoomOut>,
) : AbstractComposeView(context) {
override fun onAttachedToWindow() {
@@ -50,6 +54,8 @@
onEffectFinished = onEffectFinished,
topRoundedCornerResourceId = topRoundedCornerResourceId,
bottomRoundedCornerResourceId = bottomRoundedCornerResourceId,
+ physicalPixelDisplaySizeRatio = physicalPixelDisplaySizeRatio,
+ appZoomOutOptional = appZoomOutOptional,
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/topwindoweffects/ui/compose/SqueezeEffect.kt b/packages/SystemUI/src/com/android/systemui/topwindoweffects/ui/compose/SqueezeEffect.kt
index ca0d930..3a1083c 100644
--- a/packages/SystemUI/src/com/android/systemui/topwindoweffects/ui/compose/SqueezeEffect.kt
+++ b/packages/SystemUI/src/com/android/systemui/topwindoweffects/ui/compose/SqueezeEffect.kt
@@ -37,12 +37,19 @@
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.graphics.vector.VectorPainter
import androidx.compose.ui.graphics.vector.rememberVectorPainter
+import androidx.compose.ui.platform.LocalWindowInfo
import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.unit.dp
import com.android.systemui.lifecycle.rememberViewModel
import com.android.systemui.topwindoweffects.ui.viewmodel.SqueezeEffectViewModel
+import com.android.systemui.topwindoweffects.ui.viewmodel.SqueezeEffectViewModel.Companion.ZOOM_OUT_SCALE
+import com.android.wm.shell.appzoomout.AppZoomOut
+import java.util.Optional
-private val SqueezeEffectMaxThickness = 12.dp
+// Defines the amount the squeeze border overlaps the shrinking content.
+// This is the difference between the total squeeze thickness and the thickness purely caused by the
+// zoom effect. At full progress, this overlap is 8 dp.
+private val SqueezeEffectOverlapMaxThickness = 8.dp
private val SqueezeColor = Color.Black
@Composable
@@ -50,7 +57,9 @@
viewModelFactory: SqueezeEffectViewModel.Factory,
@DrawableRes topRoundedCornerResourceId: Int,
@DrawableRes bottomRoundedCornerResourceId: Int,
+ physicalPixelDisplaySizeRatio: Float,
onEffectFinished: () -> Unit,
+ appZoomOutOptional: Optional<AppZoomOut>,
) {
val viewModel = rememberViewModel(traceName = "SqueezeEffect") { viewModelFactory.create() }
@@ -104,50 +113,83 @@
}
}
+ LaunchedEffect(squeezeProgress.value) {
+ appZoomOutOptional.ifPresent {
+ it.setTopLevelScale(1f - squeezeProgress.value * ZOOM_OUT_SCALE)
+ }
+ }
+
+ val screenWidth = LocalWindowInfo.current.containerSize.width
+ val screenHeight = LocalWindowInfo.current.containerSize.height
+
Canvas(modifier = Modifier.fillMaxSize()) {
if (squeezeProgress.value <= 0) {
return@Canvas
}
- val squeezeThickness = SqueezeEffectMaxThickness.toPx() * squeezeProgress.value
+ // Calculate the thickness of the squeeze effect borders.
+ // The total thickness on each side is composed of two parts:
+ // 1. Zoom Thickness: This accounts for the visual space created by the AppZoomOut
+ // effect scaling the content down. It's calculated as half the total reduction
+ // in screen dimension (width or height) caused by scaling (ZOOM_OUT_SCALE),
+ // proportional to the current squeezeProgress. We divide by 2 because the
+ // reduction happens on both sides (left/right or top/bottom).
+ // 2. Overlap Thickness: An additional fixed thickness (converted from dp to px)
+ // scaled by the squeezeProgress, designed to make the border slightly overlap
+ // the scaled content for a better visual effect.
+ val horizontalZoomThickness = screenWidth * ZOOM_OUT_SCALE * squeezeProgress.value / 2f
+ val verticalZoomThickness = screenHeight * ZOOM_OUT_SCALE * squeezeProgress.value / 2f
+ val overlapThickness = SqueezeEffectOverlapMaxThickness.toPx() * squeezeProgress.value
- drawRect(color = SqueezeColor, size = Size(size.width, squeezeThickness))
+ val horizontalSqueezeThickness = horizontalZoomThickness + overlapThickness
+ val verticalSqueezeThickness = verticalZoomThickness + overlapThickness
+
+ drawRect(color = SqueezeColor, size = Size(size.width, verticalSqueezeThickness))
drawRect(
color = SqueezeColor,
- topLeft = Offset(0f, size.height - squeezeThickness),
- size = Size(size.width, squeezeThickness),
+ topLeft = Offset(0f, size.height - verticalSqueezeThickness),
+ size = Size(size.width, verticalSqueezeThickness),
)
- drawRect(color = SqueezeColor, size = Size(squeezeThickness, size.height))
+ drawRect(color = SqueezeColor, size = Size(horizontalSqueezeThickness, size.height))
drawRect(
color = SqueezeColor,
- topLeft = Offset(size.width - squeezeThickness, 0f),
- size = Size(squeezeThickness, size.height),
+ topLeft = Offset(size.width - horizontalSqueezeThickness, 0f),
+ size = Size(horizontalSqueezeThickness, size.height),
)
- drawTransform(dx = squeezeThickness, dy = squeezeThickness, rotation = 0f, corner = top)
-
drawTransform(
- dx = size.width - squeezeThickness,
- dy = squeezeThickness,
+ dx = horizontalSqueezeThickness,
+ dy = verticalSqueezeThickness,
+ rotation = 0f,
+ corner = top,
+ displaySizeRatio = physicalPixelDisplaySizeRatio,
+ )
+
+ drawTransform(
+ dx = size.width - horizontalSqueezeThickness,
+ dy = verticalSqueezeThickness,
rotation = 90f,
corner = top,
+ displaySizeRatio = physicalPixelDisplaySizeRatio,
)
drawTransform(
- dx = squeezeThickness,
- dy = size.height - squeezeThickness,
+ dx = horizontalSqueezeThickness,
+ dy = size.height - verticalSqueezeThickness,
rotation = 270f,
corner = bottom,
+ displaySizeRatio = physicalPixelDisplaySizeRatio,
)
drawTransform(
- dx = size.width - squeezeThickness,
- dy = size.height - squeezeThickness,
+ dx = size.width - horizontalSqueezeThickness,
+ dy = size.height - verticalSqueezeThickness,
rotation = 180f,
corner = bottom,
+ displaySizeRatio = physicalPixelDisplaySizeRatio,
)
}
}
@@ -157,6 +199,7 @@
dy: Float,
rotation: Float = 0f,
corner: VectorPainter,
+ displaySizeRatio: Float,
) {
withTransform(
transformBlock = {
@@ -171,6 +214,14 @@
)
}
) {
- with(corner) { draw(size = intrinsicSize) }
+ with(corner) {
+ draw(
+ size =
+ Size(
+ width = intrinsicSize.width * displaySizeRatio,
+ height = intrinsicSize.height * displaySizeRatio,
+ )
+ )
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/topwindoweffects/ui/viewmodel/SqueezeEffectViewModel.kt b/packages/SystemUI/src/com/android/systemui/topwindoweffects/ui/viewmodel/SqueezeEffectViewModel.kt
index 6e0d88b..5603aa9 100644
--- a/packages/SystemUI/src/com/android/systemui/topwindoweffects/ui/viewmodel/SqueezeEffectViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/topwindoweffects/ui/viewmodel/SqueezeEffectViewModel.kt
@@ -23,22 +23,21 @@
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
-class SqueezeEffectViewModel
-@AssistedInject
-constructor(
- keyEventInteractor: KeyEventInteractor
-) : ExclusiveActivatable() {
+class SqueezeEffectViewModel @AssistedInject constructor(keyEventInteractor: KeyEventInteractor) :
+ ExclusiveActivatable() {
private val hydrator = Hydrator("SqueezeEffectViewModel.hydrator")
- val isPowerButtonPressed: Boolean by hydrator.hydratedStateOf(
- traceName = "isPowerButtonPressed",
- source = keyEventInteractor.isPowerButtonDown
- )
+ val isPowerButtonPressed: Boolean by
+ hydrator.hydratedStateOf(
+ traceName = "isPowerButtonPressed",
+ source = keyEventInteractor.isPowerButtonDown,
+ )
- val isPowerButtonLongPressed: Boolean by hydrator.hydratedStateOf(
- traceName = "isPowerButtonLongPressed",
- source = keyEventInteractor.isPowerButtonLongPressed
- )
+ val isPowerButtonLongPressed: Boolean by
+ hydrator.hydratedStateOf(
+ traceName = "isPowerButtonLongPressed",
+ source = keyEventInteractor.isPowerButtonLongPressed,
+ )
override suspend fun onActivated(): Nothing {
hydrator.activate()
@@ -48,4 +47,8 @@
interface Factory {
fun create(): SqueezeEffectViewModel
}
-}
\ No newline at end of file
+
+ companion object {
+ const val ZOOM_OUT_SCALE = 0.05f
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/underlay/data/repository/UnderlayRepository.kt b/packages/SystemUI/src/com/android/systemui/underlay/data/repository/UnderlayRepository.kt
index e29ba59..d3a6841 100644
--- a/packages/SystemUI/src/com/android/systemui/underlay/data/repository/UnderlayRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/underlay/data/repository/UnderlayRepository.kt
@@ -50,7 +50,7 @@
initialValue = false,
)
- private companion object {
+ companion object {
const val ACTION_CREATE_UNDERLAY = "com.systemui.underlay.action.CREATE_UNDERLAY"
const val ACTION_DESTROY_UNDERLAY = "com.systemui.underlay.action.DESTROY_UNDERLAY"
}
diff --git a/packages/SystemUI/src/com/android/systemui/underlay/domain/interactor/UnderlayInteractor.kt b/packages/SystemUI/src/com/android/systemui/underlay/domain/interactor/UnderlayInteractor.kt
index ac2c544..8d63216 100644
--- a/packages/SystemUI/src/com/android/systemui/underlay/domain/interactor/UnderlayInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/underlay/domain/interactor/UnderlayInteractor.kt
@@ -20,6 +20,6 @@
import javax.inject.Inject
import kotlinx.coroutines.flow.StateFlow
-class UnderlayInteractor @Inject constructor(repository: UnderlayRepository) {
+class UnderlayInteractor @Inject constructor(private val repository: UnderlayRepository) {
val isUnderlayAttached: StateFlow<Boolean> = repository.isUnderlayAttached
}
diff --git a/packages/SystemUI/src/com/android/systemui/underlay/ui/shape/TopConcaveArcShape.kt b/packages/SystemUI/src/com/android/systemui/underlay/ui/shape/TopConcaveArcShape.kt
new file mode 100644
index 0000000..555fbe4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/underlay/ui/shape/TopConcaveArcShape.kt
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.underlay.ui.shape
+
+import androidx.compose.ui.geometry.Rect
+import androidx.compose.ui.geometry.Size
+import androidx.compose.ui.graphics.Outline
+import androidx.compose.ui.graphics.Path
+import androidx.compose.ui.graphics.Shape
+import androidx.compose.ui.unit.Density
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.LayoutDirection
+import kotlin.math.max
+import kotlin.math.min
+
+/**
+ * A [Shape] that is rectangular on the left, bottom, and right sides, but has a concave top edge
+ * formed by two arcs connected by a straight line.
+ *
+ * @property cornerRadius The radius of the arc.
+ */
+class TopConcaveArcShape(private val cornerRadius: Dp) : Shape {
+ override fun createOutline(
+ size: Size,
+ layoutDirection: LayoutDirection,
+ density: Density,
+ ): Outline {
+ val radiusPx = with(density) { cornerRadius.toPx() }
+
+ val clampedRadiusPx = max(0f, min(radiusPx, min(size.width / 2f, size.height)))
+ // Return a rectangle directly if the radius is too small.
+ if (clampedRadiusPx < 0.01f) {
+ return Outline.Rectangle(Rect(0f, 0f, size.width, size.height))
+ }
+
+ val path =
+ Path().apply {
+ moveTo(0f, 0f)
+
+ // Arc is bottom left quadrant of a circle, going from the left down to the bottom
+ arcTo(
+ rect =
+ Rect(
+ left = 0f,
+ top = -clampedRadiusPx,
+ right = clampedRadiusPx * 2,
+ bottom = clampedRadiusPx,
+ ),
+ startAngleDegrees = 180f,
+ sweepAngleDegrees = -90f,
+ forceMoveTo = false,
+ )
+ lineTo(size.width - clampedRadiusPx, clampedRadiusPx)
+ // Arc is bottom right quadrant of a circle, going from the bottom up to the right
+ arcTo(
+ rect =
+ Rect(
+ left = size.width - (clampedRadiusPx * 2),
+ top = -clampedRadiusPx,
+ right = size.width,
+ bottom = clampedRadiusPx,
+ ),
+ startAngleDegrees = 90f,
+ sweepAngleDegrees = -90f,
+ forceMoveTo = false,
+ )
+ lineTo(size.width, size.height)
+ lineTo(0f, size.height)
+ close()
+ }
+ return Outline.Generic(path)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/underlay/ui/startable/UnderlayCoreStartable.kt b/packages/SystemUI/src/com/android/systemui/underlay/ui/startable/UnderlayCoreStartable.kt
index f5cec3c..7abc9370 100644
--- a/packages/SystemUI/src/com/android/systemui/underlay/ui/startable/UnderlayCoreStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/underlay/ui/startable/UnderlayCoreStartable.kt
@@ -43,7 +43,6 @@
private val underlayWindowRootView: UnderlayWindowRootView,
@Application private val mainScope: CoroutineScope,
) : CoreStartable {
-
override fun start() {
if (!UnderlayFlag.isEnabled) {
Log.d(TAG, "Underlay flag is disabled, not starting.")
diff --git a/packages/SystemUI/src/com/android/systemui/underlay/ui/view/UnderlayWindowRootView.kt b/packages/SystemUI/src/com/android/systemui/underlay/ui/view/UnderlayWindowRootView.kt
index 0cbf940..b1b7f6b 100644
--- a/packages/SystemUI/src/com/android/systemui/underlay/ui/view/UnderlayWindowRootView.kt
+++ b/packages/SystemUI/src/com/android/systemui/underlay/ui/view/UnderlayWindowRootView.kt
@@ -20,17 +20,22 @@
import android.view.Gravity
import android.view.ViewGroup
import android.widget.FrameLayout
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.wrapContentHeight
+import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.ComposeView
import com.android.compose.theme.PlatformTheme
import com.android.systemui.compose.ComposeInitializer
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.underlay.ui.compose.UnderlayComposableProvider
import com.android.systemui.underlay.ui.compose.UnderlayContainer
import javax.inject.Inject
/** A root view of the Underlay SysUI window. */
-class UnderlayWindowRootView @Inject constructor(@Application applicationContext: Context) :
+class UnderlayWindowRootView
+@Inject
+constructor(@Application applicationContext: Context, content: UnderlayComposableProvider) :
FrameLayout(applicationContext) {
-
init {
layoutParams =
ViewGroup.LayoutParams(
@@ -49,7 +54,14 @@
defaultFocusHighlightEnabled = false
fitsSystemWindows = false
}
- setContent { PlatformTheme { UnderlayContainer() } }
+ setContent {
+ PlatformTheme {
+ UnderlayContainer(
+ modifier = Modifier.fillMaxWidth().wrapContentHeight(),
+ content = content,
+ )
+ }
+ }
}
addView(composeView)
diff --git a/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt
index a5815f9..50a7c9d 100644
--- a/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt
@@ -308,7 +308,9 @@
.flatMapLatestConflated { selectedUser ->
if (selectedUser.isEligibleForLogout()) {
flowOf(
- resources.getBoolean(R.bool.config_userSwitchingMustGoThroughLoginScreen)
+ resources.getBoolean(
+ com.android.internal.R.bool.config_userSwitchingMustGoThroughLoginScreen
+ )
)
} else {
flowOf(false)
@@ -376,7 +378,9 @@
if (
// TODO(b/378068979): remove once login screen-specific logic
// is implemented at framework level.
- appContext.resources.getBoolean(R.bool.config_userSwitchingMustGoThroughLoginScreen)
+ appContext.resources.getBoolean(
+ com.android.internal.R.bool.config_userSwitchingMustGoThroughLoginScreen
+ )
) {
UserSwitcherSettingsModel(
isSimpleUserSwitcher = false,
diff --git a/packages/SystemUI/src/com/android/systemui/user/data/repository/UserSwitcherRepository.kt b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserSwitcherRepository.kt
index 5d9f3073..cbf4178 100644
--- a/packages/SystemUI/src/com/android/systemui/user/data/repository/UserSwitcherRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserSwitcherRepository.kt
@@ -68,7 +68,9 @@
context.resources.getBoolean(R.bool.qs_show_user_switcher_for_single_user)
private val userSwitchingMustGoThroughLoginScreen =
- context.resources.getBoolean(R.bool.config_userSwitchingMustGoThroughLoginScreen)
+ context.resources.getBoolean(
+ com.android.internal.R.bool.config_userSwitchingMustGoThroughLoginScreen
+ )
override val isEnabled: Flow<Boolean> = conflatedCallbackFlow {
suspend fun updateState() {
diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt
index cc4307a..4283732 100644
--- a/packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt
@@ -19,7 +19,6 @@
import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.util.time.SystemClock
import com.android.systemui.util.time.SystemClockImpl
-import java.util.LinkedList
import java.util.concurrent.atomic.AtomicReference
import kotlin.math.max
import kotlin.time.Duration
@@ -37,6 +36,8 @@
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onStart
+import kotlinx.coroutines.sync.Mutex
+import kotlinx.coroutines.sync.withLock
/**
* Returns a new [Flow] that combines the two most recent emissions from [this] using [transform].
@@ -388,7 +389,11 @@
clock: SystemClock = SystemClockImpl(),
): Flow<List<T>> = channelFlow {
require(windowDuration.isPositive()) { "Window duration must be positive" }
- val buffer = LinkedList<Pair<Duration, T>>()
+
+ // Use a Mutex to protect access to the buffer in case this flow is collected on a
+ // multi-threaded dispatcher.
+ val bufferMutex = Mutex()
+ val buffer = ArrayDeque<Pair<Duration, T>>()
coroutineScope {
var windowAdvancementJob: Job? = null
@@ -396,27 +401,61 @@
collect { value ->
windowAdvancementJob?.cancel()
val now = clock.currentTimeMillis().milliseconds
- buffer.addLast(now to value)
- while (buffer.isNotEmpty() && buffer.first.first + windowDuration <= now) {
- buffer.removeFirst()
+ bufferMutex.withLock {
+ buffer.addLast(now to value)
+
+ while (buffer.isNotEmpty() && buffer.first().first + windowDuration <= now) {
+ buffer.removeFirst()
+ }
+ send(buffer.map { it.second })
}
- send(buffer.map { it.second })
// Keep the window advancing through time even if the source flow isn't emitting
// anymore. We stop advancing the window as soon as there are no items left in the
// buffer.
windowAdvancementJob = launch {
- while (buffer.isNotEmpty()) {
- val startOfWindow = clock.currentTimeMillis().milliseconds - windowDuration
- // Invariant: At this point, everything in the buffer is guaranteed to be in
- // the window, as we removed expired items above.
- val timeUntilNextOldest =
- (buffer.first.first - startOfWindow).coerceAtLeast(0.milliseconds)
+ while (true) {
+ // Acquire lock to check buffer state and calculate delay
+ val timeUntilNextOldest: Duration? =
+ bufferMutex.withLock {
+ // If buffer is empty, the job is done
+ if (buffer.isEmpty()) {
+ return@withLock null
+ }
+
+ // Calculate how long until the oldest element expires
+ val nowMillis = clock.currentTimeMillis().milliseconds
+ val oldestElementTime = buffer.first().first
+ val windowStartTime = nowMillis - windowDuration
+
+ // Time until the oldest element falls out of the window
+ (oldestElementTime - windowStartTime).coerceAtLeast(Duration.ZERO)
+ }
+
+ if (timeUntilNextOldest == null) {
+ break
+ }
+
+ // Delay until the oldest item is *supposed* to expire
delay(timeUntilNextOldest)
- // Remove the oldest item, as it has now fallen out of the window.
- buffer.removeFirst()
- send(buffer.map { it.second })
+
+ // Acquire lock again to remove the expired item (if it's still the oldest)
+ // and send the updated buffer state
+ bufferMutex.withLock {
+ val nowMillis = clock.currentTimeMillis().milliseconds
+ var removed = false
+ while (
+ buffer.isNotEmpty() &&
+ buffer.first().first + windowDuration <= nowMillis
+ ) {
+ buffer.removeFirst()
+ removed = true
+ }
+ if (removed) {
+ send(buffer.map { it.second })
+ }
+ }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
index 4d547705..9ed5c05 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
@@ -57,7 +57,6 @@
import androidx.annotation.Nullable;
import androidx.lifecycle.Observer;
-import com.android.internal.annotations.GuardedBy;
import com.android.settingslib.volume.MediaSessions;
import com.android.settingslib.volume.MediaSessions.SessionId;
import com.android.systemui.Dumpable;
@@ -166,7 +165,6 @@
boolean mInAudioSharing = false;
private VolumePolicy mVolumePolicy;
- @GuardedBy("this")
private UserActivityListener mUserActivityListener;
protected final VC mVolumeController = new VC();
@@ -358,9 +356,7 @@
}
public void setUserActivityListener(UserActivityListener listener) {
- synchronized (this) {
- mUserActivityListener = listener;
- }
+ mUserActivityListener = listener;
}
public void removeCallback(Callbacks callback) {
@@ -407,8 +403,9 @@
}
public void userActivity() {
- mWorker.removeMessages(W.USER_ACTIVITY);
- mWorker.sendEmptyMessage(W.USER_ACTIVITY);
+ if (mUserActivityListener != null) {
+ mUserActivityListener.onUserActivity();
+ }
}
public void setRingerMode(int value, boolean external) {
@@ -477,14 +474,6 @@
}
}
- private void onUserActivityW() {
- synchronized (this) {
- if (mUserActivityListener != null) {
- mUserActivityListener.onUserActivity();
- }
- }
- }
-
private void onShowSafetyWarningW(int flags) {
if (mShowSafetyWarning) {
mCallbacks.onShowSafetyWarning(flags);
@@ -984,13 +973,12 @@
private static final int SET_STREAM_VOLUME = 10;
private static final int SET_ACTIVE_STREAM = 11;
private static final int NOTIFY_VISIBLE = 12;
- private static final int USER_ACTIVITY = 13;
- private static final int SHOW_SAFETY_WARNING = 14;
- private static final int ACCESSIBILITY_MODE_CHANGED = 15;
- private static final int GET_CAPTIONS_COMPONENT_STATE = 16;
- private static final int SHOW_CSD_WARNING = 17;
- private static final int GET_CAPTIONS_ENABLED_STATE = 18;
- private static final int SET_CAPTIONS_ENABLED_STATE = 19;
+ private static final int SHOW_SAFETY_WARNING = 13;
+ private static final int ACCESSIBILITY_MODE_CHANGED = 14;
+ private static final int GET_CAPTIONS_COMPONENT_STATE = 15;
+ private static final int SHOW_CSD_WARNING = 16;
+ private static final int GET_CAPTIONS_ENABLED_STATE = 17;
+ private static final int SET_CAPTIONS_ENABLED_STATE = 18;
W(Looper looper) {
super(looper);
@@ -1011,7 +999,6 @@
case SET_STREAM_VOLUME: onSetStreamVolumeW(msg.arg1, msg.arg2); break;
case SET_ACTIVE_STREAM: onSetActiveStreamW(msg.arg1); break;
case NOTIFY_VISIBLE: onNotifyVisibleW(msg.arg1 != 0); break;
- case USER_ACTIVITY: onUserActivityW(); break;
case SHOW_SAFETY_WARNING: onShowSafetyWarningW(msg.arg1); break;
case GET_CAPTIONS_COMPONENT_STATE:
onGetCaptionsComponentStateW((Boolean) msg.obj); break;
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt
index dc41e90..ab111f6 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt
@@ -44,6 +44,7 @@
import com.android.systemui.volume.dialog.sliders.ui.compose.SliderTrack
import com.android.systemui.volume.dialog.sliders.ui.viewmodel.VolumeDialogOverscrollViewModel
import com.android.systemui.volume.dialog.sliders.ui.viewmodel.VolumeDialogSliderViewModel
+import com.android.systemui.volume.haptics.ui.VolumeHapticsConfigsProvider
import com.android.systemui.volume.ui.compose.slider.AccessibilityParams
import com.android.systemui.volume.ui.compose.slider.Haptics
import com.android.systemui.volume.ui.compose.slider.Slider
@@ -130,7 +131,10 @@
hapticsViewModelFactory?.let {
Haptics.Enabled(
hapticsViewModelFactory = it,
- hapticFilter = SliderHapticFeedbackFilter(),
+ hapticConfigs =
+ VolumeHapticsConfigsProvider.continuousConfigs(
+ SliderHapticFeedbackFilter()
+ ),
orientation = Orientation.Vertical,
)
} ?: Haptics.Disabled,
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogViewBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogViewBinder.kt
index 11d9df4..c8fc206 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogViewBinder.kt
@@ -117,7 +117,10 @@
launchTraced("VDVB#insets") {
root
.onApplyWindowInsets { v, newInsets ->
- val insetsValues = newInsets.getInsets(WindowInsets.Type.displayCutout())
+ val insetsValues =
+ newInsets.getInsets(
+ WindowInsets.Type.displayCutout() or WindowInsets.Type.navigationBars()
+ )
v.updatePadding(
left = insetsValues.left,
top = insetsValues.top,
diff --git a/packages/SystemUI/src/com/android/systemui/volume/haptics/ui/VolumeHapticsConfigsProvider.kt b/packages/SystemUI/src/com/android/systemui/volume/haptics/ui/VolumeHapticsConfigsProvider.kt
index 863ae62..b9344590 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/haptics/ui/VolumeHapticsConfigsProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/haptics/ui/VolumeHapticsConfigsProvider.kt
@@ -22,23 +22,55 @@
object VolumeHapticsConfigsProvider {
- fun sliderHapticFeedbackConfig(
- valueRange: ClosedFloatingPointRange<Float>,
- filter: SliderHapticFeedbackFilter = SliderHapticFeedbackFilter(),
- ): SliderHapticFeedbackConfig {
- val sliderStepSize = 1f / (valueRange.endInclusive - valueRange.start)
- return SliderHapticFeedbackConfig(
- lowerBookendScale = 0.2f,
- progressBasedDragMinScale = 0.2f,
- progressBasedDragMaxScale = 0.5f,
- deltaProgressForDragThreshold = 0f,
- additionalVelocityMaxBump = 0.2f,
- maxVelocityToScale = 0.1f, /* slider progress(from 0 to 1) per sec */
- sliderStepSize = sliderStepSize,
- filter = filter,
- )
- }
+ fun discreteConfigs(stepSize: Float, filter: SliderHapticFeedbackFilter): VolumeHapticsConfigs =
+ provideConfigs(stepSize, filter)
- val seekableSliderTrackerConfig =
- SeekableSliderTrackerConfig(lowerBookendThreshold = 0f, upperBookendThreshold = 1f)
+ fun continuousConfigs(filter: SliderHapticFeedbackFilter): VolumeHapticsConfigs =
+ provideConfigs(stepSize = 0f, filter)
+
+ private fun provideConfigs(
+ stepSize: Float,
+ filter: SliderHapticFeedbackFilter,
+ ): VolumeHapticsConfigs {
+ val hapticFeedbackConfig: SliderHapticFeedbackConfig
+ val trackerConfig: SeekableSliderTrackerConfig
+ if (stepSize == 0f) {
+ // Create a set of continuous configs
+ hapticFeedbackConfig =
+ SliderHapticFeedbackConfig(
+ additionalVelocityMaxBump = 0.1f,
+ deltaProgressForDragThreshold = 0.05f,
+ numberOfLowTicks = 4,
+ maxVelocityToScale = 0.5f, /* slider progress(from 0 to 1) per sec */
+ filter = filter,
+ )
+ trackerConfig =
+ SeekableSliderTrackerConfig(
+ lowerBookendThreshold = 0.01f,
+ upperBookendThreshold = 0.99f,
+ )
+ } else {
+ // Create a set of discrete configs
+ hapticFeedbackConfig =
+ SliderHapticFeedbackConfig(
+ lowerBookendScale = 0.2f,
+ progressBasedDragMinScale = 0.2f,
+ progressBasedDragMaxScale = 0.5f,
+ deltaProgressForDragThreshold = 0f,
+ additionalVelocityMaxBump = 0.2f,
+ maxVelocityToScale = 0.1f, /* slider progress(from 0 to 1) per sec */
+ sliderStepSize = stepSize,
+ filter = filter,
+ )
+ trackerConfig =
+ SeekableSliderTrackerConfig(lowerBookendThreshold = 0f, upperBookendThreshold = 1f)
+ }
+ return VolumeHapticsConfigs(hapticFeedbackConfig, trackerConfig)
+ }
}
+
+// A collection of configuration parameters for the haptics in the slider
+data class VolumeHapticsConfigs(
+ val hapticFeedbackConfig: SliderHapticFeedbackConfig,
+ val sliderTrackerConfig: SeekableSliderTrackerConfig,
+)
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/domain/interactor/AudioSlidersInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/domain/interactor/AudioSlidersInteractor.kt
index a326da4..3ac4a91 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/domain/interactor/AudioSlidersInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/domain/interactor/AudioSlidersInteractor.kt
@@ -95,13 +95,10 @@
}
private fun MutableList<SliderType>.addStream(stream: Int) {
- // Hide other streams except STREAM_MUSIC if the isSingleVolume mode is on. This makes sure
+ // Hide other streams except STREAM_MUSIC in single volume mode to makes sure
// the volume slider in volume panel is consistent with the volume slider inside system
// settings app.
- if (
- Flags.onlyShowMediaStreamSliderInSingleVolumeMode() &&
- audioSystemRepository.isSingleVolume &&
- stream != AudioManager.STREAM_MUSIC
+ if (audioSystemRepository.isSingleVolume && stream != AudioManager.STREAM_MUSIC
) {
return
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/ui/compose/slider/Slider.kt b/packages/SystemUI/src/com/android/systemui/volume/ui/compose/slider/Slider.kt
index e81d415..08d080e 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/ui/compose/slider/Slider.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/ui/compose/slider/Slider.kt
@@ -36,7 +36,6 @@
import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
-import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Modifier
import androidx.compose.ui.semantics.ProgressBarRangeInfo
import androidx.compose.ui.semantics.SemanticsPropertyReceiver
@@ -46,14 +45,10 @@
import androidx.compose.ui.semantics.progressBarRangeInfo
import androidx.compose.ui.semantics.setProgress
import androidx.compose.ui.semantics.stateDescription
-import com.android.systemui.haptics.slider.SliderHapticFeedbackFilter
import com.android.systemui.haptics.slider.compose.ui.SliderHapticsViewModel
import com.android.systemui.lifecycle.rememberViewModel
-import com.android.systemui.volume.haptics.ui.VolumeHapticsConfigsProvider
+import com.android.systemui.volume.haptics.ui.VolumeHapticsConfigs
import kotlin.math.round
-import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.filter
-import kotlinx.coroutines.flow.map
@Composable
fun Slider(
@@ -95,7 +90,7 @@
val sliderState =
remember(valueRange) { SliderState(value = animatedValue, valueRange = valueRange) }
val valueChange: (Float) -> Unit = { newValue ->
- hapticsViewModel?.onValueChange(newValue)
+ hapticsViewModel?.addVelocityDataPoint(newValue)
onValueChanged(newValue)
}
val semantics =
@@ -193,24 +188,23 @@
interactionSource,
valueRange,
orientation,
- VolumeHapticsConfigsProvider.sliderHapticFeedbackConfig(
- valueRange,
- hapticFilter,
- ),
- VolumeHapticsConfigsProvider.seekableSliderTrackerConfig,
+ hapticConfigs.hapticFeedbackConfig,
+ hapticConfigs.sliderTrackerConfig,
)
}
.also { hapticsViewModel ->
- var lastDiscreteStep by remember { mutableFloatStateOf(value) }
+ var lastValue by remember { mutableFloatStateOf(value) }
LaunchedEffect(value) {
- snapshotFlow { value }
- .map { round(it) }
- .filter { it != lastDiscreteStep }
- .distinctUntilChanged()
- .collect { discreteStep ->
- lastDiscreteStep = discreteStep
- hapticsViewModel.onValueChange(discreteStep)
+ val roundedValue =
+ if (hapticConfigs.hapticFeedbackConfig.sliderStepSize != 0f) {
+ round(value)
+ } else {
+ value
}
+ if (roundedValue != lastValue) {
+ lastValue = roundedValue
+ hapticsViewModel.onValueChange(roundedValue)
+ }
}
}
}
@@ -228,7 +222,7 @@
data class Enabled(
val hapticsViewModelFactory: SliderHapticsViewModel.Factory,
- val hapticFilter: SliderHapticFeedbackFilter,
+ val hapticConfigs: VolumeHapticsConfigs,
val orientation: Orientation,
) : Haptics
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/ui/navigation/VolumeNavigator.kt b/packages/SystemUI/src/com/android/systemui/volume/ui/navigation/VolumeNavigator.kt
index 680cf76..fb9b44f 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/ui/navigation/VolumeNavigator.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/ui/navigation/VolumeNavigator.kt
@@ -22,8 +22,10 @@
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.android.internal.logging.UiEventLogger
+import com.android.systemui.compose.modifiers.sysUiResTagContainer
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Main
@@ -118,7 +120,8 @@
val coroutineScope = rememberCoroutineScope()
VolumePanelRoot(
- remember(coroutineScope) { viewModelFactory.create(coroutineScope) }
+ remember(coroutineScope) { viewModelFactory.create(coroutineScope) },
+ Modifier.sysUiResTagContainer(),
)
},
isDraggable = false,
diff --git a/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/WallpaperRepository.kt b/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/WallpaperRepository.kt
index b43b9df..29b117b 100644
--- a/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/WallpaperRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/WallpaperRepository.kt
@@ -56,6 +56,7 @@
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onStart
@@ -177,6 +178,7 @@
override val shouldSendFocalArea =
lockscreenWallpaperInfo
+ .filterNotNull()
.map {
val focalAreaTarget = context.resources.getString(SysUIR.string.focal_area_target)
val shouldSendNotificationLayout = it?.component?.className == focalAreaTarget
diff --git a/packages/SystemUI/src/com/android/systemui/wallpapers/ui/viewmodel/WallpaperFocalAreaViewModel.kt b/packages/SystemUI/src/com/android/systemui/wallpapers/ui/viewmodel/WallpaperFocalAreaViewModel.kt
index 1be23c9..1358bc5 100644
--- a/packages/SystemUI/src/com/android/systemui/wallpapers/ui/viewmodel/WallpaperFocalAreaViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/wallpapers/ui/viewmodel/WallpaperFocalAreaViewModel.kt
@@ -21,6 +21,7 @@
import com.android.systemui.keyguard.shared.model.Edge
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.wallpapers.domain.interactor.WallpaperFocalAreaInteractor
import javax.inject.Inject
@@ -29,6 +30,7 @@
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.onStart
class WallpaperFocalAreaViewModel
@Inject
@@ -55,6 +57,10 @@
.filter { it.transitionState == TransitionState.FINISHED },
::Pair,
)
+ // Enforce collecting wallpaperFocalAreaBounds after rebooting
+ .onStart {
+ emit(Pair(TransitionStep(to = KeyguardState.LOCKSCREEN), TransitionStep()))
+ }
.flatMapLatest { (startedStep, _) ->
// Subscribe to bounds within the period of transitioning to the lockscreen,
// prior to any transitions away.
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
index 92a3bae..ecad5c1 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
@@ -370,6 +370,7 @@
val transitionStep = MutableStateFlow(TransitionStep())
whenever(keyguardTransitionInteractor.transition(Edge.create(to = LOCKSCREEN)))
.thenReturn(transitionStep)
+ clearInvocations(animations)
val job = underTest.listenForAnyStateToLockscreenTransition(this)
transitionStep.value =
@@ -412,6 +413,7 @@
val transitionStep = MutableStateFlow(TransitionStep())
whenever(keyguardTransitionInteractor.transition(Edge.create(to = LOCKSCREEN)))
.thenReturn(transitionStep)
+ clearInvocations(animations)
val job = underTest.listenForAnyStateToLockscreenTransition(this)
transitionStep.value =
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 9475bdb..9af0845 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -35,7 +35,6 @@
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN;
import static com.android.keyguard.KeyguardUpdateMonitor.BIOMETRIC_STATE_CANCELLING_RESTARTING;
import static com.android.keyguard.KeyguardUpdateMonitor.BIOMETRIC_STATE_STOPPED;
-import static com.android.keyguard.KeyguardUpdateMonitor.DEFAULT_CANCEL_SIGNAL_TIMEOUT;
import static com.android.keyguard.KeyguardUpdateMonitor.HAL_POWER_PRESS_TIMEOUT;
import static com.android.systemui.Flags.FLAG_GLANCEABLE_HUB_V2;
import static com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_OPENED;
@@ -50,6 +49,7 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeastOnce;
@@ -122,7 +122,6 @@
import com.android.compose.animation.scene.ObservableTransitionState;
import com.android.dx.mockito.inline.extended.ExtendedMockito;
-import com.android.internal.foldables.FoldGracePeriodProvider;
import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.logging.InstanceId;
import com.android.internal.logging.UiEventLogger;
@@ -260,8 +259,6 @@
@Mock
private BroadcastDispatcher mBroadcastDispatcher;
@Mock
- private FoldGracePeriodProvider mFoldGracePeriodProvider;
- @Mock
private TelephonyManager mTelephonyManager;
@Mock
private SensorPrivacyManager mSensorPrivacyManager;
@@ -401,7 +398,6 @@
anyInt());
mKeyguardUpdateMonitor = new TestableKeyguardUpdateMonitor(mContext);
- mKeyguardUpdateMonitor.mFoldGracePeriodProvider = mFoldGracePeriodProvider;
setupBiometrics(mKeyguardUpdateMonitor);
mKeyguardUpdateMonitor.setFaceAuthInteractor(mFaceAuthInteractor);
verify(mFaceAuthInteractor).registerListener(mFaceAuthenticationListener.capture());
@@ -1793,8 +1789,8 @@
mKeyguardUpdateMonitor.setKeyguardShowing(false, false);
mTestableLooper.processAllMessages();
- verify(mHandler).postDelayed(mKeyguardUpdateMonitor.mFpCancelNotReceived,
- DEFAULT_CANCEL_SIGNAL_TIMEOUT);
+ verify(mHandler).postDelayed(eq(mKeyguardUpdateMonitor.mFpCancelNotReceived),
+ anyLong());
mKeyguardUpdateMonitor.onFingerprintAuthenticated(0, true);
mTestableLooper.processAllMessages();
@@ -2505,16 +2501,7 @@
}
@Test
- public void forceIsDismissibleKeyguard_foldingGracePeriodNotEnabled() {
- when(mFoldGracePeriodProvider.isEnabled()).thenReturn(false);
- primaryAuthNotRequiredByStrongAuthTracker();
- mKeyguardUpdateMonitor.tryForceIsDismissibleKeyguard();
- Assert.assertFalse(mKeyguardUpdateMonitor.forceIsDismissibleIsKeepingDeviceUnlocked());
- }
-
- @Test
public void forceIsDismissibleKeyguard() {
- when(mFoldGracePeriodProvider.isEnabled()).thenReturn(true);
primaryAuthNotRequiredByStrongAuthTracker();
mKeyguardUpdateMonitor.tryForceIsDismissibleKeyguard();
Assert.assertTrue(mKeyguardUpdateMonitor.forceIsDismissibleIsKeepingDeviceUnlocked());
@@ -2522,7 +2509,6 @@
@Test
public void forceIsDismissibleKeyguard_respectsLockdown() {
- when(mFoldGracePeriodProvider.isEnabled()).thenReturn(true);
userDeviceLockDown();
mKeyguardUpdateMonitor.tryForceIsDismissibleKeyguard();
Assert.assertFalse(mKeyguardUpdateMonitor.forceIsDismissibleIsKeepingDeviceUnlocked());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java
index ac0378b..4bccf17 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java
@@ -19,7 +19,6 @@
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -37,8 +36,6 @@
import android.graphics.Rect;
import android.os.Handler;
import android.os.RemoteException;
-import android.platform.test.annotations.RequiresFlagsDisabled;
-import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.testing.AndroidTestingRunner;
@@ -51,8 +48,8 @@
import android.window.InputTransferToken;
import androidx.test.filters.LargeTest;
+
import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
-import com.android.systemui.Flags;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.AnimatorTestRule;
import com.android.systemui.model.SysUiState;
@@ -273,10 +270,8 @@
verifyFinalSpec(DEFAULT_SCALE, DEFAULT_CENTER_X, DEFAULT_CENTER_Y);
}
- @RequiresFlagsEnabled(Flags.FLAG_CREATE_WINDOWLESS_WINDOW_MAGNIFIER)
@Test
- public void
- enableWindowMagnificationScaleOne_enabledAndWindowlessFlagOn_AnimationAndCallbackTrue()
+ public void enableWindowMagnificationScaleOne_enabled_AnimationAndCallbackTrue()
throws RemoteException {
enableWindowMagnificationWithoutAnimation();
@@ -315,48 +310,6 @@
mWindowMagnificationAnimationController.getState());
}
- @RequiresFlagsDisabled(Flags.FLAG_CREATE_WINDOWLESS_WINDOW_MAGNIFIER)
- @Test
- public void
- enableWindowMagnificationScaleOne_enabledAndWindowlessFlagOff_AnimationAndCallbackTrue()
- throws RemoteException {
- enableWindowMagnificationWithoutAnimation();
-
- // Wait for Rects updated.
- waitForIdleSync();
- View mirrorView = mWindowManager.getAttachedView();
- final float targetScale = 1.0f;
- // Move the magnifier to the top left corner, within the boundary
- final float targetCenterX = mirrorView.getWidth() / 2.0f;
- final float targetCenterY = mirrorView.getHeight() / 2.0f;
-
- Mockito.reset(mSpyController);
- getInstrumentation().runOnMainSync(() -> {
- mWindowMagnificationAnimationController.enableWindowMagnification(targetScale,
- targetCenterX, targetCenterY, mAnimationCallback);
- mCurrentScale.set(mController.getScale());
- mCurrentCenterX.set(mController.getMagnificationFrameCenterX());
- mCurrentCenterY.set(mController.getMagnificationFrameCenterY());
- advanceTimeBy(mWaitAnimationDuration);
- });
-
- verify(mSpyController, atLeast(2)).updateWindowMagnificationInternal(
- mScaleCaptor.capture(),
- mCenterXCaptor.capture(), mCenterYCaptor.capture(),
- mOffsetXCaptor.capture(), mOffsetYCaptor.capture());
- verifyStartValue(mScaleCaptor, mCurrentScale.get());
- verifyStartValue(mCenterXCaptor, mCurrentCenterX.get());
- verifyStartValue(mCenterYCaptor, mCurrentCenterY.get());
- verifyStartValue(mOffsetXCaptor, 0f);
- verifyStartValue(mOffsetYCaptor, 0f);
-
- verifyFinalSpec(targetScale, targetCenterX, targetCenterY);
-
- verify(mAnimationCallback).onResult(true);
- assertEquals(WindowMagnificationAnimationController.STATE_ENABLED,
- mWindowMagnificationAnimationController.getState());
- }
-
@Test
public void enableWindowMagnificationWithScaleLessThanOne_enabled_AnimationAndInvokeCallback()
throws RemoteException {
@@ -562,9 +515,8 @@
verify(mAnimationCallback2).onResult(true);
}
- @RequiresFlagsEnabled(Flags.FLAG_CREATE_WINDOWLESS_WINDOW_MAGNIFIER)
@Test
- public void enableWindowMagnificationWithOffset_windowlessFlagOn_expectedValues() {
+ public void enableWindowMagnificationWithOffset_expectedValues() {
final float offsetRatio = -0.1f;
final Rect windowBounds = new Rect(mWindowManager.getCurrentWindowMetrics().getBounds());
@@ -599,35 +551,6 @@
.setPosition(any(SurfaceControl.class), eq(expectedX), eq(expectedY));
}
- @RequiresFlagsDisabled(Flags.FLAG_CREATE_WINDOWLESS_WINDOW_MAGNIFIER)
- @Test
- public void enableWindowMagnificationWithOffset_windowlessFlagOff_expectedValues() {
- final float offsetRatio = -0.1f;
- final Rect windowBounds = new Rect(mWindowManager.getCurrentWindowMetrics().getBounds());
-
- Mockito.reset(mSpyController);
- getInstrumentation().runOnMainSync(() -> {
- mWindowMagnificationAnimationController.enableWindowMagnification(DEFAULT_SCALE,
- windowBounds.exactCenterX(), windowBounds.exactCenterY(),
- offsetRatio, offsetRatio, mAnimationCallback);
- advanceTimeBy(mWaitAnimationDuration);
- });
-
- // Wait for Rects update
- waitForIdleSync();
- final View attachedView = mWindowManager.getAttachedView();
- assertNotNull(attachedView);
- final Rect mirrorViewBound = new Rect();
- final View mirrorView = attachedView.findViewById(R.id.surface_view);
- assertNotNull(mirrorView);
- mirrorView.getBoundsOnScreen(mirrorViewBound);
-
- assertEquals((int) (offsetRatio * mirrorViewBound.width() / 2),
- (int) (mirrorViewBound.exactCenterX() - windowBounds.exactCenterX()));
- assertEquals((int) (offsetRatio * mirrorViewBound.height() / 2),
- (int) (mirrorViewBound.exactCenterY() - windowBounds.exactCenterY()));
- }
-
@Test
public void moveWindowMagnifierToPosition_enabled_expectedValues() throws RemoteException {
final float targetCenterX = DEFAULT_CENTER_X + 100;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
index a9e6a3e..2c12ef6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
@@ -30,35 +30,25 @@
import androidx.test.filters.SmallTest
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.SysuiTestCase
-import com.android.systemui.animation.ActivityTransitionAnimator
import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor
import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams
import com.android.systemui.biometrics.ui.viewmodel.DefaultUdfpsTouchOverlayViewModel
import com.android.systemui.biometrics.ui.viewmodel.DeviceEntryUdfpsTouchOverlayViewModel
-import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
-import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
-import com.android.systemui.dump.DumpManager
+import com.android.systemui.biometrics.ui.viewmodel.PromptUdfpsTouchOverlayViewModel
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.kosmos.testScope
-import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.power.data.repository.FakePowerRepository
import com.android.systemui.power.data.repository.fakePowerRepository
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.power.domain.interactor.powerInteractor
import com.android.systemui.power.shared.model.WakeSleepReason
import com.android.systemui.power.shared.model.WakefulnessState
-import com.android.systemui.shade.domain.interactor.ShadeInteractor
-import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
-import com.android.systemui.statusbar.phone.SystemUIDialogManager
-import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController
-import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.testKosmos
-import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
@@ -67,10 +57,8 @@
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers.any
import org.mockito.ArgumentMatchers.eq
-import org.mockito.Captor
import org.mockito.Mock
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
@@ -79,12 +67,6 @@
private const val REQUEST_ID = 2L
-// Dimensions for the current display resolution.
-private const val DISPLAY_WIDTH = 1080
-private const val DISPLAY_HEIGHT = 1920
-private const val SENSOR_WIDTH = 30
-private const val SENSOR_HEIGHT = 60
-
@SmallTest
@RunWith(AndroidJUnit4::class)
@RunWithLooper(setAsMainLooper = true)
@@ -96,30 +78,17 @@
@Mock private lateinit var inflater: LayoutInflater
@Mock private lateinit var windowManager: WindowManager
@Mock private lateinit var accessibilityManager: AccessibilityManager
- @Mock private lateinit var statusBarStateController: StatusBarStateController
- @Mock private lateinit var statusBarKeyguardViewManager: StatusBarKeyguardViewManager
@Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
- @Mock private lateinit var dialogManager: SystemUIDialogManager
- @Mock private lateinit var dumpManager: DumpManager
- @Mock private lateinit var configurationController: ConfigurationController
@Mock private lateinit var keyguardStateController: KeyguardStateController
- @Mock
- private lateinit var unlockedScreenOffAnimationController: UnlockedScreenOffAnimationController
@Mock private lateinit var udfpsDisplayMode: UdfpsDisplayModeProvider
@Mock private lateinit var controllerCallback: IUdfpsOverlayControllerCallback
- @Mock private lateinit var udfpsController: UdfpsController
- @Mock private lateinit var mActivityTransitionAnimator: ActivityTransitionAnimator
- @Mock private lateinit var primaryBouncerInteractor: PrimaryBouncerInteractor
- @Mock private lateinit var alternateBouncerInteractor: AlternateBouncerInteractor
- @Mock private lateinit var mSelectedUserInteractor: SelectedUserInteractor
@Mock
private lateinit var deviceEntryUdfpsTouchOverlayViewModel:
DeviceEntryUdfpsTouchOverlayViewModel
@Mock private lateinit var defaultUdfpsTouchOverlayViewModel: DefaultUdfpsTouchOverlayViewModel
+ @Mock private lateinit var promptUdfpsTouchOverlayViewModel: PromptUdfpsTouchOverlayViewModel
@Mock private lateinit var keyguardTransitionRepository: FakeKeyguardTransitionRepository
private lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor
- @Mock private lateinit var shadeInteractor: ShadeInteractor
- @Captor private lateinit var layoutParamsCaptor: ArgumentCaptor<WindowManager.LayoutParams>
@Mock private lateinit var udfpsOverlayInteractor: UdfpsOverlayInteractor
private lateinit var powerRepository: FakePowerRepository
private lateinit var powerInteractor: PowerInteractor
@@ -138,48 +107,28 @@
keyguardTransitionInteractor = kosmos.keyguardTransitionInteractor
}
- private suspend fun withReasonSuspend(
- @RequestReason reason: Int,
- isDebuggable: Boolean = false,
- block: suspend () -> Unit,
- ) {
- withReason(reason, isDebuggable)
+ private suspend fun withReasonSuspend(@RequestReason reason: Int, block: suspend () -> Unit) {
+ withReason(reason)
block()
}
- private fun withReason(
- @RequestReason reason: Int,
- isDebuggable: Boolean = false,
- block: () -> Unit = {},
- ) {
+ private fun withReason(@RequestReason reason: Int, block: () -> Unit = {}) {
controllerOverlay =
UdfpsControllerOverlay(
- context,
inflater,
windowManager,
accessibilityManager,
- statusBarStateController,
- statusBarKeyguardViewManager,
keyguardUpdateMonitor,
- dialogManager,
- dumpManager,
- configurationController,
keyguardStateController,
- unlockedScreenOffAnimationController,
udfpsDisplayMode,
REQUEST_ID,
reason,
controllerCallback,
onTouch,
- mActivityTransitionAnimator,
- primaryBouncerInteractor,
- alternateBouncerInteractor,
- isDebuggable,
keyguardTransitionInteractor,
- mSelectedUserInteractor,
{ deviceEntryUdfpsTouchOverlayViewModel },
{ defaultUdfpsTouchOverlayViewModel },
- shadeInteractor,
+ { promptUdfpsTouchOverlayViewModel },
udfpsOverlayInteractor,
powerInteractor,
testScope,
@@ -204,7 +153,7 @@
runCurrent()
// WHEN a request comes to show the view
- controllerOverlay.show(udfpsController, overlayParams)
+ controllerOverlay.show(overlayParams)
runCurrent()
// THEN the view does not get added immediately
@@ -232,7 +181,7 @@
runCurrent()
// WHEN a request comes to show the view
- controllerOverlay.show(udfpsController, overlayParams)
+ controllerOverlay.show(overlayParams)
runCurrent()
// THEN view isn't added yet
@@ -247,7 +196,7 @@
fun neverRemoveViewThatHasNotBeenAdded() =
testScope.runTest {
withReasonSuspend(REASON_AUTH_KEYGUARD) {
- controllerOverlay.show(udfpsController, overlayParams)
+ controllerOverlay.show(overlayParams)
val view = controllerOverlay.getTouchOverlay()
view?.let {
// parent is null, signalling that the view was never added
@@ -292,7 +241,7 @@
runCurrent()
// WHEN a request comes to show the view
- controllerOverlay.show(udfpsController, overlayParams)
+ controllerOverlay.show(overlayParams)
runCurrent()
// THEN the view does not get added immediately
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothDetailsContentManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothDetailsContentManagerTest.kt
index 8b08738..7c356c0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothDetailsContentManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothDetailsContentManagerTest.kt
@@ -29,33 +29,34 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.logging.UiEventLogger
-import com.android.settingslib.bluetooth.CachedBluetoothDevice
import com.android.systemui.SysuiTestCase
-import com.android.systemui.animation.DialogTransitionAnimator
import com.android.systemui.bluetooth.ui.viewModel.BluetoothDetailsContentViewModel
-import com.android.systemui.bluetooth.ui.viewModel.BluetoothTileDialogCallback
import com.android.systemui.common.domain.interactor.SysUIStateDisplaysInteractor
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
+import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.res.R
import com.android.systemui.statusbar.phone.SystemUIDialog
import com.android.systemui.statusbar.phone.SystemUIDialogManager
import com.android.systemui.testKosmos
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.launch
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.ArgumentMatchers.anyBoolean
-import org.mockito.ArgumentMatchers.anyLong
+import org.mockito.ArgumentMatchers.anyInt
import org.mockito.junit.MockitoJUnit
import org.mockito.junit.MockitoRule
import org.mockito.kotlin.any
+import org.mockito.kotlin.anyOrNull
import org.mockito.kotlin.mock
+import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
@SmallTest
@@ -71,10 +72,6 @@
@get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule()
- private val cachedBluetoothDevice = mock<CachedBluetoothDevice>()
-
- private val bluetoothTileDialogCallback = mock<BluetoothTileDialogCallback>()
-
private val drawable = mock<Drawable>()
private val uiEventLogger = mock<UiEventLogger>()
@@ -84,7 +81,7 @@
private val sysuiDialogFactory = mock<SystemUIDialog.Factory>()
private val dialogManager = mock<SystemUIDialogManager>()
private val sysuiStateInteractor = mock<SysUIStateDisplaysInteractor>()
- private val dialogTransitionAnimator = mock<DialogTransitionAnimator>()
+ private val activityStarter = mock<ActivityStarter>()
private val fakeSystemClock = FakeSystemClock()
@@ -97,6 +94,7 @@
private lateinit var icon: Pair<Drawable, String>
private lateinit var mBluetoothDetailsContentManager: BluetoothDetailsContentManager
private lateinit var deviceItem: DeviceItem
+ private lateinit var detailsUIState: BluetoothDetailsContentViewModel.DetailsUIState
private lateinit var contentView: View
private val kosmos = testKosmos()
@@ -111,13 +109,14 @@
BluetoothDetailsContentManager(
uiProperties,
CONTENT_HEIGHT,
- bluetoothTileDialogCallback,
/* isInDialog= */ true,
{},
testDispatcher,
fakeSystemClock,
uiEventLogger,
logger,
+ dialogTransitionAnimator,
+ activityStarter,
)
whenever(sysuiDialogFactory.create(any<SystemUIDialog.Delegate>(), any())).thenAnswer {
@@ -143,45 +142,73 @@
iconWithDescription = icon,
background = null,
)
+ detailsUIState =
+ BluetoothDetailsContentViewModel.DetailsUIState(
+ deviceItem = MutableStateFlow(null),
+ shouldAnimateProgressBar = MutableStateFlow(null),
+ audioSharingButton = MutableStateFlow(null),
+ bluetoothState = MutableStateFlow(null),
+ bluetoothAutoOn = MutableStateFlow(null),
+ )
whenever(cachedBluetoothDevice.isBusy).thenReturn(false)
}
}
@Test
- fun testShowDialog_createRecyclerViewWithAdapter() {
- mBluetoothDetailsContentManager.bind(contentView)
- mBluetoothDetailsContentManager.start()
+ fun testBindView_createRecyclerViewWithAdapter() {
+ with(kosmos) {
+ testScope.runTest {
+ val job = launch {
+ mBluetoothDetailsContentManager.bind(
+ contentView = contentView,
+ dialog = null,
+ coroutineScope = this,
+ detailsUIState = detailsUIState,
+ )
+ mBluetoothDetailsContentManager.start()
- val recyclerView = contentView.requireViewById<RecyclerView>(R.id.device_list)
+ val recyclerView = contentView.requireViewById<RecyclerView>(R.id.device_list)
- assertThat(recyclerView).isNotNull()
- assertThat(recyclerView.visibility).isEqualTo(VISIBLE)
- assertThat(recyclerView.adapter).isNotNull()
- assertThat(recyclerView.layoutManager is LinearLayoutManager).isTrue()
- mBluetoothDetailsContentManager.releaseView()
+ assertThat(recyclerView).isNotNull()
+ assertThat(recyclerView.visibility).isEqualTo(VISIBLE)
+ assertThat(recyclerView.adapter).isNotNull()
+ assertThat(recyclerView.layoutManager is LinearLayoutManager).isTrue()
+ mBluetoothDetailsContentManager.releaseView()
+ }
+ job.cancel()
+ }
+ }
}
@Test
- fun testShowDialog_displayBluetoothDevice() {
+ fun testBindView_displayBluetoothDevice() {
with(kosmos) {
testScope.runTest {
- mBluetoothDetailsContentManager.bind(contentView)
- mBluetoothDetailsContentManager.start()
- fakeSystemClock.setElapsedRealtime(Long.MAX_VALUE)
- mBluetoothDetailsContentManager.onDeviceItemUpdated(
- listOf(deviceItem),
- showSeeAll = false,
- showPairNewDevice = false,
- )
+ val job = launch {
+ mBluetoothDetailsContentManager.bind(
+ contentView = contentView,
+ dialog = null,
+ coroutineScope = this,
+ detailsUIState = detailsUIState,
+ )
+ mBluetoothDetailsContentManager.start()
+ fakeSystemClock.setElapsedRealtime(Long.MAX_VALUE)
+ mBluetoothDetailsContentManager.onDeviceItemUpdated(
+ listOf(deviceItem),
+ showSeeAll = false,
+ showPairNewDevice = false,
+ )
- val recyclerView = contentView.requireViewById<RecyclerView>(R.id.device_list)
- val adapter = recyclerView?.adapter as BluetoothDetailsContentManager.Adapter
- assertThat(adapter.itemCount).isEqualTo(1)
- assertThat(adapter.getItem(0).deviceName).isEqualTo(DEVICE_NAME)
- assertThat(adapter.getItem(0).connectionSummary)
- .isEqualTo(DEVICE_CONNECTION_SUMMARY)
- assertThat(adapter.getItem(0).iconWithDescription).isEqualTo(icon)
- mBluetoothDetailsContentManager.releaseView()
+ val recyclerView = contentView.requireViewById<RecyclerView>(R.id.device_list)
+ val adapter = recyclerView.adapter as BluetoothDetailsContentManager.Adapter
+ assertThat(adapter.itemCount).isEqualTo(1)
+ assertThat(adapter.getItem(0).deviceName).isEqualTo(DEVICE_NAME)
+ assertThat(adapter.getItem(0).connectionSummary)
+ .isEqualTo(DEVICE_CONNECTION_SUMMARY)
+ assertThat(adapter.getItem(0).iconWithDescription).isEqualTo(icon)
+ mBluetoothDetailsContentManager.releaseView()
+ }
+ job.cancel()
}
}
}
@@ -228,13 +255,14 @@
BluetoothDetailsContentManager(
uiProperties,
CONTENT_HEIGHT,
- bluetoothTileDialogCallback,
/* isInDialog= */ true,
{},
testDispatcher,
fakeSystemClock,
uiEventLogger,
logger,
+ dialogTransitionAnimator,
+ activityStarter,
)
.Adapter()
.DeviceItemViewHolder(view)
@@ -281,108 +309,148 @@
fun testOnDeviceUpdated_hideSeeAll_showPairNew() {
with(kosmos) {
testScope.runTest {
- mBluetoothDetailsContentManager.bind(contentView)
- mBluetoothDetailsContentManager.start()
- fakeSystemClock.setElapsedRealtime(Long.MAX_VALUE)
- mBluetoothDetailsContentManager.onDeviceItemUpdated(
- listOf(deviceItem),
- showSeeAll = false,
- showPairNewDevice = true,
- )
+ val job = launch {
+ mBluetoothDetailsContentManager.bind(
+ contentView = contentView,
+ dialog = null,
+ coroutineScope = this,
+ detailsUIState = detailsUIState,
+ )
+ mBluetoothDetailsContentManager.start()
+ fakeSystemClock.setElapsedRealtime(Long.MAX_VALUE)
+ mBluetoothDetailsContentManager.onDeviceItemUpdated(
+ listOf(deviceItem),
+ showSeeAll = false,
+ showPairNewDevice = true,
+ )
- val seeAllButton = contentView.requireViewById<View>(R.id.see_all_button)
- val pairNewButton = contentView.requireViewById<View>(R.id.pair_new_device_button)
- val recyclerView = contentView.requireViewById<RecyclerView>(R.id.device_list)
- val adapter = recyclerView?.adapter as BluetoothDetailsContentManager.Adapter
- val scrollViewContent = contentView.requireViewById<View>(R.id.scroll_view)
+ val seeAllButton = contentView.requireViewById<View>(R.id.see_all_button)
+ val pairNewButton =
+ contentView.requireViewById<View>(R.id.pair_new_device_button)
+ val recyclerView = contentView.requireViewById<RecyclerView>(R.id.device_list)
+ val adapter = recyclerView.adapter as BluetoothDetailsContentManager.Adapter
+ val scrollViewContent = contentView.requireViewById<View>(R.id.scroll_view)
- assertThat(seeAllButton).isNotNull()
- assertThat(seeAllButton.visibility).isEqualTo(GONE)
- assertThat(pairNewButton).isNotNull()
- assertThat(pairNewButton.visibility).isEqualTo(VISIBLE)
- assertThat(adapter.itemCount).isEqualTo(1)
- assertThat(scrollViewContent.layoutParams.height).isEqualTo(WRAP_CONTENT)
- mBluetoothDetailsContentManager.releaseView()
+ assertThat(seeAllButton).isNotNull()
+ assertThat(seeAllButton.visibility).isEqualTo(GONE)
+ assertThat(pairNewButton).isNotNull()
+ assertThat(pairNewButton.visibility).isEqualTo(VISIBLE)
+ assertThat(adapter.itemCount).isEqualTo(1)
+ assertThat(scrollViewContent.layoutParams.height).isEqualTo(WRAP_CONTENT)
+ mBluetoothDetailsContentManager.releaseView()
+ }
+ job.cancel()
}
}
}
@Test
- fun testShowDialog_cachedHeightLargerThanMinHeight_displayFromCachedHeight() {
+ fun testBindView_cachedHeightLargerThanMinHeight_displayFromCachedHeight() {
with(kosmos) {
testScope.runTest {
- val cachedHeight = Int.MAX_VALUE
- val contentManager =
- BluetoothDetailsContentManager(
- BluetoothDetailsContentViewModel.UiProperties.build(ENABLED, ENABLED),
- cachedHeight,
- bluetoothTileDialogCallback,
- /* isInDialog= */ true,
- {},
- testDispatcher,
- fakeSystemClock,
- uiEventLogger,
- logger,
+ val job = launch {
+ val cachedHeight = Int.MAX_VALUE
+ val contentManager =
+ BluetoothDetailsContentManager(
+ BluetoothDetailsContentViewModel.UiProperties.build(ENABLED, ENABLED),
+ cachedHeight,
+ /* isInDialog= */ true,
+ {},
+ testDispatcher,
+ fakeSystemClock,
+ uiEventLogger,
+ logger,
+ dialogTransitionAnimator,
+ activityStarter,
+ )
+ contentManager.bind(
+ contentView = contentView,
+ dialog = null,
+ coroutineScope = this,
+ detailsUIState = detailsUIState,
)
- contentManager.bind(contentView)
- contentManager.start()
- assertThat(contentView.requireViewById<View>(R.id.scroll_view).layoutParams.height)
- .isEqualTo(cachedHeight)
- contentManager.releaseView()
+ contentManager.start()
+ assertThat(
+ contentView.requireViewById<View>(R.id.scroll_view).layoutParams.height
+ )
+ .isEqualTo(cachedHeight)
+ contentManager.releaseView()
+ }
+ job.cancel()
}
}
}
@Test
- fun testShowDialog_cachedHeightLessThanMinHeight_displayFromUiProperties() {
+ fun testBindView_cachedHeightLessThanMinHeight_displayFromUiProperties() {
with(kosmos) {
testScope.runTest {
- val contentManager =
- BluetoothDetailsContentManager(
- BluetoothDetailsContentViewModel.UiProperties.build(ENABLED, ENABLED),
- MATCH_PARENT,
- bluetoothTileDialogCallback,
- /* isInDialog= */ true,
- {},
- testDispatcher,
- fakeSystemClock,
- uiEventLogger,
- logger,
+ val job = launch {
+ val contentManager =
+ BluetoothDetailsContentManager(
+ BluetoothDetailsContentViewModel.UiProperties.build(ENABLED, ENABLED),
+ MATCH_PARENT,
+ /* isInDialog= */ true,
+ {},
+ testDispatcher,
+ fakeSystemClock,
+ uiEventLogger,
+ logger,
+ dialogTransitionAnimator,
+ activityStarter,
+ )
+ contentManager.bind(
+ contentView = contentView,
+ dialog = null,
+ coroutineScope = this,
+ detailsUIState = detailsUIState,
)
- contentManager.bind(contentView)
- contentManager.start()
- assertThat(contentView.requireViewById<View>(R.id.scroll_view).layoutParams.height)
- .isGreaterThan(MATCH_PARENT)
- contentManager.releaseView()
+ contentManager.start()
+ assertThat(
+ contentView.requireViewById<View>(R.id.scroll_view).layoutParams.height
+ )
+ .isGreaterThan(MATCH_PARENT)
+ contentManager.releaseView()
+ }
+ job.cancel()
}
}
}
@Test
- fun testShowDialog_bluetoothEnabled_autoOnToggleGone() {
+ fun testBindView_bluetoothEnabled_autoOnToggleGone() {
with(kosmos) {
testScope.runTest {
- val contentManager =
- BluetoothDetailsContentManager(
- BluetoothDetailsContentViewModel.UiProperties.build(ENABLED, ENABLED),
- MATCH_PARENT,
- bluetoothTileDialogCallback,
- /* isInDialog= */ true,
- {},
- testDispatcher,
- fakeSystemClock,
- uiEventLogger,
- logger,
+ val job = launch {
+ val contentManager =
+ BluetoothDetailsContentManager(
+ BluetoothDetailsContentViewModel.UiProperties.build(ENABLED, ENABLED),
+ MATCH_PARENT,
+ /* isInDialog= */ true,
+ {},
+ testDispatcher,
+ fakeSystemClock,
+ uiEventLogger,
+ logger,
+ dialogTransitionAnimator,
+ activityStarter,
+ )
+ contentManager.bind(
+ contentView = contentView,
+ dialog = null,
+ coroutineScope = this,
+ detailsUIState = detailsUIState,
)
- contentManager.bind(contentView)
- contentManager.start()
- assertThat(
- contentView
- .requireViewById<View>(R.id.bluetooth_auto_on_toggle_layout)
- .visibility
- )
- .isEqualTo(GONE)
- contentManager.releaseView()
+ contentManager.start()
+ assertThat(
+ contentView
+ .requireViewById<View>(R.id.bluetooth_auto_on_toggle_layout)
+ .visibility
+ )
+ .isEqualTo(GONE)
+ contentManager.releaseView()
+ }
+ job.cancel()
}
}
}
@@ -391,22 +459,30 @@
fun testOnAudioSharingButtonUpdated_visibleActive_activateButton() {
with(kosmos) {
testScope.runTest {
- mBluetoothDetailsContentManager.bind(contentView)
- mBluetoothDetailsContentManager.start()
- fakeSystemClock.setElapsedRealtime(Long.MAX_VALUE)
- mBluetoothDetailsContentManager.onAudioSharingButtonUpdated(
- visibility = VISIBLE,
- label = null,
- isActive = true,
- )
+ val job = launch {
+ mBluetoothDetailsContentManager.bind(
+ contentView = contentView,
+ dialog = null,
+ coroutineScope = this,
+ detailsUIState = detailsUIState,
+ )
+ mBluetoothDetailsContentManager.start()
+ fakeSystemClock.setElapsedRealtime(Long.MAX_VALUE)
+ mBluetoothDetailsContentManager.onAudioSharingButtonUpdated(
+ visibility = VISIBLE,
+ label = null,
+ isActive = true,
+ )
- val audioSharingButton =
- contentView.requireViewById<View>(R.id.audio_sharing_button)
+ val audioSharingButton =
+ contentView.requireViewById<View>(R.id.audio_sharing_button)
- assertThat(audioSharingButton).isNotNull()
- assertThat(audioSharingButton.visibility).isEqualTo(VISIBLE)
- assertThat(audioSharingButton.isActivated).isTrue()
- mBluetoothDetailsContentManager.releaseView()
+ assertThat(audioSharingButton).isNotNull()
+ assertThat(audioSharingButton.visibility).isEqualTo(VISIBLE)
+ assertThat(audioSharingButton.isActivated).isTrue()
+ mBluetoothDetailsContentManager.releaseView()
+ }
+ job.cancel()
}
}
}
@@ -415,22 +491,30 @@
fun testOnAudioSharingButtonUpdated_visibleNotActive_inactivateButton() {
with(kosmos) {
testScope.runTest {
- mBluetoothDetailsContentManager.bind(contentView)
- mBluetoothDetailsContentManager.start()
- fakeSystemClock.setElapsedRealtime(Long.MAX_VALUE)
- mBluetoothDetailsContentManager.onAudioSharingButtonUpdated(
- visibility = VISIBLE,
- label = null,
- isActive = false,
- )
+ val job = launch {
+ mBluetoothDetailsContentManager.bind(
+ contentView = contentView,
+ dialog = null,
+ coroutineScope = this,
+ detailsUIState = detailsUIState,
+ )
+ mBluetoothDetailsContentManager.start()
+ fakeSystemClock.setElapsedRealtime(Long.MAX_VALUE)
+ mBluetoothDetailsContentManager.onAudioSharingButtonUpdated(
+ visibility = VISIBLE,
+ label = null,
+ isActive = false,
+ )
- val audioSharingButton =
- contentView.requireViewById<View>(R.id.audio_sharing_button)
+ val audioSharingButton =
+ contentView.requireViewById<View>(R.id.audio_sharing_button)
- assertThat(audioSharingButton).isNotNull()
- assertThat(audioSharingButton.visibility).isEqualTo(VISIBLE)
- assertThat(audioSharingButton.isActivated).isFalse()
- mBluetoothDetailsContentManager.releaseView()
+ assertThat(audioSharingButton).isNotNull()
+ assertThat(audioSharingButton.visibility).isEqualTo(VISIBLE)
+ assertThat(audioSharingButton.isActivated).isFalse()
+ mBluetoothDetailsContentManager.releaseView()
+ }
+ job.cancel()
}
}
}
@@ -439,22 +523,55 @@
fun testOnAudioSharingButtonUpdated_gone_inactivateButton() {
with(kosmos) {
testScope.runTest {
- mBluetoothDetailsContentManager.bind(contentView)
- mBluetoothDetailsContentManager.start()
- fakeSystemClock.setElapsedRealtime(Long.MAX_VALUE)
- mBluetoothDetailsContentManager.onAudioSharingButtonUpdated(
- visibility = GONE,
- label = null,
- isActive = false,
- )
+ val job = launch {
+ mBluetoothDetailsContentManager.bind(
+ contentView = contentView,
+ dialog = null,
+ coroutineScope = this,
+ detailsUIState = detailsUIState,
+ )
+ mBluetoothDetailsContentManager.start()
+ fakeSystemClock.setElapsedRealtime(Long.MAX_VALUE)
+ mBluetoothDetailsContentManager.onAudioSharingButtonUpdated(
+ visibility = GONE,
+ label = null,
+ isActive = false,
+ )
- val audioSharingButton =
- contentView.requireViewById<View>(R.id.audio_sharing_button)
+ val audioSharingButton =
+ contentView.requireViewById<View>(R.id.audio_sharing_button)
- assertThat(audioSharingButton).isNotNull()
- assertThat(audioSharingButton.visibility).isEqualTo(GONE)
- assertThat(audioSharingButton.isActivated).isFalse()
- mBluetoothDetailsContentManager.releaseView()
+ assertThat(audioSharingButton).isNotNull()
+ assertThat(audioSharingButton.visibility).isEqualTo(GONE)
+ assertThat(audioSharingButton.isActivated).isFalse()
+ mBluetoothDetailsContentManager.releaseView()
+ }
+ job.cancel()
+ }
+ }
+ }
+
+ @Test
+ fun testStartSettingsActivity_activityLaunched_detailsViewDismissed() {
+ with(kosmos) {
+ testScope.runTest {
+ val job = launch {
+ mBluetoothDetailsContentManager.bind(
+ contentView = contentView,
+ dialog = null,
+ coroutineScope = this,
+ detailsUIState = detailsUIState,
+ )
+ mBluetoothDetailsContentManager.start()
+
+ val clickedView = View(context)
+ mBluetoothDetailsContentManager.onPairNewDeviceClicked(clickedView)
+
+ verify(uiEventLogger).log(BluetoothTileDialogUiEvent.PAIR_NEW_DEVICE_CLICKED)
+ verify(activityStarter)
+ .postStartActivityDismissingKeyguard(any(), anyInt(), anyOrNull())
+ }
+ job.cancel()
}
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothDetailsContentViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothDetailsContentViewModelTest.kt
index c9ca4b5..d4de29d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothDetailsContentViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothDetailsContentViewModelTest.kt
@@ -24,8 +24,6 @@
import android.view.View.VISIBLE
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.internal.logging.UiEventLogger
-import com.android.settingslib.bluetooth.CachedBluetoothDevice
import com.android.settingslib.bluetooth.LocalBluetoothManager
import com.android.settingslib.flags.Flags
import com.android.systemui.Flags.FLAG_QS_TILE_DETAILED_VIEW
@@ -36,15 +34,12 @@
import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
-import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.statusbar.phone.SystemUIDialog
import com.android.systemui.testKosmos
import com.android.systemui.util.FakeSharedPreferences
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.kotlin.getMutableStateFlow
import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.nullable
-import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.time.FakeSystemClock
import com.android.systemui.volume.domain.interactor.audioModeInteractor
import com.google.common.truth.Truth.assertThat
@@ -59,13 +54,15 @@
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyBoolean
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Mock
-import org.mockito.Mockito.anyBoolean
-import org.mockito.Mockito.never
-import org.mockito.Mockito.verify
import org.mockito.junit.MockitoJUnit
import org.mockito.junit.MockitoRule
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.never
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
@SmallTest
@RunWith(AndroidJUnit4::class)
@@ -86,16 +83,8 @@
@Mock private lateinit var deviceItemActionInteractor: DeviceItemActionInteractor
- @Mock private lateinit var activityStarter: ActivityStarter
-
@Mock private lateinit var mDialogTransitionAnimator: DialogTransitionAnimator
- @Mock private lateinit var cachedBluetoothDevice: CachedBluetoothDevice
-
- @Mock private lateinit var deviceItem: DeviceItem
-
- @Mock private lateinit var uiEventLogger: UiEventLogger
-
@Mock private lateinit var bluetoothAdapter: BluetoothAdapter
@Mock private lateinit var localBluetoothManager: LocalBluetoothManager
@@ -103,8 +92,7 @@
@Mock private lateinit var bluetoothTileDialogLogger: BluetoothTileDialogLogger
@Mock
- private lateinit var mBluetoothTileDialogDelegateDelegateFactory:
- BluetoothTileDialogDelegate.Factory
+ private lateinit var mBluetoothTileDialogDelegateFactory: BluetoothTileDialogDelegate.Factory
@Mock private lateinit var bluetoothTileDialogDelegate: BluetoothTileDialogDelegate
@@ -154,14 +142,12 @@
kosmos.audioSharingButtonViewModelFactory,
bluetoothDeviceMetadataInteractor,
mDialogTransitionAnimator,
- activityStarter,
- uiEventLogger,
bluetoothTileDialogLogger,
testScope.backgroundScope,
dispatcher,
dispatcher,
sharedPreferences,
- mBluetoothTileDialogDelegateDelegateFactory,
+ mBluetoothTileDialogDelegateFactory,
bluetoothDetailsContentManagerFactory,
)
whenever(deviceItemInteractor.deviceItemUpdate).thenReturn(MutableSharedFlow())
@@ -169,22 +155,15 @@
.thenReturn(MutableStateFlow(Unit).asStateFlow())
whenever(deviceItemInteractor.showSeeAllUpdate).thenReturn(getMutableStateFlow(false))
whenever(bluetoothDeviceMetadataInteractor.metadataUpdate).thenReturn(MutableSharedFlow())
- whenever(mBluetoothTileDialogDelegateDelegateFactory.create(any(), anyInt(), any(), any()))
+ whenever(mBluetoothTileDialogDelegateFactory.create(any(), anyInt(), any()))
.thenReturn(bluetoothTileDialogDelegate)
whenever(bluetoothTileDialogDelegate.createDialog()).thenReturn(sysuiDialog)
whenever(bluetoothTileDialogDelegate.contentManager)
.thenReturn(bluetoothDetailsContentManager)
- whenever(
- bluetoothDetailsContentManagerFactory.create(
- any(),
- anyInt(),
- any(),
- anyBoolean(),
- any(),
- )
- )
+ whenever(bluetoothDetailsContentManagerFactory.create(any(), anyInt(), anyBoolean(), any()))
.thenReturn(bluetoothDetailsContentManager)
whenever(sysuiDialog.context).thenReturn(mContext)
+ whenever<Any?>(sysuiDialog.requireViewById(anyInt())).thenReturn(mockView)
whenever(bluetoothDetailsContentManager.bluetoothStateToggle)
.thenReturn(getMutableStateFlow(false))
whenever(bluetoothDetailsContentManager.deviceItemClick)
@@ -224,7 +203,7 @@
bluetoothDetailsContentViewModel.bindDetailsView(mockView)
runCurrent()
- verify(bluetoothDetailsContentManager).bind(mockView)
+ verify(bluetoothDetailsContentManager).bind(eq(mockView), eq(null), any(), any())
verify(bluetoothDetailsContentManager).start()
}
}
@@ -250,7 +229,7 @@
bluetoothDetailsContentViewModel.bindDetailsView(mockView)
runCurrent()
- verify(bluetoothDetailsContentManager).bind(mockView)
+ verify(bluetoothDetailsContentManager).bind(eq(mockView), eq(null), any(), any())
verify(bluetoothDetailsContentManager).start()
}
}
@@ -279,38 +258,6 @@
}
@Test
- fun testStartSettingsActivity_activityLaunched_dialogDismissed() {
- testScope.runTest {
- whenever(deviceItem.cachedBluetoothDevice).thenReturn(cachedBluetoothDevice)
- bluetoothDetailsContentViewModel.showDialog(null)
- runCurrent()
-
- val clickedView = View(context)
- bluetoothDetailsContentViewModel.onPairNewDeviceClicked(clickedView)
-
- verify(uiEventLogger).log(BluetoothTileDialogUiEvent.PAIR_NEW_DEVICE_CLICKED)
- verify(activityStarter).postStartActivityDismissingKeyguard(any(), anyInt(), nullable())
- }
- }
-
- @Test
- @EnableSceneContainer
- @EnableFlags(FLAG_QS_TILE_DETAILED_VIEW)
- fun testStartSettingsActivity_activityLaunched_detailsViewDismissed() {
- testScope.runTest {
- whenever(deviceItem.cachedBluetoothDevice).thenReturn(cachedBluetoothDevice)
- bluetoothDetailsContentViewModel.bindDetailsView(mockView)
- runCurrent()
-
- val clickedView = View(context)
- bluetoothDetailsContentViewModel.onPairNewDeviceClicked(clickedView)
-
- verify(uiEventLogger).log(BluetoothTileDialogUiEvent.PAIR_NEW_DEVICE_CLICKED)
- verify(activityStarter).postStartActivityDismissingKeyguard(any(), anyInt(), nullable())
- }
- }
-
- @Test
fun testBuildUiProperties_bluetoothOn_shouldHideAutoOn() {
testScope.runTest {
val actual =
@@ -365,4 +312,18 @@
assertThat(actual).isFalse()
}
}
+
+ @Test
+ fun testUpdateTitleAndSubtitle() {
+ testScope.runTest {
+ assertThat(bluetoothDetailsContentViewModel.title).isEqualTo("")
+ assertThat(bluetoothDetailsContentViewModel.subTitle).isEqualTo("")
+
+ bluetoothDetailsContentViewModel.showDialog(expandable)
+ runCurrent()
+
+ assertThat(bluetoothDetailsContentViewModel.title).isEqualTo("Bluetooth")
+ assertThat(bluetoothDetailsContentViewModel.subTitle).isEqualTo("Bluetooth is off")
+ }
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegateTest.kt
index a399b35..9cf6880 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegateTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegateTest.kt
@@ -26,7 +26,6 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.DialogTransitionAnimator
import com.android.systemui.bluetooth.ui.viewModel.BluetoothDetailsContentViewModel
-import com.android.systemui.bluetooth.ui.viewModel.BluetoothTileDialogCallback
import com.android.systemui.common.domain.interactor.SysUIStateDisplaysInteractor
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
@@ -58,7 +57,6 @@
class BluetoothTileDialogDelegateTest : SysuiTestCase() {
companion object {
const val DEVICE_NAME = "device"
- const val DEVICE_CONNECTION_SUMMARY = "active"
const val ENABLED = true
const val CONTENT_HEIGHT = WRAP_CONTENT
}
@@ -71,8 +69,6 @@
@Mock private lateinit var bluetoothDetailsContentManager: BluetoothDetailsContentManager
- @Mock private lateinit var bluetoothTileDialogCallback: BluetoothTileDialogCallback
-
@Mock private lateinit var uiEventLogger: UiEventLogger
@Mock private lateinit var sysuiDialogFactory: SystemUIDialog.Factory
@@ -102,7 +98,6 @@
BluetoothTileDialogDelegate(
uiProperties,
CONTENT_HEIGHT,
- bluetoothTileDialogCallback,
{},
uiEventLogger,
sysuiDialogFactory,
@@ -125,15 +120,7 @@
)
}
- whenever(
- bluetoothDetailsContentManagerFactory.create(
- any(),
- anyInt(),
- any(),
- anyBoolean(),
- any(),
- )
- )
+ whenever(bluetoothDetailsContentManagerFactory.create(any(), anyInt(), anyBoolean(), any()))
.thenReturn(bluetoothDetailsContentManager)
}
@@ -142,7 +129,6 @@
val dialog = mBluetoothTileDialogDelegate.createDialog()
dialog.show()
- verify(bluetoothDetailsContentManager).bind(any())
verify(bluetoothDetailsContentManager).start()
dialog.dismiss()
verify(bluetoothDetailsContentManager).releaseView()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
index 71989ee..b4c69529 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
@@ -48,8 +48,6 @@
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.biometrics.AuthController;
-import com.android.systemui.biometrics.domain.interactor.BiometricStatusInteractor;
-import com.android.systemui.biometrics.domain.interactor.FingerprintPropertyInteractor;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dock.DockManager;
import com.android.systemui.doze.DozeTriggers.DozingUpdateUiEvent;
@@ -72,8 +70,6 @@
import com.android.systemui.util.wakelock.WakeLock;
import com.android.systemui.util.wakelock.WakeLockFake;
-import kotlinx.coroutines.CoroutineScope;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -113,12 +109,6 @@
@Mock
private SelectedUserInteractor mSelectedUserInteractor;
@Mock
- private BiometricStatusInteractor mBiometricStatusInteractor;
- @Mock
- private FingerprintPropertyInteractor mFingerprintPropertyInteractor;
- @Mock
- private CoroutineScope mScope;
- @Mock
private SessionTracker mSessionTracker;
@Captor
private ArgumentCaptor<DozeHost.Callback> mHostCallbackCaptor;
@@ -154,8 +144,7 @@
asyncSensorManager, wakeLock, mDockManager, mProximitySensor,
mProximityCheck, mDozeLog, mBroadcastDispatcher, new FakeSettings(),
mAuthController, mUiEventLogger, mSessionTracker, mKeyguardStateController,
- mDevicePostureController, mUserTracker, mSelectedUserInteractor,
- mBiometricStatusInteractor, mFingerprintPropertyInteractor, mScope);
+ mDevicePostureController, mUserTracker, mSelectedUserInteractor);
mTriggers.setDozeMachine(mMachine);
waitForSensorManager();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/CommunalTouchHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/CommunalTouchHandlerTest.java
index 55c864b..23a201c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/CommunalTouchHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/CommunalTouchHandlerTest.java
@@ -35,6 +35,7 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.ambient.touch.TouchHandler;
+import com.android.systemui.flags.DisableSceneContainer;
import com.android.systemui.kosmos.KosmosJavaAdapter;
import com.android.systemui.shared.system.InputChannelCompat;
import com.android.systemui.statusbar.phone.CentralSurfaces;
@@ -50,6 +51,7 @@
import java.util.Optional;
@SmallTest
+@DisableSceneContainer
@RunWith(AndroidJUnit4.class)
public class CommunalTouchHandlerTest extends SysuiTestCase {
private final KosmosJavaAdapter mKosmos = new KosmosJavaAdapter(this);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsImplTest.kt
new file mode 100644
index 0000000..eb05e00
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsImplTest.kt
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.globalactions
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.shade.ShadeController
+import com.android.systemui.statusbar.commandQueue
+import com.android.systemui.statusbar.policy.deviceProvisionedController
+import com.android.systemui.statusbar.policy.keyguardStateController
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.verify
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class GlobalActionsImplTest : SysuiTestCase() {
+ private val shadeController = mock<ShadeController>()
+ private lateinit var underTest: GlobalActionsImpl
+
+ @Before
+ fun setUp() {
+ val kosmos = Kosmos()
+ underTest =
+ GlobalActionsImpl(
+ context,
+ kosmos.commandQueue,
+ kosmos.globalActionsDialogLite,
+ kosmos.keyguardStateController,
+ kosmos.deviceProvisionedController,
+ shadeController,
+ mock<ShutdownUi>(),
+ )
+ }
+
+ @Test
+ fun testShutdown_collapsesShade() {
+ underTest.showShutdownUi(false, "test")
+
+ verify(shadeController).collapseShade()
+ }
+
+ @Test
+ fun testReboot_collapsesShade() {
+ underTest.showShutdownUi(true, "test")
+
+ verify(shadeController).collapseShade()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
index 6e30eae..39f7755 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
@@ -33,6 +33,7 @@
import com.android.keyguard.logging.KeyguardQuickAffordancesLogger
import com.android.systemui.SystemUIAppComponentFactoryBase
import com.android.systemui.SysuiTestCase
+import com.android.systemui.accessibility.domain.interactor.accessibilityInteractor
import com.android.systemui.animation.DialogTransitionAnimator
import com.android.systemui.dock.DockManagerFake
import com.android.systemui.flags.FakeFeatureFlags
@@ -205,7 +206,7 @@
biometricSettingsRepository = biometricSettingsRepository,
backgroundDispatcher = testDispatcher,
appContext = mContext,
- accessibilityManager = mock(),
+ accessibilityInteractor = kosmos.accessibilityInteractor,
sceneInteractor = { kosmos.sceneInteractor },
msdlPlayer = kosmos.msdlPlayer,
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
index c31c43c..e081e59 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -26,7 +26,6 @@
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN;
import static com.android.systemui.Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR;
-import static com.android.systemui.Flags.FLAG_RELOCK_WITH_POWER_BUTTON_IMMEDIATELY;
import static com.android.systemui.Flags.FLAG_SIM_PIN_BOUNCER_RESET;
import static com.android.systemui.keyguard.KeyguardViewMediator.DELAYED_KEYGUARD_ACTION;
import static com.android.systemui.keyguard.KeyguardViewMediator.KEYGUARD_LOCK_AFTER_DELAY_DEFAULT;
@@ -64,7 +63,6 @@
import android.os.PowerManager;
import android.os.PowerManager.WakeLock;
import android.os.RemoteException;
-import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.telephony.TelephonyManager;
import android.testing.AndroidTestingRunner;
@@ -77,7 +75,6 @@
import androidx.test.filters.SmallTest;
-import com.android.internal.foldables.FoldGracePeriodProvider;
import com.android.internal.logging.InstanceId;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.statusbar.IStatusBarService;
@@ -99,6 +96,7 @@
import com.android.systemui.dreams.DreamOverlayStateController;
import com.android.systemui.dreams.ui.viewmodel.DreamViewModel;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.flags.DisableSceneContainer;
import com.android.systemui.flags.FakeFeatureFlags;
import com.android.systemui.flags.SystemPropertiesHelper;
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionBootInteractor;
@@ -155,6 +153,7 @@
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@SmallTest
+@DisableSceneContainer // Class is deprecated in flexi.
public class KeyguardViewMediatorTest extends SysuiTestCase {
private static final boolean ENABLE_NEW_KEYGUARD_SHELL_TRANSITIONS =
@@ -500,11 +499,7 @@
@Test
@TestableLooper.RunWithLooper(setAsMainLooper = true)
public void showKeyguardAfterKeyguardNotEnabled() {
- // GIVEN feature is enabled
- final FoldGracePeriodProvider mockedFoldGracePeriodProvider =
- mock(FoldGracePeriodProvider.class);
- mViewMediator.mFoldGracePeriodProvider = mockedFoldGracePeriodProvider;
- when(mockedFoldGracePeriodProvider.isEnabled()).thenReturn(true);
+ // GIVEN device provisioned
when(mUpdateMonitor.isDeviceProvisioned()).thenReturn(true);
// GIVEN keyguard is not enabled and isn't showing
@@ -525,12 +520,6 @@
@Test
@TestableLooper.RunWithLooper(setAsMainLooper = true)
public void doNotShowKeyguard_deviceNotProvisioned() {
- // GIVEN feature is enabled
- final FoldGracePeriodProvider mockedFoldGracePeriodProvider =
- mock(FoldGracePeriodProvider.class);
- mViewMediator.mFoldGracePeriodProvider = mockedFoldGracePeriodProvider;
- when(mockedFoldGracePeriodProvider.isEnabled()).thenReturn(true);
-
// GIVEN keyguard is not enabled and isn't showing
mViewMediator.onSystemReady();
mViewMediator.setKeyguardEnabled(false);
@@ -551,31 +540,6 @@
@Test
@TestableLooper.RunWithLooper(setAsMainLooper = true)
- public void showKeyguardAfterKeyguardNotEnabled_featureNotEnabled() {
- // GIVEN feature is NOT enabled
- final FoldGracePeriodProvider mockedFoldGracePeriodProvider =
- mock(FoldGracePeriodProvider.class);
- mViewMediator.mFoldGracePeriodProvider = mockedFoldGracePeriodProvider;
- when(mockedFoldGracePeriodProvider.isEnabled()).thenReturn(false);
- when(mUpdateMonitor.isDeviceProvisioned()).thenReturn(true);
-
- // GIVEN keyguard is not enabled and isn't showing
- mViewMediator.onSystemReady();
- mViewMediator.setKeyguardEnabled(false);
- TestableLooper.get(this).processAllMessages();
- captureKeyguardUpdateMonitorCallback();
- assertFalse(mViewMediator.isShowingAndNotOccluded());
-
- // WHEN showKeyguard is requested
- mViewMediator.showDismissibleKeyguard();
-
- // THEN keyguard is still NOT shown
- TestableLooper.get(this).processAllMessages();
- assertFalse(mViewMediator.isShowingAndNotOccluded());
- }
-
- @Test
- @TestableLooper.RunWithLooper(setAsMainLooper = true)
public void doNotHideKeyguard_whenLockdown_onKeyguardNotEnabledExternally() {
// GIVEN keyguard is enabled and lockdown occurred so the keyguard is showing
mViewMediator.onSystemReady();
@@ -984,8 +948,7 @@
@Test
@TestableLooper.RunWithLooper(setAsMainLooper = true)
- @EnableFlags(FLAG_RELOCK_WITH_POWER_BUTTON_IMMEDIATELY)
- public void testCancelKeyguardExitAnimationDueToSleep_withPendingLockAndRelockFlag_keyguardWillBeShowing() {
+ public void testCancelKeyguardExitAnimationDueToSleep_withPendingLock_keyguardWillBeShowing() {
startMockKeyguardExitAnimation();
mViewMediator.onStartedGoingToSleep(PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON);
@@ -1009,24 +972,6 @@
@Test
@TestableLooper.RunWithLooper(setAsMainLooper = true)
- @DisableFlags(FLAG_RELOCK_WITH_POWER_BUTTON_IMMEDIATELY)
- public void testCancelKeyguardExitAnimationDueToSleep_withPendingLock_keyguardWillBeShowing() {
- startMockKeyguardExitAnimation();
-
- mViewMediator.onStartedGoingToSleep(PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON);
- mViewMediator.onFinishedGoingToSleep(PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON, false);
-
- cancelMockKeyguardExitAnimation();
-
- mViewMediator.maybeHandlePendingLock();
- TestableLooper.get(this).processAllMessages();
-
- assertTrue(mViewMediator.isShowingAndNotOccluded());
- verify(mKeyguardUnlockAnimationController).notifyFinishedKeyguardExitAnimation(true);
- }
-
- @Test
- @TestableLooper.RunWithLooper(setAsMainLooper = true)
public void testCancelKeyguardExitAnimationThenSleep_withPendingLock_keyguardWillBeShowing() {
startMockKeyguardExitAnimation();
cancelMockKeyguardExitAnimation();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTestKt.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTestKt.kt
index ddc0e82..46e94b6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTestKt.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTestKt.kt
@@ -49,6 +49,7 @@
import com.android.systemui.dreams.DreamOverlayStateController
import com.android.systemui.dreams.ui.viewmodel.dreamViewModel
import com.android.systemui.dump.dumpManager
+import com.android.systemui.flags.DisableSceneContainer
import com.android.systemui.flags.featureFlagsClassic
import com.android.systemui.flags.systemPropertiesHelper
import com.android.systemui.jank.interactionJankMonitor
@@ -96,6 +97,7 @@
@SmallTest
@RunWithLooper(setAsMainLooper = true)
@RunWith(AndroidTestingRunner::class)
+@DisableSceneContainer // Class is deprecated in flexi.
class KeyguardViewMediatorTestKt : SysuiTestCase() {
private val kosmos =
testKosmos().useUnconfinedTestDispatcher().also {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
index 037c73b..217ce95 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
@@ -24,6 +24,7 @@
import com.android.internal.widget.LockPatternUtils
import com.android.keyguard.logging.KeyguardQuickAffordancesLogger
import com.android.systemui.SysuiTestCase
+import com.android.systemui.accessibility.domain.interactor.accessibilityInteractor
import com.android.systemui.animation.ActivityTransitionAnimator
import com.android.systemui.animation.DialogTransitionAnimator
import com.android.systemui.animation.Expandable
@@ -317,7 +318,7 @@
biometricSettingsRepository = biometricSettingsRepository,
backgroundDispatcher = testDispatcher,
appContext = mContext,
- accessibilityManager = mock(),
+ accessibilityInteractor = kosmos.accessibilityInteractor,
sceneInteractor = { kosmos.sceneInteractor },
msdlPlayer = kosmos.msdlPlayer,
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorSceneContainerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorSceneContainerTest.kt
index 51b3c1c..becbe9d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorSceneContainerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorSceneContainerTest.kt
@@ -25,6 +25,7 @@
import com.android.internal.widget.LockPatternUtils
import com.android.keyguard.logging.KeyguardQuickAffordancesLogger
import com.android.systemui.SysuiTestCase
+import com.android.systemui.accessibility.domain.interactor.accessibilityInteractor
import com.android.systemui.animation.ActivityTransitionAnimator
import com.android.systemui.animation.DialogTransitionAnimator
import com.android.systemui.animation.Expandable
@@ -317,7 +318,7 @@
biometricSettingsRepository = biometricSettingsRepository,
backgroundDispatcher = testDispatcher,
appContext = mContext,
- accessibilityManager = mock(),
+ accessibilityInteractor = kosmos.accessibilityInteractor,
sceneInteractor = { kosmos.sceneInteractor },
msdlPlayer = kosmos.msdlPlayer,
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt
index 8c01baf..1f2b260 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt
@@ -26,6 +26,7 @@
import com.android.internal.widget.LockPatternUtils
import com.android.keyguard.logging.KeyguardQuickAffordancesLogger
import com.android.systemui.SysuiTestCase
+import com.android.systemui.accessibility.domain.interactor.accessibilityInteractor
import com.android.systemui.animation.DialogTransitionAnimator
import com.android.systemui.animation.Expandable
import com.android.systemui.common.shared.model.Icon
@@ -291,7 +292,7 @@
biometricSettingsRepository = biometricSettingsRepository,
backgroundDispatcher = kosmos.testDispatcher,
appContext = mContext,
- accessibilityManager = mock(),
+ accessibilityInteractor = kosmos.accessibilityInteractor,
sceneInteractor = { kosmos.sceneInteractor },
msdlPlayer = kosmos.msdlPlayer,
),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/lowlightclock/AmbientLightModeMonitorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/lowlightclock/AmbientLightModeMonitorTest.kt
index 8a2dc15..617226e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/lowlightclock/AmbientLightModeMonitorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/lowlightclock/AmbientLightModeMonitorTest.kt
@@ -51,7 +51,7 @@
MockitoAnnotations.initMocks(this)
ambientLightModeMonitor =
- AmbientLightModeMonitor(
+ AmbientLightModeMonitorImpl(
Optional.of(algorithm),
sensorManager,
Optional.of(Provider { sensor }),
@@ -98,7 +98,7 @@
@Test
fun shouldNotRegisterForSensorUpdatesIfSensorNotAvailable() {
val ambientLightModeMonitor =
- AmbientLightModeMonitor(Optional.of(algorithm), sensorManager, Optional.empty())
+ AmbientLightModeMonitorImpl(Optional.of(algorithm), sensorManager, Optional.empty())
val callback = mock(AmbientLightModeMonitor.Callback::class.java)
ambientLightModeMonitor.start(callback)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/lowlightclock/LowLightConditionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/lowlightclock/LowLightConditionTest.kt
deleted file mode 100644
index da68442..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/lowlightclock/LowLightConditionTest.kt
+++ /dev/null
@@ -1,157 +0,0 @@
-/*
- * Copyright (C) 2025 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.systemui.lowlightclock
-
-import android.testing.AndroidTestingRunner
-import androidx.test.filters.SmallTest
-import com.android.internal.logging.UiEventLogger
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.condition.testStart
-import com.android.systemui.condition.testStop
-import com.android.systemui.kosmos.runCurrent
-import com.android.systemui.kosmos.runTest
-import com.android.systemui.kosmos.testScope
-import com.android.systemui.testKosmos
-import com.google.common.truth.Truth
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.ArgumentMatchers
-import org.mockito.Mock
-import org.mockito.Mockito
-import org.mockito.MockitoAnnotations
-import org.mockito.kotlin.argumentCaptor
-
-@SmallTest
-@RunWith(AndroidTestingRunner::class)
-class LowLightConditionTest : SysuiTestCase() {
- private val kosmos = testKosmos()
-
- @Mock private lateinit var ambientLightModeMonitor: AmbientLightModeMonitor
-
- @Mock private lateinit var uiEventLogger: UiEventLogger
-
- private lateinit var condition: LowLightCondition
-
- @Before
- fun setUp() {
- MockitoAnnotations.initMocks(this)
-
- condition = LowLightCondition(kosmos.testScope, ambientLightModeMonitor, uiEventLogger)
- }
-
- @Test
- fun testLowLightFalse() =
- kosmos.runTest {
- testStart(condition)
- changeLowLightMode(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT)
- Truth.assertThat(condition.isConditionMet).isFalse()
- }
-
- @Test
- fun testLowLightTrue() =
- kosmos.runTest {
- testStart(condition)
- changeLowLightMode(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK)
- Truth.assertThat(condition.isConditionMet).isTrue()
- }
-
- @Test
- fun testUndecidedLowLightStateIgnored() =
- kosmos.runTest {
- testStart(condition)
- changeLowLightMode(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK)
- Truth.assertThat(condition.isConditionMet).isTrue()
- changeLowLightMode(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_UNDECIDED)
- Truth.assertThat(condition.isConditionMet).isTrue()
- }
-
- @Test
- fun testLowLightChange() =
- kosmos.runTest {
- testStart(condition)
- changeLowLightMode(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT)
- Truth.assertThat(condition.isConditionMet).isFalse()
- changeLowLightMode(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK)
- Truth.assertThat(condition.isConditionMet).isTrue()
- }
-
- @Test
- fun testResetIsConditionMetUponStop() =
- kosmos.runTest {
- testStart(condition)
- runCurrent()
- changeLowLightMode(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK)
- Truth.assertThat(condition.isConditionMet).isTrue()
-
- testStop(condition)
- Truth.assertThat(condition.isConditionMet).isFalse()
- }
-
- @Test
- fun testLoggingAmbientLightNotLowToLow() =
- kosmos.runTest {
- testStart(condition)
- changeLowLightMode(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK)
- // Only logged once.
- Mockito.verify(uiEventLogger, Mockito.times(1)).log(ArgumentMatchers.any())
- // Logged with the correct state.
- Mockito.verify(uiEventLogger).log(LowLightDockEvent.AMBIENT_LIGHT_TO_DARK)
- }
-
- @Test
- fun testLoggingAmbientLightLowToLow() =
- kosmos.runTest {
- testStart(condition)
- changeLowLightMode(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK)
- Mockito.reset(uiEventLogger)
-
- changeLowLightMode(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK)
- // Doesn't log.
- Mockito.verify(uiEventLogger, Mockito.never()).log(ArgumentMatchers.any())
- }
-
- @Test
- fun testLoggingAmbientLightNotLowToNotLow() =
- kosmos.runTest {
- testStart(condition)
- changeLowLightMode(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT)
- // Doesn't log.
- Mockito.verify(uiEventLogger, Mockito.never()).log(ArgumentMatchers.any())
- }
-
- @Test
- fun testLoggingAmbientLightLowToNotLow() =
- kosmos.runTest {
- testStart(condition)
- runCurrent()
- changeLowLightMode(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK)
- Mockito.reset(uiEventLogger)
-
- changeLowLightMode(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT)
- // Only logged once.
- Mockito.verify(uiEventLogger).log(ArgumentMatchers.any())
- // Logged with the correct state.
- Mockito.verify(uiEventLogger).log(LowLightDockEvent.AMBIENT_LIGHT_TO_LIGHT)
- }
-
- private fun changeLowLightMode(mode: Int) {
- val ambientLightCallbackCaptor = argumentCaptor<AmbientLightModeMonitor.Callback>()
-
- Mockito.verify(ambientLightModeMonitor).start(ambientLightCallbackCaptor.capture())
- ambientLightCallbackCaptor.lastValue.onChange(mode)
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImplTest.kt
index 8e6e9dc..ab070ce 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImplTest.kt
@@ -33,7 +33,6 @@
import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.NotificationLockscreenUserManager
import com.android.systemui.testKosmos
-import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import java.util.concurrent.Executor
import kotlinx.coroutines.test.TestScope
@@ -80,7 +79,6 @@
private lateinit var dataMain: MediaData
private lateinit var dataGuest: MediaData
private lateinit var dataPrivateProfile: MediaData
- private val clock = FakeSystemClock()
private val repository: MediaFilterRepository = with(kosmos) { mediaFilterRepository }
private val mediaLogger = kosmos.mockMediaLogger
@@ -94,7 +92,6 @@
userTracker,
lockscreenUserManager,
executor,
- clock,
repository,
mediaLogger,
)
@@ -270,63 +267,63 @@
@Test
fun hasAnyMedia_mediaSet_returnsTrue() =
testScope.runTest {
- val selectedUserEntries by collectLastValue(repository.selectedUserEntries)
+ val currentUserEntries by collectLastValue(repository.currentUserEntries)
mediaDataFilter.onMediaDataLoaded(KEY, oldKey = null, data = dataMain)
- assertThat(hasAnyMedia(selectedUserEntries)).isTrue()
+ assertThat(hasAnyMedia(currentUserEntries)).isTrue()
}
@Test
fun hasActiveMedia_inactiveMediaSet_returnsFalse() =
testScope.runTest {
- val selectedUserEntries by collectLastValue(repository.selectedUserEntries)
+ val currentUserEntries by collectLastValue(repository.currentUserEntries)
val data = dataMain.copy(active = false)
mediaDataFilter.onMediaDataLoaded(KEY, oldKey = null, data = data)
- assertThat(hasActiveMedia(selectedUserEntries)).isFalse()
+ assertThat(hasActiveMedia(currentUserEntries)).isFalse()
}
@Test
fun hasActiveMedia_activeMediaSet_returnsTrue() =
testScope.runTest {
- val selectedUserEntries by collectLastValue(repository.selectedUserEntries)
+ val currentUserEntries by collectLastValue(repository.currentUserEntries)
val data = dataMain.copy(active = true)
mediaDataFilter.onMediaDataLoaded(KEY, oldKey = null, data = data)
- assertThat(hasActiveMedia(selectedUserEntries)).isTrue()
+ assertThat(hasActiveMedia(currentUserEntries)).isTrue()
}
@Test
fun hasAnyMedia_onlyCurrentUser() =
testScope.runTest {
- val selectedUserEntries by collectLastValue(repository.selectedUserEntries)
- assertThat(hasAnyMedia(selectedUserEntries)).isFalse()
+ val currentUserEntries by collectLastValue(repository.currentUserEntries)
+ assertThat(hasAnyMedia(currentUserEntries)).isFalse()
mediaDataFilter.onMediaDataLoaded(KEY, oldKey = null, data = dataGuest)
- assertThat(hasAnyMedia(selectedUserEntries)).isFalse()
+ assertThat(hasAnyMedia(currentUserEntries)).isFalse()
}
@Test
fun hasActiveMedia_onlyCurrentUser() =
testScope.runTest {
- val selectedUserEntries by collectLastValue(repository.selectedUserEntries)
- assertThat(hasActiveMedia(selectedUserEntries)).isFalse()
+ val currentUserEntries by collectLastValue(repository.currentUserEntries)
+ assertThat(hasActiveMedia(currentUserEntries)).isFalse()
val data = dataGuest.copy(active = true)
mediaDataFilter.onMediaDataLoaded(KEY, oldKey = null, data = data)
- assertThat(hasActiveMedia(selectedUserEntries)).isFalse()
- assertThat(hasAnyMedia(selectedUserEntries)).isFalse()
+ assertThat(hasActiveMedia(currentUserEntries)).isFalse()
+ assertThat(hasAnyMedia(currentUserEntries)).isFalse()
}
@Test
fun onNotificationRemoved_doesNotHaveMedia() =
testScope.runTest {
- val selectedUserEntries by collectLastValue(repository.selectedUserEntries)
+ val currentUserEntries by collectLastValue(repository.currentUserEntries)
mediaDataFilter.onMediaDataLoaded(KEY, oldKey = null, data = dataMain)
mediaDataFilter.onMediaDataRemoved(KEY, false)
- assertThat(hasAnyMedia(selectedUserEntries)).isFalse()
+ assertThat(hasAnyMedia(currentUserEntries)).isFalse()
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessorTest.kt
index 779f5a0..7bd9cf7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessorTest.kt
@@ -35,7 +35,6 @@
import android.os.Bundle
import android.os.UserHandle
import android.platform.test.annotations.DisableFlags
-import android.platform.test.annotations.EnableFlags
import android.service.notification.StatusBarNotification
import android.testing.TestableLooper.RunWithLooper
import androidx.media.utils.MediaConstants
@@ -1167,7 +1166,6 @@
}
@Test
- @EnableFlags(Flags.FLAG_MEDIA_CONTROLS_DRAWABLES_REUSE_BUGFIX)
fun postWithPlaybackActions_drawablesReused() {
whenever(notificationLockscreenUserManager.isCurrentProfile(USER_ID)).thenReturn(true)
whenever(notificationLockscreenUserManager.isProfileAvailable(USER_ID)).thenReturn(true)
@@ -1180,7 +1178,7 @@
.setState(PlaybackState.STATE_PLAYING, 0, 10f)
.setActions(stateActions)
whenever(controller.playbackState).thenReturn(stateBuilder.build())
- val userEntries by testScope.collectLastValue(mediaFilterRepository.selectedUserEntries)
+ val userEntries by testScope.collectLastValue(mediaFilterRepository.currentUserEntries)
mediaDataProcessor.addInternalListener(mediaDataFilter)
mediaDataFilter.mediaDataProcessor = mediaDataProcessor
@@ -1199,39 +1197,6 @@
.isEqualTo(firstSemanticActions.prevOrCustom?.icon)
}
- @Test
- @DisableFlags(Flags.FLAG_MEDIA_CONTROLS_DRAWABLES_REUSE_BUGFIX)
- fun postWithPlaybackActions_drawablesNotReused() {
- whenever(notificationLockscreenUserManager.isCurrentProfile(USER_ID)).thenReturn(true)
- whenever(notificationLockscreenUserManager.isProfileAvailable(USER_ID)).thenReturn(true)
- val stateActions =
- PlaybackState.ACTION_PAUSE or
- PlaybackState.ACTION_SKIP_TO_PREVIOUS or
- PlaybackState.ACTION_SKIP_TO_NEXT
- val stateBuilder =
- PlaybackState.Builder()
- .setState(PlaybackState.STATE_PLAYING, 0, 10f)
- .setActions(stateActions)
- whenever(controller.playbackState).thenReturn(stateBuilder.build())
- val userEntries by testScope.collectLastValue(mediaFilterRepository.selectedUserEntries)
-
- mediaDataProcessor.addInternalListener(mediaDataFilter)
- mediaDataFilter.mediaDataProcessor = mediaDataProcessor
- addNotificationAndLoad()
-
- assertThat(userEntries).hasSize(1)
- val firstSemanticActions = userEntries?.values?.toList()?.get(0)?.semanticActions!!
-
- addNotificationAndLoad()
-
- assertThat(userEntries).hasSize(1)
- val secondSemanticActions = userEntries?.values?.toList()?.get(0)?.semanticActions!!
- assertThat(secondSemanticActions.nextOrCustom?.icon)
- .isNotEqualTo(firstSemanticActions.nextOrCustom?.icon)
- assertThat(secondSemanticActions.prevOrCustom?.icon)
- .isNotEqualTo(firstSemanticActions.prevOrCustom?.icon)
- }
-
@DisableFlags(Flags.FLAG_MEDIA_CONTROLS_BUTTON_MEDIA3)
@Test
fun testPlaybackActions_reservedSpace() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManagerTest.kt
index 9f3537f..4f8dda5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManagerTest.kt
@@ -37,6 +37,7 @@
import android.platform.test.flag.junit.FlagsParameterization
import android.testing.TestableLooper
import androidx.test.filters.SmallTest
+import com.android.media.flags.Flags.FLAG_ENABLE_SUGGESTED_DEVICE_API
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast
import com.android.settingslib.bluetooth.LocalBluetoothManager
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager
@@ -50,7 +51,7 @@
import com.android.systemui.media.controls.MediaTestUtils
import com.android.systemui.media.controls.shared.model.MediaData
import com.android.systemui.media.controls.shared.model.MediaDeviceData
-import com.android.systemui.media.controls.shared.model.SuggestedMediaDeviceData
+import com.android.systemui.media.controls.shared.model.SuggestionData
import com.android.systemui.media.controls.util.LocalMediaManagerFactory
import com.android.systemui.media.controls.util.MediaControllerFactory
import com.android.systemui.media.muteawait.MediaMuteAwaitConnectionManager
@@ -71,6 +72,7 @@
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Mock
import org.mockito.Mockito.any
+import org.mockito.Mockito.clearInvocations
import org.mockito.Mockito.mock
import org.mockito.Mockito.never
import org.mockito.Mockito.reset
@@ -309,12 +311,12 @@
fun deviceListUpdate() {
manager.onMediaDataLoaded(KEY, null, mediaData)
fakeBgExecutor.runAllReady()
+ fakeFgExecutor.runAllReady()
val deviceCallback = captureCallback()
verify(muteAwaitManager).startListening()
// WHEN the device list changes
deviceCallback.onDeviceListUpdate(mutableListOf(device))
assertThat(fakeBgExecutor.runAllReady()).isEqualTo(1)
- assertThat(fakeFgExecutor.runAllReady()).isEqualTo(1)
// THEN the update is dispatched to the listener
val data = captureDeviceData(KEY)
assertThat(data.enabled).isTrue()
@@ -322,6 +324,7 @@
assertThat(data.icon).isEqualTo(icon)
}
+ @RequiresFlagsEnabled(FLAG_ENABLE_SUGGESTED_DEVICE_API)
@Test
fun suggestedDeviceUpdate() {
val suggestedDeviceName = "suggested_device_name"
@@ -333,14 +336,15 @@
// Need to load media data to load LocalMediaManager the first time
manager.onMediaDataLoaded(KEY, null, mediaData)
fakeBgExecutor.runAllReady()
+ fakeFgExecutor.runAllReady()
+ clearInvocations(listener)
val deviceCallback = captureCallback()
// WHEN the device list changes
deviceCallback.onSuggestedDeviceUpdated(suggestedDeviceState)
assertThat(fakeBgExecutor.runAllReady()).isEqualTo(1)
- // Executes twice, once onMediaDataLoaded, once onSuggestedDeviceUpdated
- assertThat(fakeFgExecutor.runAllReady()).isEqualTo(2)
+ assertThat(fakeFgExecutor.runAllReady()).isEqualTo(1)
// THEN the update is dispatched to the listener
- val data = captureSuggestedDeviceData(KEY)
+ val data = captureSuggestionData(KEY).suggestedMediaDeviceData!!
assertThat(data.name).isEqualTo(suggestedDeviceName)
assertThat(data.connectionState).isEqualTo(connectionState)
assertThat(data.icon).isEqualTo(icon)
@@ -350,11 +354,11 @@
fun selectedDeviceStateChanged() {
manager.onMediaDataLoaded(KEY, null, mediaData)
fakeBgExecutor.runAllReady()
+ fakeFgExecutor.runAllReady()
val deviceCallback = captureCallback()
// WHEN the selected device changes state
deviceCallback.onSelectedDeviceStateChanged(device, 1)
assertThat(fakeBgExecutor.runAllReady()).isEqualTo(1)
- assertThat(fakeFgExecutor.runAllReady()).isEqualTo(1)
// THEN the update is dispatched to the listener
val data = captureDeviceData(KEY)
assertThat(data.enabled).isTrue()
@@ -471,7 +475,6 @@
}
@Test
- @EnableFlags(com.android.systemui.Flags.FLAG_MEDIA_CONTROLS_DRAWABLES_REUSE_BUGFIX)
fun onMediaDataLoaded_withRemotePlaybackType_usesNonNullRoutingSessionName_drawableReused() {
whenever(routingSession.name).thenReturn(REMOTE_DEVICE_NAME)
whenever(routingSession.selectedRoutes).thenReturn(listOf("selectedRoute", "selectedRoute"))
@@ -484,20 +487,6 @@
assertThat(secondData.icon).isEqualTo(firstData.icon)
}
- @Test
- @DisableFlags(com.android.systemui.Flags.FLAG_MEDIA_CONTROLS_DRAWABLES_REUSE_BUGFIX)
- fun onMediaDataLoaded_withRemotePlaybackType_usesNonNullRoutingSessionName_drawableNotReused() {
- whenever(routingSession.name).thenReturn(REMOTE_DEVICE_NAME)
- whenever(routingSession.selectedRoutes).thenReturn(listOf("selectedRoute", "selectedRoute"))
- whenever(playbackInfo.playbackType).thenReturn(PlaybackInfo.PLAYBACK_TYPE_REMOTE)
-
- val firstData = loadMediaAndCaptureDeviceData()
- reset(listener)
- val secondData = loadMediaAndCaptureDeviceData()
-
- assertThat(secondData.icon).isNotEqualTo(firstData.icon)
- }
-
@RequiresFlagsDisabled(FLAG_USE_PLAYBACK_INFO_FOR_ROUTING_CONTROLS)
@Test
fun onMediaDataLoaded_withRemotePlaybackInfo_noMatchingRoutingSession_setsDisabledDevice() {
@@ -527,8 +516,7 @@
@Test
@RequiresFlagsEnabled(FLAG_USE_PLAYBACK_INFO_FOR_ROUTING_CONTROLS)
- @EnableFlags(com.android.systemui.Flags.FLAG_MEDIA_CONTROLS_DRAWABLES_REUSE_BUGFIX)
- fun onMediaDataLoaded_withRemotePlaybackInfo_noMatchingRoutingSession_drawableReused() {
+ fun onMediaDataLoaded_withRemotePlaybackInfo_noMatchingRoutingSession() {
whenever(playbackInfo.playbackType).thenReturn(PlaybackInfo.PLAYBACK_TYPE_REMOTE)
whenever(mr2.getRoutingSessionForMediaController(any())).thenReturn(null)
context.orCreateTestableResources.removeOverride(R.drawable.ic_media_home_devices)
@@ -540,21 +528,6 @@
assertThat(secondData.icon).isEqualTo(firstData.icon)
}
- @Test
- @RequiresFlagsEnabled(FLAG_USE_PLAYBACK_INFO_FOR_ROUTING_CONTROLS)
- @DisableFlags(com.android.systemui.Flags.FLAG_MEDIA_CONTROLS_DRAWABLES_REUSE_BUGFIX)
- fun onMediaDataLoaded_withRemotePlaybackInfo_noMatchingRoutingSession_drawableNotReused() {
- whenever(playbackInfo.playbackType).thenReturn(PlaybackInfo.PLAYBACK_TYPE_REMOTE)
- whenever(mr2.getRoutingSessionForMediaController(any())).thenReturn(null)
- context.orCreateTestableResources.removeOverride(R.drawable.ic_media_home_devices)
-
- val firstData = loadMediaAndCaptureDeviceData()
- reset(listener)
- val secondData = loadMediaAndCaptureDeviceData()
-
- assertThat(secondData.icon).isNotEqualTo(firstData.icon)
- }
-
@RequiresFlagsDisabled(FLAG_USE_PLAYBACK_INFO_FOR_ROUTING_CONTROLS)
@Test
fun onSelectedDeviceStateChanged_withRemotePlaybackInfo_noMatchingRoutingSession_setsDisabledDevice() {
@@ -795,6 +768,10 @@
whenever(device.icon).thenReturn(firstIcon)
loadMediaAndCaptureDeviceData()
+ // Run anything in progress to not conflate with later interactions
+ fakeBgExecutor.runAllReady()
+ fakeFgExecutor.runAllReady()
+ clearInvocations(listener)
// and later the manager gets a callback with only the icon changed
val deviceCallback = captureCallback()
@@ -922,10 +899,7 @@
@Test
@DisableFlags(Flags.FLAG_LEGACY_LE_AUDIO_SHARING)
- @EnableFlags(
- Flags.FLAG_ENABLE_LE_AUDIO_SHARING,
- com.android.systemui.Flags.FLAG_MEDIA_CONTROLS_DRAWABLES_REUSE_BUGFIX,
- )
+ @EnableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
fun onBroadcastStarted_currentMediaDeviceDataIsBroadcasting_drawablesReused() {
val broadcastCallback = setupBroadcastCallback()
setupLeAudioConfiguration(true)
@@ -940,29 +914,7 @@
}
@Test
- @DisableFlags(
- Flags.FLAG_LEGACY_LE_AUDIO_SHARING,
- com.android.systemui.Flags.FLAG_MEDIA_CONTROLS_DRAWABLES_REUSE_BUGFIX,
- )
- @EnableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
- fun onBroadcastStarted_currentMediaDeviceDataIsBroadcasting_drawablesNotReused() {
- val broadcastCallback = setupBroadcastCallback()
- setupLeAudioConfiguration(true)
- setupBroadcastPackage(BROADCAST_APP_NAME)
- broadcastCallback.onBroadcastStarted(1, 1)
-
- val firstDeviceData = loadMediaAndCaptureDeviceData()
- reset(listener)
- val secondDeviceData = loadMediaAndCaptureDeviceData()
-
- assertThat(firstDeviceData.icon).isNotEqualTo(secondDeviceData.icon)
- }
-
- @Test
- @EnableFlags(
- Flags.FLAG_LEGACY_LE_AUDIO_SHARING,
- com.android.systemui.Flags.FLAG_MEDIA_CONTROLS_DRAWABLES_REUSE_BUGFIX,
- )
+ @EnableFlags(Flags.FLAG_LEGACY_LE_AUDIO_SHARING)
@DisableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
fun onBroadcastStarted_legacy_currentMediaDeviceDataIsNotBroadcasting_drawableReused() {
val broadcastCallback = setupBroadcastCallback()
@@ -978,25 +930,6 @@
}
@Test
- @EnableFlags(Flags.FLAG_LEGACY_LE_AUDIO_SHARING)
- @DisableFlags(
- Flags.FLAG_ENABLE_LE_AUDIO_SHARING,
- com.android.systemui.Flags.FLAG_MEDIA_CONTROLS_DRAWABLES_REUSE_BUGFIX,
- )
- fun onBroadcastStarted_legacy_currentMediaDeviceDataIsNotBroadcasting_drawableNotReused() {
- val broadcastCallback = setupBroadcastCallback()
- setupLeAudioConfiguration(true)
- setupBroadcastPackage(NORMAL_APP_NAME)
- broadcastCallback.onBroadcastStarted(1, 1)
-
- val firstDeviceData = loadMediaAndCaptureDeviceData()
- reset(listener)
- val secondDeviceData = loadMediaAndCaptureDeviceData()
-
- assertThat(firstDeviceData.icon).isNotEqualTo(secondDeviceData.icon)
- }
-
- @Test
@DisableFlags(
Flags.FLAG_LEGACY_LE_AUDIO_SHARING,
com.android.media.flags.Flags.FLAG_ENABLE_OUTPUT_SWITCHER_PERSONAL_AUDIO_SHARING,
@@ -1103,12 +1036,9 @@
return captor.getValue()
}
- private fun captureSuggestedDeviceData(
- key: String,
- oldKey: String? = null,
- ): SuggestedMediaDeviceData {
- val captor = ArgumentCaptor.forClass(SuggestedMediaDeviceData::class.java)
- verify(listener).onSuggestedMediaDeviceChanged(eq(key), eq(oldKey), captor.capture())
+ private fun captureSuggestionData(key: String, oldKey: String? = null): SuggestionData {
+ val captor = ArgumentCaptor.forClass(SuggestionData::class.java)
+ verify(listener).onSuggestionDataChanged(eq(key), eq(oldKey), captor.capture())
return captor.getValue()
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/animation/ColorSchemeTransitionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/animation/ColorSchemeTransitionTest.kt
index 90a8ad5..1836306e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/animation/ColorSchemeTransitionTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/animation/ColorSchemeTransitionTest.kt
@@ -18,12 +18,9 @@
import android.animation.ValueAnimator
import android.graphics.Color
-import android.platform.test.annotations.DisableFlags
-import android.platform.test.annotations.EnableFlags
import android.testing.TestableLooper
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.media.controls.ui.view.GutsViewHolder
import com.android.systemui.media.controls.ui.view.MediaViewHolder
@@ -160,15 +157,6 @@
verify(applyColor).invoke(expectedColor)
}
- @DisableFlags(Flags.FLAG_MEDIA_CONTROLS_A11Y_COLORS)
- @Test
- fun testColorSchemeTransition_update_legacy() {
- colorSchemeTransition.updateColorScheme(colorScheme)
- verify(mockAnimatingTransition, times(8)).updateColorScheme(colorScheme)
- verify(gutsViewHolder).colorScheme = colorScheme
- }
-
- @EnableFlags(Flags.FLAG_MEDIA_CONTROLS_A11Y_COLORS)
@Test
fun testColorSchemeTransition_update() {
colorSchemeTransition.updateColorScheme(colorScheme)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt
index 9ef6932..f119bb8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt
@@ -21,17 +21,16 @@
import android.content.res.Configuration
import android.database.ContentObserver
import android.os.LocaleList
-import android.platform.test.flag.junit.FlagsParameterization
import android.provider.Settings
import android.testing.TestableLooper
import android.util.MathUtils.abs
import android.view.View
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.compose.animation.scene.ObservableTransitionState
import com.android.compose.animation.scene.SceneKey
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitorCallback
-import com.android.systemui.Flags.mediaControlsUmoInflationInBackground
import com.android.systemui.SysuiTestCase
import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
import com.android.systemui.dump.DumpManager
@@ -99,8 +98,6 @@
import org.mockito.kotlin.any
import org.mockito.kotlin.capture
import org.mockito.kotlin.eq
-import platform.test.runner.parameterized.ParameterizedAndroidJunit4
-import platform.test.runner.parameterized.Parameters
private val DATA = MediaTestUtils.emptyMediaData
@@ -109,8 +106,8 @@
@SmallTest
@TestableLooper.RunWithLooper(setAsMainLooper = true)
-@RunWith(ParameterizedAndroidJunit4::class)
-class MediaCarouselControllerTest(flags: FlagsParameterization) : SysuiTestCase() {
+@RunWith(AndroidJUnit4::class)
+class MediaCarouselControllerTest : SysuiTestCase() {
private val kosmos = testKosmos().useUnconfinedTestDispatcher()
private val testDispatcher = kosmos.testDispatcher
private val secureSettings = kosmos.fakeSettings
@@ -150,20 +147,6 @@
private var originalResumeSetting =
Settings.Secure.getInt(context.contentResolver, Settings.Secure.MEDIA_CONTROLS_RESUME, 1)
- companion object {
- @JvmStatic
- @Parameters(name = "{0}")
- fun getParams(): List<FlagsParameterization> {
- return FlagsParameterization.progressionOf(
- com.android.systemui.Flags.FLAG_MEDIA_CONTROLS_UMO_INFLATION_IN_BACKGROUND
- )
- }
- }
-
- init {
- mSetFlagsRule.setFlagsParameterization(flags)
- }
-
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
@@ -1000,9 +983,7 @@
}
private fun runAllReady() {
- if (mediaControlsUmoInflationInBackground()) {
- bgExecutor.runAllReady()
- uiExecutor.runAllReady()
- }
+ bgExecutor.runAllReady()
+ uiExecutor.runAllReady()
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaControlPanelTest.kt
index 330b326..31b2596 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaControlPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaControlPanelTest.kt
@@ -75,6 +75,7 @@
import com.android.systemui.media.controls.shared.model.MediaDeviceData
import com.android.systemui.media.controls.shared.model.MediaNotificationAction
import com.android.systemui.media.controls.shared.model.SuggestedMediaDeviceData
+import com.android.systemui.media.controls.shared.model.SuggestionData
import com.android.systemui.media.controls.ui.binder.SeekBarObserver
import com.android.systemui.media.controls.ui.view.GutsViewHolder
import com.android.systemui.media.controls.ui.view.MediaViewHolder
@@ -1203,8 +1204,8 @@
player.bindPlayer(
mediaData.copy(
- suggestedDevice =
- createSuggestedMediaDeviceData(DEVICE_NAME, MediaDeviceState.STATE_DISCONNECTED)
+ suggestionData =
+ createSuggestionData(DEVICE_NAME, MediaDeviceState.STATE_DISCONNECTED)
),
PACKAGE,
)
@@ -1225,8 +1226,8 @@
player.bindPlayer(
mediaData.copy(
- suggestedDevice =
- createSuggestedMediaDeviceData(DEVICE_NAME, MediaDeviceState.STATE_CONNECTING)
+ suggestionData =
+ createSuggestionData(DEVICE_NAME, MediaDeviceState.STATE_CONNECTING)
),
PACKAGE,
)
@@ -1247,11 +1248,8 @@
player.bindPlayer(
mediaData.copy(
- suggestedDevice =
- createSuggestedMediaDeviceData(
- DEVICE_NAME,
- MediaDeviceState.STATE_CONNECTING_FAILED,
- )
+ suggestionData =
+ createSuggestionData(DEVICE_NAME, MediaDeviceState.STATE_CONNECTING_FAILED)
),
PACKAGE,
)
@@ -1272,8 +1270,7 @@
player.bindPlayer(
mediaData.copy(
- suggestedDevice =
- createSuggestedMediaDeviceData(DEVICE_NAME, MediaDeviceState.STATE_CONNECTED)
+ suggestionData = createSuggestionData(DEVICE_NAME, MediaDeviceState.STATE_CONNECTED)
),
PACKAGE,
)
@@ -1952,11 +1949,18 @@
whenever(mediaViewController.collapsedLayout).thenReturn(collapsedSet)
}
- private fun createSuggestedMediaDeviceData(deviceName: String, state: Int) =
- SuggestedMediaDeviceData(
- name = deviceName,
- icon = suggestionDrawable,
- connectionState = state,
- connect = {},
+ private fun createSuggestionData(deviceName: String, state: Int) =
+ SuggestionData(
+ suggestedMediaDeviceData =
+ SuggestedMediaDeviceData(
+ name = deviceName,
+ icon = suggestionDrawable,
+ connectionState = state,
+ connect = {},
+ ),
+ onSuggestionSpaceVisible =
+ object : Runnable {
+ override fun run() {}
+ },
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManagerTest.kt
index 3de44ad..18b6681 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManagerTest.kt
@@ -799,6 +799,53 @@
assertThat(mediaCarouselScrollHandler.visibleToUser).isFalse()
}
+ @Test
+ fun testStatusBarOnStateChanged_carouselVisibleToUser() {
+ setLockScreenShadeVisibleToUser()
+
+ statusBarCallback.value.onStateChanged(StatusBarState.SHADE_LOCKED)
+
+ verify(mediaCarouselController).onCarouselVisibleToUser()
+ }
+
+ @Test
+ fun testStatusBarOnDozingChanged_carouselVisibleToUser() {
+ setLockScreenVisibleToUser()
+
+ statusBarCallback.value.onDozingChanged(false)
+
+ verify(mediaCarouselController).onCarouselVisibleToUser()
+ }
+
+ @Test
+ fun testStatusBarOnExpandedChanged_carouselVisibleToUser() {
+ setHomeScreenShadeVisibleToUser()
+
+ statusBarCallback.value.onExpandedChanged(true)
+
+ verify(mediaCarouselController).onCarouselVisibleToUser()
+ }
+
+ private fun setLockScreenShadeVisibleToUser() {
+ whenever(statusBarStateController.isDozing).thenReturn(false)
+ whenever(keyguardViewController.isBouncerShowing).thenReturn(false)
+ whenever(statusBarStateController.state).thenReturn(StatusBarState.SHADE_LOCKED)
+ }
+
+ private fun setHomeScreenShadeVisibleToUser() {
+ whenever(statusBarStateController.isDozing).thenReturn(false)
+ whenever(statusBarStateController.state).thenReturn(StatusBarState.SHADE)
+ whenever(statusBarStateController.isExpanded).thenReturn(true)
+ }
+
+ private fun setLockScreenVisibleToUser() {
+ whenever(statusBarStateController.isDozing).thenReturn(false)
+ whenever(keyguardViewController.isBouncerShowing).thenReturn(false)
+ whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
+ whenever(statusBarStateController.isExpanded).thenReturn(true)
+ settings.putInt(Settings.Secure.MEDIA_CONTROLS_LOCK_SCREEN, 1)
+ }
+
private fun enableSplitShade() {
context
.getOrCreateTestableResources()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaSwitchingControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaSwitchingControllerTest.java
index 2b7b392..c236df4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaSwitchingControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaSwitchingControllerTest.java
@@ -667,6 +667,7 @@
InputMediaDevice.create(
mContext,
TEST_DEVICE_3_ID,
+ "",
AudioDeviceInfo.TYPE_BUILTIN_MIC,
MAX_VOLUME,
CURRENT_VOLUME,
@@ -676,6 +677,7 @@
InputMediaDevice.create(
mContext,
TEST_DEVICE_4_ID,
+ "",
AudioDeviceInfo.TYPE_WIRED_HEADSET,
MAX_VOLUME,
CURRENT_VOLUME,
@@ -1456,6 +1458,7 @@
InputMediaDevice.create(
mContext,
TEST_DEVICE_1_ID,
+ "",
AudioDeviceInfo.TYPE_BUILTIN_MIC,
MAX_VOLUME,
CURRENT_VOLUME,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/DragAndDropTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/DragAndDropTest.kt
index a04d4e6..12a3556 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/DragAndDropTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/DragAndDropTest.kt
@@ -217,8 +217,10 @@
label = AnnotatedString(tileSpec),
appName = null,
isCurrent = true,
+ isDualTarget = false,
availableEditActions =
if (isRemovable) setOf(AvailableEditActions.REMOVE) else emptySet(),
+ appIcon = null,
category = TileCategory.UNKNOWN,
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/EditModeTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/EditModeTest.kt
index e3a7380..81c9b6a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/EditModeTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/EditModeTest.kt
@@ -25,8 +25,6 @@
import androidx.compose.ui.test.doubleClick
import androidx.compose.ui.test.junit4.ComposeContentTestRule
import androidx.compose.ui.test.junit4.createComposeRule
-import androidx.compose.ui.test.onAllNodesWithText
-import androidx.compose.ui.test.onFirst
import androidx.compose.ui.test.onNodeWithContentDescription
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
@@ -105,7 +103,7 @@
composeRule.setContent { EditTileGridUnderTest() }
composeRule.waitForIdle()
- composeRule.onNodeWithContentDescription("tileF").performClick() // Tap to add
+ composeRule.onNodeWithText("tileF").performClick() // Tap to add
composeRule.assertCurrentTilesGridContainsExactly(
listOf("tileA", "tileB", "tileC", "tileD_large", "tileE", "tileF")
@@ -119,10 +117,10 @@
composeRule.waitForIdle()
// Double tap first "tileA", i.e. the one in the current grid
- composeRule.onAllNodesWithText("tileA").onFirst().performTouchInput { doubleClick() }
+ composeRule.onNodeWithContentDescription("tileA").performTouchInput { doubleClick() }
// Tap on tileE to position tileA in its spot
- composeRule.onAllNodesWithText("tileE").onFirst().performClick()
+ composeRule.onNodeWithContentDescription("tileE").performClick()
// Assert tileA moved to tileE's position
composeRule.assertCurrentTilesGridContainsExactly(
@@ -135,7 +133,7 @@
composeRule.setContent { EditTileGridUnderTest() }
composeRule.waitForIdle()
- composeRule.onNodeWithContentDescription("tileF").performClick() // Tap to add
+ composeRule.onNodeWithText("tileF").performClick() // Tap to add
composeRule.waitForIdle()
composeRule.onNodeWithText("Undo").assertExists()
@@ -169,8 +167,10 @@
label = AnnotatedString(tileSpec),
appName = null,
isCurrent = isCurrent,
+ isDualTarget = false,
availableEditActions =
if (isRemovable) setOf(AvailableEditActions.REMOVE) else emptySet(),
+ appIcon = null,
category = TileCategory.UNKNOWN,
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/ResizingTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/ResizingTest.kt
index 335c7ad..5cbb505 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/ResizingTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/ResizingTest.kt
@@ -23,8 +23,7 @@
import androidx.compose.ui.test.ExperimentalTestApi
import androidx.compose.ui.test.click
import androidx.compose.ui.test.junit4.createComposeRule
-import androidx.compose.ui.test.onAllNodesWithText
-import androidx.compose.ui.test.onFirst
+import androidx.compose.ui.test.onNodeWithContentDescription
import androidx.compose.ui.test.performClick
import androidx.compose.ui.test.performCustomAccessibilityActionWithLabel
import androidx.compose.ui.test.performTouchInput
@@ -62,8 +61,13 @@
private val snapshotViewModelFactory = kosmos.infiniteGridSnapshotViewModelFactory
@Composable
- private fun EditTileGridUnderTest(listState: EditTileListState) {
- val largeTilesSpecs = TestLargeTilesSpecs.toMutableSet()
+ private fun EditTileGridUnderTest(
+ listState: EditTileListState,
+ tiles: List<EditTileViewModel> = TestEditTiles,
+ largeTiles: Set<TileSpec> = TestLargeTilesSpecs,
+ onResize: (EditAction.ResizeTile) -> Unit = {},
+ ) {
+ val largeTilesSpecs = remember { largeTiles.toMutableSet() }
PlatformTheme {
DefaultEditTileGrid(
listState = listState,
@@ -74,9 +78,11 @@
) { action ->
when (action) {
is EditAction.ResizeTile -> {
+ onResize(action)
+
if (action.toIcon) largeTilesSpecs.remove(action.tileSpec)
else largeTilesSpecs.add(action.tileSpec)
- listState.updateTiles(TestEditTiles, largeTilesSpecs)
+ listState.updateTiles(tiles, largeTilesSpecs)
}
else -> {}
}
@@ -88,46 +94,50 @@
fun toggleIconTileWithA11yAction_shouldBeLarge() {
val listState =
EditTileListState(TestEditTiles, TestLargeTilesSpecs, columns = 4, largeTilesSpan = 2)
- composeRule.setContent { EditTileGridUnderTest(listState) }
+ var resizedAction: EditAction.ResizeTile? = null
+ composeRule.setContent { EditTileGridUnderTest(listState) { resizedAction = it } }
composeRule.waitForIdle()
composeRule
- .onAllNodesWithText("tileA")
- .onFirst()
+ .onNodeWithContentDescription("tileA")
.performCustomAccessibilityActionWithLabel(
context.getString(R.string.accessibility_qs_edit_toggle_tile_size_action)
)
assertTileHasWidth(listState.tiles, "tileA", 2)
+ assertThat(resizedAction!!.tileSpec).isEqualTo(TestEditTiles[0].tileSpec)
+ assertThat(resizedAction!!.toIcon).isFalse()
}
@Test
fun toggleLargeTileWithA11yAction_shouldBeIcon() {
val listState =
EditTileListState(TestEditTiles, TestLargeTilesSpecs, columns = 4, largeTilesSpan = 2)
- composeRule.setContent { EditTileGridUnderTest(listState) }
+ var resizedAction: EditAction.ResizeTile? = null
+ composeRule.setContent { EditTileGridUnderTest(listState) { resizedAction = it } }
composeRule.waitForIdle()
composeRule
- .onAllNodesWithText("tileD_large")
- .onFirst()
+ .onNodeWithContentDescription("tileD_large")
.performCustomAccessibilityActionWithLabel(
context.getString(R.string.accessibility_qs_edit_toggle_tile_size_action)
)
assertTileHasWidth(listState.tiles, "tileD_large", 1)
+ assertThat(resizedAction!!.tileSpec).isEqualTo(TestEditTiles[3].tileSpec)
+ assertThat(resizedAction!!.toIcon).isTrue()
}
@Test
fun tapOnIconResizingHandle_shouldBeLarge() {
val listState =
EditTileListState(TestEditTiles, TestLargeTilesSpecs, columns = 4, largeTilesSpan = 2)
- composeRule.setContent { EditTileGridUnderTest(listState) }
+ var resizedAction: EditAction.ResizeTile? = null
+ composeRule.setContent { EditTileGridUnderTest(listState) { resizedAction = it } }
composeRule.waitForIdle()
composeRule
- .onAllNodesWithText("tileA")
- .onFirst()
+ .onNodeWithContentDescription("tileA")
.performClick() // Select
.performTouchInput { // Tap on resizing handle
click(centerRight)
@@ -135,18 +145,20 @@
composeRule.waitForIdle()
assertTileHasWidth(listState.tiles, "tileA", 2)
+ assertThat(resizedAction!!.tileSpec).isEqualTo(TestEditTiles[0].tileSpec)
+ assertThat(resizedAction!!.toIcon).isFalse()
}
@Test
fun tapOnLargeResizingHandle_shouldBeIcon() {
val listState =
EditTileListState(TestEditTiles, TestLargeTilesSpecs, columns = 4, largeTilesSpan = 2)
- composeRule.setContent { EditTileGridUnderTest(listState) }
+ var resizedAction: EditAction.ResizeTile? = null
+ composeRule.setContent { EditTileGridUnderTest(listState) { resizedAction = it } }
composeRule.waitForIdle()
composeRule
- .onAllNodesWithText("tileD_large")
- .onFirst()
+ .onNodeWithContentDescription("tileD_large")
.performClick() // Select
.performTouchInput { // Tap on resizing handle
click(centerRight)
@@ -154,18 +166,20 @@
composeRule.waitForIdle()
assertTileHasWidth(listState.tiles, "tileD_large", 1)
+ assertThat(resizedAction!!.tileSpec).isEqualTo(TestEditTiles[3].tileSpec)
+ assertThat(resizedAction!!.toIcon).isTrue()
}
@Test
fun resizedIcon_shouldBeLarge() {
val listState =
EditTileListState(TestEditTiles, TestLargeTilesSpecs, columns = 4, largeTilesSpan = 2)
- composeRule.setContent { EditTileGridUnderTest(listState) }
+ var resizedAction: EditAction.ResizeTile? = null
+ composeRule.setContent { EditTileGridUnderTest(listState) { resizedAction = it } }
composeRule.waitForIdle()
composeRule
- .onAllNodesWithText("tileA")
- .onFirst()
+ .onNodeWithContentDescription("tileA")
.performClick() // Select
.performTouchInput { // Resize up
swipeRight(startX = right, endX = right * 2)
@@ -173,18 +187,20 @@
composeRule.waitForIdle()
assertTileHasWidth(listState.tiles, "tileA", 2)
+ assertThat(resizedAction!!.tileSpec).isEqualTo(TestEditTiles[0].tileSpec)
+ assertThat(resizedAction!!.toIcon).isFalse()
}
@Test
fun resizedLarge_shouldBeIcon() {
val listState =
EditTileListState(TestEditTiles, TestLargeTilesSpecs, columns = 4, largeTilesSpan = 2)
- composeRule.setContent { EditTileGridUnderTest(listState) }
+ var resizedAction: EditAction.ResizeTile? = null
+ composeRule.setContent { EditTileGridUnderTest(listState) { resizedAction = it } }
composeRule.waitForIdle()
composeRule
- .onAllNodesWithText("tileD_large")
- .onFirst()
+ .onNodeWithContentDescription("tileD_large")
.performClick() // Select
.performTouchInput { // Resize down
swipeLeft()
@@ -192,6 +208,39 @@
composeRule.waitForIdle()
assertTileHasWidth(listState.tiles, "tileD_large", 1)
+ assertThat(resizedAction!!.tileSpec).isEqualTo(TestEditTiles[3].tileSpec)
+ assertThat(resizedAction!!.toIcon).isTrue()
+ }
+
+ @Test
+ fun resizedIconFromEdge_shouldBeLarge() {
+ val testTiles =
+ listOf(
+ createEditTile("tileA"),
+ createEditTile("tileB"),
+ createEditTile("tileC"),
+ createEditTile("tileD"),
+ )
+ val listState = EditTileListState(testTiles, emptySet(), columns = 4, largeTilesSpan = 2)
+ var resizedAction: EditAction.ResizeTile? = null
+
+ composeRule.setContent {
+ EditTileGridUnderTest(listState, testTiles) { resizedAction = it }
+ }
+ composeRule.waitForIdle()
+
+ composeRule
+ .onNodeWithContentDescription("tileD")
+ .performClick() // Select
+ .performTouchInput { // Tap on resizing handle
+ click(centerRight)
+ }
+ composeRule.waitForIdle()
+
+ // Assert that tileD was resized to large
+ assertTileHasWidth(listState.tiles, "tileD", 2)
+ assertThat(resizedAction!!.tileSpec).isEqualTo(testTiles[3].tileSpec)
+ assertThat(resizedAction!!.toIcon).isFalse()
}
private fun assertTileHasWidth(tiles: List<GridCell>, spec: String, expectedWidth: Int) {
@@ -209,7 +258,9 @@
label = AnnotatedString(tileSpec),
appName = null,
isCurrent = true,
+ isDualTarget = false,
availableEditActions = emptySet(),
+ appIcon = null,
category = TileCategory.UNKNOWN,
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/CastDetailsContentTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/CastDetailsContentTest.kt
new file mode 100644
index 0000000..ffce917
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/CastDetailsContentTest.kt
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.dialog
+
+import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithContentDescription
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.test.onNodeWithText
+import androidx.compose.ui.test.performClick
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.internal.app.MediaRouteChooserContentManager
+import com.android.internal.app.MediaRouteControllerContentManager
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.qs.tiles.impl.mediaroute.mediaRouteChooserContentManager
+import com.android.systemui.qs.tiles.impl.mediaroute.mediaRouteControllerContentManager
+import com.android.systemui.testKosmos
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.any
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.stub
+import org.mockito.kotlin.verify
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class CastDetailsContentTest : SysuiTestCase() {
+ @get:Rule val composeRule = createComposeRule()
+ private val kosmos = testKosmos()
+ private val chooserContentManager: MediaRouteChooserContentManager =
+ kosmos.mediaRouteChooserContentManager
+ private val controllerContentManager: MediaRouteControllerContentManager =
+ kosmos.mediaRouteControllerContentManager
+ private val viewModel: CastDetailsViewModel =
+ mock<CastDetailsViewModel> {
+ on { createChooserContentManager() } doReturn chooserContentManager
+ on { createControllerContentManager() } doReturn controllerContentManager
+ }
+
+ @Test
+ fun shouldShowChooserDialogTrue_showChooserUI() {
+ viewModel.stub { on { shouldShowChooserDialog() } doReturn true }
+
+ composeRule.setContent { CastDetailsContent(viewModel) }
+ composeRule.waitForIdle()
+
+ composeRule.onNodeWithTag(CastDetailsViewModel.CHOOSER_VIEW_TEST_TAG).assertExists()
+ composeRule
+ .onNodeWithTag(CastDetailsViewModel.CONTROLLER_VIEW_TEST_TAG)
+ .assertDoesNotExist()
+ composeRule.onNodeWithContentDescription("device icon").assertDoesNotExist()
+ composeRule.onNodeWithText("Disconnect").assertDoesNotExist()
+
+ verify(chooserContentManager).bindViews(any())
+ verify(chooserContentManager).onAttachedToWindow()
+ }
+
+ @Test
+ fun shouldShowChooserDialogFalse_showControllerUI() {
+ viewModel.stub { on { shouldShowChooserDialog() } doReturn false }
+
+ composeRule.setContent { CastDetailsContent(viewModel) }
+ composeRule.waitForIdle()
+
+ composeRule.onNodeWithTag(CastDetailsViewModel.CONTROLLER_VIEW_TEST_TAG).assertExists()
+ composeRule.onNodeWithContentDescription("device icon").assertExists()
+ composeRule.onNodeWithText("Disconnect").assertExists()
+ composeRule.onNodeWithTag(CastDetailsViewModel.CHOOSER_VIEW_TEST_TAG).assertDoesNotExist()
+
+ verify(controllerContentManager).bindViews(any())
+ verify(controllerContentManager).onAttachedToWindow()
+ }
+
+ @Test
+ fun clickOnDisconnectButton_shouldCallDisconnect() {
+ viewModel.stub { on { shouldShowChooserDialog() } doReturn false }
+
+ composeRule.setContent { CastControllerDisconnectButton(controllerContentManager) }
+ composeRule.waitForIdle()
+
+ composeRule.onNodeWithText("Disconnect").assertIsDisplayed().performClick()
+ composeRule.waitForIdle()
+
+ verify(controllerContentManager).onDisconnectButtonClick()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentManagerTest.kt
index 3f408cc..8134c04 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentManagerTest.kt
@@ -19,6 +19,7 @@
import android.content.Intent
import android.os.Handler
import android.os.fakeExecutorHandler
+import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.telephony.SubscriptionManager
import android.telephony.TelephonyManager
@@ -90,6 +91,7 @@
private var airplaneModeSummaryText: TextView? = null
private var mockitoSession: MockitoSession? = null
private var sharedWifiButton: LinearLayout? = null
+ private var addNetworkButton: LinearLayout? = null
private lateinit var contentView: View
@Before
@@ -152,6 +154,7 @@
wifiScanNotify = contentView.requireViewById(R.id.wifi_scan_notify_layout)
airplaneModeSummaryText = contentView.requireViewById(R.id.airplane_mode_summary)
sharedWifiButton = contentView.requireViewById(R.id.share_wifi_button)
+ addNetworkButton = contentView.requireViewById(R.id.add_network_button)
}
@After
@@ -406,6 +409,7 @@
val secondaryLayout =
contentView.requireViewById<LinearLayout>(R.id.secondary_mobile_network_layout)
assertThat(secondaryLayout.visibility).isEqualTo(View.GONE)
+ assertThat(addNetworkButton!!.visibility).isEqualTo(View.GONE)
}
}
@@ -422,6 +426,7 @@
internetDetailsContentManager.lifecycleOwner!!
) {
assertThat(connectedWifi!!.visibility).isEqualTo(View.GONE)
+ assertThat(addNetworkButton!!.visibility).isEqualTo(View.GONE)
}
}
@@ -441,6 +446,7 @@
assertThat(wifiList!!.visibility).isEqualTo(View.VISIBLE)
verify(internetAdapter).setMaxEntriesCount(3)
assertThat(seeAll!!.visibility).isEqualTo(View.INVISIBLE)
+ assertThat(addNetworkButton!!.visibility).isEqualTo(View.GONE)
}
}
@@ -460,6 +466,7 @@
assertThat(wifiList!!.visibility).isEqualTo(View.VISIBLE)
verify(internetAdapter).setMaxEntriesCount(3)
assertThat(seeAll!!.visibility).isEqualTo(View.INVISIBLE)
+ assertThat(addNetworkButton!!.visibility).isEqualTo(View.GONE)
}
}
@@ -478,6 +485,7 @@
assertThat(wifiList!!.visibility).isEqualTo(View.VISIBLE)
verify(internetAdapter).setMaxEntriesCount(2)
assertThat(seeAll!!.visibility).isEqualTo(View.INVISIBLE)
+ assertThat(addNetworkButton!!.visibility).isEqualTo(View.GONE)
}
}
@@ -498,6 +506,7 @@
assertThat(wifiList!!.visibility).isEqualTo(View.VISIBLE)
verify(internetAdapter).setMaxEntriesCount(3)
assertThat(seeAll!!.visibility).isEqualTo(View.VISIBLE)
+ assertThat(addNetworkButton!!.visibility).isEqualTo(View.GONE)
}
}
@@ -517,10 +526,12 @@
assertThat(wifiList!!.visibility).isEqualTo(View.VISIBLE)
verify(internetAdapter).setMaxEntriesCount(2)
assertThat(seeAll!!.visibility).isEqualTo(View.VISIBLE)
+ assertThat(addNetworkButton!!.visibility).isEqualTo(View.GONE)
}
}
@Test
+ @EnableFlags(Flags.FLAG_QS_WIFI_CONFIG)
fun updateContent_deviceLockedAndNoConnectedWifi_showWifiToggle() {
// The preconditions WiFi entries are already in setUp()
whenever(internetDetailsContentController.isDeviceLocked).thenReturn(true)
@@ -538,10 +549,12 @@
assertThat(connectedWifi!!.visibility).isEqualTo(View.GONE)
assertThat(wifiList!!.visibility).isEqualTo(View.GONE)
assertThat(seeAll!!.visibility).isEqualTo(View.GONE)
+ assertThat(addNetworkButton!!.visibility).isEqualTo(View.GONE)
}
}
@Test
+ @EnableFlags(Flags.FLAG_QS_WIFI_CONFIG)
fun updateContent_deviceLockedAndHasConnectedWifi_showWifiToggleWithBackground() {
// The preconditions WiFi ON and WiFi entries are already in setUp()
whenever(internetDetailsContentController.isDeviceLocked).thenReturn(true)
@@ -558,6 +571,41 @@
assertThat(connectedWifi!!.visibility).isEqualTo(View.GONE)
assertThat(wifiList!!.visibility).isEqualTo(View.GONE)
assertThat(seeAll!!.visibility).isEqualTo(View.GONE)
+ assertThat(addNetworkButton!!.visibility).isEqualTo(View.GONE)
+ }
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_QS_WIFI_CONFIG)
+ fun updateContent_showAddNetworkButton() {
+ // The preconditions WiFi ON and WiFi entries are already in setUp()
+ internetDetailsContentManager.wifiEntriesCount =
+ InternetDetailsContentController.MAX_WIFI_ENTRY_COUNT - 1
+ internetDetailsContentManager.hasMoreWifiEntries = true
+ internetDetailsContentManager.updateContent(false)
+ bgExecutor.runAllReady()
+
+ internetDetailsContentManager.internetContentData.observe(
+ internetDetailsContentManager.lifecycleOwner!!
+ ) {
+ assertThat(addNetworkButton!!.visibility).isEqualTo(View.VISIBLE)
+ }
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_QS_WIFI_CONFIG)
+ fun updateContent_notShowAddNetworkButtonWhenFlagDisabled() {
+ // The preconditions WiFi ON and WiFi entries are already in setUp()
+ internetDetailsContentManager.wifiEntriesCount =
+ InternetDetailsContentController.MAX_WIFI_ENTRY_COUNT - 1
+ internetDetailsContentManager.hasMoreWifiEntries = true
+ internetDetailsContentManager.updateContent(false)
+ bgExecutor.runAllReady()
+
+ internetDetailsContentManager.internetContentData.observe(
+ internetDetailsContentManager.lifecycleOwner!!
+ ) {
+ assertThat(addNetworkButton!!.visibility).isEqualTo(View.GONE)
}
}
@@ -609,7 +657,7 @@
internetDetailsContentManager.lifecycleOwner!!
) {
val primaryLayout =
- contentView.requireViewById<LinearLayout>(R.id.mobile_network_layout)
+ contentView.requireViewById<LinearLayout>(R.id.mobile_connected_layout)
val secondaryLayout =
contentView.requireViewById<LinearLayout>(R.id.secondary_mobile_network_layout)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeHeaderControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeHeaderControllerTest.kt
index 0ed23d1..f307243 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeHeaderControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeHeaderControllerTest.kt
@@ -800,43 +800,6 @@
}
@Test
- fun sameInsetsTwice_listenerCallsOnApplyWindowInsetsOnlyOnce() {
- val windowInsets = createWindowInsets()
-
- val captor = ArgumentCaptor.forClass(View.OnApplyWindowInsetsListener::class.java)
- verify(view).setOnApplyWindowInsetsListener(capture(captor))
-
- val listener = captor.value
-
- listener.onApplyWindowInsets(view, windowInsets)
-
- verify(view, times(1)).onApplyWindowInsets(any())
-
- listener.onApplyWindowInsets(view, windowInsets)
-
- verify(view, times(1)).onApplyWindowInsets(any())
- }
-
- @Test
- fun twoDifferentInsets_listenerCallsOnApplyWindowInsetsTwice() {
- val windowInsets1 = WindowInsets(Rect(1, 2, 3, 4))
- val windowInsets2 = WindowInsets(Rect(5, 6, 7, 8))
-
- val captor = ArgumentCaptor.forClass(View.OnApplyWindowInsetsListener::class.java)
- verify(view).setOnApplyWindowInsetsListener(capture(captor))
-
- val listener = captor.value
-
- listener.onApplyWindowInsets(view, windowInsets1)
-
- verify(view, times(1)).onApplyWindowInsets(any())
-
- listener.onApplyWindowInsets(view, windowInsets2)
-
- verify(view, times(2)).onApplyWindowInsets(any())
- }
-
- @Test
fun alarmIconNotIgnored() {
verify(statusIcons, Mockito.never())
.addIgnoredSlot(context.getString(com.android.internal.R.string.status_bar_alarm_clock))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextViewTest.kt
index bc16f7e..4c029d9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextViewTest.kt
@@ -19,7 +19,7 @@
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.plugins.clocks.ClockLogger
+import com.android.systemui.customization.clocks.ClockLogger
import com.android.systemui.plugins.clocks.ClockMessageBuffers
import com.android.systemui.plugins.clocks.ClockSettings
import com.android.systemui.shared.clocks.ClockContext
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/featurepods/popups/ui/viewmodel/StatusBarPopupChipsViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/featurepods/popups/ui/viewmodel/StatusBarPopupChipsViewModelTest.kt
index 510d6fe..c29029a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/featurepods/popups/ui/viewmodel/StatusBarPopupChipsViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/featurepods/popups/ui/viewmodel/StatusBarPopupChipsViewModelTest.kt
@@ -27,12 +27,10 @@
import com.android.systemui.lifecycle.activateIn
import com.android.systemui.media.controls.data.repository.mediaFilterRepository
import com.android.systemui.media.controls.shared.model.MediaData
-import com.android.systemui.media.controls.shared.model.MediaDataLoadingModel
import com.android.systemui.statusbar.featurepods.popups.StatusBarPopupChips
import com.android.systemui.statusbar.featurepods.popups.shared.model.PopupChipId
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -61,10 +59,8 @@
kosmos.runTest {
val shownPopupChips = underTest.shownPopupChips
val userMedia = MediaData(active = true, song = "test")
- val instanceId = userMedia.instanceId
- mediaFilterRepository.addSelectedUserMediaEntry(userMedia)
- mediaFilterRepository.addMediaDataLoadingState(MediaDataLoadingModel.Loaded(instanceId))
+ mediaFilterRepository.addCurrentUserMediaEntry(userMedia)
Snapshot.takeSnapshot {
assertThat(shownPopupChips).hasSize(1)
@@ -78,10 +74,8 @@
val shownPopupChips = underTest.shownPopupChips
val userMedia = MediaData(active = true, song = "test")
- val instanceId = userMedia.instanceId
- mediaFilterRepository.addSelectedUserMediaEntry(userMedia)
- mediaFilterRepository.addMediaDataLoadingState(MediaDataLoadingModel.Loaded(instanceId))
+ mediaFilterRepository.addCurrentUserMediaEntry(userMedia)
Snapshot.takeSnapshot {
assertThat(shownPopupChips).hasSize(1)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/gesture/GenericGestureDetectorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/gesture/GenericGestureDetectorTest.kt
index 0f58990..5776e02 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/gesture/GenericGestureDetectorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/gesture/GenericGestureDetectorTest.kt
@@ -14,7 +14,7 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
-@TestableLooper.RunWithLooper
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
class GenericGestureDetectorTest : SysuiTestCase() {
private lateinit var gestureDetector: TestGestureDetector
@@ -33,23 +33,23 @@
@Test
fun callbackRegistered_isGestureListening() {
- gestureDetector.addOnGestureDetectedCallback("tag"){}
+ gestureDetector.addOnGestureDetectedCallback("tag") {}
assertThat(gestureDetector.isGestureListening).isTrue()
}
@Test
fun multipleCallbacksRegistered_isGestureListening() {
- gestureDetector.addOnGestureDetectedCallback("tag"){}
- gestureDetector.addOnGestureDetectedCallback("tag2"){}
+ gestureDetector.addOnGestureDetectedCallback("tag") {}
+ gestureDetector.addOnGestureDetectedCallback("tag2") {}
assertThat(gestureDetector.isGestureListening).isTrue()
}
@Test
fun allCallbacksUnregistered_notGestureListening() {
- gestureDetector.addOnGestureDetectedCallback("tag"){}
- gestureDetector.addOnGestureDetectedCallback("tag2"){}
+ gestureDetector.addOnGestureDetectedCallback("tag") {}
+ gestureDetector.addOnGestureDetectedCallback("tag2") {}
gestureDetector.removeOnGestureDetectedCallback("tag")
gestureDetector.removeOnGestureDetectedCallback("tag2")
@@ -59,8 +59,8 @@
@Test
fun someButNotAllCallbacksUnregistered_isGestureListening() {
- gestureDetector.addOnGestureDetectedCallback("tag"){}
- gestureDetector.addOnGestureDetectedCallback("tag2"){}
+ gestureDetector.addOnGestureDetectedCallback("tag") {}
+ gestureDetector.addOnGestureDetectedCallback("tag2") {}
gestureDetector.removeOnGestureDetectedCallback("tag2")
@@ -70,9 +70,7 @@
@Test
fun onInputEvent_meetsGestureCriteria_allCallbacksNotified() {
var callbackNotified = false
- gestureDetector.addOnGestureDetectedCallback("tag"){
- callbackNotified = true
- }
+ gestureDetector.addOnGestureDetectedCallback("tag") { callbackNotified = true }
gestureDetector.onInputEvent(
MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, CORRECT_X, 0f, 0)
@@ -84,9 +82,7 @@
@Test
fun onInputEvent_doesNotMeetGestureCriteria_callbackNotNotified() {
var callbackNotified = false
- gestureDetector.addOnGestureDetectedCallback("tag"){
- callbackNotified = true
- }
+ gestureDetector.addOnGestureDetectedCallback("tag") { callbackNotified = true }
gestureDetector.onInputEvent(
MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, CORRECT_X - 5, 0f, 0)
@@ -98,30 +94,19 @@
@Test
fun callbackUnregisteredThenGestureDetected_oldCallbackNotNotified() {
var oldCallbackNotified = false
- gestureDetector.addOnGestureDetectedCallback("tag"){
- oldCallbackNotified = true
- }
- gestureDetector.addOnGestureDetectedCallback("tag2"){}
+ gestureDetector.addOnGestureDetectedCallback("tag") { oldCallbackNotified = true }
+ gestureDetector.addOnGestureDetectedCallback("tag2") {}
gestureDetector.removeOnGestureDetectedCallback("tag")
gestureDetector.onInputEvent(
- MotionEvent.obtain(
- 0,
- 0,
- MotionEvent.ACTION_DOWN,
- CORRECT_X,
- 0f,
- 0
- )
+ MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, CORRECT_X, 0f, 0)
)
assertThat(oldCallbackNotified).isFalse()
}
- inner class TestGestureDetector : GenericGestureDetector(
- "fakeTag",
- displayTracker.defaultDisplayId
- ) {
+ inner class TestGestureDetector :
+ GenericGestureDetector("fakeTag", displayTracker.defaultDisplayId) {
var isGestureListening = false
override fun onInputEvent(ev: InputEvent) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
index aa1b615..b1f49f7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
@@ -617,18 +617,6 @@
}
@Test
- @DisableFlags(com.android.systemui.Flags.FLAG_NOTIFICATION_REENTRANT_DISMISS)
- public void testCanDismiss_immediately() throws Exception {
- ExpandableNotificationRow row = mKosmos.createRow();
- when(mKosmos.getMockNotificationDismissibilityProvider().isDismissable(
- row.getKey()))
- .thenReturn(true);
- row.performDismiss(false);
- verify(mKosmos.getMockNotifCollection()).registerFutureDismissal(any(), anyInt(), any());
- }
-
- @Test
- @EnableFlags(com.android.systemui.Flags.FLAG_NOTIFICATION_REENTRANT_DISMISS)
public void testCanDismiss() throws Exception {
ExpandableNotificationRow row = mKosmos.createRow();
when(mKosmos.getMockNotificationDismissibilityProvider().isDismissable(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java
index 1cc6fb1..7bf67d0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java
@@ -190,8 +190,11 @@
mDependency.injectTestDependency(ShadeController.class, mShadeController);
// Inflate the layout
final LayoutInflater layoutInflater = LayoutInflater.from(mContext);
+ int layoutId = Flags.notificationsRedesignTemplates()
+ ? R.layout.notification_2025_conversation_info
+ : R.layout.notification_conversation_info;
mNotificationInfo = (NotificationConversationInfo) layoutInflater.inflate(
- R.layout.notification_conversation_info,
+ layoutId,
null);
mNotificationInfo.setGutsParent(mNotificationGuts);
doAnswer((Answer<Object>) invocation -> {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index 505827d..fa90ab7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -48,6 +48,7 @@
import android.annotation.DimenRes;
import android.graphics.Insets;
import android.graphics.Rect;
+import android.graphics.RectF;
import android.os.SystemClock;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
@@ -1272,7 +1273,7 @@
// WHEN we generate a disappear event
mStackScroller.generateHeadsUpAnimation(
- row, /* isHeadsUp = */ false, /* hasStatusBarChip= */ false);
+ row, /* isHeadsUp = */ false, /* statusBarChipBounds= */ null);
// THEN headsUpAnimatingAway is true
verify(headsUpAnimatingAwayListener).accept(true);
@@ -1289,7 +1290,7 @@
prepareStackScrollerForHunAnimations(headsUpAnimatingAwayListener);
mStackScroller.generateHeadsUpAnimation(
- row, /* isHeadsUp = */ false, /* hasStatusBarChip= */ true);
+ row, /* isHeadsUp = */ false, /* statusBarChipBounds= */ new RectF(0f, 0f, 1f, 1f));
verify(row, never()).setHasStatusBarChipDuringHeadsUpAnimation(anyBoolean());
}
@@ -1304,7 +1305,7 @@
prepareStackScrollerForHunAnimations(headsUpAnimatingAwayListener);
mStackScroller.generateHeadsUpAnimation(
- row, /* isHeadsUp = */ false, /* hasStatusBarChip= */ false);
+ row, /* isHeadsUp = */ false, /* statusBarChipBounds= */ null);
verify(row).setHasStatusBarChipDuringHeadsUpAnimation(false);
}
@@ -1319,7 +1320,7 @@
prepareStackScrollerForHunAnimations(headsUpAnimatingAwayListener);
mStackScroller.generateHeadsUpAnimation(
- row, /* isHeadsUp = */ false, /* hasStatusBarChip= */ true);
+ row, /* isHeadsUp = */ false, /* statusBarChipBounds= */ new RectF(0f, 0f, 1f, 1f));
verify(row).setHasStatusBarChipDuringHeadsUpAnimation(true);
}
@@ -1337,7 +1338,7 @@
// WHEN we generate a disappear event
mStackScroller.generateHeadsUpAnimation(
- row, /* isHeadsUp = */ false, /* hasStatusBarChip= */ false);
+ row, /* isHeadsUp = */ false, /* statusBarChipBounds= */ null);
// THEN nothing happens
verify(headsUpAnimatingAwayListener, never()).accept(anyBoolean());
@@ -1353,11 +1354,11 @@
prepareStackScrollerForHunAnimations(headsUpAnimatingAwayListener);
// BUT there is a pending appear event
mStackScroller.generateHeadsUpAnimation(
- row, /* isHeadsUp = */ true, /* hasStatusBarChip= */ false);
+ row, /* isHeadsUp = */ true, /* statusBarChipBounds= */ null);
// WHEN we generate a disappear event
mStackScroller.generateHeadsUpAnimation(
- row, /* isHeadsUp = */ false, /* hasStatusBarChip= */ false);
+ row, /* isHeadsUp = */ false, /* statusBarChipBounds= */ null);
// THEN nothing happens
verify(headsUpAnimatingAwayListener, never()).accept(anyBoolean());
@@ -1374,7 +1375,7 @@
// WHEN we generate a disappear event
mStackScroller.generateHeadsUpAnimation(
- row, /* isHeadsUp = */ true, /* hasStatusBarChip= */ false);
+ row, /* isHeadsUp = */ true, /* statusBarChipBounds= */ null);
// THEN headsUpAnimatingWay is not set
verify(headsUpAnimatingAwayListener, never()).accept(anyBoolean());
@@ -1403,7 +1404,7 @@
// WHEN we generate an add event
mStackScroller.generateHeadsUpAnimation(
- row, /* isHeadsUp = */ true, /* hasStatusBarChip= */ false);
+ row, /* isHeadsUp = */ true, /* statusBarChipBounds= */ null);
// THEN nothing happens
assertThat(mStackScroller.isAddOrRemoveAnimationPending()).isFalse();
@@ -1419,7 +1420,7 @@
// AND there is a HUN animating away
mStackScroller.generateHeadsUpAnimation(
- row, /* isHeadsUp = */ false, /* hasStatusBarChip= */ false);
+ row, /* isHeadsUp = */ false, /* statusBarChipBounds= */ null);
assertTrue("a HUN should be animating away", mStackScroller.mHeadsUpAnimatingAway);
// WHEN the child animations are finished
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
index b17a58f..579cef7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
@@ -82,7 +82,11 @@
@After
public void tearDown() {
- mStaticMockSession.finishMocking();
+
+ if (mStaticMockSession != null) {
+ mStaticMockSession.finishMocking();
+ }
+
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index 31afaeb..1fc3281 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -82,8 +82,6 @@
import com.android.systemui.shade.transition.LinearLargeScreenShadeInterpolator;
import com.android.systemui.statusbar.policy.FakeConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.util.concurrency.FakeExecutor;
-import com.android.systemui.util.time.FakeSystemClock;
import com.android.systemui.util.wakelock.DelayedWakeLock;
import com.android.systemui.utils.os.FakeHandler;
@@ -282,8 +280,6 @@
mKeyguardUpdateMonitor,
mDockManager,
mConfigurationController,
- new FakeExecutor(new FakeSystemClock()),
- mKosmos.getJavaAdapter(),
mScreenOffAnimationController,
mKeyguardUnlockAnimationController,
mStatusBarKeyguardViewManager,
@@ -1234,8 +1230,6 @@
mKeyguardUpdateMonitor,
mDockManager,
mConfigurationController,
- new FakeExecutor(new FakeSystemClock()),
- mKosmos.getJavaAdapter(),
mScreenOffAnimationController,
mKeyguardUnlockAnimationController,
mStatusBarKeyguardViewManager,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java
index b085e6d..b217c6e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java
@@ -71,7 +71,7 @@
// NOTE: This @DisabledOnRavenwood annotation is inherited to all subclasses (unless overridden
// via a more-specific @EnabledOnRavenwood annotation); this means that by default all
// subclasses will be "ignored" when executed on the Ravenwood testing environment; more
-// background on Ravenwood is available at go/ravenwood-docs
+// background on Ravenwood is available at go/ravenwood
@DisabledOnRavenwood
public abstract class SysuiTestCase {
/**
@@ -192,9 +192,12 @@
private Instrumentation mRealInstrumentation;
private SysuiTestDependency mSysuiDependency;
+ static {
+ assertTempFilesAreCreatable();
+ }
+
@Before
public void SysuiSetup() throws Exception {
- assertTempFilesAreCreatable();
ProtoLog.REQUIRE_PROTOLOGTOOL = false;
mSysuiDependency = new SysuiTestDependency(mContext, shouldFailOnLeakedReceiver());
mDependency = mSysuiDependency.install();
@@ -217,18 +220,10 @@
}
private static Boolean sCanCreateTempFiles = null;
-
- private static void assertTempFilesAreCreatable() {
+ private static void assertTempFilesAreCreatable() throws RuntimeException {
// TODO(b/391948934): hopefully remove this hack
if (sCanCreateTempFiles == null) {
- try {
- File tempFile = File.createTempFile("confirm_temp_file_createable", "txt");
- sCanCreateTempFiles = true;
- assertTrue(tempFile.delete());
- } catch (IOException e) {
- sCanCreateTempFiles = false;
- throw new RuntimeException(e);
- }
+ attemptToCreateTempFile();
}
if (!sCanCreateTempFiles) {
Assert.fail(
@@ -238,6 +233,32 @@
}
}
+ private static void attemptToCreateTempFile() throws RuntimeException {
+ // If I understand https://buganizer.corp.google.com/issues/123230176#comment11 correctly,
+ // we may have to wait for the temp folder to become available.
+ int retriesRemaining = 20;
+ IOException latestFailure = null;
+ while (sCanCreateTempFiles == null && retriesRemaining > 0) {
+ retriesRemaining--;
+ try {
+ File tempFile = File.createTempFile("confirm_temp_file_createable", "txt");
+ sCanCreateTempFiles = true;
+ assertTrue(tempFile.delete());
+ return;
+ } catch (IOException e) {
+ latestFailure = e;
+ try {
+ Thread.sleep(500);
+ } catch (InterruptedException ie) {
+ // just keep waiting
+ }
+ }
+ }
+
+ sCanCreateTempFiles = false;
+ throw new RuntimeException(latestFailure);
+ }
+
protected boolean shouldFailOnLeakedReceiver() {
return false;
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/accessibility/data/repository/FakeAccessibilityRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/accessibility/data/repository/FakeAccessibilityRepository.kt
index c369237..25ae8f5 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/accessibility/data/repository/FakeAccessibilityRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/accessibility/data/repository/FakeAccessibilityRepository.kt
@@ -28,8 +28,10 @@
class FakeAccessibilityRepository(
override val isTouchExplorationEnabled: MutableStateFlow<Boolean>,
override val isEnabled: MutableStateFlow<Boolean>,
+ override val isEnabledFiltered: MutableStateFlow<Boolean>,
) : AccessibilityRepository {
- @Inject constructor() : this(MutableStateFlow(false), MutableStateFlow(false))
+ @Inject
+ constructor() : this(MutableStateFlow(false), MutableStateFlow(false), MutableStateFlow(false))
private var recommendedTimeout: Duration = 0.milliseconds
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeBiometricStatusRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeBiometricStatusRepository.kt
index dce4775..e9b7a69 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeBiometricStatusRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeBiometricStatusRepository.kt
@@ -17,9 +17,7 @@
package com.android.systemui.biometrics.data.repository
-import android.hardware.biometrics.BiometricSourceType
import com.android.systemui.biometrics.shared.model.AuthenticationReason
-import com.android.systemui.biometrics.shared.model.AuthenticationState
import com.android.systemui.keyguard.shared.model.FingerprintAuthenticationStatus
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
@@ -38,16 +36,6 @@
override val fingerprintAcquiredStatus: Flow<FingerprintAuthenticationStatus> =
_fingerprintAcquiredStatus.asStateFlow().filterNotNull()
- private val _authenticationState =
- MutableStateFlow<AuthenticationState>(
- AuthenticationState.Idle(
- BiometricSourceType.FINGERPRINT,
- AuthenticationReason.NotRunning,
- )
- )
- override val authenticationState: StateFlow<AuthenticationState> =
- _authenticationState.asStateFlow()
-
fun setFingerprintAuthenticationReason(reason: AuthenticationReason) {
_fingerprintAuthenticationReason.value = reason
}
@@ -55,8 +43,4 @@
fun setFingerprintAcquiredStatus(status: FingerprintAuthenticationStatus) {
_fingerprintAcquiredStatus.value = status
}
-
- fun setAuthenticationState(state: AuthenticationState) {
- _authenticationState.value = state
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/model/DualShadeEducationalTooltipModel.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/domain/interactor/FakeCredentialInteractorKosmos.kt
similarity index 72%
copy from packages/SystemUI/src/com/android/systemui/scene/domain/model/DualShadeEducationalTooltipModel.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/domain/interactor/FakeCredentialInteractorKosmos.kt
index f63e132..6e77bf4 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/model/DualShadeEducationalTooltipModel.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/domain/interactor/FakeCredentialInteractorKosmos.kt
@@ -14,11 +14,9 @@
* limitations under the License.
*/
-package com.android.systemui.scene.domain.model
+package com.android.systemui.biometrics.domain.interactor
-/** Enumerates the type of tooltip to show. */
-enum class DualShadeEducationalTooltipModel {
- None,
- ForNotificationsShade,
- ForQuickSettingsShade,
-}
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+
+val Kosmos.credentialInteractor by Fixture { FakeCredentialInteractor() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/domain/interactor/FingerprintPropertyInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/domain/interactor/FingerprintPropertyInteractorKosmos.kt
index 7621d49..d1709d6 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/domain/interactor/FingerprintPropertyInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/domain/interactor/FingerprintPropertyInteractorKosmos.kt
@@ -22,8 +22,6 @@
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
import com.android.systemui.kosmos.applicationCoroutineScope
-import com.android.systemui.kosmos.testDispatcher
-import com.android.systemui.util.settings.fakeSettings
val Kosmos.fingerprintPropertyInteractor by Fixture {
FingerprintPropertyInteractor(
@@ -33,7 +31,5 @@
configurationInteractor = configurationInteractor,
displayStateInteractor = displayStateInteractor,
udfpsOverlayInteractor = udfpsOverlayInteractor,
- secureSettings = fakeSettings,
- backgroundDispatcher = testDispatcher,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/domain/interactor/PromptCredentialInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/domain/interactor/PromptCredentialInteractorKosmos.kt
new file mode 100644
index 0000000..0be2c28
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/domain/interactor/PromptCredentialInteractorKosmos.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics.domain.interactor
+
+import com.android.systemui.biometrics.data.repository.promptRepository
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+import kotlinx.coroutines.Dispatchers
+
+val Kosmos.promptCredentialInteractor by Fixture {
+ PromptCredentialInteractor(Dispatchers.Main.immediate, promptRepository, credentialInteractor)
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorKosmos.kt
index 787a471..9ba6fb1 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorKosmos.kt
@@ -27,7 +27,7 @@
fingerprintPropertyRepository = fingerprintPropertyRepository,
displayStateInteractor = displayStateInteractor,
promptRepository = promptRepository,
- credentialInteractor = FakeCredentialInteractor(),
+ credentialInteractor = credentialInteractor,
lockPatternUtils = lockPatternUtils,
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/model/DualShadeEducationalTooltipModel.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/ui/viewmodel/CredentialViewModelKosmos.kt
similarity index 61%
copy from packages/SystemUI/src/com/android/systemui/scene/domain/model/DualShadeEducationalTooltipModel.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/ui/viewmodel/CredentialViewModelKosmos.kt
index f63e132..72df1fc 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/model/DualShadeEducationalTooltipModel.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/ui/viewmodel/CredentialViewModelKosmos.kt
@@ -14,11 +14,13 @@
* limitations under the License.
*/
-package com.android.systemui.scene.domain.model
+package com.android.systemui.biometrics.ui.viewmodel
-/** Enumerates the type of tooltip to show. */
-enum class DualShadeEducationalTooltipModel {
- None,
- ForNotificationsShade,
- ForQuickSettingsShade,
+import android.content.applicationContext
+import com.android.systemui.biometrics.domain.interactor.promptCredentialInteractor
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+
+val Kosmos.credentialViewModel by Fixture {
+ CredentialViewModel(applicationContext, promptCredentialInteractor)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeLightRevealScrimRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeLightRevealScrimRepository.kt
index 4181dc3..2dc4b35 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeLightRevealScrimRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeLightRevealScrimRepository.kt
@@ -40,7 +40,9 @@
override val isAnimating: Boolean
get() = false
- override val maxAlpha: MutableStateFlow<Float> = MutableStateFlow(1f)
+ override val wallpaperSupportsAmbientMode = MutableStateFlow(false)
+
+ override val useDarkWallpaperScrim = MutableStateFlow(false)
override fun startRevealAmountAnimator(reveal: Boolean, duration: Long) {
if (reveal) {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/InWindowLauncherUnlockAnimationInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/InWindowLauncherUnlockAnimationInteractorKosmos.kt
index dbbb203..53925dd 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/InWindowLauncherUnlockAnimationInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/InWindowLauncherUnlockAnimationInteractorKosmos.kt
@@ -28,6 +28,7 @@
InWindowLauncherUnlockAnimationInteractor(
repository = inWindowLauncherUnlockAnimationRepository,
scope = applicationCoroutineScope,
+ backgroundScope = applicationCoroutineScope,
transitionInteractor = keyguardTransitionInteractor,
surfaceBehindRepository = Lazy { keyguardSurfaceBehindRepository },
activityManager = activityManagerWrapper,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorKosmos.kt
index 59174c3..21ba991 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorKosmos.kt
@@ -18,9 +18,9 @@
import android.app.admin.devicePolicyManager
import android.content.applicationContext
-import android.view.accessibility.AccessibilityManager
import com.android.internal.widget.lockPatternUtils
import com.android.keyguard.logging.KeyguardQuickAffordancesLogger
+import com.android.systemui.accessibility.domain.interactor.accessibilityInteractor
import com.android.systemui.animation.dialogTransitionAnimator
import com.android.systemui.dock.dockManager
import com.android.systemui.flags.featureFlagsClassic
@@ -54,7 +54,7 @@
devicePolicyManager = devicePolicyManager,
dockManager = dockManager,
biometricSettingsRepository = biometricSettingsRepository,
- accessibilityManager = mock<AccessibilityManager>(),
+ accessibilityInteractor = accessibilityInteractor,
backgroundDispatcher = testDispatcher,
appContext = applicationContext,
sceneInteractor = { sceneInteractor },
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorKosmos.kt
index 8bb2fce..d044239 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorKosmos.kt
@@ -21,6 +21,7 @@
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.statusbar.notification.domain.interactor.notificationLaunchAnimationInteractor
+import com.android.systemui.statusbar.policy.domain.interactor.deviceProvisioningInteractor
val Kosmos.windowManagerLockscreenVisibilityInteractor by
Kosmos.Fixture {
@@ -36,5 +37,6 @@
sceneInteractor = { sceneInteractor },
deviceEntryInteractor = { deviceEntryInteractor },
wakeToGoneInteractor = keyguardWakeDirectlyToGoneInteractor,
+ deviceProvisioningInteractor = { deviceProvisioningInteractor },
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsKosmos.kt
index 0b364ea..83ffbb9 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsKosmos.kt
@@ -16,6 +16,7 @@
package com.android.systemui.keyguard.ui.viewmodel
+import android.content.applicationContext
import com.android.systemui.bouncer.domain.interactor.mockPrimaryBouncerInteractor
import com.android.systemui.keyguard.domain.interactor.keyguardDismissActionInteractor
import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow
@@ -23,13 +24,16 @@
import com.android.systemui.kosmos.Kosmos.Fixture
import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.statusbar.sysuiStatusBarStateController
+import com.android.systemui.window.domain.interactor.windowRootViewBlurInteractor
val Kosmos.bouncerToGoneFlows by Fixture {
BouncerToGoneFlows(
+ context = applicationContext,
statusBarStateController = sysuiStatusBarStateController,
primaryBouncerInteractor = mockPrimaryBouncerInteractor,
keyguardDismissActionInteractor = { keyguardDismissActionInteractor },
shadeInteractor = shadeInteractor,
animationFlow = keyguardTransitionAnimationFlow,
+ windowRootViewBlurInteractor = windowRootViewBlurInteractor,
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/model/DualShadeEducationalTooltipModel.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/lowlight/AmbientLightModeMonitorKosmos.kt
similarity index 69%
copy from packages/SystemUI/src/com/android/systemui/scene/domain/model/DualShadeEducationalTooltipModel.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/lowlight/AmbientLightModeMonitorKosmos.kt
index f63e132..6e0085e 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/model/DualShadeEducationalTooltipModel.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/lowlight/AmbientLightModeMonitorKosmos.kt
@@ -14,11 +14,10 @@
* limitations under the License.
*/
-package com.android.systemui.scene.domain.model
+package com.android.systemui.lowlight
-/** Enumerates the type of tooltip to show. */
-enum class DualShadeEducationalTooltipModel {
- None,
- ForNotificationsShade,
- ForQuickSettingsShade,
-}
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.lowlightclock.AmbientLightModeMonitor
+
+val Kosmos.ambientLightModeMonitor: AmbientLightModeMonitor by
+ Kosmos.Fixture { FakeAmbientLightModeMonitor() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/lowlight/FakeAmbientLightModeMonitor.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/lowlight/FakeAmbientLightModeMonitor.kt
new file mode 100644
index 0000000..b96fffa
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/lowlight/FakeAmbientLightModeMonitor.kt
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.lowlight
+
+import com.android.systemui.lowlightclock.AmbientLightModeMonitor
+
+class FakeAmbientLightModeMonitor : AmbientLightModeMonitor {
+ @AmbientLightModeMonitor.AmbientLightMode private var ambientLightMode: Int? = null
+ private var callback: AmbientLightModeMonitor.Callback? = null
+ private var _started: Boolean = false
+
+ val started: Boolean
+ get() = _started
+
+ override fun start(callback: AmbientLightModeMonitor.Callback) {
+ _started = true
+ this.callback = callback
+ ambientLightMode?.let { callback.onChange(it) }
+ }
+
+ override fun stop() {
+ _started = false
+ this.callback = null
+ }
+
+ fun setAmbientLightMode(@AmbientLightModeMonitor.AmbientLightMode mode: Int) {
+ ambientLightMode = mode
+ callback?.onChange(mode)
+ }
+}
+
+val AmbientLightModeMonitor.fake
+ get() = this as FakeAmbientLightModeMonitor
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterKosmos.kt
index 0222210..579ef76 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterKosmos.kt
@@ -22,7 +22,6 @@
import com.android.systemui.media.controls.shared.mediaLogger
import com.android.systemui.settings.userTracker
import com.android.systemui.statusbar.notificationLockscreenUserManager
-import com.android.systemui.util.time.fakeSystemClock
val Kosmos.mediaDataFilter by
Kosmos.Fixture {
@@ -30,7 +29,6 @@
userTracker = userTracker,
lockscreenUserManager = notificationLockscreenUserManager,
executor = fakeExecutor,
- systemClock = fakeSystemClock,
mediaFilterRepository = mediaFilterRepository,
mediaLogger = mediaLogger,
)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/media/remedia/data/repository/MediaPipelineRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/media/remedia/data/repository/MediaPipelineRepositoryKosmos.kt
new file mode 100644
index 0000000..f6dc1c6
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/media/remedia/data/repository/MediaPipelineRepositoryKosmos.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.media.remedia.data.repository
+
+import com.android.systemui.Flags
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.media.controls.data.repository.mediaFilterRepository
+
+val Kosmos.mediaPipelineRepository by
+ Kosmos.Fixture {
+ if (Flags.mediaControlsInCompose()) {
+ mediaRepository
+ } else {
+ mediaFilterRepository
+ }
+ }
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/model/DualShadeEducationalTooltipModel.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/media/remedia/data/repository/MediaRepositoryKosmos.kt
similarity index 65%
copy from packages/SystemUI/src/com/android/systemui/scene/domain/model/DualShadeEducationalTooltipModel.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/media/remedia/data/repository/MediaRepositoryKosmos.kt
index f63e132..4ccbb88 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/model/DualShadeEducationalTooltipModel.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/media/remedia/data/repository/MediaRepositoryKosmos.kt
@@ -14,11 +14,11 @@
* limitations under the License.
*/
-package com.android.systemui.scene.domain.model
+package com.android.systemui.media.remedia.data.repository
-/** Enumerates the type of tooltip to show. */
-enum class DualShadeEducationalTooltipModel {
- None,
- ForNotificationsShade,
- ForQuickSettingsShade,
-}
+import android.content.applicationContext
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.util.time.systemClock
+
+val Kosmos.mediaRepository by
+ Kosmos.Fixture { MediaRepositoryImpl(context = applicationContext, systemClock = systemClock) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/model/FakeSceneContainerPlugin.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/model/FakeSceneContainerPlugin.kt
new file mode 100644
index 0000000..ed1df11
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/model/FakeSceneContainerPlugin.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.model
+
+class FakeSceneContainerPlugin : SceneContainerPlugin {
+
+ private val overridesByDisplayId = mutableMapOf<Int, MutableMap<Long, Boolean>>()
+
+ override fun flagValueOverride(flag: Long, displayId: Int): Boolean? {
+ return overridesByDisplayId[displayId]?.get(flag)
+ }
+
+ fun override(flag: Long, value: Boolean?, displayId: Int) {
+ val displaySpecificOverrides = overridesByDisplayId.getOrPut(displayId) { mutableMapOf() }
+ if (value != null) {
+ displaySpecificOverrides[flag] = value
+ } else {
+ displaySpecificOverrides.remove(flag)
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/model/SceneContainerPluginKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/model/SceneContainerPluginKosmos.kt
index 79506f9..3e08670 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/model/SceneContainerPluginKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/model/SceneContainerPluginKosmos.kt
@@ -22,8 +22,10 @@
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.shade.data.repository.fakeShadeDisplaysRepository
-val Kosmos.sceneContainerPlugin by Fixture {
- SceneContainerPlugin(
+val Kosmos.sceneContainerPlugin: SceneContainerPlugin by Fixture { fakeSceneContainerPlugin }
+val Kosmos.fakeSceneContainerPlugin by Fixture { FakeSceneContainerPlugin() }
+val Kosmos.sceneContainerPluginImpl: SceneContainerPlugin by Fixture {
+ SceneContainerPluginImpl(
sceneInteractor = { sceneInteractor },
occlusionInteractor = { sceneContainerOcclusionInteractor },
shadeDisplaysRepository = { fakeShadeDisplaysRepository },
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/AppIconRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/AppIconRepositoryKosmos.kt
new file mode 100644
index 0000000..088092c
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/AppIconRepositoryKosmos.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.panels.data.repository
+
+import android.content.packageManager
+import android.content.pm.ApplicationInfo
+import com.android.systemui.common.shared.model.ContentDescription
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.common.shared.model.asIcon
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.appIconRepository by
+ Kosmos.Fixture {
+ object : AppIconRepository {
+ override fun loadIcon(applicationInfo: ApplicationInfo): Icon.Loaded? {
+ // Return the app icon unmodified
+ return applicationInfo
+ .loadIcon(packageManager)
+ ?.asIcon(ContentDescription.Loaded(null))
+ }
+ }
+ }
+
+val Kosmos.appIconRepositoryFactory by
+ Kosmos.Fixture {
+ object : AppIconRepository.Factory {
+ override fun create(): AppIconRepository {
+ return appIconRepository
+ }
+ }
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/IconAndNameCustomRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/IconAndNameCustomRepositoryKosmos.kt
index d686699..2b85aa3 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/IconAndNameCustomRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/IconAndNameCustomRepositoryKosmos.kt
@@ -30,5 +30,6 @@
installedTilesRepository,
userTracker,
backgroundCoroutineContext,
+ appIconRepositoryFactory,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/ui/model/NewQSTileFactoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/ui/model/NewQSTileFactoryKosmos.kt
index 3c29730..762af694 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/ui/model/NewQSTileFactoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/ui/model/NewQSTileFactoryKosmos.kt
@@ -29,7 +29,7 @@
import com.android.systemui.qs.tiles.base.shared.model.qSTileConfigProvider
import com.android.systemui.qs.tiles.base.ui.viewmodel.QSTileViewModel
import com.android.systemui.qs.tiles.base.ui.viewmodel.QSTileViewModelFactory
-import com.android.systemui.qs.tiles.base.ui.viewmodel.qsTileViewModelAdaperFactory
+import com.android.systemui.qs.tiles.base.ui.viewmodel.qsTileViewModelAdapterFactory
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
@@ -77,7 +77,7 @@
Kosmos.Fixture {
NewQSTileFactory(
qSTileConfigProvider,
- qsTileViewModelAdaperFactory,
+ qsTileViewModelAdapterFactory,
newFactoryTileMap,
customTileViewModelFactory,
)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/ui/viewmodel/QSTileViewModelAdapterKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/ui/viewmodel/QSTileViewModelAdapterKosmos.kt
index cbadf8e..bf55081 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/ui/viewmodel/QSTileViewModelAdapterKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/ui/viewmodel/QSTileViewModelAdapterKosmos.kt
@@ -16,18 +16,21 @@
package com.android.systemui.qs.tiles.base.ui.viewmodel
+import android.content.testableContext
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.qs.QSHost
import com.android.systemui.util.mockito.mock
+import org.mockito.kotlin.whenever
-val Kosmos.qsTileViewModelAdaperFactory by
+val Kosmos.qsTileViewModelAdapterFactory by
Kosmos.Fixture {
object : QSTileViewModelAdapter.Factory {
override fun create(qsTileViewModel: QSTileViewModel): QSTileViewModelAdapter {
return QSTileViewModelAdapter(
applicationCoroutineScope,
- mock(),
+ mock<QSHost> { whenever(context).thenReturn(testableContext) },
qsTileViewModel,
testDispatcher,
)
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/model/DualShadeEducationalTooltipModel.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/mediaroute/MediaRouteChooserContentManagerKosmos.kt
similarity index 64%
copy from packages/SystemUI/src/com/android/systemui/scene/domain/model/DualShadeEducationalTooltipModel.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/mediaroute/MediaRouteChooserContentManagerKosmos.kt
index f63e132..ad1143b 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/model/DualShadeEducationalTooltipModel.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/mediaroute/MediaRouteChooserContentManagerKosmos.kt
@@ -14,11 +14,11 @@
* limitations under the License.
*/
-package com.android.systemui.scene.domain.model
+package com.android.systemui.qs.tiles.impl.mediaroute
-/** Enumerates the type of tooltip to show. */
-enum class DualShadeEducationalTooltipModel {
- None,
- ForNotificationsShade,
- ForQuickSettingsShade,
-}
+import com.android.internal.app.MediaRouteChooserContentManager
+import com.android.systemui.kosmos.Kosmos
+import org.mockito.kotlin.mock
+
+val Kosmos.mediaRouteChooserContentManager: MediaRouteChooserContentManager by
+ Kosmos.Fixture { mock<MediaRouteChooserContentManager>() }
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/model/DualShadeEducationalTooltipModel.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/mediaroute/MediaRouteControllerContentManagerKosmos.kt
similarity index 63%
copy from packages/SystemUI/src/com/android/systemui/scene/domain/model/DualShadeEducationalTooltipModel.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/mediaroute/MediaRouteControllerContentManagerKosmos.kt
index f63e132..6e02d59 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/model/DualShadeEducationalTooltipModel.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/mediaroute/MediaRouteControllerContentManagerKosmos.kt
@@ -14,11 +14,11 @@
* limitations under the License.
*/
-package com.android.systemui.scene.domain.model
+package com.android.systemui.qs.tiles.impl.mediaroute
-/** Enumerates the type of tooltip to show. */
-enum class DualShadeEducationalTooltipModel {
- None,
- ForNotificationsShade,
- ForQuickSettingsShade,
-}
+import com.android.internal.app.MediaRouteControllerContentManager
+import com.android.systemui.kosmos.Kosmos
+import org.mockito.kotlin.mock
+
+val Kosmos.mediaRouteControllerContentManager: MediaRouteControllerContentManager by
+ Kosmos.Fixture { mock<MediaRouteControllerContentManager>() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/ui/viewmodel/DualShadeEducationalTooltipsViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/ui/viewmodel/DualShadeEducationalTooltipsViewModelKosmos.kt
index 5955573..0d1c7e0 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/ui/viewmodel/DualShadeEducationalTooltipsViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/ui/viewmodel/DualShadeEducationalTooltipsViewModelKosmos.kt
@@ -20,13 +20,11 @@
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
import com.android.systemui.scene.domain.interactor.dualShadeEducationInteractor
-import com.android.systemui.statusbar.ui.systemBarUtilsState
val Kosmos.dualShadeEducationalTooltipsViewModelFactory by Fixture {
object : DualShadeEducationalTooltipsViewModel.Factory {
override fun create(context: Context): DualShadeEducationalTooltipsViewModel {
return DualShadeEducationalTooltipsViewModel(
- systemBarUtilsState = systemBarUtilsState,
interactor = dualShadeEducationInteractor,
context = context,
)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ShadeTestUtil.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ShadeTestUtil.kt
index 9401812..31678e7 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ShadeTestUtil.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ShadeTestUtil.kt
@@ -14,18 +14,25 @@
* limitations under the License.
*/
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
package com.android.systemui.shade
+import com.android.compose.animation.scene.ContentKey
import com.android.compose.animation.scene.ObservableTransitionState
+import com.android.compose.animation.scene.OverlayKey
import com.android.compose.animation.scene.SceneKey
import com.android.systemui.SysuiTestableContext
import com.android.systemui.res.R
import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.scene.shared.model.Overlays
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shade.data.repository.FakeShadeRepository
import com.android.systemui.shade.data.repository.ShadeRepository
import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.shade.domain.interactor.ShadeModeInteractor
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.TestScope
@@ -208,42 +215,51 @@
val shadeRepository: ShadeRepository,
val context: SysuiTestableContext,
val shadeInteractor: ShadeInteractor,
+ val shadeModeInteractor: ShadeModeInteractor,
) : ShadeTestUtilDelegate {
val isUserInputOngoing = MutableStateFlow(true)
+ private val notificationsShade: ContentKey
+ get() = if (shadeModeInteractor.isDualShade) Overlays.NotificationsShade else Scenes.Shade
+
+ private val quickSettingsShade: ContentKey
+ get() =
+ if (shadeModeInteractor.isDualShade) {
+ Overlays.QuickSettingsShade
+ } else {
+ Scenes.QuickSettings
+ }
+
override fun setShadeAndQsExpansion(shadeExpansion: Float, qsExpansion: Float) {
shadeRepository.setLegacyIsQsExpanded(qsExpansion > 0f)
- if (shadeExpansion == 1f) {
- setIdleScene(Scenes.Shade)
- } else if (qsExpansion == 1f) {
- setIdleScene(Scenes.QuickSettings)
- } else if (shadeExpansion == 0f && qsExpansion == 0f) {
- setIdleScene(Scenes.Lockscreen)
- } else if (shadeExpansion == 0f) {
- setTransitionProgress(Scenes.Lockscreen, Scenes.QuickSettings, qsExpansion)
- } else if (qsExpansion == 0f) {
- setTransitionProgress(Scenes.Lockscreen, Scenes.Shade, shadeExpansion)
- } else {
- setTransitionProgress(Scenes.Shade, Scenes.QuickSettings, qsExpansion)
+ when {
+ shadeExpansion == 1f -> setIdleContent(notificationsShade)
+ qsExpansion == 1f -> setIdleContent(quickSettingsShade)
+ shadeExpansion == 0f && qsExpansion == 0f -> setIdleScene(Scenes.Lockscreen)
+ shadeExpansion == 0f ->
+ setTransitionProgress(Scenes.Lockscreen, quickSettingsShade, qsExpansion)
+ qsExpansion == 0f ->
+ setTransitionProgress(Scenes.Lockscreen, notificationsShade, shadeExpansion)
+ else -> setTransitionProgress(notificationsShade, quickSettingsShade, qsExpansion)
}
// Requesting a value will cause the stateIn to begin flowing, otherwise incorrect values
- // may not flow fast enough to the stateIn
+ // may not flow fast enough to the stateIn.
shadeInteractor.isAnyFullyExpanded.value
}
/** Sets shade expansion to a value between 0-1. */
override fun setShadeExpansion(shadeExpansion: Float) {
- setShadeAndQsExpansion(shadeExpansion, 0f)
+ setShadeAndQsExpansion(shadeExpansion, qsExpansion = 0f)
}
/** Sets QS expansion to a value between 0-1. */
override fun setQsExpansion(qsExpansion: Float) {
- setShadeAndQsExpansion(0f, qsExpansion)
+ setShadeAndQsExpansion(shadeExpansion = 0f, qsExpansion)
}
override fun programmaticCollapseShade() {
- setTransitionProgress(Scenes.Shade, Scenes.Lockscreen, .5f, false)
+ setTransitionProgress(notificationsShade, Scenes.Lockscreen, .5f, false)
}
override fun setQsFullscreen(qsFullscreen: Boolean) {
@@ -257,12 +273,16 @@
}
override fun setLockscreenShadeExpansion(lockscreenShadeExpansion: Float) {
- if (lockscreenShadeExpansion == 0f) {
- setIdleScene(Scenes.Lockscreen)
- } else if (lockscreenShadeExpansion == 1f) {
- setIdleScene(Scenes.Shade)
- } else {
- setTransitionProgress(Scenes.Lockscreen, Scenes.Shade, lockscreenShadeExpansion)
+ when (lockscreenShadeExpansion) {
+ 0f -> setIdleScene(Scenes.Lockscreen)
+ 1f ->
+ setIdleContent(contentKey = notificationsShade, backgroundScene = Scenes.Lockscreen)
+ else ->
+ setTransitionProgress(
+ Scenes.Lockscreen,
+ notificationsShade,
+ lockscreenShadeExpansion,
+ )
}
}
@@ -274,33 +294,93 @@
isUserInputOngoing.value = tracking
}
+ private fun setIdleContent(
+ contentKey: ContentKey,
+ backgroundScene: SceneKey = sceneInteractor.currentScene.value,
+ ) {
+ when (contentKey) {
+ is SceneKey -> setIdleScene(contentKey)
+ is OverlayKey -> setIdleOverlay(contentKey, backgroundScene)
+ }
+ }
+
private fun setIdleScene(scene: SceneKey) {
sceneInteractor.changeScene(scene, "ShadeTestUtil.setIdleScene")
- val transitionState =
- MutableStateFlow<ObservableTransitionState>(ObservableTransitionState.Idle(scene))
- sceneInteractor.setTransitionState(transitionState)
+ sceneInteractor.setTransitionState(flowOf(ObservableTransitionState.Idle(scene)))
+ testScope.runCurrent()
+ }
+
+ private fun setIdleOverlay(overlay: OverlayKey, currentScene: SceneKey) {
+ sceneInteractor.showOverlay(overlay, "ShadeTestUtil.setIdleOnOverlay")
+ sceneInteractor.setTransitionState(
+ flowOf(
+ ObservableTransitionState.Idle(
+ currentScene = currentScene,
+ currentOverlays = setOf(overlay),
+ )
+ )
+ )
testScope.runCurrent()
}
private fun setTransitionProgress(
- from: SceneKey,
- to: SceneKey,
+ from: ContentKey,
+ to: ContentKey,
progress: Float,
isInitiatedByUserInput: Boolean = true,
) {
- sceneInteractor.changeScene(from, "ShadeTestUtil.setTransitionProgress")
+ val loggingReason = "ShadeTestUtil.setTransitionProgress"
+ // Set the initial state
+ when (from) {
+ is SceneKey -> sceneInteractor.changeScene(from, loggingReason)
+ is OverlayKey -> sceneInteractor.showOverlay(from, loggingReason)
+ }
+
val transitionState =
- MutableStateFlow<ObservableTransitionState>(
- ObservableTransitionState.Transition(
- fromScene = from,
- toScene = to,
- currentScene = flowOf(to),
- progress = MutableStateFlow(progress),
- isInitiatedByUserInput = isInitiatedByUserInput,
- isUserInputOngoing = isUserInputOngoing,
- )
- )
- sceneInteractor.setTransitionState(transitionState)
+ when {
+ from is SceneKey && to is SceneKey ->
+ ObservableTransitionState.Transition(
+ fromScene = from,
+ toScene = to,
+ currentScene = flowOf(to),
+ progress = flowOf(progress),
+ isInitiatedByUserInput = isInitiatedByUserInput,
+ isUserInputOngoing = isUserInputOngoing,
+ )
+ from is SceneKey && to is OverlayKey ->
+ ObservableTransitionState.Transition.showOverlay(
+ overlay = to,
+ fromScene = from,
+ currentOverlays = flowOf(emptySet()),
+ progress = flowOf(progress),
+ isInitiatedByUserInput = isInitiatedByUserInput,
+ isUserInputOngoing = isUserInputOngoing,
+ )
+ from is OverlayKey && to is SceneKey ->
+ ObservableTransitionState.Transition.hideOverlay(
+ overlay = from,
+ toScene = to,
+ currentOverlays = flowOf(emptySet()),
+ progress = flowOf(progress),
+ isInitiatedByUserInput = isInitiatedByUserInput,
+ isUserInputOngoing = isUserInputOngoing,
+ )
+ from is OverlayKey && to is OverlayKey ->
+ ObservableTransitionState.Transition.ReplaceOverlay(
+ fromOverlay = from,
+ toOverlay = to,
+ currentScene = sceneInteractor.currentScene.value,
+ currentOverlays = flowOf(emptySet()),
+ progress = flowOf(progress),
+ isInitiatedByUserInput = isInitiatedByUserInput,
+ isUserInputOngoing = isUserInputOngoing,
+ previewProgress = flowOf(0f),
+ isInPreviewStage = flowOf(false),
+ )
+ else -> error("Invalid content keys for transition: from=$from, to=$to")
+ }
+
+ sceneInteractor.setTransitionState(flowOf(transitionState))
testScope.runCurrent()
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ShadeTestUtilKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ShadeTestUtilKosmos.kt
index 0a61023..e483101 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ShadeTestUtilKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ShadeTestUtilKosmos.kt
@@ -23,6 +23,7 @@
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.shade.data.repository.fakeShadeRepository
import com.android.systemui.shade.domain.interactor.shadeInteractor
+import com.android.systemui.shade.domain.interactor.shadeModeInteractor
var Kosmos.shadeTestUtil: ShadeTestUtil by
Kosmos.Fixture {
@@ -34,6 +35,7 @@
fakeShadeRepository,
testableContext,
shadeInteractor,
+ shadeModeInteractor,
)
} else {
ShadeTestUtilLegacyImpl(
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractorKosmos.kt
index a96deb2..bb072106 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractorKosmos.kt
@@ -18,6 +18,7 @@
import android.content.testableContext
import android.provider.Settings
+import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
import com.android.systemui.kosmos.applicationCoroutineScope
@@ -79,4 +80,5 @@
addOverride(R.bool.config_use_split_notification_shade, isLargeScreen)
addOverride(R.bool.config_use_large_screen_shade_header, isLargeScreen)
}
+ fakeConfigurationRepository.onAnyConfigurationChange()
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/startable/ShadeStartableKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/startable/ShadeStartableKosmos.kt
index 10534a0..bd76bfc 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/startable/ShadeStartableKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/startable/ShadeStartableKosmos.kt
@@ -26,9 +26,10 @@
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.shade.ShadeExpansionStateManager
import com.android.systemui.shade.data.repository.shadeRepository
-import com.android.systemui.shade.domain.interactor.panelExpansionInteractor
+import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.shade.transition.ScrimShadeTransitionController
import com.android.systemui.statusbar.notification.stack.notificationStackScrollLayoutController
+import com.android.systemui.statusbar.notificationShadeDepthController
import com.android.systemui.statusbar.phone.scrimController
import com.android.systemui.statusbar.policy.splitShadeStateController
import com.android.systemui.statusbar.pulseExpansionHandler
@@ -44,14 +45,15 @@
touchLog = mock<LogBuffer>(),
configurationRepository = configurationRepository,
shadeRepository = shadeRepository,
+ shadeInteractorProvider = { shadeInteractor },
splitShadeStateController = splitShadeStateController,
scrimShadeTransitionController = mock<ScrimShadeTransitionController>(),
sceneInteractorProvider = { sceneInteractor },
- panelExpansionInteractorProvider = { panelExpansionInteractor },
shadeExpansionStateManager = shadeExpansionStateManager,
pulseExpansionHandler = pulseExpansionHandler,
displayStateInteractor = displayStateInteractor,
nsslc = notificationStackScrollLayoutController,
scrimController = scrimController,
+ depthController = notificationShadeDepthController,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelKosmos.kt
index 9a9f335..5efe0ac 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelKosmos.kt
@@ -24,6 +24,7 @@
import com.android.systemui.kairos.kairos
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.plugins.activityStarter
+import com.android.systemui.scene.domain.interactor.dualShadeEducationInteractor
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.shade.domain.interactor.privacyChipInteractor
import com.android.systemui.shade.domain.interactor.shadeHeaderClockInteractor
@@ -52,6 +53,7 @@
statusBarIconController = mock<StatusBarIconController>(),
kairosNetwork = kairos,
mobileIconsViewModelKairos = mock(),
+ dualShadeEducationInteractor = dualShadeEducationInteractor,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/NotificationShadeDepthControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/NotificationShadeDepthControllerKosmos.kt
index 3960cd1..1100a89 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/NotificationShadeDepthControllerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/NotificationShadeDepthControllerKosmos.kt
@@ -16,8 +16,41 @@
package com.android.systemui.statusbar
+import android.view.Choreographer
+import com.android.systemui.dump.dumpManager
+import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.plugins.statusbar.statusBarStateController
+import com.android.systemui.shade.data.repository.shadeDisplaysRepository
+import com.android.systemui.shade.domain.interactor.shadeModeInteractor
+import com.android.systemui.statusbar.phone.biometricUnlockController
+import com.android.systemui.statusbar.phone.dozeParameters
+import com.android.systemui.statusbar.policy.keyguardStateController
+import com.android.systemui.util.WallpaperController
import com.android.systemui.util.mockito.mock
+import com.android.systemui.wallpapers.domain.interactor.wallpaperInteractor
+import com.android.systemui.window.domain.interactor.windowRootViewBlurInteractor
+import java.util.Optional
-var Kosmos.notificationShadeDepthController by Fixture { mock<NotificationShadeDepthController>() }
+var Kosmos.notificationShadeDepthController by Fixture {
+ NotificationShadeDepthController(
+ statusBarStateController = statusBarStateController,
+ blurUtils = mock<BlurUtils>(),
+ biometricUnlockController = biometricUnlockController,
+ keyguardStateController = keyguardStateController,
+ keyguardInteractor = keyguardInteractor,
+ choreographer = mock<Choreographer>(),
+ wallpaperController = mock<WallpaperController>(),
+ wallpaperInteractor = wallpaperInteractor,
+ notificationShadeWindowController = notificationShadeWindowController,
+ dozeParameters = dozeParameters,
+ shadeModeInteractor = shadeModeInteractor,
+ windowRootViewBlurInteractor = windowRootViewBlurInteractor,
+ appZoomOutOptional = Optional.empty(),
+ applicationScope = applicationCoroutineScope,
+ dumpManager = dumpManager,
+ shadeDisplaysRepository = { shadeDisplaysRepository },
+ )
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/MagneticNotificationRowManagerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/MagneticNotificationRowManagerKosmos.kt
index 85e8fa5..d06873d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/MagneticNotificationRowManagerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/MagneticNotificationRowManagerKosmos.kt
@@ -18,8 +18,10 @@
import com.android.systemui.dump.dumpManager
import com.android.systemui.haptics.msdl.msdlPlayer
+import com.android.systemui.haptics.vibratorHelper
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.statusbar.notification.row.NotificationRowLogger
+import com.android.systemui.util.time.systemClock
import org.mockito.kotlin.mock
var Kosmos.magneticNotificationRowManager: MagneticNotificationRowManager by
@@ -29,8 +31,10 @@
Kosmos.Fixture {
MagneticNotificationRowManagerImpl(
msdlPlayer,
+ vibratorHelper,
NotificationTargetsHelper(),
NotificationRoundnessManager(dumpManager),
mock<NotificationRowLogger>(),
+ systemClock,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelKosmos.kt
index 219ecbf..cf681b7 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelKosmos.kt
@@ -21,6 +21,7 @@
import com.android.systemui.kosmos.Kosmos.Fixture
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.shade.domain.interactor.shadeInteractor
+import com.android.systemui.shade.domain.interactor.shadeModeInteractor
import com.android.systemui.statusbar.chips.ui.viewmodel.ongoingActivityChipsViewModel
import com.android.systemui.statusbar.domain.interactor.remoteInputInteractor
import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor
@@ -45,6 +46,7 @@
headsUpNotificationInteractor,
remoteInputInteractor,
shadeInteractor,
+ shadeModeInteractor,
userSetupInteractor,
testDispatcher,
dumpManager,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/systemstatusicons/ethernet/ui/viewmodel/EthernetIconViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/systemstatusicons/ethernet/ui/viewmodel/EthernetIconViewModelKosmos.kt
new file mode 100644
index 0000000..5dd84ea
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/systemstatusicons/ethernet/ui/viewmodel/EthernetIconViewModelKosmos.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.systemstatusicons.ethernet.ui.viewmodel
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.statusbar.pipeline.ethernet.domain.ethernetInteractor
+
+private val Kosmos.ethernetIconViewModel: EthernetIconViewModel by
+ Kosmos.Fixture { EthernetIconViewModel(ethernetInteractor) }
+
+val Kosmos.ethernetIconViewModelFactory: EthernetIconViewModel.Factory by
+ Kosmos.Fixture {
+ object : EthernetIconViewModel.Factory {
+ override fun create(): EthernetIconViewModel = ethernetIconViewModel
+ }
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/systemstatusicons/ui/viewmodel/SystemStatusIconsViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/systemstatusicons/ui/viewmodel/SystemStatusIconsViewModelKosmos.kt
index 8f68808..49f2560 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/systemstatusicons/ui/viewmodel/SystemStatusIconsViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/systemstatusicons/ui/viewmodel/SystemStatusIconsViewModelKosmos.kt
@@ -18,11 +18,13 @@
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.statusbar.systemstatusicons.airplane.ui.viewmodel.airplaneModeIconViewModelFactory
+import com.android.systemui.statusbar.systemstatusicons.ethernet.ui.viewmodel.ethernetIconViewModelFactory
val Kosmos.systemStatusIconsViewModel by
Kosmos.Fixture {
SystemStatusIconsViewModel(
- airplaneModeIconViewModelFactory = airplaneModeIconViewModelFactory
+ airplaneModeIconViewModelFactory = airplaneModeIconViewModelFactory,
+ ethernetIconViewModelFactory = ethernetIconViewModelFactory,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/ui/FakeSystemBarUtilsProxy.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/ui/FakeSystemBarUtilsProxy.kt
index 7e993b4..0dc6375 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/ui/FakeSystemBarUtilsProxy.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/ui/FakeSystemBarUtilsProxy.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.ui
+import android.content.Context
import com.android.systemui.statusbar.policy.FakeConfigurationController
class FakeSystemBarUtilsProxy(
@@ -41,6 +42,11 @@
}
}
- override fun getStatusBarHeight(): Int = fakeStatusBarHeight
- override fun getStatusBarHeaderHeightKeyguard(): Int = fakeKeyguardStatusBarHeight
+ override fun getStatusBarHeight(context: Context?): Int {
+ return fakeStatusBarHeight
+ }
+
+ override fun getStatusBarHeaderHeightKeyguard(context: Context?): Int {
+ return fakeKeyguardStatusBarHeight
+ }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/topwindoweffects/data/repository/FakeSqueezeEffectRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/topwindoweffects/data/repository/FakeSqueezeEffectRepository.kt
index bf04899..d646c5b 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/topwindoweffects/data/repository/FakeSqueezeEffectRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/topwindoweffects/data/repository/FakeSqueezeEffectRepository.kt
@@ -16,18 +16,21 @@
package com.android.systemui.topwindoweffects.data.repository
-import com.android.systemui.topwindoweffects.data.entity.SqueezeEffectCornerResourceId
+import com.android.systemui.topwindoweffects.data.entity.SqueezeEffectCornersInfo
import kotlinx.coroutines.flow.MutableStateFlow
class FakeSqueezeEffectRepository : SqueezeEffectRepository {
-
- private var roundedCornersResourceId = SqueezeEffectCornerResourceId(top = -1, bottom = -1)
-
- fun setRoundedCornerResourceId(top: Int, bottom: Int) {
- roundedCornersResourceId = SqueezeEffectCornerResourceId(top = top, bottom = bottom)
- }
+ var invocationEffectInitialDelayMs = 100L
+ var roundedCornersInfo =
+ SqueezeEffectCornersInfo(
+ topResourceId = -1,
+ bottomResourceId = -1,
+ physicalPixelDisplaySizeRatio = 1f,
+ )
override val isSqueezeEffectEnabled = MutableStateFlow(false)
- override suspend fun getRoundedCornersResourceId() = roundedCornersResourceId
+ override suspend fun getInvocationEffectInitialDelayMs() = invocationEffectInitialDelayMs
+
+ override suspend fun getRoundedCornersInfo() = roundedCornersInfo
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/underlay/data/repository/UnderlayRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/underlay/data/repository/UnderlayRepositoryKosmos.kt
new file mode 100644
index 0000000..ad79c16
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/underlay/data/repository/UnderlayRepositoryKosmos.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.underlay.data.repository
+
+import com.android.systemui.broadcast.broadcastDispatcher
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.backgroundScope
+
+val Kosmos.underlayRepository by
+ Kosmos.Fixture {
+ UnderlayRepository(
+ backgroundScope = backgroundScope,
+ broadcastDispatcher = broadcastDispatcher,
+ )
+ }
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/model/DualShadeEducationalTooltipModel.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/underlay/domain/interactor/UnderlayInteractorKosmos.kt
similarity index 66%
copy from packages/SystemUI/src/com/android/systemui/scene/domain/model/DualShadeEducationalTooltipModel.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/underlay/domain/interactor/UnderlayInteractorKosmos.kt
index f63e132..6f290f2 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/model/DualShadeEducationalTooltipModel.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/underlay/domain/interactor/UnderlayInteractorKosmos.kt
@@ -14,11 +14,10 @@
* limitations under the License.
*/
-package com.android.systemui.scene.domain.model
+package com.android.systemui.underlay.domain.interactor
-/** Enumerates the type of tooltip to show. */
-enum class DualShadeEducationalTooltipModel {
- None,
- ForNotificationsShade,
- ForQuickSettingsShade,
-}
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.underlay.data.repository.underlayRepository
+
+val Kosmos.underlayInteractor by Fixture { UnderlayInteractor(underlayRepository) }
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/model/DualShadeEducationalTooltipModel.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/underlay/ui/compose/UnderlayComposableProviderKosmos.kt
similarity index 72%
copy from packages/SystemUI/src/com/android/systemui/scene/domain/model/DualShadeEducationalTooltipModel.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/underlay/ui/compose/UnderlayComposableProviderKosmos.kt
index f63e132..48b2803 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/model/DualShadeEducationalTooltipModel.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/underlay/ui/compose/UnderlayComposableProviderKosmos.kt
@@ -14,11 +14,9 @@
* limitations under the License.
*/
-package com.android.systemui.scene.domain.model
+package com.android.systemui.underlay.ui.compose
-/** Enumerates the type of tooltip to show. */
-enum class DualShadeEducationalTooltipModel {
- None,
- ForNotificationsShade,
- ForQuickSettingsShade,
-}
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+
+val Kosmos.underlayComposableProvider by Fixture { UnderlayComposableProviderImpl() }
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/BuildScope.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/BuildScope.kt
index cade784..71421eb 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/BuildScope.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/BuildScope.kt
@@ -23,6 +23,7 @@
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.DisposableHandle
+import kotlinx.coroutines.ExperimentalForInheritanceCoroutinesApi
import kotlinx.coroutines.Job
import kotlinx.coroutines.awaitCancellation
import kotlinx.coroutines.flow.Flow
@@ -113,10 +114,30 @@
// TODO: remove disposable handle return? might add more confusion than convenience
fun <A> Events<A>.observe(
coroutineContext: CoroutineContext = EmptyCoroutineContext,
- block: EffectScope.(A) -> Unit = {},
+ block: suspend EffectScope.(A) -> Unit,
): DisposableHandle
/**
+ * Invokes [block] whenever this [Events] emits a value, allowing side-effects to be safely
+ * performed in reaction to the emission.
+ *
+ * Specifically, [block] is deferred to the end of the transaction, and is only actually
+ * executed if this [BuildScope] is still active by that time. It can be deactivated due to a
+ * -Latest combinator, for example.
+ *
+ * Note that unlike with [observe], [block] will be invoked *synchronously* within the current
+ * transaction. This means that it will run on whatever thread the current transaction is
+ * running on (determined by the [dispatcher][kotlinx.coroutines.CoroutineDispatcher] used to
+ * stand up the [KairosNetwork]), and will ignore whatever dispatcher is specified for this
+ * [BuildScope]. This avoids the overhead associated with the dispatcher.
+ *
+ * Generally, you should prefer [observe] over this method.
+ *
+ * @see observe
+ */
+ fun <A> Events<A>.observeSync(block: TransactionEffectScope.(A) -> Unit = {}): DisposableHandle
+
+ /**
* Returns an [Events] containing the results of applying each [BuildSpec] emitted from the
* original [Events], and a [DeferredValue] containing the result of applying [initialSpecs]
* immediately.
@@ -169,7 +190,7 @@
*/
// TODO: see TODO for [events]
fun <In, Out> coalescingEvents(
- getInitialValue: () -> Out,
+ getInitialValue: KairosScope.() -> Out,
coalesce: (old: Out, new: In) -> Out,
builder: suspend CoalescingEventProducerScope<In>.() -> Unit,
): Events<Out>
@@ -214,16 +235,17 @@
*
* @see observe
*/
- fun <A> Events<A>.observeBuild(block: BuildScope.(A) -> Unit = {}): DisposableHandle =
- mapBuild(block).observe()
+ fun <A> Events<A>.observeBuild(block: BuildScope.(A) -> Unit): DisposableHandle =
+ mapBuild(block).observeSync()
/**
* Returns a [StateFlow] whose [value][StateFlow.value] tracks the current
* [value of this State][State.sample], and will emit at the same rate as [State.changes].
*/
+ @OptIn(ExperimentalForInheritanceCoroutinesApi::class)
fun <A> State<A>.toStateFlow(): StateFlow<A> {
val innerStateFlow = MutableStateFlow(sampleDeferred())
- changes.observe { innerStateFlow.value = deferredOf(it) }
+ changes.observeSync { innerStateFlow.value = deferredOf(it) }
return object : StateFlow<A> {
override val replayCache: List<A>
get() = innerStateFlow.replayCache.map { it.value }
@@ -245,7 +267,7 @@
val result = MutableSharedFlow<A>(replay, extraBufferCapacity = 1)
deferredBuildScope {
result.tryEmit(sample())
- changes.observe { a -> result.tryEmit(a) }
+ changes.observeSync { a -> result.tryEmit(a) }
}
return result
}
@@ -256,7 +278,7 @@
*/
fun <A> Events<A>.toSharedFlow(replay: Int = 0): SharedFlow<A> {
val result = MutableSharedFlow<A>(replay, extraBufferCapacity = 1)
- observe { a -> result.tryEmit(a) }
+ observeSync { a -> result.tryEmit(a) }
return result
}
@@ -558,7 +580,7 @@
/** Returns a [Deferred] containing the next value to be emitted from this [Events]. */
fun <R> Events<R>.nextDeferred(): Deferred<R> {
lateinit var next: CompletableDeferred<R>
- val job = launchScope { nextOnly().observe { next.complete(it) } }
+ val job = launchScope { nextOnly().observeSync { next.complete(it) } }
next = CompletableDeferred(parent = job)
return next
}
@@ -607,8 +629,8 @@
* registered [observers][observe] are unregistered, and any pending [side-effects][effect] are
* cancelled).
*/
- fun <A> Events<A>.observeLatestBuild(block: BuildScope.(A) -> Unit = {}): DisposableHandle =
- mapLatestBuild { block(it) }.observe()
+ fun <A> Events<A>.observeLatestBuild(block: BuildScope.(A) -> Unit): DisposableHandle =
+ mapLatestBuild { block(it) }.observeSync()
/**
* Invokes [block] whenever this [Events] emits a value, allowing side-effects to be safely
@@ -616,7 +638,7 @@
*
* With each invocation of [block], running effects from the previous invocation are cancelled.
*/
- fun <A> Events<A>.observeLatest(block: EffectScope.(A) -> Unit = {}): DisposableHandle {
+ fun <A> Events<A>.observeLatest(block: suspend EffectScope.(A) -> Unit): DisposableHandle {
var innerJob: Job? = null
return observeBuild {
innerJob?.cancel()
@@ -630,11 +652,11 @@
*
* With each invocation of [block], running effects from the previous invocation are cancelled.
*/
- fun <A> State<A>.observeLatest(block: EffectScope.(A) -> Unit = {}): Job = launchScope {
- var innerJob = effect { block(sample()) }
+ fun <A> State<A>.observeLatest(block: TransactionEffectScope.(A) -> Unit): Job = launchScope {
+ var innerJob = effectSync { block(sample()) }
changes.observeBuild {
innerJob.cancel()
- innerJob = effect { block(it) }
+ innerJob = effectSync { block(it) }
}
}
@@ -647,7 +669,7 @@
* each invocation of [block], changes from the previous invocation are undone (any registered
* [observers][observe] are unregistered, and any pending [side-effects][effect] are cancelled).
*/
- fun <A> State<A>.observeLatestBuild(block: BuildScope.(A) -> Unit = {}): Job = launchScope {
+ fun <A> State<A>.observeLatestBuild(block: BuildScope.(A) -> Unit): Job = launchScope {
var innerJob: Job = launchScope { block(sample()) }
changes.observeBuild {
innerJob.cancel()
@@ -669,20 +691,20 @@
* [effect].
*
* ```
- * fun <A> State<A>.observeBuild(block: BuildScope.(A) -> Unit = {}): Job = launchScope {
+ * fun <A> State<A>.observeBuild(block: BuildScope.(A) -> Unit): Job = launchScope {
* block(sample())
* changes.observeBuild(block)
* }
* ```
*/
- fun <A> State<A>.observeBuild(block: BuildScope.(A) -> Unit = {}): Job = launchScope {
+ fun <A> State<A>.observeBuild(block: BuildScope.(A) -> Unit): Job = launchScope {
block(sample())
changes.observeBuild(block)
}
/**
* Invokes [block] with the current value of this [State], re-invoking whenever it changes,
- * allowing side-effects to be safely performed in reaction value changing.
+ * allowing side-effects to be safely performed in reaction to the value changing.
*
* Specifically, [block] is deferred to the end of the transaction, and is only actually
* executed if this [BuildScope] is still active by that time. It can be deactivated due to a
@@ -694,9 +716,34 @@
*/
fun <A> State<A>.observe(
coroutineContext: CoroutineContext = EmptyCoroutineContext,
- block: EffectScope.(A) -> Unit = {},
+ block: suspend EffectScope.(A) -> Unit,
): DisposableHandle =
now.map { sample() }.mergeWith(changes) { _, new -> new }.observe(coroutineContext, block)
+
+ /**
+ * Invokes [block] with the current value of this [State], re-invoking whenever it changes,
+ * allowing side-effects to be safely performed in reaction to the value changing.
+ *
+ * Specifically, [block] is deferred to the end of the transaction, and is only actually
+ * executed if this [BuildScope] is still active by that time. It can be deactivated due to a
+ * -Latest combinator, for example.
+ *
+ * If the [State] is changing within the *current* transaction (i.e. [changes] is presently
+ * emitting) then [block] will be invoked for the first time with the new value; otherwise, it
+ * will be invoked with the [current][sample] value.
+ *
+ * Note that unlike with [observe], [block] will be invoked *synchronously* within the current
+ * transaction. This means that it will run on whatever thread the current transaction is
+ * running on (determined by the [dispatcher][kotlinx.coroutines.CoroutineDispatcher] used to
+ * stand up the [KairosNetwork]), and will ignore whatever dispatcher is specified for this
+ * [BuildScope]. This avoids the overhead associated with the dispatcher.
+ *
+ * Generally, you should prefer [observe] over this method.
+ *
+ * @see observe
+ */
+ fun <A> State<A>.observeSync(block: TransactionEffectScope.(A) -> Unit = {}): DisposableHandle =
+ now.map { sample() }.mergeWith(changes) { _, new -> new }.observeSync(block)
}
/**
@@ -710,13 +757,13 @@
* ```
*/
@ExperimentalKairosApi
-fun <A> BuildScope.asyncEvent(block: suspend () -> A): Events<A> =
+fun <A> BuildScope.asyncEvent(block: suspend KairosScope.() -> A): Events<A> =
events {
// TODO: if block completes synchronously, it would be nice to emit within this
// transaction
emit(block())
}
- .apply { observe() }
+ .apply { observeSync() }
/**
* Performs a side-effect in a safe manner w/r/t the current Kairos transaction.
@@ -736,10 +783,40 @@
@ExperimentalKairosApi
fun BuildScope.effect(
context: CoroutineContext = EmptyCoroutineContext,
- block: EffectScope.() -> Unit,
+ block: suspend EffectScope.() -> Unit,
): Job = launchScope(context) { now.observe { block() } }
/**
+ * Performs a side-effect in a safe manner w/r/t the current Kairos transaction.
+ *
+ * Specifically, [block] is deferred to the end of the current transaction, and is only actually
+ * executed if this [BuildScope] is still active by that time. It can be deactivated due to a
+ * -Latest combinator, for example.
+ *
+ * Note that unlike with [effect], [block] will be invoked *synchronously* within the current
+ * transaction. This means that it will run on whatever thread the current transaction is running on
+ * (determined by the [dispatcher][kotlinx.coroutines.CoroutineDispatcher] used to stand up the
+ * [KairosNetwork]), and will ignore whatever dispatcher is specified for this [BuildScope]. This
+ * avoids the overhead associated with the dispatcher.
+ *
+ * Generally, you should prefer [effect] over this method.
+ *
+ * ```
+ * fun BuildScope.effectImmediate(
+ * context: CoroutineContext = EmptyCoroutineContext,
+ * block: EffectScope.() -> Unit,
+ * ): Job =
+ * launchScope(context) { now.observeImmediate { block() } }
+ * ```
+ *
+ * @see effect
+ */
+@ExperimentalKairosApi
+fun BuildScope.effectSync(block: TransactionEffectScope.() -> Unit): Job = launchScope {
+ now.observeSync { block() }
+}
+
+/**
* Launches [block] in a new coroutine, returning a [Job] bound to the coroutine.
*
* This coroutine is not actually started until the *end* of the current Kairos transaction. This is
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/DeferredValue.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/DeferredValue.kt
index 4b9bb0e..c28083e 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/DeferredValue.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/DeferredValue.kt
@@ -16,8 +16,6 @@
package com.android.systemui.kairos
-import com.android.systemui.kairos.internal.CompletableLazy
-
/**
* A value that may not be immediately (synchronously) available, but is guaranteed to be available
* before this transaction is completed.
@@ -33,5 +31,4 @@
}
/** Returns an already-available [DeferredValue] containing [value]. */
-@ExperimentalKairosApi
-fun <A> deferredOf(value: A): DeferredValue<A> = DeferredValue(CompletableLazy(value))
+@ExperimentalKairosApi fun <A> deferredOf(value: A): DeferredValue<A> = DeferredValue(lazyOf(value))
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/EffectScope.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/EffectScope.kt
index 14d45d4..b482f2a 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/EffectScope.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/EffectScope.kt
@@ -26,13 +26,12 @@
/**
* Scope for external side-effects triggered by the Kairos network.
*
- * This still occurs within the context of a transaction, so general suspending calls are disallowed
- * to prevent blocking the transaction. You can [launch] new coroutines to perform long-running
- * asynchronous work. These coroutines are kept alive for the duration of the containing
- * [BuildScope] that this side-effect scope is running in.
+ * You can [launch] new coroutines to perform long-running asynchronous work. These coroutines are
+ * kept alive for the duration of the containing [BuildScope] that this side-effect scope is running
+ * in.
*/
@ExperimentalKairosApi
-interface EffectScope : HasNetwork, TransactionScope {
+interface EffectScope : HasNetwork {
/**
* Creates a coroutine that is a child of this [EffectScope], and returns its future result as a
* [Deferred].
@@ -58,4 +57,21 @@
): Job = async(context, start, block)
}
+/**
+ * A combination of an [EffectScope] and a [TransactionScope], available within the lambda arguments
+ * passed to [BuildScope] `-Sync` APIs, such as [BuildScope.observeSync].
+ *
+ * This scope occurs within the context of a transaction, allowing you to
+ * [sample][TransactionScope.sample] states, but general suspending calls are disallowed to prevent
+ * blocking the transaction. You can [launch] new coroutines to perform long-running asynchronous
+ * work. These coroutines are kept alive for the duration of the containing [BuildScope] that this
+ * side-effect scope is running in.
+ */
+@ExperimentalKairosApi interface TransactionEffectScope : EffectScope, TransactionScope
+
+/**
+ * A [CoroutineScope] that also has access to a [KairosNetwork] that is bound to the former. All
+ * usages of [KairosNetwork.activateSpec] with the [kairosNetwork] will be canceled when this
+ * coroutine scope is canceled.
+ */
@ExperimentalKairosApi interface KairosCoroutineScope : HasNetwork, CoroutineScope
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Events.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Events.kt
index f9c36f7..74b0a9f 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Events.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Events.kt
@@ -264,7 +264,7 @@
*/
fun emit(value: In) {
val (scheduled, _) =
- storage.getAndUpdate { (_, batch) -> true to CompletableLazy(coalesce(batch, value)) }
+ storage.getAndUpdate { (_, batch) -> true to lazyOf(coalesce(batch, value)) }
if (!scheduled) {
@Suppress("DeferredResultUnused")
network.transaction(
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Incremental.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Incremental.kt
index 02941bd..d5a63b3 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Incremental.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Incremental.kt
@@ -137,7 +137,7 @@
* [loopback] is unset before it is [observed][BuildScope.observe] or
* [sampled][TransactionScope.sample]. Note that it is safe to invoke
* [TransactionScope.sampleDeferred] before [loopback] is set, provided the [DeferredValue] is not
- * [queried][KairosScope.get].
+ * [queried][DeferredValue.value].
*/
@ExperimentalKairosApi
class IncrementalLoop<K, V>(private val name: String? = null) : Incremental<K, V>() {
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/KairosNetwork.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/KairosNetwork.kt
index 076aaa4..208136c 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/KairosNetwork.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/KairosNetwork.kt
@@ -17,13 +17,14 @@
package com.android.systemui.kairos
import com.android.systemui.kairos.internal.BuildScopeImpl
+import com.android.systemui.kairos.internal.EvalScope
import com.android.systemui.kairos.internal.Network
+import com.android.systemui.kairos.internal.NoScope
import com.android.systemui.kairos.internal.StateScopeImpl
-import com.android.systemui.kairos.internal.util.awaitCancellationAndThen
import com.android.systemui.kairos.internal.util.childScope
+import com.android.systemui.kairos.internal.util.invokeOnCancel
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext
-import kotlin.coroutines.coroutineContext
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.CoroutineScope
@@ -34,7 +35,6 @@
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.job
import kotlinx.coroutines.launch
-import kotlinx.coroutines.plus
/** Marks APIs that are still **experimental** and shouldn't be used in general production code. */
@RequiresOptIn(
@@ -68,7 +68,7 @@
/** Returns a [CoalescingMutableEvents] that can emit values into this [KairosNetwork]. */
fun <In, Out> coalescingMutableEvents(
coalesce: (old: Out, new: In) -> Out,
- getInitialValue: () -> Out,
+ getInitialValue: KairosScope.() -> Out,
): CoalescingMutableEvents<In, Out>
/** Returns a [MutableState] that can emit values into this [KairosNetwork]. */
@@ -116,7 +116,7 @@
fun <In, Out> CoalescingMutableEvents(
network: KairosNetwork,
coalesce: (old: Out, new: In) -> Out,
- getInitialValue: () -> Out,
+ getInitialValue: KairosScope.() -> Out,
): CoalescingMutableEvents<In, Out> = network.coalescingMutableEvents(coalesce, getInitialValue)
/** Returns a [CoalescingMutableEvents] that can emit values into this [KairosNetwork]. */
@@ -143,30 +143,21 @@
internal class LocalNetwork(
private val network: Network,
private val scope: CoroutineScope,
- private val endSignal: Events<Any>,
+ private val aliveLazy: Lazy<State<Boolean>>,
) : KairosNetwork {
+
override suspend fun <R> transact(block: TransactionScope.() -> R): R =
network.transaction("KairosNetwork.transact") { block() }.awaitOrCancel()
override suspend fun activateSpec(spec: BuildSpec<*>): Unit = coroutineScope {
- val stopEmitter = conflatedMutableEvents<Unit>()
lateinit var completionHandle: DisposableHandle
+ val childEndSignal = conflatedMutableEvents<Unit>().apply { invokeOnCancel { emit(Unit) } }
val job =
launch(start = CoroutineStart.LAZY) {
network
.transaction("KairosNetwork.activateSpec") {
- val buildScope =
- BuildScopeImpl(
- stateScope =
- StateScopeImpl(
- evalScope = this,
- endSignalLazy = lazy { mergeLeft(stopEmitter, endSignal) },
- ),
- coroutineScope = this@coroutineScope,
- )
- buildScope.launchScope {
- spec.applySpec()
- launchEffect { awaitCancellationAndThen { stopEmitter.emit(Unit) } }
+ reenterBuildScope(this@coroutineScope).childBuildScope(childEndSignal).run {
+ launchScope { spec.applySpec() }
}
}
.awaitOrCancel()
@@ -177,6 +168,12 @@
job.start()
}
+ private fun EvalScope.reenterBuildScope(coroutineScope: CoroutineScope) =
+ BuildScopeImpl(
+ stateScope = StateScopeImpl(evalScope = this, aliveLazy = aliveLazy),
+ coroutineScope = coroutineScope,
+ )
+
private suspend fun <T> Deferred<T>.awaitOrCancel(): T =
try {
await()
@@ -195,13 +192,13 @@
override fun <In, Out> coalescingMutableEvents(
coalesce: (old: Out, new: In) -> Out,
- getInitialValue: () -> Out,
+ getInitialValue: KairosScope.() -> Out,
): CoalescingMutableEvents<In, Out> =
CoalescingMutableEvents(
null,
coalesce = { old, new -> coalesce(old.value, new) },
network,
- getInitialValue,
+ { NoScope.getInitialValue() },
)
override fun <T> conflatedMutableEvents(): CoalescingMutableEvents<T, T> =
@@ -225,19 +222,53 @@
@ExperimentalKairosApi
class RootKairosNetwork
internal constructor(private val network: Network, private val scope: CoroutineScope, job: Job) :
- Job by job, KairosNetwork by LocalNetwork(network, scope, emptyEvents)
+ Job by job, KairosNetwork by LocalNetwork(network, scope, lazyOf(stateOf(true)))
-/** Constructs a new [RootKairosNetwork] in the given [CoroutineScope]. */
+/** Constructs a new [RootKairosNetwork] in the given [CoroutineScope] and [CoalescingPolicy]. */
@ExperimentalKairosApi
fun CoroutineScope.launchKairosNetwork(
- context: CoroutineContext = EmptyCoroutineContext
+ context: CoroutineContext = EmptyCoroutineContext,
+ coalescingPolicy: CoalescingPolicy = CoalescingPolicy.Normal,
): RootKairosNetwork {
val scope = childScope(context)
- val network = Network(scope)
+ val network = Network(scope, coalescingPolicy)
scope.launch(CoroutineName("launchKairosNetwork scheduler")) { network.runInputScheduler() }
return RootKairosNetwork(network, scope, scope.coroutineContext.job)
}
+/** Constructs a new [RootKairosNetwork] in the given [CoroutineScope] and [CoalescingPolicy]. */
+fun KairosNetwork(
+ scope: CoroutineScope,
+ coalescingPolicy: CoalescingPolicy = CoalescingPolicy.Normal,
+): RootKairosNetwork = scope.launchKairosNetwork(coalescingPolicy = coalescingPolicy)
+
+/** Configures how multiple input events are processed by the network. */
+enum class CoalescingPolicy {
+ /**
+ * Each input event is processed in its own transaction. This policy has the least overhead but
+ * can cause backpressure if the network becomes flooded with inputs.
+ */
+ None,
+ /**
+ * Input events are processed as they appear. Compared to [Eager], this policy will not
+ * internally [yield][kotlinx.coroutines.yield] to allow more inputs to be processed before
+ * starting a transaction. This means that if there is a race between an input and a transaction
+ * occurring, it is beholden to the
+ * [CoroutineDispatcher][kotlinx.coroutines.CoroutineDispatcher] to determine the ordering.
+ *
+ * Note that any input events which miss being included in a transaction will be immediately
+ * scheduled for a subsequent transaction.
+ */
+ Normal,
+ /**
+ * Input events are processed eagerly. Compared to [Normal], this policy will internally
+ * [yield][kotlinx.coroutines.yield] to allow for as many input events to be processed as
+ * possible. This can be useful for noisy networks where many inputs can be handled
+ * simultaneously, potentially improving throughput.
+ */
+ Eager,
+}
+
@ExperimentalKairosApi
interface HasNetwork : KairosScope {
/**
@@ -271,7 +302,7 @@
@ExperimentalKairosApi
fun <In, Out> HasNetwork.CoalescingMutableEvents(
coalesce: (old: Out, new: In) -> Out,
- getInitialValue: () -> Out,
+ getInitialValue: KairosScope.() -> Out,
): CoalescingMutableEvents<In, Out> =
CoalescingMutableEvents(kairosNetwork, coalesce, getInitialValue)
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/State.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/State.kt
index e8b005e..3872c57 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/State.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/State.kt
@@ -257,7 +257,7 @@
* Multiple invocations of [setValue] that occur before a transaction are conflated; only the
* most recent value is used.
*/
- fun setValue(value: T) = input.emit(CompletableLazy(value))
+ fun setValue(value: T) = input.emit(lazyOf(value))
/**
* Sets the value held by this [State]. The [DeferredValue] will not be queried until this
@@ -280,7 +280,7 @@
* unset before it is [observed][BuildScope.observe] or [sampled][TransactionScope.sample].
*
* Note that it is safe to invoke [TransactionScope.sampleDeferred] before [loopback] is set,
- * provided the returned [DeferredValue] is not [queried][KairosScope.get].
+ * provided the returned [DeferredValue] is not [queried][DeferredValue.value].
*
* @sample com.android.systemui.kairos.KairosSamples.stateLoop
*/
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/StateScope.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/StateScope.kt
index 8020896..6fafa493 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/StateScope.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/StateScope.kt
@@ -93,7 +93,7 @@
/**
* Returns an [Events] containing the results of applying each [Stateful] emitted from the
- * original [Events], and a [DeferredValue] containing the result of applying [init]
+ * original [Events], and a [DeferredValue] containing the result of applying [initialValues]
* immediately.
*
* If the [Maybe] contained within the value for an associated key is [absent][Maybe.absent],
@@ -105,7 +105,7 @@
* The optional [numKeys] argument is an optimization used to initialize the internal storage.
*/
fun <K, A, B> Events<MapPatch<K, Stateful<A>>>.applyLatestStatefulForKey(
- init: DeferredValue<Map<K, Stateful<B>>>,
+ initialValues: DeferredValue<Map<K, Stateful<B>>>,
numKeys: Int? = null,
): Pair<Events<MapPatch<K, A>>, DeferredValue<Map<K, B>>>
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/ToColdFlow.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/ToColdFlow.kt
index 3d2768b..f0e4d4e 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/ToColdFlow.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/ToColdFlow.kt
@@ -26,7 +26,7 @@
*/
@ExperimentalKairosApi
fun <A> Events<A>.toColdConflatedFlow(network: KairosNetwork): Flow<A> =
- channelFlow { network.activateSpec { observe { trySend(it) } } }.conflate()
+ channelFlow { network.activateSpec { observeSync { trySend(it) } } }.conflate()
/**
* Returns a cold [Flow] that, when collected, emits from this [State]. [network] is needed to
@@ -34,7 +34,7 @@
*/
@ExperimentalKairosApi
fun <A> State<A>.toColdConflatedFlow(network: KairosNetwork): Flow<A> =
- channelFlow { network.activateSpec { observe { trySend(it) } } }.conflate()
+ channelFlow { network.activateSpec { observeSync { trySend(it) } } }.conflate()
/**
* Returns a cold [Flow] that, when collected, applies this [BuildSpec] in a new transaction in this
@@ -45,7 +45,7 @@
@ExperimentalKairosApi
@JvmName("eventsSpecToColdConflatedFlow")
fun <A> BuildSpec<Events<A>>.toColdConflatedFlow(network: KairosNetwork): Flow<A> =
- channelFlow { network.activateSpec { applySpec().observe { trySend(it) } } }.conflate()
+ channelFlow { network.activateSpec { applySpec().observeSync { trySend(it) } } }.conflate()
/**
* Returns a cold [Flow] that, when collected, applies this [BuildSpec] in a new transaction in this
@@ -56,7 +56,7 @@
@ExperimentalKairosApi
@JvmName("stateSpecToColdConflatedFlow")
fun <A> BuildSpec<State<A>>.toColdConflatedFlow(network: KairosNetwork): Flow<A> =
- channelFlow { network.activateSpec { applySpec().observe { trySend(it) } } }.conflate()
+ channelFlow { network.activateSpec { applySpec().observeSync { trySend(it) } } }.conflate()
/**
* Returns a cold [Flow] that, when collected, applies this [Transactional] in a new transaction in
@@ -65,7 +65,7 @@
@ExperimentalKairosApi
@JvmName("transactionalFlowToColdConflatedFlow")
fun <A> Transactional<Events<A>>.toColdConflatedFlow(network: KairosNetwork): Flow<A> =
- channelFlow { network.activateSpec { sample().observe { trySend(it) } } }.conflate()
+ channelFlow { network.activateSpec { sample().observeSync { trySend(it) } } }.conflate()
/**
* Returns a cold [Flow] that, when collected, applies this [Transactional] in a new transaction in
@@ -74,7 +74,7 @@
@ExperimentalKairosApi
@JvmName("transactionalStateToColdConflatedFlow")
fun <A> Transactional<State<A>>.toColdConflatedFlow(network: KairosNetwork): Flow<A> =
- channelFlow { network.activateSpec { sample().observe { trySend(it) } } }.conflate()
+ channelFlow { network.activateSpec { sample().observeSync { trySend(it) } } }.conflate()
/**
* Returns a cold [Flow] that, when collected, applies this [Stateful] in a new transaction in this
@@ -85,7 +85,7 @@
@ExperimentalKairosApi
@JvmName("statefulFlowToColdConflatedFlow")
fun <A> Stateful<Events<A>>.toColdConflatedFlow(network: KairosNetwork): Flow<A> =
- channelFlow { network.activateSpec { applyStateful().observe { trySend(it) } } }.conflate()
+ channelFlow { network.activateSpec { applyStateful().observeSync { trySend(it) } } }.conflate()
/**
* Returns a cold [Flow] that, when collected, applies this [Transactional] in a new transaction in
@@ -96,4 +96,4 @@
@ExperimentalKairosApi
@JvmName("statefulStateToColdConflatedFlow")
fun <A> Stateful<State<A>>.toColdConflatedFlow(network: KairosNetwork): Flow<A> =
- channelFlow { network.activateSpec { applyStateful().observe { trySend(it) } } }.conflate()
+ channelFlow { network.activateSpec { applyStateful().observeSync { trySend(it) } } }.conflate()
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Transactional.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Transactional.kt
index 5050511..f0becc4 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Transactional.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Transactional.kt
@@ -16,7 +16,6 @@
package com.android.systemui.kairos
-import com.android.systemui.kairos.internal.CompletableLazy
import com.android.systemui.kairos.internal.InitScope
import com.android.systemui.kairos.internal.NoScope
import com.android.systemui.kairos.internal.TransactionalImpl
@@ -40,7 +39,7 @@
/** A constant [Transactional] that produces [value] whenever it is sampled. */
@ExperimentalKairosApi
fun <A> transactionalOf(value: A): Transactional<A> =
- Transactional(stateOf(TransactionalImpl.Const(CompletableLazy(value))))
+ Transactional(stateOf(TransactionalImpl.Const(lazyOf(value))))
/**
* Returns a [Transactional] that acts as a deferred-reference to the [Transactional] produced by
@@ -106,4 +105,4 @@
/** Returns a [Transactional] that, when queried, samples this [State]. */
fun <A> State<A>.asTransactional(): Transactional<A> =
- Transactional(map { TransactionalImpl.Const(CompletableLazy(it)) })
+ Transactional(map { TransactionalImpl.Const(lazyOf(it)) })
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/BuildScopeImpl.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/BuildScopeImpl.kt
index 0b0b065..86e90db 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/BuildScopeImpl.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/BuildScopeImpl.kt
@@ -27,9 +27,11 @@
import com.android.systemui.kairos.EventsInit
import com.android.systemui.kairos.KairosCoroutineScope
import com.android.systemui.kairos.KairosNetwork
+import com.android.systemui.kairos.KairosScope
import com.android.systemui.kairos.KeyedEvents
import com.android.systemui.kairos.LocalNetwork
import com.android.systemui.kairos.MutableEvents
+import com.android.systemui.kairos.TransactionEffectScope
import com.android.systemui.kairos.TransactionScope
import com.android.systemui.kairos.groupByKey
import com.android.systemui.kairos.init
@@ -37,7 +39,6 @@
import com.android.systemui.kairos.internal.util.invokeOnCancel
import com.android.systemui.kairos.internal.util.launchImmediate
import com.android.systemui.kairos.launchEffect
-import com.android.systemui.kairos.mergeLeft
import com.android.systemui.kairos.util.Maybe
import com.android.systemui.kairos.util.Maybe.Absent
import com.android.systemui.kairos.util.Maybe.Present
@@ -45,6 +46,7 @@
import java.util.concurrent.atomic.AtomicReference
import kotlin.coroutines.ContinuationInterceptor
import kotlin.coroutines.CoroutineContext
+import kotlin.coroutines.EmptyCoroutineContext
import kotlinx.coroutines.CompletableJob
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.CoroutineStart
@@ -61,8 +63,8 @@
private val job: Job
get() = coroutineScope.coroutineContext.job
- override val kairosNetwork: KairosNetwork by lazy {
- LocalNetwork(network, coroutineScope, endSignal)
+ override val kairosNetwork: LocalNetwork by lazy {
+ LocalNetwork(network, coroutineScope, stateScope.aliveLazy)
}
override fun <T> events(builder: suspend EventProducerScope<T>.() -> Unit): Events<T> =
@@ -75,7 +77,7 @@
)
override fun <In, Out> coalescingEvents(
- getInitialValue: () -> Out,
+ getInitialValue: KairosScope.() -> Out,
coalesce: (old: Out, new: In) -> Out,
builder: suspend CoalescingEventProducerScope<In>.() -> Unit,
): Events<Out> =
@@ -83,11 +85,11 @@
constructEvents = { inputNode ->
val events =
CoalescingMutableEvents(
- null,
+ name = null,
coalesce = { old, new: In -> coalesce(old.value, new) },
- network,
- getInitialValue,
- inputNode,
+ network = network,
+ getInitialValue = { NoScope.getInitialValue() },
+ impl = inputNode,
)
events to CoalescingEventProducerScope<In> { value -> events.emit(value) }
},
@@ -111,77 +113,26 @@
override fun <A> Events<A>.observe(
coroutineContext: CoroutineContext,
- block: EffectScope.(A) -> Unit,
+ block: suspend EffectScope.(A) -> Unit,
): DisposableHandle {
- val subRef = AtomicReference<Maybe<Output<A>>?>(null)
- val childScope = coroutineScope.childScope()
- var cancelHandle: DisposableHandle? = null
- val handle = DisposableHandle {
- cancelHandle?.dispose()
- subRef.getAndSet(Absent)?.let { output ->
- if (output is Present) {
- @Suppress("DeferredResultUnused")
- network.transaction("observeEffect cancelled") {
- scheduleDeactivation(output.value)
- }
- }
- }
+ val interceptor = coroutineContext[ContinuationInterceptor]
+ return observeInternal(coroutineContext) { effectScope, output ->
+ scheduleDispatchedOutput(interceptor = interceptor) { effectScope.block(output) }
}
- // When our scope is cancelled, deactivate this observer.
- cancelHandle = childScope.coroutineContext.job.invokeOnCompletion { handle.dispose() }
- val localNetwork = LocalNetwork(network, childScope, endSignal)
- val outputNode =
- Output<A>(
- interceptor =
- coroutineContext[ContinuationInterceptor]
- ?: coroutineScope.coroutineContext[ContinuationInterceptor],
- onDeath = { subRef.set(Absent) },
- onEmit = { output ->
- if (subRef.get() is Present) {
- // Not cancelled, safe to emit
- val scope =
- object : EffectScope, TransactionScope by this {
- override fun <R> async(
- context: CoroutineContext,
- start: CoroutineStart,
- block: suspend KairosCoroutineScope.() -> R,
- ): Deferred<R> =
- childScope.async(context, start) {
- object : KairosCoroutineScope, CoroutineScope by this {
- override val kairosNetwork: KairosNetwork
- get() = localNetwork
- }
- .block()
- }
-
- override val kairosNetwork: KairosNetwork
- get() = localNetwork
- }
- scope.block(output)
- }
- },
- )
- // Defer, in case any EventsLoops / StateLoops still need to be set
- deferAction {
- // Check for immediate cancellation
- if (subRef.get() != null) return@deferAction
- this@observe.takeUntil(endSignal)
- .init
- .connect(evalScope = stateScope.evalScope)
- .activate(evalScope = stateScope.evalScope, outputNode.schedulable)
- ?.let { (conn, needsEval) ->
- outputNode.upstream = conn
- if (!subRef.compareAndSet(null, Maybe.present(outputNode))) {
- // Job's already been cancelled, schedule deactivation
- scheduleDeactivation(outputNode)
- } else if (needsEval) {
- outputNode.schedule(0, evalScope = stateScope.evalScope)
- }
- } ?: run { childScope.cancel() }
- }
- return handle
}
+ override fun <A> Events<A>.observeSync(
+ block: TransactionEffectScope.(A) -> Unit
+ ): DisposableHandle =
+ observeInternal(EmptyCoroutineContext) { effectScope, output ->
+ val scope =
+ object :
+ TransactionEffectScope,
+ TransactionScope by this@observeInternal,
+ EffectScope by effectScope {}
+ scope.block(output)
+ }
+
override fun <A, B> Events<A>.mapBuild(transform: BuildScope.(A) -> B): Events<B> {
val childScope = coroutineScope.childScope()
return EventsInit(
@@ -226,10 +177,96 @@
val changes: Events<Map<K, Maybe<A>>> =
EventsInit(constInit("applyLatestForKey", changesNode.cached()))
// Ensure effects are observed; otherwise init will stay alive longer than expected
- changes.observe()
+ changes.observeSync()
return changes to DeferredValue(initOut)
}
+ private fun <A> Events<A>.observeInternal(
+ context: CoroutineContext,
+ block: EvalScope.(EffectScope, A) -> Unit,
+ ): DisposableHandle {
+ val subRef = AtomicReference<Maybe<Output<A>>?>(null)
+ val childScope: CoroutineScope = coroutineScope.childScope(context)
+ var cancelHandle: DisposableHandle? = null
+ val handle = DisposableHandle {
+ cancelHandle?.dispose()
+ subRef.getAndSet(Absent)?.let { output ->
+ if (output is Present) {
+ @Suppress("DeferredResultUnused")
+ network.transaction("observeEffect cancelled") {
+ scheduleDeactivation(output.value)
+ }
+ }
+ }
+ }
+ // When our scope is cancelled, deactivate this observer.
+ cancelHandle = childScope.coroutineContext.job.invokeOnCompletion { handle.dispose() }
+ val effectScope: EffectScope = effectScope(childScope)
+ val outputNode =
+ Output<A>(
+ onDeath = { subRef.set(Absent) },
+ onEmit = onEmit@{ output ->
+ if (subRef.get() !is Present) return@onEmit
+ // Not cancelled, safe to emit]
+ block(effectScope, output)
+ },
+ )
+ // Defer, in case any EventsLoops / StateLoops still need to be set
+ deferAction {
+ // Check for immediate cancellation
+ if (subRef.get() != null) return@deferAction
+ truncateToScope(this@observeInternal)
+ .init
+ .connect(evalScope = stateScope.evalScope)
+ .activate(evalScope = stateScope.evalScope, outputNode.schedulable)
+ ?.let { (conn, needsEval) ->
+ outputNode.upstream = conn
+ if (!subRef.compareAndSet(null, Maybe.present(outputNode))) {
+ // Job's already been cancelled, schedule deactivation
+ scheduleDeactivation(outputNode)
+ } else if (needsEval) {
+ outputNode.schedule(0, evalScope = stateScope.evalScope)
+ }
+ } ?: run { childScope.cancel() }
+ }
+ return handle
+ }
+
+ private fun effectScope(childScope: CoroutineScope) =
+ object : EffectScope {
+ override fun <R> async(
+ context: CoroutineContext,
+ start: CoroutineStart,
+ block: suspend KairosCoroutineScope.() -> R,
+ ): Deferred<R> =
+ childScope.async(context, start) newScope@{
+ val childEndSignal: Events<Unit> =
+ this@BuildScopeImpl.newStopEmitter("EffectScope.async").apply {
+ this@newScope.invokeOnCancel { emit(Unit) }
+ }
+ val childStateScope: StateScopeImpl =
+ this@BuildScopeImpl.stateScope.childStateScope(childEndSignal)
+ val localNetwork =
+ LocalNetwork(
+ network = this@BuildScopeImpl.network,
+ scope = this@newScope,
+ aliveLazy = childStateScope.aliveLazy,
+ )
+ val scope =
+ object : KairosCoroutineScope, CoroutineScope by this@newScope {
+ override val kairosNetwork: KairosNetwork = localNetwork
+ }
+ scope.block()
+ }
+
+ override val kairosNetwork: KairosNetwork =
+ LocalNetwork(
+ network = this@BuildScopeImpl.network,
+ scope = childScope,
+ aliveLazy = this@BuildScopeImpl.stateScope.aliveLazy,
+ )
+ }
+
private fun <A, T : Events<A>, S> buildEvents(
name: String? = null,
constructEvents: (InputNode<A>) -> Pair<T, S>,
@@ -267,7 +304,7 @@
},
)
emitter = constructEvents(inputNode)
- return emitter.first.takeUntil(mergeLeft(stopEmitter, endSignal))
+ return truncateToScope(emitter.first.takeUntil(stopEmitter))
}
private fun newStopEmitter(name: String): CoalescingMutableEvents<Unit, Unit> =
@@ -278,7 +315,7 @@
getInitialValue = {},
)
- private fun childBuildScope(newEnd: Events<Any>): BuildScopeImpl {
+ fun childBuildScope(newEnd: Events<Any>): BuildScopeImpl {
val newCoroutineScope: CoroutineScope = coroutineScope.childScope()
return BuildScopeImpl(
stateScope = stateScope.childStateScope(newEnd),
@@ -293,20 +330,18 @@
(newCoroutineScope.coroutineContext.job as CompletableJob).complete()
}
)
- endSignalOnce.observe { newCoroutineScope.cancel() }
+ alive.observeSync { if (!it) newCoroutineScope.cancel() }
}
}
private fun mutableChildBuildScope(coroutineContext: CoroutineContext): BuildScopeImpl {
val childScope = coroutineScope.childScope(coroutineContext)
- val stopEmitter = lazy {
+ val stopEmitter =
newStopEmitter("mutableChildBuildScope").apply {
childScope.invokeOnCancel { emit(Unit) }
}
- }
return BuildScopeImpl(
- stateScope =
- StateScopeImpl(evalScope = stateScope.evalScope, endSignalLazy = stopEmitter),
+ stateScope = stateScope.childStateScope(stopEmitter),
coroutineScope = childScope,
)
}
@@ -317,11 +352,6 @@
coroutineScope: CoroutineScope,
) =
BuildScopeImpl(
- stateScope =
- StateScopeImpl(
- evalScope = this,
- endSignalLazy = outerScope.stateScope.endSignalLazy,
- endSignalOnceLazy = outerScope.stateScope.endSignalOnceLazy,
- ),
+ stateScope = StateScopeImpl(evalScope = this, aliveLazy = outerScope.stateScope.aliveLazy),
coroutineScope,
)
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/EvalScopeImpl.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/EvalScopeImpl.kt
index e1baf55..a05aca2e 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/EvalScopeImpl.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/EvalScopeImpl.kt
@@ -60,7 +60,7 @@
"now",
this,
{ switchOff.init.connect(evalScope = this) },
- CompletableLazy(
+ lazyOf(
EventsInit(
constInit(
"now",
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/InternalScopes.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/InternalScopes.kt
index cd22143..164ceba 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/InternalScopes.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/InternalScopes.kt
@@ -18,8 +18,10 @@
import com.android.systemui.kairos.BuildScope
import com.android.systemui.kairos.Events
+import com.android.systemui.kairos.State
import com.android.systemui.kairos.StateScope
import com.android.systemui.kairos.TransactionScope
+import kotlin.coroutines.ContinuationInterceptor
internal interface InitScope {
val networkId: Any
@@ -28,10 +30,9 @@
internal interface EvalScope : NetworkScope, DeferScope, TransactionScope
internal interface InternalStateScope : EvalScope, StateScope {
- val endSignal: Events<Any>
- val endSignalOnce: Events<Any>
+ val alive: State<Boolean>
- fun childStateScope(newEnd: Events<Any>): InternalStateScope
+ fun <A> truncateToScope(events: Events<A>): Events<A>
}
internal interface InternalBuildScope : InternalStateScope, BuildScope
@@ -48,6 +49,8 @@
fun scheduleOutput(output: Output<*>)
+ fun scheduleDispatchedOutput(interceptor: ContinuationInterceptor?, block: suspend () -> Unit)
+
fun scheduleMuxMover(muxMover: MuxDeferredNode<*, *, *>)
fun schedule(state: StateSource<*>)
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Network.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Network.kt
index 452aa49..63de801 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Network.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Network.kt
@@ -16,6 +16,7 @@
package com.android.systemui.kairos.internal
+import com.android.systemui.kairos.CoalescingPolicy
import com.android.systemui.kairos.State
import com.android.systemui.kairos.internal.util.HeteroMap
import com.android.systemui.kairos.internal.util.logDuration
@@ -40,7 +41,8 @@
private val nextNetworkId = AtomicLong()
-internal class Network(val coroutineScope: CoroutineScope) : NetworkScope {
+internal class Network(val coroutineScope: CoroutineScope, val coalescingPolicy: CoalescingPolicy) :
+ NetworkScope {
override val networkId: Any = nextNetworkId.getAndIncrement()
@@ -68,7 +70,9 @@
override val transactionStore = TransactionStore()
private val stateWrites = ArrayDeque<StateSource<*>>()
- private val outputsByDispatcher = HashMap<ContinuationInterceptor, ArrayDeque<Output<*>>>()
+ private val fastOutputs = ArrayDeque<Output<*>>()
+ private val outputsByDispatcher =
+ HashMap<ContinuationInterceptor, ArrayDeque<suspend () -> Unit>>()
private val muxMovers = ArrayDeque<MuxDeferredNode<*, *, *>>()
private val deactivations = ArrayDeque<PushNode<*>>()
private val outputDeactivations = ArrayDeque<Output<*>>()
@@ -76,9 +80,16 @@
private val inputScheduleChan = Channel<ScheduledAction<*>>()
override fun scheduleOutput(output: Output<*>) {
- val continuationInterceptor: ContinuationInterceptor =
- output.interceptor ?: Dispatchers.Unconfined
- outputsByDispatcher.computeIfAbsent(continuationInterceptor) { ArrayDeque() }.add(output)
+ fastOutputs.add(output)
+ }
+
+ override fun scheduleDispatchedOutput(
+ interceptor: ContinuationInterceptor?,
+ block: suspend () -> Unit,
+ ) {
+ outputsByDispatcher
+ .computeIfAbsent(interceptor ?: Dispatchers.Unconfined) { ArrayDeque() }
+ .add(block)
}
override fun scheduleMuxMover(muxMover: MuxDeferredNode<*, *, *>) {
@@ -103,10 +114,21 @@
for (first in inputScheduleChan) {
// Drain and conflate all transaction requests into a single transaction
actions.add(first)
- while (true) {
- yield()
- val func = inputScheduleChan.tryReceive().getOrNull() ?: break
- actions.add(func)
+ when (coalescingPolicy) {
+ CoalescingPolicy.None -> {}
+ CoalescingPolicy.Normal -> {
+ while (true) {
+ val func = inputScheduleChan.tryReceive().getOrNull() ?: break
+ actions.add(func)
+ }
+ }
+ CoalescingPolicy.Eager -> {
+ while (true) {
+ yield()
+ val func = inputScheduleChan.tryReceive().getOrNull() ?: break
+ actions.add(func)
+ }
+ }
}
transactionMutex.withLock {
val e = epoch
@@ -165,15 +187,16 @@
}
/** Performs a transactional update of the Kairos network. */
- private suspend fun doTransaction(logIndent: Int) {
+ private fun doTransaction(logIndent: Int) {
// Traverse network, then run outputs
logDuration(logIndent, "traverse network") {
do {
val numNodes =
logDuration("drainEval") { scheduler.drainEval(currentLogIndent, this@Network) }
logLn("drained $numNodes nodes")
- } while (logDuration("evalOutputs") { evalScope { evalOutputs(this) } })
+ } while (logDuration("evalOutputs") { evalScope { evalFastOutputs(this) } })
}
+ coroutineScope.launch { evalLaunchedOutputs() }
// Update states
logDuration(logIndent, "update states") {
evalScope { evalStateWriters(currentLogIndent, this) }
@@ -195,12 +218,18 @@
logDuration(logIndent, "deactivations") { evalDeactivations() }
}
- /** Invokes all [Output]s that have received data within this transaction. */
- private suspend fun evalOutputs(evalScope: EvalScope): Boolean {
- if (outputsByDispatcher.isEmpty()) {
+ private fun evalFastOutputs(evalScope: EvalScope): Boolean {
+ if (fastOutputs.isEmpty()) {
return false
}
- // Outputs can enqueue other outputs, so we need two loops
+ while (true) {
+ fastOutputs.removeFirstOrNull()?.visit(evalScope) ?: break
+ }
+ return true
+ }
+
+ private suspend fun evalLaunchedOutputs() {
+ // Outputs might enqueue other outputs, so we need two loops
while (outputsByDispatcher.isNotEmpty()) {
var launchedAny = false
coroutineScope {
@@ -210,7 +239,7 @@
launch(key) {
while (outputs.isNotEmpty()) {
val output = outputs.removeFirst()
- launch { output.visit(evalScope) }
+ launch { output() }
}
}
}
@@ -220,7 +249,6 @@
outputsByDispatcher.clear()
}
}
- return true
}
private fun evalMuxMovers(logIndent: Int, evalScope: EvalScope) {
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Output.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Output.kt
index f245262..b23afa7 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Output.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Output.kt
@@ -16,13 +16,7 @@
package com.android.systemui.kairos.internal
-import kotlin.coroutines.ContinuationInterceptor
-
-internal class Output<A>(
- val interceptor: ContinuationInterceptor? = null,
- val onDeath: () -> Unit = {},
- val onEmit: EvalScope.(A) -> Unit,
-) {
+internal class Output<A>(val onDeath: () -> Unit = {}, val onEmit: EvalScope.(A) -> Unit) {
val schedulable = Schedulable.O(this)
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/StateImpl.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/StateImpl.kt
index da83258..c257457 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/StateImpl.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/StateImpl.kt
@@ -90,7 +90,7 @@
}
internal class StateSource<S>(init: Lazy<S>) : StateStore<S>() {
- constructor(init: S) : this(CompletableLazy(init))
+ constructor(init: S) : this(lazyOf(init))
lateinit var upstreamConnection: NodeConnection<S>
@@ -109,7 +109,7 @@
/** called by network after eval phase has completed */
fun updateState(logIndent: Int, evalScope: EvalScope) {
// write the latch
- _current = CompletableLazy(upstreamConnection.getPushEvent(logIndent, evalScope))
+ _current = lazyOf(upstreamConnection.getPushEvent(logIndent, evalScope))
writeEpoch = evalScope.epoch + 1
}
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/StateScopeImpl.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/StateScopeImpl.kt
index f5a0ce9..ed0dc7b 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/StateScopeImpl.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/StateScopeImpl.kt
@@ -28,26 +28,20 @@
import com.android.systemui.kairos.StateScope
import com.android.systemui.kairos.Stateful
import com.android.systemui.kairos.emptyEvents
+import com.android.systemui.kairos.flatMap
import com.android.systemui.kairos.groupByKey
import com.android.systemui.kairos.init
import com.android.systemui.kairos.mapCheap
-import com.android.systemui.kairos.mergeLeft
+import com.android.systemui.kairos.mapCheapUnsafe
+import com.android.systemui.kairos.stateOf
import com.android.systemui.kairos.switchEvents
import com.android.systemui.kairos.util.Maybe
import com.android.systemui.kairos.util.map
-internal class StateScopeImpl(
- val evalScope: EvalScope,
- val endSignalLazy: Lazy<Events<Any>>,
- endSignalOnceLazy: Lazy<Events<Any>>? = null,
-) : InternalStateScope, EvalScope by evalScope {
+internal class StateScopeImpl(val evalScope: EvalScope, val aliveLazy: Lazy<State<Boolean>>) :
+ InternalStateScope, EvalScope by evalScope {
- override val endSignal: Events<Any> by endSignalLazy
-
- val endSignalOnceLazy: Lazy<Events<Any>> =
- endSignalOnceLazy ?: lazy { endSignal.nextOnlyInternal("StateScope.endSignal") }
-
- override val endSignalOnce: Events<Any> by this.endSignalOnceLazy
+ override val alive: State<Boolean> by aliveLazy
override fun <A> deferredStateScope(block: StateScope.() -> A): DeferredValue<A> =
DeferredValue(deferAsync { block() })
@@ -55,7 +49,7 @@
override fun <A> Events<A>.holdStateDeferred(initialValue: DeferredValue<A>): State<A> {
val operatorName = "holdStateDeferred"
// Ensure state is only collected until the end of this scope
- return truncateToScope(operatorName)
+ return truncateToScope(this@holdStateDeferred)
.holdStateInternalDeferred(operatorName, initialValue.unwrapped)
}
@@ -79,12 +73,12 @@
}
override fun <K, A, B> Events<Map<K, Maybe<Stateful<A>>>>.applyLatestStatefulForKey(
- init: DeferredValue<Map<K, Stateful<B>>>,
+ initialValues: DeferredValue<Map<K, Stateful<B>>>,
numKeys: Int?,
): Pair<Events<Map<K, Maybe<A>>>, DeferredValue<Map<K, B>>> {
val eventsByKey: KeyedEvents<K, Maybe<Stateful<A>>> = groupByKey(numKeys)
val initOut: Lazy<Map<K, B>> = deferAsync {
- init.unwrapped.value.mapValues { (k, stateful) ->
+ initialValues.unwrapped.value.mapValues { (k, stateful) ->
val newEnd = eventsByKey[k]
val newScope = childStateScope(newEnd)
newScope.stateful()
@@ -126,18 +120,22 @@
)
}
- override fun childStateScope(newEnd: Events<Any>) =
- StateScopeImpl(evalScope, lazy { mergeLeft(newEnd, endSignal) })
+ fun childStateScope(childEndSignal: Events<Any>) =
+ StateScopeImpl(
+ evalScope,
+ aliveLazy =
+ lazy {
+ val isChildAlive: State<Boolean> =
+ childEndSignal
+ .nextOnlyInternal("childStateScope.endSignalOnce")
+ .mapCheap { false }
+ .holdState(true)
+ alive.flatMap { isAlive -> if (isAlive) isChildAlive else stateOf(false) }
+ },
+ )
- private fun <A> Events<A>.truncateToScope(operatorName: String): Events<A> =
- if (endSignalOnce === emptyEvents) {
- this
- } else {
- endSignalOnce
- .mapCheap { emptyEvents }
- .holdStateInternal(operatorName, this)
- .switchEvents()
- }
+ override fun <A> truncateToScope(events: Events<A>): Events<A> =
+ alive.mapCheapUnsafe { if (it) events else emptyEvents }.switchEvents()
private fun <A> Events<A>.nextOnlyInternal(operatorName: String): Events<A> =
if (this === emptyEvents) {
@@ -151,12 +149,12 @@
}
}
- private fun <A> Events<A>.holdStateInternal(operatorName: String, init: A): State<A> =
- holdStateInternalDeferred(operatorName, CompletableLazy(init))
+ private fun <A> Events<A>.holdStateInternal(operatorName: String, initialValue: A): State<A> =
+ holdStateInternalDeferred(operatorName, lazyOf(initialValue))
private fun <A> Events<A>.holdStateInternalDeferred(
operatorName: String,
- init: Lazy<A>,
+ initialValue: Lazy<A>,
): State<A> {
val changes = this@holdStateInternalDeferred
val name = operatorName
@@ -166,15 +164,11 @@
operatorName,
evalScope,
{ changes.init.connect(evalScope = this) },
- init,
+ initialValue,
)
return StateInit(constInit(name, impl))
}
}
private fun EvalScope.reenterStateScope(outerScope: StateScopeImpl) =
- StateScopeImpl(
- evalScope = this,
- endSignalLazy = outerScope.endSignalLazy,
- endSignalOnceLazy = outerScope.endSignalOnceLazy,
- )
+ StateScopeImpl(evalScope = this, aliveLazy = outerScope.aliveLazy)
diff --git a/packages/SystemUI/utils/kairos/test/com/android/systemui/kairos/KairosSamples.kt b/packages/SystemUI/utils/kairos/test/com/android/systemui/kairos/KairosSamples.kt
index 88a5b7a..b019491 100644
--- a/packages/SystemUI/utils/kairos/test/com/android/systemui/kairos/KairosSamples.kt
+++ b/packages/SystemUI/utils/kairos/test/com/android/systemui/kairos/KairosSamples.kt
@@ -40,10 +40,10 @@
val ints = emitter.mapMaybe { it.toIntOrNull().toMaybe() }
var observedInput: String? = null
- emitter.observe { observedInput = it }
+ emitter.observeSync { observedInput = it }
var observedInt: Int? = null
- ints.observe { observedInt = it }
+ ints.observeSync { observedInt = it }
launchEffect {
// parse succeeds
@@ -76,7 +76,7 @@
}
var observedSquare: Int? = null
- squared.observe { observedSquare = it }
+ squared.observeSync { observedSquare = it }
launchEffect {
emitter.emit(10)
@@ -97,7 +97,7 @@
val squared = emitter.map { it * it }
var observedSquare: Int? = null
- squared.observe { observedSquare = it }
+ squared.observeSync { observedSquare = it }
launchEffect {
emitter.emit(10)
@@ -117,7 +117,7 @@
newCount = emitter.map { count.sample() + 1 }
var observedCount = 0
- count.observe { observedCount = it }
+ count.observeSync { observedCount = it }
launchEffect {
emitter.emit(Unit)
@@ -136,7 +136,7 @@
count = emitter.map { count.sample() + 1 }.holdState(0)
var observedCount = 0
- count.observe { observedCount = it }
+ count.observeSync { observedCount = it }
launchEffect {
emitter.emit(Unit)
@@ -154,11 +154,11 @@
val state = emitter.holdState(0)
var numEmissions = 0
- emitter.observe { numEmissions++ }
+ emitter.observeSync { numEmissions++ }
var observedState = 0
var numChangeEmissions = 0
- state.changes.observe {
+ state.changes.observeSync {
observedState = it
numChangeEmissions++
}
@@ -198,10 +198,10 @@
val (lefts, rights) = emitter.partitionThese()
var observedLeft: Int? = null
- lefts.observe { observedLeft = it }
+ lefts.observeSync { observedLeft = it }
var observedRight: String? = null
- rights.observe { observedRight = it }
+ rights.observeSync { observedRight = it }
launchEffect {
emitter.emit(These.first(10))
@@ -228,7 +228,7 @@
val output = mergeLeft(fizzbuzz, emitter.mapCheap { it.toString() })
var observedOutput: String? = null
- output.observe { observedOutput = it }
+ output.observeSync { observedOutput = it }
launchEffect {
emitter.emit(1)
@@ -257,13 +257,13 @@
val groupB = grouped["B"]
var numEmissions = 0
- emitter.observe { numEmissions++ }
+ emitter.observeSync { numEmissions++ }
var observedA: Int? = null
- groupA.observe { observedA = it }
+ groupA.observeSync { observedA = it }
var observedB: Int? = null
- groupB.observe { observedB = it }
+ groupB.observeSync { observedB = it }
launchEffect {
// emit to group A
@@ -308,7 +308,7 @@
negate.map { negate -> if (negate) emitter.map { it * -1 } else emitter }.switchEvents()
var observed: Int? = null
- output.observe { observed = it }
+ output.observeSync { observed = it }
launchEffect {
// emit like normal
@@ -341,10 +341,10 @@
val promptSwitch = switchedIn.switchEventsPromptly()
var observedDeferred: Int? = null
- deferredSwitch.observe { observedDeferred = it }
+ deferredSwitch.observeSync { observedDeferred = it }
var observedPrompt: Int? = null
- promptSwitch.observe { observedPrompt = it }
+ promptSwitch.observeSync { observedPrompt = it }
launchEffect {
emitter.emit(3)
@@ -375,7 +375,7 @@
var store = 0
val transactional = transactionally { store++ }
- effect {
+ effectSync {
assertEquals(store, 0)
assertEquals(transactional.sample(), 0)
assertEquals(store, 1)
@@ -388,18 +388,18 @@
fun BuildScope.states() {
val constantState = stateOf(10)
- effect { assertEquals(constantState.sample(), 10) }
+ effectSync { assertEquals(constantState.sample(), 10) }
val mappedConstantState: State<Int> = constantState.map { it * 2 }
- effect { assertEquals(mappedConstantState.sample(), 20) }
+ effectSync { assertEquals(mappedConstantState.sample(), 20) }
val emitter = MutableEvents<Int>()
val heldState: State<Int?> = emitter.holdState(null)
- effect { assertEquals(heldState.sample(), null) }
+ effectSync { assertEquals(heldState.sample(), null) }
var observed: Int? = null
var wasObserved = false
- heldState.observe {
+ heldState.observeSync {
observed = it
wasObserved = true
}
@@ -412,10 +412,10 @@
val combinedStates: State<Pair<Int, Int?>> =
combine(mappedConstantState, heldState) { a, b -> Pair(a, b) }
- effect { assertEquals(combinedStates.sample(), 20 to null) }
+ effectSync { assertEquals(combinedStates.sample(), 20 to null) }
var observedPair: Pair<Int, Int?>? = null
- combinedStates.observe { observedPair = it }
+ combinedStates.observeSync { observedPair = it }
launchEffect {
emitter.emit(12)
assertEquals(observedPair, 20 to 12)
@@ -427,11 +427,11 @@
fun BuildScope.holdState() {
val emitter = MutableEvents<Int>()
val heldState: State<Int?> = emitter.holdState(null)
- effect { assertEquals(heldState.sample(), null) }
+ effectSync { assertEquals(heldState.sample(), null) }
var observed: Int? = null
var wasObserved = false
- heldState.observe {
+ heldState.observeSync {
observed = it
wasObserved = true
}
@@ -456,7 +456,7 @@
val squared: State<Int> = held.map { it * it }
var observed: Int? = null
- squared.observe { observed = it }
+ squared.observeSync { observed = it }
launchEffect {
assertEquals(observed, 0)
@@ -476,7 +476,7 @@
val combined = squared.combine(negated) { a, b -> Pair(a, b) }
val observed = mutableListOf<Pair<Int, Int>>()
- combined.observe { observed.add(it) }
+ combined.observeSync { observed.add(it) }
launchEffect {
emitter.emit(10)
@@ -502,7 +502,7 @@
toggleState.flatMap { b -> if (b) firstCount else secondCount }
var observed: Int? = null
- activeCount.observe { observed = it }
+ activeCount.observeSync { observed = it }
launchEffect {
assertEquals(observed, 0)
@@ -532,10 +532,10 @@
val squared = incremental.mapValues { (key, value) -> value * value }
var observedUpdate: MapPatch<String, Int>? = null
- squared.updates.observe { observedUpdate = it }
+ squared.updates.observeSync { observedUpdate = it }
var observedState: Map<String, Int>? = null
- squared.observe { observedState = it }
+ squared.observeSync { observedState = it }
launchEffect {
assertEquals(observedState, emptyMap())
@@ -570,7 +570,7 @@
val merged: Events<Map<String, Int>> = incremental.mergeEventsIncrementally()
var observed: Map<String, Int>? = null
- merged.observe { observed = it }
+ merged.observeSync { observed = it }
launchEffect {
// add events entry: A
@@ -614,10 +614,10 @@
val promptMerge: Events<Map<String, Int>> = incremental.mergeEventsIncrementallyPromptly()
var observedDeferred: Map<String, Int>? = null
- deferredMerge.observe { observedDeferred = it }
+ deferredMerge.observeSync { observedDeferred = it }
var observedPrompt: Map<String, Int>? = null
- promptMerge.observe { observedPrompt = it }
+ promptMerge.observeSync { observedPrompt = it }
launchEffect {
val emitterA = MutableEvents<Int>()
diff --git a/packages/SystemUI/utils/kairos/test/com/android/systemui/kairos/KairosTests.kt b/packages/SystemUI/utils/kairos/test/com/android/systemui/kairos/KairosTests.kt
index ffe6e95..fa36255 100644
--- a/packages/SystemUI/utils/kairos/test/com/android/systemui/kairos/KairosTests.kt
+++ b/packages/SystemUI/utils/kairos/test/com/android/systemui/kairos/KairosTests.kt
@@ -43,7 +43,7 @@
fun basic() = runFrpTest { network ->
val emitter = network.mutableEvents<Int>()
var result: Int? = null
- activateSpec(network) { emitter.observe { result = it } }
+ activateSpec(network) { emitter.observeSync { result = it } }
runCurrent()
emitter.emit(3)
runCurrent()
@@ -246,8 +246,8 @@
val amts = eAmt.filter { amt -> amt >= 0 }
- amts.observe { currentAmt = it }
- eSold.observe { wasSold = true }
+ amts.observeSync { currentAmt = it }
+ eSold.observeSync { wasSold = true }
eSold.nextDeferred()
}
@@ -479,7 +479,7 @@
fun switchIndirect() = runFrpTest { network ->
val emitter = network.mutableEvents<Unit>()
activateSpec(network) {
- emptyEvents.map { emitter.map { 1 } }.flatten().map { "$it" }.observe()
+ emptyEvents.map { emitter.map { 1 } }.flatten().map { "$it" }.observeSync()
}
runCurrent()
}
@@ -884,7 +884,7 @@
val state =
activateSpecWithResult(network) {
val state = combine(a.holdState(0), b.holdState(0)) { a, b -> Pair(a, b) }
- state.changes.observe { observed = it }
+ state.changes.observeSync { observed = it }
state
}
assertEquals(0 to 0, network.transact { state.sample() })
@@ -953,7 +953,7 @@
activateSpecWithResult(network) {
val tres =
merge(e2.map { 1 }, e2.map { 2 }, transformCoincidence = { a, b -> a + b })
- tres.observeBuild()
+ tres.observeSync()
val switch = emitter.map { tres }.flatten()
merge(switch, e2.map { null }, transformCoincidence = { a, _ -> a })
.filterNotNull()
@@ -1084,7 +1084,7 @@
fun inputEventsCompleted() = runFrpTest { network ->
val results = mutableListOf<Int>()
val e = network.mutableEvents<Int>()
- activateSpec(network) { e.nextOnly().observe { results.add(it) } }
+ activateSpec(network) { e.nextOnly().observeSync { results.add(it) } }
runCurrent()
e.emit(10)
@@ -1317,7 +1317,7 @@
var observedCount: Int? = null
activateSpec(network) {
val (c, j) = asyncScope { input.foldState(0) { _, x -> x + 1 } }
- deferredBuildScopeAction { c.value.observe { observedCount = it } }
+ deferredBuildScopeAction { c.value.observeSync { observedCount = it } }
}
runCurrent()
assertEquals(0, observedCount)
@@ -1345,7 +1345,7 @@
effectRunning = false
}
}
- merge(emptyEvents, input.nextOnly()).observe {
+ merge(emptyEvents, input.nextOnly()).observeSync {
count++
j.cancel()
}
@@ -1375,13 +1375,13 @@
val specJob =
activateSpec(network) {
val handle =
- input.observe {
+ input.observeSync {
launch {
runningCount++
awaitClose { runningCount-- }
}
}
- stopper.nextOnly().observe { handle.dispose() }
+ stopper.nextOnly().observeSync { handle.dispose() }
}
runCurrent()
assertEquals(0, runningCount)
@@ -1410,7 +1410,7 @@
var runningCount = 0
val specJob =
activateSpec(network) {
- input.takeUntil(stopper).observe {
+ input.takeUntil(stopper).observeSync {
launch {
runningCount++
awaitClose { runningCount-- }
diff --git a/packages/SystemUI/utils/src/com/android/systemui/utils/windowmanager/WindowManagerUtils.kt b/packages/SystemUI/utils/src/com/android/systemui/utils/windowmanager/WindowManagerUtils.kt
index 643e934..bb088a1 100644
--- a/packages/SystemUI/utils/src/com/android/systemui/utils/windowmanager/WindowManagerUtils.kt
+++ b/packages/SystemUI/utils/src/com/android/systemui/utils/windowmanager/WindowManagerUtils.kt
@@ -33,17 +33,7 @@
return if (!enableViewCaptureTracing()) {
context.getSystemService(WindowManager::class.java)
} else {
- /**
- * We use this token to supply windowContextToken to [WindowManager] for
- * [WindowContext].
- */
- val windowContextToken = context.windowContextToken
-
- ViewCaptureAwareWindowManagerFactory.getInstance(
- context,
- parent = null,
- windowContextToken,
- )
+ ViewCaptureAwareWindowManagerFactory.getInstance(context)
}
}
}
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodContext.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodContext.java
index 9e6b12f..571c2d7 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodContext.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodContext.java
@@ -39,8 +39,11 @@
import com.android.internal.annotations.GuardedBy;
import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
import java.io.IOException;
-import java.util.Objects;
+import java.nio.file.Files;
import java.util.concurrent.Executor;
import java.util.function.Supplier;
@@ -56,8 +59,7 @@
private final ArrayMap<Class<?>, String> mClassToName = new ArrayMap<>();
private final ArrayMap<String, Supplier<?>> mNameToFactory = new ArrayMap<>();
- private final File mFilesDir;
- private final File mCacheDir;
+ private final File mDataDir;
private final Supplier<Resources> mResourcesSupplier;
private RavenwoodContext mAppContext;
@@ -79,8 +81,8 @@
mPackageName = packageName;
mMainThread = mainThread;
mResourcesSupplier = resourcesSupplier;
- mFilesDir = createTempDir(packageName + "_files-dir");
- mCacheDir = createTempDir(packageName + "_cache-dir");
+
+ mDataDir = Files.createTempDirectory(mPackageName).toFile();
// Services provided by a typical shipping device
registerService(ClipboardManager.class,
@@ -113,11 +115,6 @@
}
}
- void cleanUp() {
- deleteDir(mFilesDir);
- deleteDir(mCacheDir);
- }
-
@Override
public String getSystemServiceName(Class<?> serviceClass) {
// TODO: pivot to using SystemServiceRegistry
@@ -162,12 +159,12 @@
@Override
public UserHandle getUser() {
- return android.os.UserHandle.of(android.os.UserHandle.myUserId());
+ return UserHandle.of(UserHandle.myUserId());
}
@Override
public int getUserId() {
- return android.os.UserHandle.myUserId();
+ return UserHandle.myUserId();
}
@Override
@@ -176,19 +173,74 @@
}
@Override
- public File getFilesDir() {
- return mFilesDir;
+ public FileInputStream openFileInput(String name) throws FileNotFoundException {
+ return new FileInputStream(getFileStreamPath(name));
}
@Override
- public File getCacheDir() {
- return mCacheDir;
+ public FileOutputStream openFileOutput(String name, int mode) throws FileNotFoundException {
+ final boolean append = (mode & MODE_APPEND) != 0;
+ return new FileOutputStream(getFileStreamPath(name), append);
}
@Override
public boolean deleteFile(String name) {
- File f = new File(name);
- return f.delete();
+ return getFileStreamPath(name).delete();
+ }
+
+ @Override
+ public File getDataDir() {
+ return mDataDir;
+ }
+
+ @Override
+ public File getDir(String name, int mode) {
+ name = "app_" + name;
+ File file = new File(getDataDir(), name);
+ if (!file.exists()) {
+ file.mkdir();
+ }
+ return file;
+ }
+
+ private File makePrivateDir(String name) {
+ var dir = new File(getDataDir(), name);
+ dir.mkdirs();
+ return dir;
+ }
+
+ private File getPreferencesDir() {
+ return makePrivateDir("shared_prefs");
+ }
+
+ @Override
+ public File getFilesDir() {
+ return makePrivateDir("files");
+ }
+
+ @Override
+ public File getCacheDir() {
+ return makePrivateDir("cache");
+ }
+
+ @Override
+ public File getCodeCacheDir() {
+ return makePrivateDir("code_cache");
+ }
+
+ @Override
+ public File getNoBackupFilesDir() {
+ return makePrivateDir("no_backup");
+ }
+
+ @Override
+ public File getFileStreamPath(String name) {
+ return new File(getFilesDir(), name);
+ }
+
+ @Override
+ public File getSharedPreferencesPath(String name) {
+ return new File(getPreferencesDir(), name + ".xml");
}
@Override
@@ -257,34 +309,10 @@
}
}
};
- return () -> {
- return singleton.get();
- };
+ return () -> singleton.get();
}
public interface ThrowingSupplier<T> {
T get() throws Exception;
}
-
-
- static File createTempDir(String prefix) throws IOException {
- // Create a temp file, delete it and recreate it as a directory.
- final File dir = File.createTempFile(prefix + "-", "");
- dir.delete();
- dir.mkdirs();
- return dir;
- }
-
- static void deleteDir(File dir) {
- File[] children = dir.listFiles();
- if (children != null) {
- for (File child : children) {
- if (child.isDirectory()) {
- deleteDir(child);
- } else {
- child.delete();
- }
- }
- }
- }
}
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodSystemServer.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodSystemServer.java
index 3346635..3ea3be9 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodSystemServer.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodSystemServer.java
@@ -113,7 +113,7 @@
throw new RuntimeException("The requested service " + serviceClass
+ " is not yet supported under the Ravenwood deviceless testing "
+ "environment; consider requesting support from the API owner or "
- + "consider using Mockito; more details at go/ravenwood-docs");
+ + "consider using Mockito; more details at go/ravenwood");
}
// Start service and then depth-first traversal of any dependencies
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodUnsupportedApiException.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodUnsupportedApiException.java
index 9620a89..01f8d1a 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodUnsupportedApiException.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodUnsupportedApiException.java
@@ -19,6 +19,6 @@
public RavenwoodUnsupportedApiException() {
super("This method is not yet supported under the Ravenwood deviceless testing "
+ "environment; consider requesting support from the API owner or "
- + "consider using Mockito; more details at go/ravenwood-docs");
+ + "consider using Mockito; more details at go/ravenwood");
}
}
diff --git a/ravenwood/texts/ravenwood-annotation-allowed-classes.txt b/ravenwood/texts/ravenwood-annotation-allowed-classes.txt
index c035688..92f8ef85 100644
--- a/ravenwood/texts/ravenwood-annotation-allowed-classes.txt
+++ b/ravenwood/texts/ravenwood-annotation-allowed-classes.txt
@@ -58,6 +58,7 @@
android.util.JsonReader
android.util.JsonWriter
android.util.KeyValueListParser
+android.util.ListenerGroup
android.util.LocalLog
android.util.Log
android.util.LogPrinter
@@ -618,3 +619,5 @@
android.text.style.WrapTogetherSpan
android.text.util.Rfc822Token
android.text.util.Rfc822Tokenizer
+android.graphics.drawable.Drawable$ConstantState
+android.graphics.drawable.BitmapDrawable$BitmapState
diff --git a/ravenwood/texts/ravenwood-framework-policies.txt b/ravenwood/texts/ravenwood-framework-policies.txt
index 5c17662..0e3f8cc 100644
--- a/ravenwood/texts/ravenwood-framework-policies.txt
+++ b/ravenwood/texts/ravenwood-framework-policies.txt
@@ -52,35 +52,26 @@
# Just enough to support mocking, no further functionality
class android.content.BroadcastReceiver allow-annotation
method <init> ()V allow-annotation
-
-# TODO: Convert the following policies to "allow-annotation".
-class android.content.Context keep # no-pta
- method <init> ()V keep
- method getSystemService (Ljava/lang/Class;)Ljava/lang/Object; keep # no-pta
-class android.content.pm.PackageManager # no-pta
+class android.content.Context # no-pta
method <init> ()V keep
class android.text.ClipboardManager keep # no-pta
method <init> ()V keep
# Just enough to allow ResourcesManager to run
-class android.hardware.display.DisplayManagerGlobal keep # no-pta
- method getInstance ()Landroid/hardware/display/DisplayManagerGlobal; ignore # no-pta
+class android.hardware.display.DisplayManagerGlobal allow-annotation
+ method getInstance ()Landroid/hardware/display/DisplayManagerGlobal; allow-annotation
# Bare minimum to support running ImageDecoderTest
-class android.graphics.drawable.Drawable$ConstantState keepclass # no-pta
-class android.graphics.drawable.BitmapDrawable$BitmapState keepclass # no-pta
-class android.graphics.drawable.BitmapDrawable keep # no-pta
- method <init> (Landroid/content/res/Resources;Landroid/graphics/Bitmap;)V keep
- method init * keep
- method updateLocalState * keep
- method computeBitmapSize * keep
- method getIntrinsicWidth * keep
- method getIntrinsicHeight * keep
- method getBitmap * keep
-class android.graphics.drawable.Drawable keep # no-pta
- method <init> ()V keep
- method resolveDensity * keep
- method updateBlendModeFilter * ignore
+class android.graphics.drawable.BitmapDrawable allow-annotation
+ method <init> (Landroid/content/res/Resources;Landroid/graphics/Bitmap;)V allow-annotation
+ method init * allow-annotation
+ method updateLocalState * allow-annotation
+ method computeBitmapSize * allow-annotation
+ method getIntrinsicWidth * allow-annotation
+ method getIntrinsicHeight * allow-annotation
+ method getBitmap * allow-annotation
+class android.graphics.drawable.Drawable allow-annotation
+ method <init> ()V keep # no-pta
+ method resolveDensity * allow-annotation
+ method updateBlendModeFilter * allow-annotation
-class android.os.StrictMode keep # no-pta
- method noteSlowCall (Ljava/lang/String;)V ignore
diff --git a/services/Android.bp b/services/Android.bp
index d345bef..72fa3f6 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -90,6 +90,13 @@
FULL_SYSTEM_OPTIMIZE_JAVA: {
optimize: {
optimize: true,
+ // TODO(b/212737576): Apply this property for the SYSTEM_OPTIMIZE_JAVA case after
+ // resolving all downstream cyclic dependencies. For now, full optimization is
+ // more sensitive to downstream lib references, so it stands the most to benefit.
+ trace_references_from: select(soong_config_variable("ANDROID", "system_server_trace_refs"), {
+ any @ trace_refs: trace_refs,
+ default: [],
+ }),
},
},
},
diff --git a/services/accessibility/accessibility.aconfig b/services/accessibility/accessibility.aconfig
index a8beebc..4d6f1ca 100644
--- a/services/accessibility/accessibility.aconfig
+++ b/services/accessibility/accessibility.aconfig
@@ -55,16 +55,6 @@
}
flag {
- name: "clear_shortcuts_when_activity_updates_to_service"
- namespace: "accessibility"
- description: "When an a11y activity is updated to an a11y service, clears the associated shortcuts so that we don't skip the AccessibilityServiceWarning."
- bug: "358092445"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
name: "deprecate_package_list_observer"
namespace: "accessibility"
description: "Stops using the deprecated PackageListObserver."
@@ -166,6 +156,13 @@
}
flag {
+ name: "enable_mouse_key_enhancement"
+ namespace: "accessibility"
+ description: "Whether user can change mouse key acceleration, speed and use numeric keypad."
+ bug: "409330539"
+}
+
+flag {
name: "event_dispatcher_raw_event"
namespace: "accessibility"
description: "Fixes EventDispatcher#sendMotionEvent callers to properly provide raw event"
@@ -270,13 +267,6 @@
}
flag {
- name: "pinch_zoom_zero_min_span"
- namespace: "accessibility"
- description: "Whether to set min span of ScaleGestureDetector to zero."
- bug: "295327792"
-}
-
-flag {
name: "pointer_up_motion_event_in_touch_exploration"
namespace: "accessibility"
description: "Allows POINTER_UP motionEvents to trigger during touch exploration."
@@ -314,16 +304,6 @@
}
flag {
- name: "reset_input_dispatcher_before_first_touch_exploration"
- namespace: "accessibility"
- description: "Resets InputDispatcher state by sending ACTION_CANCEL before the first TouchExploration hover events"
- bug: "364408887"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
name: "scan_packages_without_lock"
namespace: "accessibility"
description: "Scans packages for accessibility service/activity info without holding the A11yMS lock"
@@ -336,7 +316,6 @@
description: "Sends accessibility events in TouchExplorer#onAccessibilityEvent based on internal state to keep it consistent. This reduces test flakiness."
bug: "295575684"
}
-
flag {
name: "send_hover_events_based_on_event_stream"
namespace: "accessibility"
@@ -355,10 +334,10 @@
}
flag {
- name: "skip_package_change_before_user_switch"
+ name: "touch_explorer_use_virtual_device_id"
namespace: "accessibility"
- description: "Skip onSomePackageChanged callback if the SwitchUser signal is not received yet."
- bug: "340927041"
+ description: "Use a VIRTUAL device id for injected TouchExplorer events to avoid temporary conflicts with real pointer touches still on the screen when TouchExplorer starts up."
+ bug: "364408887"
metadata {
purpose: PURPOSE_BUGFIX
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 97ba3de..8b06e76 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -69,6 +69,7 @@
import static com.android.server.accessibility.AccessibilityUserState.doesShortcutTargetsStringContain;
import static com.android.server.pm.UserManagerService.enforceCurrentUserIfVisibleBackgroundEnabled;
import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
+import static com.android.window.flags.Flags.scvhSurfaceControlLifetimeFix;
import android.accessibilityservice.AccessibilityGestureEvent;
import android.accessibilityservice.AccessibilityService;
@@ -519,6 +520,14 @@
}
@Override
+ public void onUserStarting(@androidx.annotation.NonNull TargetUser user) {
+ super.onUserStarting(user);
+ if (Flags.managerLifecycleUserChange()) {
+ mService.switchUser(user.getUserIdentifier());
+ }
+ }
+
+ @Override
public void onUserSwitching(@androidx.annotation.Nullable TargetUser from,
@androidx.annotation.NonNull TargetUser to) {
super.onUserSwitching(from, to);
@@ -1085,9 +1094,7 @@
intent.getStringExtra(Intent.EXTRA_SETTING_NEW_VALUE);
final int restoredFromSdk =
intent.getIntExtra(Intent.EXTRA_SETTING_RESTORED_FROM_SDK_INT, 0);
- final int userId =
- android.view.accessibility.Flags.restoreA11ySecureSettingsOnHsumDevice()
- ? getSendingUserId() : UserHandle.USER_SYSTEM;
+ final int userId = getSendingUserId();
switch (which) {
case Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES -> {
synchronized (mLock) {
@@ -2126,14 +2133,22 @@
mMagnificationController.updateUserIdIfNeeded(userId);
List<AccessibilityServiceInfo> parsedAccessibilityServiceInfos = null;
List<AccessibilityShortcutInfo> parsedAccessibilityShortcutInfos = null;
- parsedAccessibilityServiceInfos = parseAccessibilityServiceInfos(userId);
- parsedAccessibilityShortcutInfos = parseAccessibilityShortcutInfos(userId);
+
synchronized (mLock) {
+ if (Flags.managerLifecycleUserChange()) {
+ userId = mSecurityPolicy.resolveProfileParentLocked(userId);
+ }
if (mCurrentUserId == userId && mInitialized) {
Slog.w(LOG_TAG, String.format("userId: %d is already initialized", userId));
return;
}
+ }
+ // parse outside of a lock, but after verifying userId
+ parsedAccessibilityServiceInfos = parseAccessibilityServiceInfos(userId);
+ parsedAccessibilityShortcutInfos = parseAccessibilityShortcutInfos(userId);
+
+ synchronized (mLock) {
// Disconnect from services for the old user.
AccessibilityUserState oldUserState = getCurrentUserStateLocked();
oldUserState.onSwitchToAnotherUserLocked();
@@ -2602,17 +2617,15 @@
private boolean readInstalledAccessibilityShortcutLocked(AccessibilityUserState userState,
List<AccessibilityShortcutInfo> parsedAccessibilityShortcutInfos) {
if (!parsedAccessibilityShortcutInfos.equals(userState.mInstalledShortcuts)) {
- if (Flags.clearShortcutsWhenActivityUpdatesToService()) {
- List<String> componentNames = userState.mInstalledShortcuts.stream()
- .filter(a11yActivity ->
- !parsedAccessibilityShortcutInfos.contains(a11yActivity))
- .map(a11yActivity -> a11yActivity.getComponentName().flattenToString())
- .toList();
- if (!componentNames.isEmpty()) {
- enableShortcutsForTargets(
- /* enable= */ false, UserShortcutType.ALL,
- componentNames, userState.mUserId);
- }
+ List<String> componentNames = userState.mInstalledShortcuts.stream()
+ .filter(a11yActivity ->
+ !parsedAccessibilityShortcutInfos.contains(a11yActivity))
+ .map(a11yActivity -> a11yActivity.getComponentName().flattenToString())
+ .toList();
+ if (!componentNames.isEmpty()) {
+ enableShortcutsForTargets(
+ /* enable= */ false, UserShortcutType.ALL,
+ componentNames, userState.mUserId);
}
userState.mInstalledShortcuts.clear();
@@ -6776,6 +6789,10 @@
result = AccessibilityService.OVERLAY_RESULT_SUCCESS;
}
+ if (scvhSurfaceControlLifetimeFix()) {
+ sc.release();
+ }
+
if (callback != null) {
// Send the result back to the service.
try {
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
index 6cba363..4173322 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
@@ -310,6 +310,16 @@
}
@Override
+ public void onNullBinding(ComponentName componentName) {
+ // Per guidance from ServiceConnection we must call Context#unbindService here to
+ // release the tracking resources associated with the ServiceConnection, to prevent
+ // Background Activity Launches (BAL).
+ synchronized (mLock) {
+ unbindLocked();
+ }
+ }
+
+ @Override
protected boolean hasRightsToCurrentUserLocked() {
// We treat calls from a profile as if made by its parent as profiles
// share the accessibility state of the parent. The call below
diff --git a/services/accessibility/java/com/android/server/accessibility/MotionEventInjector.java b/services/accessibility/java/com/android/server/accessibility/MotionEventInjector.java
index a7203b0b..a3383f7 100644
--- a/services/accessibility/java/com/android/server/accessibility/MotionEventInjector.java
+++ b/services/accessibility/java/com/android/server/accessibility/MotionEventInjector.java
@@ -16,6 +16,8 @@
package com.android.server.accessibility;
+import static com.android.server.accessibility.gestures.EventDispatcher.VIRTUAL_TOUCHSCREEN_DEVICE_ID;
+
import android.accessibilityservice.AccessibilityTrace;
import android.accessibilityservice.GestureDescription;
import android.accessibilityservice.GestureDescription.GestureStep;
@@ -30,7 +32,6 @@
import android.util.Slog;
import android.util.SparseIntArray;
import android.view.InputDevice;
-import android.view.KeyCharacterMap;
import android.view.MotionEvent;
import android.view.WindowManagerPolicyConstants;
@@ -526,7 +527,7 @@
}
return MotionEvent.obtain(downTime, eventTime, action, touchPointsSize,
sPointerProps, sPointerCoords, EVENT_META_STATE, EVENT_BUTTON_STATE,
- EVENT_X_PRECISION, EVENT_Y_PRECISION, KeyCharacterMap.VIRTUAL_KEYBOARD,
+ EVENT_X_PRECISION, EVENT_Y_PRECISION, VIRTUAL_TOUCHSCREEN_DEVICE_ID,
EVENT_EDGE_FLAGS, EVENT_SOURCE, EVENT_FLAGS);
}
diff --git a/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickController.java b/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickController.java
index 1574502..5a2f540 100644
--- a/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickController.java
+++ b/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickController.java
@@ -29,6 +29,7 @@
import static com.android.server.accessibility.autoclick.AutoclickTypePanel.AUTOCLICK_TYPE_DOUBLE_CLICK;
import static com.android.server.accessibility.autoclick.AutoclickTypePanel.AUTOCLICK_TYPE_DRAG;
import static com.android.server.accessibility.autoclick.AutoclickTypePanel.AUTOCLICK_TYPE_LEFT_CLICK;
+import static com.android.server.accessibility.autoclick.AutoclickTypePanel.AUTOCLICK_TYPE_LONG_PRESS;
import static com.android.server.accessibility.autoclick.AutoclickTypePanel.AUTOCLICK_TYPE_RIGHT_CLICK;
import static com.android.server.accessibility.autoclick.AutoclickTypePanel.AUTOCLICK_TYPE_SCROLL;
import static com.android.server.accessibility.autoclick.AutoclickTypePanel.AutoclickType;
@@ -85,9 +86,16 @@
public static final int DEFAULT_AUTOCLICK_DELAY_TIME = Flags.enableAutoclickIndicator()
? AUTOCLICK_DELAY_WITH_INDICATOR_DEFAULT : AUTOCLICK_DELAY_DEFAULT;
+ // Duration before a press turns into a long press.
+ // Factor 1.5 is needed, otherwise a long press is not safely detected.
+ public static final long LONG_PRESS_TIMEOUT =
+ (long) (ViewConfiguration.getLongPressTimeout() * 1.5f);
+
private static final String LOG_TAG = AutoclickController.class.getSimpleName();
- // TODO(b/393559560): Finalize scroll amount.
- private static final float SCROLL_AMOUNT = 1.0f;
+ private static final float SCROLL_AMOUNT = 0.5f;
+ protected static final long CONTINUOUS_SCROLL_INTERVAL = 30;
+ private Handler mContinuousScrollHandler;
+ private Runnable mContinuousScrollRunnable;
private final AccessibilityTraceManager mTrace;
private final Context mContext;
@@ -117,7 +125,8 @@
private @AutoclickType int mActiveClickType = AUTOCLICK_TYPE_LEFT_CLICK;
// Default scroll direction is DIRECTION_NONE.
- private @AutoclickScrollPanel.ScrollDirection int mHoveredDirection = DIRECTION_NONE;
+ @VisibleForTesting
+ protected @AutoclickScrollPanel.ScrollDirection int mHoveredDirection = DIRECTION_NONE;
// True during the duration of a dragging event.
private boolean mDragModeIsDragging = false;
@@ -125,6 +134,12 @@
// move.
private long mDragModeClickDownTime;
+ // True during the duration of a long press event.
+ private boolean mHasOngoingLongPress = false;
+ // The MotionEvent downTime attribute associated with the originating click for a long press
+ // move.
+ private long mLongPressDownTime;
+
@VisibleForTesting
final ClickPanelControllerInterface clickPanelController =
new ClickPanelControllerInterface() {
@@ -136,6 +151,10 @@
if (clickType != AUTOCLICK_TYPE_SCROLL && mAutoclickScrollPanel != null) {
mAutoclickScrollPanel.hide();
}
+
+ if (clickType != AUTOCLICK_TYPE_DRAG && mDragModeIsDragging) {
+ mClickScheduler.clearDraggingState();
+ }
}
@Override
@@ -165,31 +184,22 @@
// Update the hover direction.
if (hovered) {
mHoveredDirection = direction;
- } else if (mHoveredDirection == direction) {
- // Safety check: Only clear hover tracking if this is the same button
- // we're currently tracking.
- mHoveredDirection = AutoclickScrollPanel.DIRECTION_NONE;
- }
- // For exit button, we only trigger hover state changes, the autoclick system
- // will handle the countdown.
- if (direction == AutoclickScrollPanel.DIRECTION_EXIT) {
- return;
- }
-
- // Handle all non-exit buttons when hovered.
- if (hovered) {
- // Clear the indicator.
- if (mAutoclickIndicatorScheduler != null) {
- mAutoclickIndicatorScheduler.cancel();
- if (mAutoclickIndicatorView != null) {
- mAutoclickIndicatorView.clearIndicator();
- }
+ // For exit button, return early and the autoclick system will handle the
+ // countdown then exit scroll mode.
+ if (direction == AutoclickScrollPanel.DIRECTION_EXIT) {
+ return;
}
- // Perform scroll action.
+
+ // For scroll directions, start continuous scrolling.
if (direction != DIRECTION_NONE) {
- handleScroll(direction);
+ startContinuousScroll(direction);
}
+ } else if (mHoveredDirection == direction) {
+ // If not hovered, stop scrolling — but only if the mouse leaves the same
+ // button that started it. This avoids stopping the scroll when the mouse
+ // briefly moves over other buttons.
+ stopContinuousScroll();
}
}
};
@@ -251,6 +261,16 @@
mAutoclickScrollPanel = new AutoclickScrollPanel(mContext, mWindowManager,
mScrollPanelController);
+ // Initialize continuous scroll handler and runnable.
+ mContinuousScrollHandler = new Handler(handler.getLooper());
+ mContinuousScrollRunnable = new Runnable() {
+ @Override
+ public void run() {
+ handleScroll(mHoveredDirection);
+ mContinuousScrollHandler.postDelayed(this, CONTINUOUS_SCROLL_INTERVAL);
+ }
+ };
+
mAutoclickTypePanel.show();
mWindowManager.addView(mAutoclickIndicatorView, mAutoclickIndicatorView.getLayoutParams());
}
@@ -307,6 +327,11 @@
mAutoclickScrollPanel.hide();
mAutoclickScrollPanel = null;
}
+
+ if (mContinuousScrollHandler != null) {
+ mContinuousScrollHandler.removeCallbacks(mContinuousScrollRunnable);
+ mContinuousScrollHandler = null;
+ }
}
private void scheduleClick(MotionEvent event, int policyFlags) {
@@ -330,10 +355,10 @@
private boolean isPaused() {
return Flags.enableAutoclickIndicator() && mAutoclickTypePanel.isPaused()
- && !isHovered();
+ && !isPanelHovered();
}
- private boolean isHovered() {
+ private boolean isPanelHovered() {
return Flags.enableAutoclickIndicator() && mAutoclickTypePanel.isHovered();
}
@@ -350,6 +375,14 @@
* Handles scroll operations in the specified direction.
*/
private void handleScroll(@AutoclickScrollPanel.ScrollDirection int direction) {
+ // Remove the autoclick indicator view when hovering on directional buttons.
+ if (mAutoclickIndicatorScheduler != null) {
+ mAutoclickIndicatorScheduler.cancel();
+ if (mAutoclickIndicatorView != null) {
+ mAutoclickIndicatorView.clearIndicator();
+ }
+ }
+
final long now = SystemClock.uptimeMillis();
// Create pointer properties.
@@ -410,8 +443,25 @@
if (mAutoclickScrollPanel != null) {
mAutoclickScrollPanel.hide();
}
+ stopContinuousScroll();
}
+ private void startContinuousScroll(@AutoclickScrollPanel.ScrollDirection int direction) {
+ if (mContinuousScrollHandler != null) {
+ handleScroll(direction);
+ mContinuousScrollHandler.postDelayed(mContinuousScrollRunnable,
+ CONTINUOUS_SCROLL_INTERVAL);
+ }
+ }
+
+ private void stopContinuousScroll() {
+ if (mContinuousScrollHandler != null) {
+ mContinuousScrollHandler.removeCallbacks(mContinuousScrollRunnable);
+ }
+ mHoveredDirection = DIRECTION_NONE;
+ }
+
+
@VisibleForTesting
void onChangeForTesting(boolean selfChange, Uri uri) {
mAutoclickSettingsObserver.onChange(selfChange, uri);
@@ -422,6 +472,11 @@
return mDragModeIsDragging;
}
+ @VisibleForTesting
+ boolean hasOngoingLongPressForTesting() {
+ return mHasOngoingLongPress;
+ }
+
/**
* Observes autoclick setting values, and updates ClickScheduler delay and indicator size
* whenever the setting value changes.
@@ -618,6 +673,8 @@
// update scheduled time.
if (mIndicatorCallbackActive
&& scheduledShowIndicatorTime > mScheduledShowIndicatorTime) {
+ // Clear any existing indicator.
+ mAutoclickIndicatorView.clearIndicator();
mScheduledShowIndicatorTime = scheduledShowIndicatorTime;
return;
}
@@ -764,6 +821,11 @@
if (mDragModeIsDragging) {
clearDraggingState();
}
+
+ if (mHasOngoingLongPress) {
+ clearLongPressState();
+ }
+
resetInternalState();
mHandler.removeCallbacks(this);
}
@@ -785,6 +847,23 @@
mDragModeIsDragging = false;
}
+ // Cancel the pending long press to avoid potential side effects from
+ // leaving it in an inconsistent state.
+ private void clearLongPressState() {
+ if (mLastMotionEvent != null) {
+ // A final ACTION_CANCEL event needs to be sent to alert the system that long press
+ // has ended.
+ MotionEvent cancelEvent = MotionEvent.obtain(mLastMotionEvent);
+ cancelEvent.setAction(MotionEvent.ACTION_CANCEL);
+ cancelEvent.setDownTime(mLongPressDownTime);
+ AutoclickController.super.onMotionEvent(cancelEvent, cancelEvent,
+ mEventPolicyFlags);
+ }
+
+ resetSelectedClickTypeIfNecessary();
+ mHasOngoingLongPress = false;
+ }
+
/**
* Updates the meta state that should be used for click sequence.
*/
@@ -869,7 +948,7 @@
}
mLastMotionEvent = MotionEvent.obtain(event);
mEventPolicyFlags = policyFlags;
- mHoveredState = isHovered();
+ mHoveredState = isPanelHovered();
if (useAsAnchor) {
final int pointerIndex = mLastMotionEvent.getActionIndex();
@@ -909,8 +988,11 @@
float deltaX = mAnchorCoords.x - event.getX(pointerIndex);
float deltaY = mAnchorCoords.y - event.getY(pointerIndex);
double delta = Math.hypot(deltaX, deltaY);
+ // If the panel is hovered, always use the default slop so it's easier to click the
+ // closely spaced buttons.
double slop =
- ((Flags.enableAutoclickIndicator() && mIgnoreMinorCursorMovement)
+ ((Flags.enableAutoclickIndicator() && mIgnoreMinorCursorMovement
+ && !isPanelHovered())
? mMovementSlop
: DEFAULT_MOVEMENT_SLOP);
return delta > slop;
@@ -936,6 +1018,18 @@
return;
}
+ // Clear pending long press in case another click action jumps between long pressing
+ // down and up events.
+ if (mHasOngoingLongPress) {
+ clearLongPressState();
+ }
+
+ // Always triggers left-click when the cursor hovers over the autoclick type panel, to
+ // always allow users to change a different click type. Otherwise, if one chooses the
+ // right-click, this user won't be able to rely on autoclick to select other click
+ // types.
+ int selectedClickType = mHoveredState ? AUTOCLICK_TYPE_LEFT_CLICK : mActiveClickType;
+
// Handle scroll-specific click behavior.
if (handleScrollClick()) {
return;
@@ -959,38 +1053,30 @@
final long now = SystemClock.uptimeMillis();
int actionButton = BUTTON_PRIMARY;
- if (mHoveredState) {
- // Always triggers left-click when the cursor hovers over the autoclick type
- // panel, to always allow users to change a different click type. Otherwise, if
- // one chooses the right-click, this user won't be able to rely on autoclick to
- // select other click types.
- actionButton = BUTTON_PRIMARY;
- } else {
- switch (mActiveClickType) {
- case AUTOCLICK_TYPE_LEFT_CLICK:
- actionButton = BUTTON_PRIMARY;
- break;
- case AUTOCLICK_TYPE_RIGHT_CLICK:
- actionButton = BUTTON_SECONDARY;
- break;
- case AUTOCLICK_TYPE_DOUBLE_CLICK:
- actionButton = BUTTON_PRIMARY;
- long doubleTapMinimumTimeout = ViewConfiguration.getDoubleTapMinTime();
- sendMotionEvent(actionButton, now);
- sendMotionEvent(actionButton, now + doubleTapMinimumTimeout);
- return;
- case AUTOCLICK_TYPE_DRAG:
- if (mDragModeIsDragging) {
- endDragEvent();
- } else {
- startDragEvent();
- }
- return;
- default:
- break;
- }
+ switch (selectedClickType) {
+ case AUTOCLICK_TYPE_RIGHT_CLICK:
+ actionButton = BUTTON_SECONDARY;
+ break;
+ case AUTOCLICK_TYPE_DOUBLE_CLICK:
+ actionButton = BUTTON_PRIMARY;
+ long doubleTapMinimumTimeout = ViewConfiguration.getDoubleTapMinTime();
+ sendMotionEvent(actionButton, now);
+ sendMotionEvent(actionButton, now + doubleTapMinimumTimeout);
+ return;
+ case AUTOCLICK_TYPE_DRAG:
+ if (mDragModeIsDragging) {
+ endDragEvent();
+ } else {
+ startDragEvent();
+ }
+ return;
+ case AUTOCLICK_TYPE_LONG_PRESS:
+ actionButton = BUTTON_PRIMARY;
+ sendLongPress();
+ return;
+ default:
+ break;
}
-
sendMotionEvent(actionButton, now);
}
@@ -1042,22 +1128,8 @@
}
private void sendMotionEvent(int actionButton, long eventTime) {
- MotionEvent downEvent =
- MotionEvent.obtain(
- /* downTime= */ eventTime,
- /* eventTime= */ eventTime,
- MotionEvent.ACTION_DOWN,
- /* pointerCount= */ 1,
- mTempPointerProperties,
- mTempPointerCoords,
- mMetaState,
- actionButton,
- /* xPrecision= */ 1.0f,
- /* yPrecision= */ 1.0f,
- mLastMotionEvent.getDeviceId(),
- /* edgeFlags= */ 0,
- mLastMotionEvent.getSource(),
- mLastMotionEvent.getFlags());
+ MotionEvent downEvent = buildMotionEvent(
+ eventTime, eventTime, actionButton, mLastMotionEvent);
MotionEvent pressEvent = MotionEvent.obtain(downEvent);
pressEvent.setAction(MotionEvent.ACTION_BUTTON_PRESS);
@@ -1085,6 +1157,66 @@
upEvent.recycle();
}
+ // TODO(b/400744833): Reset Autoclick type to left click whenever a long press happens.
+ private void sendLongPress() {
+ mHasOngoingLongPress = true;
+ mLongPressDownTime = SystemClock.uptimeMillis();
+ MotionEvent downEvent = buildMotionEvent(
+ mLongPressDownTime, mLongPressDownTime, BUTTON_PRIMARY, mLastMotionEvent);
+
+ MotionEvent pressEvent = MotionEvent.obtain(downEvent);
+ pressEvent.setAction(MotionEvent.ACTION_BUTTON_PRESS);
+
+ AutoclickController.super.onMotionEvent(downEvent, downEvent, mEventPolicyFlags);
+ AutoclickController.super.onMotionEvent(pressEvent, pressEvent, mEventPolicyFlags);
+
+ mHandler.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ long upTime = SystemClock.uptimeMillis();
+ MotionEvent releaseEvent = buildMotionEvent(
+ mLongPressDownTime, upTime, BUTTON_PRIMARY, downEvent);
+ releaseEvent.setAction(MotionEvent.ACTION_BUTTON_RELEASE);
+ releaseEvent.setButtonState(0);
+
+ MotionEvent upEvent = MotionEvent.obtain(releaseEvent);
+ upEvent.setAction(MotionEvent.ACTION_UP);
+ upEvent.setButtonState(0);
+
+ AutoclickController.super.onMotionEvent(
+ releaseEvent, releaseEvent, mEventPolicyFlags);
+ AutoclickController.super.onMotionEvent(
+ upEvent, upEvent, mEventPolicyFlags);
+
+ downEvent.recycle();
+ pressEvent.recycle();
+ releaseEvent.recycle();
+ upEvent.recycle();
+ mHasOngoingLongPress = false;
+ }
+ }, LONG_PRESS_TIMEOUT);
+ }
+
+ private @NonNull MotionEvent buildMotionEvent(
+ long downTime, long eventTime, int actionButton,
+ @NonNull MotionEvent lastMotionEvent) {
+ return MotionEvent.obtain(
+ /* downTime= */ downTime,
+ /* eventTime= */ eventTime,
+ MotionEvent.ACTION_DOWN,
+ /* pointerCount= */ 1,
+ mTempPointerProperties,
+ mTempPointerCoords,
+ mMetaState,
+ actionButton,
+ /* xPrecision= */ 1.0f,
+ /* yPrecision= */ 1.0f,
+ lastMotionEvent.getDeviceId(),
+ /* edgeFlags= */ 0,
+ lastMotionEvent.getSource(),
+ lastMotionEvent.getFlags());
+ }
+
// To start a drag event, only send the DOWN and BUTTON_PRESS events.
private void startDragEvent() {
mDragModeClickDownTime = SystemClock.uptimeMillis();
diff --git a/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickIndicatorView.java b/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickIndicatorView.java
index 013a46b..e825c8b 100644
--- a/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickIndicatorView.java
+++ b/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickIndicatorView.java
@@ -97,7 +97,9 @@
WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
- layoutParams.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
+ layoutParams.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS
+ | WindowManager.LayoutParams.PRIVATE_FLAG_EXCLUDE_FROM_SCREEN_MAGNIFICATION
+ | WindowManager.LayoutParams.PRIVATE_FLAG_NOT_MAGNIFIABLE;
layoutParams.setFitInsetsTypes(WindowInsets.Type.statusBars());
layoutParams.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
layoutParams.format = PixelFormat.TRANSLUCENT;
diff --git a/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickScrollPanel.java b/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickScrollPanel.java
index 1cd9977..fe111c3 100644
--- a/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickScrollPanel.java
+++ b/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickScrollPanel.java
@@ -20,6 +20,9 @@
import android.annotation.IntDef;
import android.content.Context;
+import android.graphics.BlendMode;
+import android.graphics.BlendModeColorFilter;
+import android.graphics.Color;
import android.graphics.PixelFormat;
import android.util.DisplayMetrics;
import android.view.Gravity;
@@ -66,6 +69,7 @@
private final WindowManager mWindowManager;
private final WindowManager.LayoutParams mParams;
private ScrollPanelControllerInterface mScrollPanelController;
+ private final AutoclickScrollPointIndicator mAutoclickScrollPointIndicator;
// Scroll panel buttons.
private final ImageButton mUpButton;
@@ -98,6 +102,7 @@
mContext = context;
mWindowManager = windowManager;
mScrollPanelController = controller;
+ mAutoclickScrollPointIndicator = new AutoclickScrollPointIndicator(context);
mContentView = (AutoclickLinearLayout) LayoutInflater.from(context).inflate(
R.layout.accessibility_autoclick_scroll_panel, null);
mParams = getDefaultLayoutParams();
@@ -129,6 +134,9 @@
setupHoverListenerForButton(mRightButton, DIRECTION_RIGHT);
setupHoverListenerForButton(mDownButton, DIRECTION_DOWN);
setupHoverListenerForButton(mExitButton, DIRECTION_EXIT);
+
+ // Add click listener for exit button.
+ mExitButton.setOnClickListener(v -> hide());
}
/**
@@ -154,6 +162,7 @@
}
// Position the panel at the cursor location
positionPanelAtCursor(cursorX, cursorY);
+ mAutoclickScrollPointIndicator.show(cursorX, cursorY);
mWindowManager.addView(mContentView, mParams);
mInScrollMode = true;
}
@@ -199,6 +208,7 @@
if (!mInScrollMode) {
return;
}
+ mAutoclickScrollPointIndicator.hide();
mWindowManager.removeView(mContentView);
mInScrollMode = false;
}
@@ -217,15 +227,6 @@
case MotionEvent.ACTION_HOVER_ENTER:
hovered = true;
break;
- case MotionEvent.ACTION_HOVER_MOVE:
- // For direction buttons, continuously trigger scroll on hover move.
- if (direction != DIRECTION_EXIT) {
- hovered = true;
- } else {
- // Ignore hover move events for exit button.
- return true;
- }
- break;
case MotionEvent.ACTION_HOVER_EXIT:
hovered = false;
break;
@@ -233,6 +234,8 @@
return true;
}
+ // Update the button background color based on hover state.
+ toggleSelectedButtonStyle(button, hovered);
// Notify the controller about the hover change.
mScrollPanelController.onHoverButtonChange(direction, hovered);
return true;
@@ -240,6 +243,30 @@
}
/**
+ * Updates the button's style based on hover state.
+ *
+ * @param button The button to update the style for.
+ * @param hovered Whether the button is being hovered or not.
+ */
+ private void toggleSelectedButtonStyle(ImageButton button, boolean hovered) {
+ if (hovered) {
+ int tintColor = mContext.getColor(
+ com.android.internal.R.color.materialColorOnPrimary);
+
+ // Apply semi-transparent (22%) tint.
+ // SRC_ATOP preserves the button's texture and shadows while applying the tint.
+ button.getBackground().setColorFilter(new BlendModeColorFilter(
+ Color.argb(56, Color.red(tintColor), Color.green(tintColor),
+ Color.blue(tintColor)),
+
+ BlendMode.SRC_ATOP));
+ } else {
+ // Clear the color filter to remove the effect.
+ button.getBackground().clearColorFilter();
+ }
+ }
+
+ /**
* Retrieves the layout params for AutoclickScrollPanel, used when it's added to the Window
* Manager.
*/
diff --git a/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickScrollPointIndicator.java b/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickScrollPointIndicator.java
new file mode 100644
index 0000000..f645cf9
--- /dev/null
+++ b/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickScrollPointIndicator.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.accessibility.autoclick;
+
+import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.PixelFormat;
+import android.util.DisplayMetrics;
+import android.view.View;
+import android.view.WindowManager;
+
+import androidx.annotation.VisibleForTesting;
+
+import com.android.internal.R;
+
+/**
+ * A visual indicator that displays a point at the scroll cursor location.
+ */
+public class AutoclickScrollPointIndicator extends View {
+ // 16dp diameter (8dp radius).
+ private static final float POINT_RADIUS_DP = 8f;
+
+ private final WindowManager mWindowManager;
+ private final Paint mPaint;
+ private final float mPointSizePx;
+
+ // x and y coordinates of the cursor point indicator.
+ private float mX;
+ private float mY;
+
+ private boolean mIsVisible = false;
+
+ public AutoclickScrollPointIndicator(Context context) {
+ super(context);
+
+ mWindowManager = context.getSystemService(WindowManager.class);
+
+ // Convert dp to pixels based on screen density.
+ float density = getResources().getDisplayMetrics().density;
+ mPointSizePx = POINT_RADIUS_DP * density;
+
+ // Setup paint for drawing.
+ mPaint = new Paint();
+ mPaint.setAntiAlias(true);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ // Get the screen dimensions.
+ DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
+ int screenWidth = displayMetrics.widthPixels;
+ int screenHeight = displayMetrics.heightPixels;
+
+ setMeasuredDimension(screenWidth, screenHeight);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+
+ // Draw a solid circle with materialColorPrimary.
+ mPaint.setStyle(Paint.Style.FILL);
+ mPaint.setColor(getContext().getColor(R.color.materialColorPrimary));
+ canvas.drawCircle(mX, mY, mPointSizePx, mPaint);
+ }
+
+ /**
+ * Shows the cursor point indicator at the specified coordinates.
+ *
+ * @param x The x-coordinate of the cursor.
+ * @param y The y-coordinate of the cursor.
+ */
+ public void show(float x, float y) {
+ mX = x;
+ mY = y;
+
+ if (!mIsVisible) {
+ mWindowManager.addView(this, getLayoutParams());
+ mIsVisible = true;
+ }
+
+ invalidate();
+ }
+
+ /**
+ * Hides the cursor point indicator.
+ */
+ public void hide() {
+ if (mIsVisible) {
+ mWindowManager.removeView(this);
+ mIsVisible = false;
+ }
+ }
+
+ /**
+ * Retrieves the layout params for AutoclickScrollPointIndicator, used when it's added to the
+ * Window Manager.
+ */
+ public WindowManager.LayoutParams getLayoutParams() {
+ final WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams();
+ layoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+ layoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
+ | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
+ layoutParams.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
+ layoutParams.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+ layoutParams.format = PixelFormat.TRANSLUCENT;
+ layoutParams.setTitle(AutoclickScrollPointIndicator.class.getSimpleName());
+ layoutParams.inputFeatures |= WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL;
+
+ return layoutParams;
+ }
+
+ @VisibleForTesting
+ public boolean isVisible() {
+ return mIsVisible;
+ }
+}
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/EventDispatcher.java b/services/accessibility/java/com/android/server/accessibility/gestures/EventDispatcher.java
index 5c0bbf4..6ef3ef3 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/EventDispatcher.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/EventDispatcher.java
@@ -39,7 +39,16 @@
* gesture dispatch. TouchExplorer is responsible for insuring that the receiver of motion events is
* set correctly so that events go to the right place.
*/
-class EventDispatcher {
+public class EventDispatcher {
+
+ /**
+ * Device ID used for touchscreen events injected by touch exploration and gesture dispatch.
+ *
+ * <p>Using a virtual device ID that differs from a real touchscreen ID helps to prevent
+ * conflicts in inputflinger/InputDispatcher between injected and real touch events.
+ */
+ public static final int VIRTUAL_TOUCHSCREEN_DEVICE_ID = -1;
+
private static final String LOG_TAG = "EventDispatcher";
private static final int CLICK_LOCATION_NONE = 0;
private static final int CLICK_LOCATION_ACCESSIBILITY_FOCUS = 1;
@@ -125,10 +134,13 @@
event.getPointerProperties(i, p);
properties[i] = p;
}
+ final int deviceId = Flags.touchExplorerUseVirtualDeviceId()
+ ? VIRTUAL_TOUCHSCREEN_DEVICE_ID
+ : rawEvent.getDeviceId();
event = MotionEvent.obtain(downTime, event.getEventTime(), event.getAction(),
event.getPointerCount(), properties, coords,
event.getMetaState(), event.getButtonState(),
- event.getXPrecision(), event.getYPrecision(), rawEvent.getDeviceId(),
+ event.getXPrecision(), event.getYPrecision(), deviceId,
event.getEdgeFlags(), rawEvent.getSource(), event.getDisplayId(), event.getFlags(),
event.getClassification());
// If the user is long pressing but the long pressing pointer
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
index b02fe27..3daf0d2 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
@@ -414,8 +414,15 @@
mSendTouchExplorationEndDelayed.forceSendAndRemove();
}
- // Announce the end of a new touch interaction.
- mDispatcher.sendAccessibilityEvent(TYPE_TOUCH_INTERACTION_END);
+ if (!Flags.pointerUpMotionEventInTouchExploration()) {
+ // Announce the end of a new touch interaction.
+ mDispatcher.sendAccessibilityEvent(TYPE_TOUCH_INTERACTION_END);
+ } else {
+ // If there are no pointers left on screen, announce the end of the touch interaction.
+ if (mReceivedPointerTracker.getReceivedPointerDownCount() == 0) {
+ mDispatcher.sendAccessibilityEvent(TYPE_TOUCH_INTERACTION_END);
+ }
+ }
mSendTouchInteractionEndDelayed.cancel();
// Try to use the standard accessibility API to click
if (!mAms.performActionOnAccessibilityFocusedItem(
@@ -1611,19 +1618,6 @@
dispatchGesture(gestureEvent);
}
if (!mEvents.isEmpty() && !mRawEvents.isEmpty()) {
- if (Flags.resetInputDispatcherBeforeFirstTouchExploration()
- && !mState.hasResetInputDispatcherState()) {
- // Cancel any possible ongoing touch gesture from before touch exploration
- // started. This clears out the InputDispatcher event stream state so that it
- // is ready to accept new injected HOVER events.
- mDispatcher.sendMotionEvent(
- mEvents.get(0),
- ACTION_CANCEL,
- mRawEvents.get(0),
- mPointerIdBits,
- mPolicyFlags);
- setHasResetInputDispatcherState(true);
- }
// Deliver a down event.
mDispatcher.sendMotionEvent(
mEvents.get(0),
@@ -1784,9 +1778,4 @@
+ ", mDraggingPointerId: " + mDraggingPointerId
+ " }";
}
-
- @VisibleForTesting
- void setHasResetInputDispatcherState(boolean value) {
- mState.setHasResetInputDispatcherState(value);
- }
}
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/TouchState.java b/services/accessibility/java/com/android/server/accessibility/gestures/TouchState.java
index 568abd1..7a20678 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchState.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchState.java
@@ -89,7 +89,6 @@
private MotionEvent mLastInjectedHoverEvent;
// The last injected hover event used for performing clicks.
private MotionEvent mLastInjectedHoverEventForClick;
- private boolean mHasResetInputDispatcherState;
// The time of the last injected down.
private long mLastInjectedDownEventTime;
// Keep track of which pointers sent to the system are down.
@@ -376,14 +375,6 @@
return mLastInjectedDownEventTime;
}
- boolean hasResetInputDispatcherState() {
- return mHasResetInputDispatcherState;
- }
-
- void setHasResetInputDispatcherState(boolean value) {
- mHasResetInputDispatcherState = value;
- }
-
public int getLastTouchedWindowId() {
return mLastTouchedWindowId;
}
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
index 004b3ff..af37ec89 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
@@ -113,6 +113,8 @@
private final Rect mTempRect = new Rect();
// Whether the following typing focus feature for magnification is enabled.
private boolean mMagnificationFollowTypingEnabled = true;
+ // Whether the following keyboard focus feature for magnification is enabled.
+ private boolean mMagnificationFollowKeyboardEnabled = false;
// Whether the always on magnification feature is enabled.
private boolean mAlwaysOnMagnificationEnabled = false;
private final DisplayManagerInternal mDisplayManagerInternal;
@@ -1108,9 +1110,9 @@
@Override
public void onRectangleOnScreenRequested(int displayId, int left, int top, int right,
- int bottom) {
+ int bottom, int source) {
synchronized (mLock) {
- if (!mMagnificationFollowTypingEnabled) {
+ if (!shouldFollow(source)) {
return;
}
final DisplayMagnification display = mDisplays.get(displayId);
@@ -1129,6 +1131,17 @@
}
}
+ private boolean shouldFollow(int source) {
+ // Treat UNDEFINED as following typing to preserve behavior for backwards compatibility.
+ if (mMagnificationFollowTypingEnabled
+ && (source == View.RECTANGLE_ON_SCREEN_REQUEST_SOURCE_TEXT_CURSOR
+ || source == View.RECTANGLE_ON_SCREEN_REQUEST_SOURCE_UNDEFINED)) {
+ return true;
+ }
+ return mMagnificationFollowKeyboardEnabled
+ && source == View.RECTANGLE_ON_SCREEN_REQUEST_SOURCE_INPUT_FOCUS;
+ }
+
void setMagnificationFollowTypingEnabled(boolean enabled) {
mMagnificationFollowTypingEnabled = enabled;
}
@@ -1137,6 +1150,14 @@
return mMagnificationFollowTypingEnabled;
}
+ void setMagnificationFollowKeyboardEnabled(boolean enabled) {
+ mMagnificationFollowKeyboardEnabled = enabled;
+ }
+
+ boolean isMagnificationFollowKeyboardEnabled() {
+ return mMagnificationFollowKeyboardEnabled;
+ }
+
void setAlwaysOnMagnificationEnabled(boolean enabled) {
mAlwaysOnMagnificationEnabled = enabled;
}
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
index 59b4a16..919b334 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
@@ -1745,6 +1745,7 @@
* BroadcastReceiver used to cancel the magnification shortcut when the screen turns off
*/
private static class ScreenStateReceiver extends BroadcastReceiver {
+ private static final String TAG = ScreenStateReceiver.class.getName();
private final Context mContext;
private final FullScreenMagnificationGestureHandler mGestureHandler;
@@ -1759,7 +1760,16 @@
}
public void unregister() {
- mContext.unregisterReceiver(this);
+ try {
+ mContext.unregisterReceiver(this);
+ } catch (IllegalArgumentException exception) {
+ // b/399282180: the unregister happens when the handler is destroyed (cleanup). The
+ // cleanup process should not cause the system crash, also the failure of unregister
+ // will not affect the user experience since it's for the destroyed handler.
+ // Therefore, we use try-catch here, to catch the exception to prevent crash, and
+ // log the exception for future investigations.
+ Slog.e(TAG, "Failed to unregister receiver: " + exception);
+ }
}
@Override
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionManager.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionManager.java
index fe06406..4e96955 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionManager.java
@@ -45,6 +45,7 @@
import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.view.MotionEvent;
+import android.view.View;
import android.view.accessibility.IMagnificationConnection;
import android.view.accessibility.IMagnificationConnectionCallback;
import android.view.accessibility.MagnificationAnimationCallback;
@@ -144,6 +145,8 @@
private SparseArray<WindowMagnifier> mWindowMagnifiers = new SparseArray<>();
// Whether the following typing focus feature for magnification is enabled.
private boolean mMagnificationFollowTypingEnabled = true;
+ // Whether the following keyboard focus feature for magnification is enabled.
+ private boolean mMagnificationFollowKeyboardEnabled = false;
@GuardedBy("mLock")
private final SparseBooleanArray mIsImeVisibleArray = new SparseBooleanArray();
@GuardedBy("mLock")
@@ -408,24 +411,39 @@
@Override
public void onRectangleOnScreenRequested(int displayId, int left, int top, int right,
- int bottom) {
- if (!mMagnificationFollowTypingEnabled) {
- return;
- }
-
+ int bottom, int source) {
float toCenterX = (float) (left + right) / 2;
float toCenterY = (float) (top + bottom) / 2;
synchronized (mLock) {
- if (mIsImeVisibleArray.get(displayId, false)
- && !isPositionInSourceBounds(displayId, toCenterX, toCenterY)
- && isTrackingTypingFocusEnabled(displayId)) {
+ if (isPositionInSourceBounds(displayId, toCenterX, toCenterY)) {
+ return;
+ }
+
+ boolean followTyping = shouldFollowTyping(source)
+ && mIsImeVisibleArray.get(displayId, false)
+ && isTrackingTypingFocusEnabled(displayId);
+ boolean followKeyboard = shouldFollowKeyboard(source);
+
+ if (followTyping || followKeyboard) {
moveWindowMagnifierToPositionInternal(displayId, toCenterX, toCenterY,
STUB_ANIMATION_CALLBACK);
}
}
}
+ private boolean shouldFollowTyping(int source) {
+ // Treat UNDEFINED as following typing to preserve behavior for backwards compatibility.
+ return mMagnificationFollowTypingEnabled
+ && (source == View.RECTANGLE_ON_SCREEN_REQUEST_SOURCE_TEXT_CURSOR
+ || source == View.RECTANGLE_ON_SCREEN_REQUEST_SOURCE_UNDEFINED);
+ }
+
+ private boolean shouldFollowKeyboard(int source) {
+ return mMagnificationFollowKeyboardEnabled
+ && source == View.RECTANGLE_ON_SCREEN_REQUEST_SOURCE_INPUT_FOCUS;
+ }
+
void setMagnificationFollowTypingEnabled(boolean enabled) {
mMagnificationFollowTypingEnabled = enabled;
}
@@ -434,6 +452,15 @@
return mMagnificationFollowTypingEnabled;
}
+ void setMagnificationFollowKeyboardEnabled(boolean enabled) {
+ mMagnificationFollowKeyboardEnabled = enabled;
+ }
+
+ boolean isMagnificationFollowKeyboardEnabled() {
+ return mMagnificationFollowKeyboardEnabled;
+ }
+
+
/**
* Get the ID of the last service that changed the magnification config.
*
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
index 396ea33..4790b40 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
@@ -50,6 +50,7 @@
import android.util.SparseLongArray;
import android.util.TypedValue;
import android.view.Display;
+import android.view.View;
import android.view.ViewConfiguration;
import android.view.accessibility.MagnificationAnimationCallback;
@@ -744,14 +745,20 @@
@Override
public void onRectangleOnScreenRequested(int displayId, int left, int top, int right,
- int bottom) {
+ int bottom, int source) {
WindowManagerInternal.AccessibilityControllerInternal.UiChangesForAccessibilityCallbacks
delegate;
synchronized (mLock) {
delegate = mAccessibilityCallbacksDelegateArray.get(displayId);
}
if (delegate != null) {
- delegate.onRectangleOnScreenRequested(displayId, left, top, right, bottom);
+ if (android.view.accessibility.Flags.requestRectangleWithSource()) {
+ delegate.onRectangleOnScreenRequested(displayId, left, top, right, bottom, source);
+ } else {
+ delegate.onRectangleOnScreenRequested(displayId, left, top, right, bottom,
+ View.RECTANGLE_ON_SCREEN_REQUEST_SOURCE_UNDEFINED);
+ }
+
}
}
diff --git a/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java b/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java
index 64072e0..8cbc107ce 100644
--- a/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java
+++ b/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java
@@ -85,6 +85,7 @@
import java.util.WeakHashMap;
import java.util.concurrent.CompletionException;
import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
/** Implementation of the AppFunctionManagerService. */
public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub {
@@ -592,7 +593,9 @@
.registerObserverCallbackAsync(
"android",
new ObserverSpec.Builder().build(),
- THREAD_POOL_EXECUTOR,
+ // AppFunctionMetadataObserver implements a simple callback that
+ // does not block and should be safe to run on any thread.
+ Runnable::run,
appFunctionMetadataObserver)
.whenComplete(
(voidResult, ex) -> {
diff --git a/services/appfunctions/java/com/android/server/appfunctions/MetadataSyncAdapter.java b/services/appfunctions/java/com/android/server/appfunctions/MetadataSyncAdapter.java
index 8305ebb..9d1b5c7 100644
--- a/services/appfunctions/java/com/android/server/appfunctions/MetadataSyncAdapter.java
+++ b/services/appfunctions/java/com/android/server/appfunctions/MetadataSyncAdapter.java
@@ -99,26 +99,30 @@
* synchronization was successful.
*/
public AndroidFuture<Boolean> submitSyncRequest() {
- SearchContext staticMetadataSearchContext =
- new SearchContext.Builder(
- AppFunctionStaticMetadataHelper.APP_FUNCTION_STATIC_METADATA_DB)
- .build();
- SearchContext runtimeMetadataSearchContext =
- new SearchContext.Builder(
- AppFunctionRuntimeMetadata.APP_FUNCTION_RUNTIME_METADATA_DB)
- .build();
AndroidFuture<Boolean> settableSyncStatus = new AndroidFuture<>();
Runnable runnable =
() -> {
+ SearchContext staticMetadataSearchContext =
+ new SearchContext.Builder(
+ AppFunctionStaticMetadataHelper
+ .APP_FUNCTION_STATIC_METADATA_DB)
+ .build();
+ SearchContext runtimeMetadataSearchContext =
+ new SearchContext.Builder(
+ AppFunctionRuntimeMetadata
+ .APP_FUNCTION_RUNTIME_METADATA_DB)
+ .build();
try (FutureAppSearchSession staticMetadataSearchSession =
new FutureAppSearchSessionImpl(
mAppSearchManager,
- AppFunctionExecutors.THREAD_POOL_EXECUTOR,
+ // Fine to use Runnable::run as all the callback does is
+ // set the result in the future.
+ Runnable::run,
staticMetadataSearchContext);
FutureAppSearchSession runtimeMetadataSearchSession =
new FutureAppSearchSessionImpl(
mAppSearchManager,
- AppFunctionExecutors.THREAD_POOL_EXECUTOR,
+ Runnable::run,
runtimeMetadataSearchContext)) {
trySyncAppFunctionMetadataBlocking(
@@ -133,8 +137,12 @@
synchronized (mLock) {
if (mCurrentSyncTask != null && !mCurrentSyncTask.isDone()) {
var unused = mCurrentSyncTask.cancel(false);
+ mCurrentSyncTask = null;
}
- mCurrentSyncTask = mExecutor.submit(runnable);
+
+ if (!mExecutor.isShutdown()) {
+ mCurrentSyncTask = mExecutor.submit(runnable);
+ }
}
return settableSyncStatus;
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index 5edf08c..8634890 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -131,10 +131,10 @@
private final Context mContext;
private final UserManager mUserManager;
+ private final Object mLock = new Object();
+
+ @GuardedBy("mLock")
private final boolean mGlobalDisable;
- // Lock to write backup suppress files.
- // TODD(b/121198006): remove this object and synchronized all methods on "this".
- private final Object mStateLock = new Object();
private final Handler mHandler;
private final Set<ComponentName> mTransportWhitelist;
@@ -155,18 +155,21 @@
};
/**
- * The user that the backup is activated by default for.
+ * Tracks whether {@link UserManager#getMainUser()} returned a non-null value
+ * during {@link BackupManagerService} construction.
+ *
+ * <p>This is used to handle an edge case on first boot where the main user might be created
+ * *after* BackupManagerService starts. If the main user didn't exist at boot, we might
+ * need to stop the backup service for the system user later.
*
- * <p>If there is a {@link UserManager#getMainUser()}, this will be that user. If not, it will
- * be {@link UserHandle#USER_SYSTEM}.
- *
- * <p>Note: on the first ever boot of a new device, this might change once the first user is
- * unlocked. See {@link #updateDefaultBackupUserIdIfNeeded()}.
- *
- * @see #isBackupActivatedForUser(int)
+ * @see #stopServiceForSystemUserIfMainUserCreated()
*/
- @UserIdInt private int mDefaultBackupUserId;
+ private boolean mDidMainUserExistAtBoot = false;
+ /**
+ * Tracks whether any user unlock event has occurred since the system booted.
+ * @see #stopServiceForSystemUserIfMainUserCreated()
+ */
private boolean mHasFirstUserUnlockedSinceBoot = false;
public BackupManagerService(Context context) {
@@ -183,9 +186,11 @@
mTransportWhitelist = (transportWhitelist == null) ? emptySet() : transportWhitelist;
mContext.registerReceiver(
mUserRemovedReceiver, new IntentFilter(Intent.ACTION_USER_REMOVED));
- UserHandle mainUser = getUserManager().getMainUser();
- mDefaultBackupUserId = mainUser == null ? UserHandle.USER_SYSTEM : mainUser.getIdentifier();
- Slog.d(TAG, "Default backup user id = " + mDefaultBackupUserId);
+ mDidMainUserExistAtBoot = getUserManager().getMainUser() != null;
+ if (!mDidMainUserExistAtBoot) {
+ // This might happen on the first boot if BMS starts before the main user is created.
+ Slog.d(TAG, "Main user does not exist yet");
+ }
}
@VisibleForTesting
@@ -215,13 +220,13 @@
/** Stored in the system user's directory and the file is indexed by the user it refers to. */
@VisibleForTesting
- protected File getRememberActivatedFileForNonSystemUser(int userId) {
+ protected File getRememberActivatedFileForNonSystemUser(@UserIdInt int userId) {
return UserBackupManagerFiles.getStateFileInSystemDir(REMEMBER_ACTIVATED_FILENAME, userId);
}
/** Stored in the system user's directory and the file is indexed by the user it refers to. */
@VisibleForTesting
- protected File getActivatedFileForUser(int userId) {
+ protected File getActivatedFileForUser(@UserIdInt int userId) {
return UserBackupManagerFiles.getStateFileInSystemDir(BACKUP_ACTIVATED_FILENAME, userId);
}
@@ -231,7 +236,7 @@
* When the user is removed, the user's own dir gets removed by the OS. This method ensures that
* the part of the user backup state which is in the system dir also gets removed.
*/
- private void onRemovedNonSystemUser(int userId) {
+ private void onRemovedNonSystemUser(@UserIdInt int userId) {
Slog.i(TAG, "Removing state for non system user " + userId);
File dir = UserBackupManagerFiles.getStateDirInSystemDir(userId);
if (!FileUtils.deleteContentsAndDir(dir)) {
@@ -265,8 +270,9 @@
/**
* Deactivates the backup service for user {@code userId}.
*
- * If this is the system user or the {@link #mDefaultBackupUserId} user, it creates a "suppress"
- * file for this user.
+ * If the {@code userId} is the system user OR the user's backup is initially set to active
+ * (as determined by {@code isDefaultBackupActiveUser(userId)}), it creates a "suppress" file
+ * for this user.
*
* Otherwise, it deleties the user's "activated" file.
*
@@ -274,9 +280,9 @@
* users until it is reactivated for the system user, at which point the per-user activation
* status will be the source of truth.
*/
- @GuardedBy("mStateLock")
- private void deactivateBackupForUserLocked(int userId) throws IOException {
- if (userId == UserHandle.USER_SYSTEM || userId == mDefaultBackupUserId) {
+ @GuardedBy("mLock")
+ private void deactivateBackupForUserLocked(@UserIdInt int userId) throws IOException {
+ if (userId == UserHandle.USER_SYSTEM || isDefaultBackupActiveUser(userId)) {
createFile(getSuppressFileForUser(userId));
} else {
deleteFile(getActivatedFileForUser(userId));
@@ -286,14 +292,14 @@
/**
* Activates the backup service for user {@code userId}.
*
- * If this is the system user or the {@link #mDefaultBackupUserId} user, it deletes its
- * "suppress" file.
+ * If the {@code userId} is the system user OR the user's backup is initially set to active
+ * (as determined by {@code isDefaultBackupActiveUser(userId)}), it deletes its "suppress" file.
*
* Otherwise, it creates the user's "activated" file.
*/
- @GuardedBy("mStateLock")
- private void activateBackupForUserLocked(int userId) throws IOException {
- if (userId == UserHandle.USER_SYSTEM || userId == mDefaultBackupUserId) {
+ @GuardedBy("mLock")
+ private void activateBackupForUserLocked(@UserIdInt int userId) throws IOException {
+ if (userId == UserHandle.USER_SYSTEM || isDefaultBackupActiveUser(userId)) {
deleteFile(getSuppressFileForUser(userId));
} else {
createFile(getActivatedFileForUser(userId));
@@ -307,41 +313,60 @@
* @return true if the user is ready for backup and false otherwise.
*/
@Override
- public boolean isUserReadyForBackup(int userId) {
+ public boolean isUserReadyForBackup(@UserIdInt int userId) {
enforceCallingPermissionOnUserId(userId, "isUserReadyForBackup()");
return mUserServices.get(userId) != null;
}
/**
- * If there is a "suppress" file for the system user, backup is inactive for ALL users.
+ * Determines whether backup is active for a specific user.
*
- * Otherwise, the below logic applies:
- *
- * For the {@link #mDefaultBackupUserId}, backup is active by default. Backup is only
- * deactivated if there exists a "suppress" file for the user, which can be created by calling
- * {@link #setBackupServiceActive}.
- *
- * For non-main users, backup is only active if there exists an "activated" file for the user,
- * which can also be created by calling {@link #setBackupServiceActive}.
+ * Backup status is determined by the first matching condition in the following precedence:
+ * 1. Global suppression: If a suppression file exists for the system user
+ * {@code UserHandle.USER_SYSTEM}, backup is disabled for all users.
+ * 2. User-specific suppression: Check if a suppression file exists for the specified user.
+ * 3. Default user activation: Check if the user's backup is initially set to active.
+ * 4. Explicit activation file: Check if an activation file exists for the user.
*/
- private boolean isBackupActivatedForUser(int userId) {
+ private boolean isBackupActivatedForUser(@UserIdInt int userId) {
+ // 1. Global suppression.
if (getSuppressFileForUser(UserHandle.USER_SYSTEM).exists()) {
return false;
}
- boolean isDefaultUser = userId == mDefaultBackupUserId;
-
- // If the default user is not the system user, we are in headless mode and the system user
- // doesn't have an actual human user associated with it.
- if ((userId == UserHandle.USER_SYSTEM) && !isDefaultUser) {
+ // 2. User-specific suppression.
+ if (getSuppressFileForUser(userId).exists()) {
return false;
}
- if (isDefaultUser && getSuppressFileForUser(userId).exists()) {
+ // 3. Default user activation.
+ if (isDefaultBackupActiveUser(userId)) {
+ return true;
+ }
+
+ // 4. Explicit activation file.
+ return getActivatedFileForUser(userId).exists();
+ }
+
+ /** Returns whether the user's backup is initially set to active. */
+ private boolean isDefaultBackupActiveUser(@UserIdInt int userId) {
+ // In non-HSUM, the system user is the primary user and handles backup.
+ if (!UserManager.isHeadlessSystemUserMode()) {
+ return userId == UserHandle.USER_SYSTEM;
+ }
+
+ // In HSUM, the system user is not a real user and should not have backup activated.
+ if (userId == UserHandle.USER_SYSTEM) {
return false;
}
- return isDefaultUser || getActivatedFileForUser(userId).exists();
+ UserHandle mainUser = getUserManager().getMainUser();
+ if (mainUser == null) {
+ return false;
+ }
+
+ // In HSUM, the main user is the one whose backup should be active by default.
+ return userId == mainUser.getIdentifier();
}
@VisibleForTesting
@@ -364,7 +389,7 @@
* UserBackupManagerService} and registering it with this service.
*/
@VisibleForTesting
- void startServiceForUser(int userId) {
+ void startServiceForUser(@UserIdInt int userId) {
// We know that the user is unlocked here because it is called from setBackupServiceActive
// and unlockUser which have these guarantees. So we can check if the file exists.
if (mGlobalDisable) {
@@ -391,7 +416,8 @@
* UserBackupManagerService} with this service and setting enabled state.
*/
@VisibleForTesting
- void startServiceForUser(int userId, UserBackupManagerService userBackupManagerService) {
+ void startServiceForUser(
+ @UserIdInt int userId, UserBackupManagerService userBackupManagerService) {
mUserServices.put(userId, userBackupManagerService);
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup enable");
@@ -401,7 +427,7 @@
/** Stops the backup service for user {@code userId} when the user is stopped. */
@VisibleForTesting
- protected void stopServiceForUser(int userId) {
+ protected void stopServiceForUser(@UserIdInt int userId) {
UserBackupManagerService userBackupManagerService = mUserServices.removeReturnOld(userId);
if (userBackupManagerService != null) {
@@ -429,7 +455,7 @@
* Called from {@link BackupManagerService.Lifecycle} when a user {@code userId} is stopped.
* Offloads work onto the handler thread {@link #mHandlerThread} to keep stopping time low.
*/
- void onStopUser(int userId) {
+ void onStopUser(@UserIdInt int userId) {
postToHandler(
() -> {
if (!mGlobalDisable) {
@@ -441,7 +467,7 @@
/** Returns {@link UserBackupManagerService} for user {@code userId}. */
@Nullable
- public UserBackupManagerService getUserService(int userId) {
+ public UserBackupManagerService getUserService(@UserIdInt int userId) {
return mUserServices.get(userId);
}
@@ -450,7 +476,7 @@
* processes. Other users can be acted on by callers who have both android.permission.BACKUP and
* android.permission.INTERACT_ACROSS_USERS_FULL permissions.
*/
- private void enforcePermissionsOnUser(int userId) throws SecurityException {
+ private void enforcePermissionsOnUser(@UserIdInt int userId) throws SecurityException {
boolean isRestrictedUser =
userId == UserHandle.USER_SYSTEM
|| getUserManager().getUserInfo(userId).isManagedProfile();
@@ -474,7 +500,7 @@
* system user also deactivates backup in all users. We are not guaranteed that {@code userId}
* is unlocked at this point yet, so handle both cases.
*/
- public void setBackupServiceActive(int userId, boolean makeActive) {
+ public void setBackupServiceActive(@UserIdInt int userId, boolean makeActive) {
enforcePermissionsOnUser(userId);
// In Q, backup is OFF by default for non-system users. In the future, we will change that
@@ -500,7 +526,7 @@
return;
}
- synchronized (mStateLock) {
+ synchronized (mLock) {
Slog.i(TAG, "Making backup " + (makeActive ? "" : "in") + "active");
if (makeActive) {
try {
@@ -544,20 +570,21 @@
* @return true if the service is active.
*/
@Override
- public boolean isBackupServiceActive(int userId) {
+ public boolean isBackupServiceActive(@UserIdInt int userId) {
int callingUid = Binder.getCallingUid();
if (CompatChanges.isChangeEnabled(
BackupManager.IS_BACKUP_SERVICE_ACTIVE_ENFORCE_PERMISSION_IN_SERVICE, callingUid)) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
"isBackupServiceActive");
}
- synchronized (mStateLock) {
+ synchronized (mLock) {
return !mGlobalDisable && isBackupActivatedForUser(userId);
}
}
@Override
- public void dataChangedForUser(int userId, String packageName) throws RemoteException {
+ public void dataChangedForUser(@UserIdInt int userId, String packageName)
+ throws RemoteException {
if (isUserReadyForBackup(userId)) {
dataChanged(userId, packageName);
}
@@ -588,7 +615,8 @@
@Override
public void initializeTransportsForUser(
- int userId, String[] transportNames, IBackupObserver observer) throws RemoteException {
+ @UserIdInt int userId, String[] transportNames, IBackupObserver observer)
+ throws RemoteException {
if (isUserReadyForBackup(userId)) {
initializeTransports(userId, transportNames, observer);
}
@@ -606,7 +634,8 @@
}
@Override
- public void clearBackupDataForUser(int userId, String transportName, String packageName)
+ public void clearBackupDataForUser(
+ @UserIdInt int userId, String transportName, String packageName)
throws RemoteException {
if (isUserReadyForBackup(userId)) {
clearBackupData(userId, transportName, packageName);
@@ -663,7 +692,7 @@
}
@Override
- public void restoreAtInstallForUser(int userId, String packageName, int token)
+ public void restoreAtInstallForUser(@UserIdInt int userId, String packageName, int token)
throws RemoteException {
if (isUserReadyForBackup(userId)) {
restoreAtInstall(userId, packageName, token);
@@ -689,7 +718,7 @@
}
@Override
- public void setFrameworkSchedulingEnabledForUser(int userId, boolean isEnabled) {
+ public void setFrameworkSchedulingEnabledForUser(@UserIdInt int userId, boolean isEnabled) {
UserBackupManagerService userBackupManagerService =
getServiceForUserIfCallerHasPermission(userId,
"setFrameworkSchedulingEnabledForUser()");
@@ -723,7 +752,8 @@
}
@Override
- public void setAutoRestoreForUser(int userId, boolean doAutoRestore) throws RemoteException {
+ public void setAutoRestoreForUser(@UserIdInt int userId, boolean doAutoRestore)
+ throws RemoteException {
if (isUserReadyForBackup(userId)) {
setAutoRestore(userId, doAutoRestore);
}
@@ -859,7 +889,7 @@
}
@Override
- public void fullTransportBackupForUser(int userId, String[] packageNames)
+ public void fullTransportBackupForUser(@UserIdInt int userId, String[] packageNames)
throws RemoteException {
if (isUserReadyForBackup(userId)) {
fullTransportBackup(userId, packageNames);
@@ -898,7 +928,7 @@
@Override
public void acknowledgeFullBackupOrRestoreForUser(
- int userId,
+ @UserIdInt int userId,
int token,
boolean allow,
String curPassword,
@@ -941,7 +971,7 @@
@Override
- public String getCurrentTransportForUser(int userId) throws RemoteException {
+ public String getCurrentTransportForUser(@UserIdInt int userId) throws RemoteException {
return (isUserReadyForBackup(userId)) ? getCurrentTransport(userId) : null;
}
@@ -967,7 +997,7 @@
*/
@Override
@Nullable
- public ComponentName getCurrentTransportComponentForUser(int userId) {
+ public ComponentName getCurrentTransportComponentForUser(@UserIdInt int userId) {
return (isUserReadyForBackup(userId)) ? getCurrentTransportComponent(userId) : null;
}
@@ -986,7 +1016,7 @@
}
@Override
- public String[] listAllTransportsForUser(int userId) throws RemoteException {
+ public String[] listAllTransportsForUser(@UserIdInt int userId) throws RemoteException {
return (isUserReadyForBackup(userId)) ? listAllTransports(userId) : null;
}
@@ -1007,7 +1037,8 @@
}
@Override
- public ComponentName[] listAllTransportComponentsForUser(int userId) throws RemoteException {
+ public ComponentName[] listAllTransportComponentsForUser(@UserIdInt int userId)
+ throws RemoteException {
return (isUserReadyForBackup(userId))
? listAllTransportComponents(userId) : null;
}
@@ -1041,7 +1072,7 @@
@Override
public void updateTransportAttributesForUser(
- int userId,
+ @UserIdInt int userId,
ComponentName transportComponent,
String name,
@Nullable Intent configurationIntent,
@@ -1106,7 +1137,7 @@
}
@Override
- public String selectBackupTransportForUser(int userId, String transport)
+ public String selectBackupTransportForUser(@UserIdInt int userId, String transport)
throws RemoteException {
return (isUserReadyForBackup(userId))
? selectBackupTransport(userId, transport) : null;
@@ -1135,7 +1166,7 @@
}
@Override
- public void selectBackupTransportAsyncForUser(int userId, ComponentName transport,
+ public void selectBackupTransportAsyncForUser(@UserIdInt int userId, ComponentName transport,
ISelectBackupTransportCallback listener) throws RemoteException {
if (isUserReadyForBackup(userId)) {
selectBackupTransportAsync(userId, transport, listener);
@@ -1167,7 +1198,7 @@
}
@Override
- public Intent getConfigurationIntentForUser(int userId, String transport)
+ public Intent getConfigurationIntentForUser(@UserIdInt int userId, String transport)
throws RemoteException {
return isUserReadyForBackup(userId) ? getConfigurationIntent(userId, transport)
: null;
@@ -1195,7 +1226,8 @@
}
@Override
- public String getDestinationStringForUser(int userId, String transport) throws RemoteException {
+ public String getDestinationStringForUser(@UserIdInt int userId, String transport)
+ throws RemoteException {
return isUserReadyForBackup(userId) ? getDestinationString(userId, transport)
: null;
}
@@ -1225,7 +1257,7 @@
}
@Override
- public Intent getDataManagementIntentForUser(int userId, String transport)
+ public Intent getDataManagementIntentForUser(@UserIdInt int userId, String transport)
throws RemoteException {
return isUserReadyForBackup(userId)
? getDataManagementIntent(userId, transport) : null;
@@ -1249,7 +1281,7 @@
}
@Override
- public CharSequence getDataManagementLabelForUser(int userId, String transport)
+ public CharSequence getDataManagementLabelForUser(@UserIdInt int userId, String transport)
throws RemoteException {
return isUserReadyForBackup(userId) ? getDataManagementLabel(userId, transport)
: null;
@@ -1271,7 +1303,7 @@
@Override
public IRestoreSession beginRestoreSessionForUser(
- int userId, String packageName, String transportID) throws RemoteException {
+ @UserIdInt int userId, String packageName, String transportID) throws RemoteException {
return isUserReadyForBackup(userId)
? beginRestoreSession(userId, packageName, transportID) : null;
}
@@ -1292,7 +1324,8 @@
}
@Override
- public void opCompleteForUser(int userId, int token, long result) throws RemoteException {
+ public void opCompleteForUser(@UserIdInt int userId, int token, long result)
+ throws RemoteException {
if (isUserReadyForBackup(userId)) {
opComplete(userId, token, result);
}
@@ -1317,7 +1350,7 @@
}
@Override
- public long getAvailableRestoreTokenForUser(int userId, String packageName) {
+ public long getAvailableRestoreTokenForUser(@UserIdInt int userId, String packageName) {
return isUserReadyForBackup(userId) ? getAvailableRestoreToken(userId, packageName) : 0;
}
@@ -1335,7 +1368,7 @@
}
@Override
- public boolean isAppEligibleForBackupForUser(int userId, String packageName) {
+ public boolean isAppEligibleForBackupForUser(@UserIdInt int userId, String packageName) {
return isUserReadyForBackup(userId) && isAppEligibleForBackup(userId,
packageName);
}
@@ -1350,7 +1383,7 @@
}
@Override
- public String[] filterAppsEligibleForBackupForUser(int userId, String[] packages) {
+ public String[] filterAppsEligibleForBackupForUser(@UserIdInt int userId, String[] packages) {
return isUserReadyForBackup(userId) ? filterAppsEligibleForBackup(userId, packages) : null;
}
@@ -1696,7 +1729,7 @@
// backup is not essential for device functioning.
mBackupManagerService.postToHandler(
() -> {
- mBackupManagerService.updateDefaultBackupUserIdIfNeeded();
+ mBackupManagerService.stopServiceForSystemUserIfMainUserCreated();
mBackupManagerService.startServiceForUser(user.getUserIdentifier());
mBackupManagerService.mHasFirstUserUnlockedSinceBoot = true;
});
@@ -1714,18 +1747,24 @@
}
/**
- * On the first ever boot of a new device, the 'main' user might not exist for a short period of
- * time and be created after {@link BackupManagerService} is created. In this case the {@link
- * #mDefaultBackupUserId} will be the system user initially, but we need to change it to the
- * newly created {@link UserManager#getMainUser()} later.
- *
- * <p>{@link Lifecycle#onUserUnlocking(SystemService.TargetUser)} (for any user) is the earliest
- * point where we know that a main user (if there is going to be one) is created.
+ * Handles an edge case specific to the *first boot* of a device.
+ *
+ * This method is called upon the *first* user unlock event after boot. It checks if:
+ * - This is indeed the first user unlock since boot.
+ * - The main user did *not* exist when BMS was constructed.
+ * - A main user now exists and it's *not* the system user.
+ * If all conditions are met, then the method checks if USER_SYSTEM's service should be
+ * stopped based on current state.
+ *
+ * Normally, the service for USER_SYSTEM isn't expected to be running yet when this executes
+ * during the unlock sequence. This method acts primarily as a safeguard against a rare race
+ * condition where `setBackupServiceActive(USER_SYSTEM)` could be called concurrently after
+ * the unlock task is posted but before this method runs.
*/
- private void updateDefaultBackupUserIdIfNeeded() {
+ private void stopServiceForSystemUserIfMainUserCreated() {
// The default user can only change before any user unlocks since boot, and it will only
// change from the system user to a non-system user.
- if (mHasFirstUserUnlockedSinceBoot || mDefaultBackupUserId != UserHandle.USER_SYSTEM) {
+ if (mHasFirstUserUnlockedSinceBoot || mDidMainUserExistAtBoot) {
return;
}
@@ -1734,20 +1773,12 @@
return;
}
- if (mDefaultBackupUserId != mainUser.getIdentifier()) {
- int oldDefaultBackupUserId = mDefaultBackupUserId;
- mDefaultBackupUserId = mainUser.getIdentifier();
- // We don't expect the service to be started for the old default user but we attempt to
- // stop its service to be safe.
- if (!isBackupActivatedForUser(oldDefaultBackupUserId)) {
- stopServiceForUser(oldDefaultBackupUserId);
- }
- Slog.i(
- TAG,
- "Default backup user changed from "
- + oldDefaultBackupUserId
- + " to "
- + mDefaultBackupUserId);
+ if (mainUser.getIdentifier() == UserHandle.USER_SYSTEM) {
+ return;
+ }
+
+ if (!isBackupActivatedForUser(UserHandle.USER_SYSTEM)) {
+ stopServiceForUser(UserHandle.USER_SYSTEM);
}
}
}
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index 4f56483..63aada1 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -231,8 +231,6 @@
if (associations.isEmpty()) return;
mCompanionExemptionProcessor.updateAtm(userId, associations);
- ExecutorService executor = Executors.newSingleThreadExecutor();
- executor.execute(mCompanionExemptionProcessor::updateAutoRevokeExemptions);
}
@Override
@@ -240,6 +238,11 @@
Slog.i(TAG, "onUserUnlocked() user=" + user);
// Notify and bind the app after the phone is unlocked.
mDevicePresenceProcessor.sendDevicePresenceEventOnUnlocked(user.getUserIdentifier());
+
+ Binder.withCleanCallingIdentity(() -> {
+ ExecutorService executor = Executors.newSingleThreadExecutor();
+ executor.execute(mCompanionExemptionProcessor::updateAutoRevokeExemptions);
+ });
}
private void onPackageRemoveOrDataClearedInternal(
diff --git a/services/companion/java/com/android/server/companion/datatransfer/continuity/OWNERS b/services/companion/java/com/android/server/companion/datatransfer/continuity/OWNERS
new file mode 100644
index 0000000..4a4cb1e
--- /dev/null
+++ b/services/companion/java/com/android/server/companion/datatransfer/continuity/OWNERS
@@ -0,0 +1,2 @@
+include /core/java/android/companion/OWNERS
+joeantonetti@google.com
diff --git a/services/companion/java/com/android/server/companion/datatransfer/continuity/TaskContinuityManagerService.java b/services/companion/java/com/android/server/companion/datatransfer/continuity/TaskContinuityManagerService.java
new file mode 100644
index 0000000..3cf9401
--- /dev/null
+++ b/services/companion/java/com/android/server/companion/datatransfer/continuity/TaskContinuityManagerService.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.companion.datatransfer.continuity;
+
+import android.companion.datatransfer.continuity.ITaskContinuityManager;
+import android.content.Context;
+
+import com.android.server.SystemService;
+
+
+/**
+ * Service to handle task continuity features
+ *
+ * @hide
+ *
+ */
+public final class TaskContinuityManagerService extends SystemService {
+
+ private TaskContinuityManagerServiceImpl mTaskContinuityManagerService;
+
+ public TaskContinuityManagerService(Context context) {
+ super(context);
+ }
+
+ @Override
+ public void onStart() {
+ mTaskContinuityManagerService = new TaskContinuityManagerServiceImpl();
+ publishBinderService(Context.TASK_CONTINUITY_SERVICE, mTaskContinuityManagerService);
+ }
+
+ private final class TaskContinuityManagerServiceImpl extends ITaskContinuityManager.Stub {
+
+ }
+}
diff --git a/services/companion/java/com/android/server/companion/virtual/InputController.java b/services/companion/java/com/android/server/companion/virtual/InputController.java
index 7456c50..cbf9dc3 100644
--- a/services/companion/java/com/android/server/companion/virtual/InputController.java
+++ b/services/companion/java/com/android/server/companion/virtual/InputController.java
@@ -102,26 +102,28 @@
private final NativeWrapper mNativeWrapper;
private final DisplayManagerInternal mDisplayManagerInternal;
private final InputManagerInternal mInputManagerInternal;
+ private final InputManager mInputManager;
private final WindowManager mWindowManager;
private final AttributionSource mAttributionSource;
private final DeviceCreationThreadVerifier mThreadVerifier;
- InputController(@NonNull Handler handler,
+ InputController(@NonNull Handler handler, @NonNull InputManager inputManager,
@NonNull WindowManager windowManager, AttributionSource attributionSource) {
- this(new NativeWrapper(), handler, windowManager, attributionSource,
+ this(new NativeWrapper(), handler, inputManager, windowManager, attributionSource,
// Verify that virtual devices are not created on the handler thread.
() -> !handler.getLooper().isCurrentThread());
}
@VisibleForTesting
- InputController(@NonNull NativeWrapper nativeWrapper,
- @NonNull Handler handler, @NonNull WindowManager windowManager,
+ InputController(@NonNull NativeWrapper nativeWrapper, @NonNull Handler handler,
+ @NonNull InputManager inputManager, @NonNull WindowManager windowManager,
AttributionSource attributionSource,
@NonNull DeviceCreationThreadVerifier threadVerifier) {
mHandler = handler;
mNativeWrapper = nativeWrapper;
mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
mInputManagerInternal = LocalServices.getService(InputManagerInternal.class);
+ mInputManager = inputManager;
mWindowManager = windowManager;
mAttributionSource = attributionSource;
mThreadVerifier = threadVerifier;
@@ -266,7 +268,7 @@
}
void setMouseScalingEnabled(boolean enabled, int displayId) {
- mInputManagerInternal.setMouseScalingEnabled(enabled, displayId);
+ mInputManager.setMouseScalingEnabled(enabled, displayId);
}
void setDisplayEligibilityForPointerCapture(boolean isEligible, int displayId) {
diff --git a/services/companion/java/com/android/server/companion/virtual/ViewConfigurationController.java b/services/companion/java/com/android/server/companion/virtual/ViewConfigurationController.java
new file mode 100644
index 0000000..5952e13
--- /dev/null
+++ b/services/companion/java/com/android/server/companion/virtual/ViewConfigurationController.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.companion.virtual;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.companion.virtual.ViewConfigurationParams;
+import android.content.Context;
+import android.content.om.FabricatedOverlay;
+import android.content.om.OverlayConstraint;
+import android.content.om.OverlayIdentifier;
+import android.content.om.OverlayManager;
+import android.content.om.OverlayManagerTransaction;
+import android.os.Binder;
+import android.util.Slog;
+import android.util.TypedValue;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.List;
+
+/**
+ * Controls the application of {@link ViewConfigurationParams} for a virtual device.
+ */
+@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+public class ViewConfigurationController {
+
+ private static final String TAG = "ViewConfigurationController";
+ private static final String FRAMEWORK_PACKAGE_NAME = "android";
+
+ private static final String TAP_TIMEOUT_RESOURCE_NAME = "integer/config_tapTimeoutMillis";
+ private static final String DOUBLE_TAP_TIMEOUT_RESOURCE_NAME =
+ "integer/config_doubleTapTimeoutMillis";
+ private static final String DOUBLE_TAP_MIN_TIME_RESOURCE_NAME =
+ "integer/config_doubleTapMinTimeMillis";
+ private static final String TOUCH_SLOP_RESOURCE_NAME =
+ "dimen/config_viewConfigurationTouchSlop";
+ private static final String MIN_FLING_VELOCITY_RESOURCE_NAME =
+ "dimen/config_viewMinFlingVelocity";
+ private static final String MAX_FLING_VELOCITY_RESOURCE_NAME =
+ "dimen/config_viewMaxFlingVelocity";
+ private static final String SCROLL_FRICTION_RESOURCE_NAME = "dimen/config_scrollFriction";
+
+ private final OverlayManager mOverlayManager;
+ private final Object mLock = new Object();
+
+ @GuardedBy("mLock")
+ private OverlayIdentifier mOverlayIdentifier = null;
+
+ public ViewConfigurationController(@NonNull Context context) {
+ mOverlayManager = context.getSystemService(OverlayManager.class);
+ }
+
+ /**
+ * Applies given {@link ViewConfigurationParams} for the given {@code deviceId}.
+ */
+ public void applyViewConfigurationParams(int deviceId,
+ @Nullable ViewConfigurationParams viewConfigurationParams) {
+ if (viewConfigurationParams == null) {
+ return;
+ }
+
+ FabricatedOverlay overlay = new FabricatedOverlay.Builder(
+ FRAMEWORK_PACKAGE_NAME /* owningPackage */,
+ "vdOverlay" + deviceId /* overlayName */,
+ FRAMEWORK_PACKAGE_NAME /* targetPackage */)
+ .build();
+ OverlayIdentifier overlayIdentifier = overlay.getIdentifier();
+ setResourceDpValue(overlay, TOUCH_SLOP_RESOURCE_NAME,
+ viewConfigurationParams.getTouchSlopDp());
+ setResourceDpValue(overlay, MIN_FLING_VELOCITY_RESOURCE_NAME,
+ viewConfigurationParams.getMinimumFlingVelocityDpPerSecond());
+ setResourceDpValue(overlay, MAX_FLING_VELOCITY_RESOURCE_NAME,
+ viewConfigurationParams.getMaximumFlingVelocityDpPerSecond());
+ setResourceFloatValue(overlay, SCROLL_FRICTION_RESOURCE_NAME,
+ viewConfigurationParams.getScrollFriction());
+ setResourceIntValue(overlay, TAP_TIMEOUT_RESOURCE_NAME,
+ (int) viewConfigurationParams.getTapTimeoutDuration().toMillis());
+ setResourceIntValue(overlay, DOUBLE_TAP_TIMEOUT_RESOURCE_NAME,
+ (int) viewConfigurationParams.getDoubleTapTimeoutDuration().toMillis());
+ setResourceIntValue(overlay, DOUBLE_TAP_MIN_TIME_RESOURCE_NAME,
+ (int) viewConfigurationParams.getDoubleTapMinTimeDuration().toMillis());
+
+ int callingUserId = Binder.getCallingUserHandle().getIdentifier();
+ Binder.withCleanCallingIdentity(() -> {
+ mOverlayManager.commit(
+ new OverlayManagerTransaction.Builder()
+ .registerFabricatedOverlay(overlay)
+ .setEnabled(overlayIdentifier, true /* enable */, callingUserId,
+ List.of(new OverlayConstraint(OverlayConstraint.TYPE_DEVICE_ID,
+ deviceId)))
+ .build());
+ synchronized (mLock) {
+ mOverlayIdentifier = overlayIdentifier;
+ }
+ });
+ }
+
+ /**
+ * Clears the applied {@link ViewConfigurationParams}.
+ */
+ public void close() {
+ OverlayManagerTransaction transaction;
+ synchronized (mLock) {
+ if (mOverlayIdentifier == null) {
+ return;
+ }
+
+ transaction = new OverlayManagerTransaction.Builder().unregisterFabricatedOverlay(
+ mOverlayIdentifier).build();
+ }
+
+ Binder.withCleanCallingIdentity(() -> mOverlayManager.commit(transaction));
+ }
+
+ private static void setResourceDpValue(@NonNull FabricatedOverlay overlay,
+ @NonNull String resourceName, float value) {
+ if (value == ViewConfigurationParams.INVALID_VALUE) {
+ return;
+ }
+
+ if (!android.content.res.Flags.dimensionFrro()) {
+ Slog.e(TAG, "Dimension resource overlay is not supported");
+ return;
+ }
+
+ overlay.setResourceValue(resourceName, value, TypedValue.COMPLEX_UNIT_DIP,
+ null /* configuration */);
+ }
+
+ private static void setResourceFloatValue(@NonNull FabricatedOverlay overlay,
+ @NonNull String resourceName, float value) {
+ if (value == ViewConfigurationParams.INVALID_VALUE) {
+ return;
+ }
+
+ if (!android.content.res.Flags.dimensionFrro()) {
+ Slog.e(TAG, "Dimension resource overlay is not supported");
+ return;
+ }
+
+ overlay.setResourceValue(resourceName, value, null /* configuration */);
+ }
+
+ private static void setResourceIntValue(@NonNull FabricatedOverlay overlay,
+ @NonNull String resourceName, int value) {
+ if (value == ViewConfigurationParams.INVALID_VALUE) {
+ return;
+ }
+
+ overlay.setResourceValue(resourceName, TypedValue.TYPE_INT_DEC, value,
+ null /* configuration */);
+ }
+}
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
index 0023b6d..500113b 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
@@ -74,6 +74,7 @@
import android.hardware.display.DisplayManagerInternal;
import android.hardware.display.IVirtualDisplayCallback;
import android.hardware.display.VirtualDisplayConfig;
+import android.hardware.input.InputManager;
import android.hardware.input.VirtualDpadConfig;
import android.hardware.input.VirtualKeyEvent;
import android.hardware.input.VirtualKeyboardConfig;
@@ -193,6 +194,7 @@
private final InputController mInputController;
private final SensorController mSensorController;
private final CameraAccessController mCameraAccessController;
+ @Nullable private final ViewConfigurationController mViewConfigurationController;
@Nullable // Null if virtual camera flag is off.
private final VirtualCameraController mVirtualCameraController;
private VirtualAudioController mVirtualAudioController;
@@ -404,7 +406,10 @@
isVirtualCameraEnabled()
? new VirtualCameraController(
params.getDevicePolicy(POLICY_TYPE_CAMERA), deviceId)
- : null);
+ : null,
+ android.content.res.Flags.rroConstraints()
+ && Flags.viewconfigurationApis()
+ ? new ViewConfigurationController(context) : null);
}
@VisibleForTesting
@@ -424,7 +429,8 @@
Consumer<ArraySet<Integer>> runningAppsChangedCallback,
VirtualDeviceParams params,
DisplayManagerGlobal displayManager,
- VirtualCameraController virtualCameraController) {
+ VirtualCameraController virtualCameraController,
+ ViewConfigurationController viewConfigurationController) {
mVirtualDeviceLog = virtualDeviceLog;
mOwnerPackageName = attributionSource.getPackageName();
mAttributionSource = attributionSource;
@@ -469,8 +475,8 @@
mBaseVirtualDisplayFlags = flags;
if (inputController == null) {
- mInputController = new InputController(
- context.getMainThreadHandler(),
+ mInputController = new InputController(context.getMainThreadHandler(),
+ context.getSystemService(InputManager.class),
context.getSystemService(WindowManager.class), mAttributionSource);
} else {
mInputController = inputController;
@@ -482,6 +488,11 @@
mCameraAccessController.startObservingIfNeeded();
}
mVirtualCameraController = virtualCameraController;
+ mViewConfigurationController = viewConfigurationController;
+ if (mViewConfigurationController != null) {
+ mViewConfigurationController.applyViewConfigurationParams(deviceId,
+ params.getViewConfigurationParams());
+ }
try {
token.linkToDeath(this, 0);
} catch (RemoteException e) {
@@ -827,6 +838,9 @@
if (mVirtualCameraController != null) {
mVirtualCameraController.close();
}
+ if (mViewConfigurationController != null) {
+ mViewConfigurationController.close();
+ }
}
@Override
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
index b90f910..bfc0ca2 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
@@ -467,8 +467,7 @@
runningUids -> notifyRunningAppsChanged(deviceId, runningUids);
VirtualDeviceImpl virtualDevice = new VirtualDeviceImpl(getContext(), associationInfo,
VirtualDeviceManagerService.this, mVirtualDeviceLog, token, attributionSource,
- deviceId,
- cameraAccessController, mPendingTrampolineCallback, activityListener,
+ deviceId, cameraAccessController, mPendingTrampolineCallback, activityListener,
soundEffectListener, runningAppsChangedCallback, params);
Counter.logIncrement("virtual_devices.value_virtual_devices_created_count");
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
index 145303d..73fc613 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
@@ -40,6 +40,7 @@
import android.app.assist.AssistStructure;
import android.content.ComponentName;
import android.content.ContentCaptureOptions;
+import android.content.Intent;
import android.content.pm.ActivityPresentationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
@@ -61,12 +62,14 @@
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.EventLog;
+import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.view.contentcapture.ContentCaptureCondition;
import android.view.contentcapture.DataRemovalRequest;
import android.view.contentcapture.DataShareRequest;
+import android.view.contentcapture.flags.Flags;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.os.IResultReceiver;
@@ -94,6 +97,9 @@
static final int EVENT_LOG_CONNECT_STATE_CONNECTED = 1;
static final int EVENT_LOG_CONNECT_STATE_DISCONNECTED = 2;
+ private final String ACTION_CREATE_UNDERLAY = "com.systemui.underlay.action.CREATE_UNDERLAY";
+ private final String ACTION_DESTROY_UNDERLAY = "com.systemui.underlay.action.DESTROY_UNDERLAY";
+
@GuardedBy("mLock")
private final SparseArray<ContentCaptureServerSession> mSessions = new SparseArray<>();
@@ -354,8 +360,9 @@
// Make sure service is bound, just in case the initial connection failed somehow
mRemoteService.ensureBoundLocked();
+ final ActivityId activityId = new ActivityId(taskId, shareableActivityToken);
final ContentCaptureServerSession newSession = new ContentCaptureServerSession(mLock,
- activityToken, new ActivityId(taskId, shareableActivityToken), this, componentName,
+ activityToken, activityId, this, componentName,
clientReceiver, taskId, displayId, sessionId, uid, flags);
if (mMaster.verbose) {
Slog.v(TAG, "startSession(): new session for "
@@ -363,6 +370,11 @@
}
mSessions.put(sessionId, newSession);
newSession.notifySessionStartedLocked(clientReceiver);
+
+ if (Flags.enableSystemUiUnderlay()) {
+ if (mMaster.debug) Slog.d(mTag, "startSessionLocked " + componentName);
+ createSystemUIUnderlay(newSession.getSessionId(), activityId);
+ }
}
@GuardedBy("mLock")
@@ -498,6 +510,17 @@
return null;
}
+ @GuardedBy("mLock")
+ private ContentCaptureServerSession getSession(@NonNull ActivityId activityId) {
+ for (int i = 0; i < mSessions.size(); i++) {
+ final ContentCaptureServerSession session = mSessions.valueAt(i);
+ if (session.isActivitySession(activityId)) {
+ return session;
+ }
+ }
+ return null;
+ }
+
/**
* Destroys the service and all state associated with it.
*
@@ -562,9 +585,43 @@
if (mMaster.verbose) Slog.v(mTag, "onActivityEvent(): " + event);
+ if (Flags.enableSystemUiUnderlay()) {
+ if (type == ActivityEvent.TYPE_ACTIVITY_STARTED) {
+ ContentCaptureServerSession session = getSession(activityId);
+ if (session != null) {
+ createSystemUIUnderlay(session.getSessionId(), activityId);
+ }
+ } else if (type == ActivityEvent.TYPE_ACTIVITY_STOPPED) {
+ ContentCaptureServerSession session = getSession(activityId);
+ if (session != null) {
+ destroySystemUIUnderlay(session.getSessionId(), activityId);
+ }
+ }
+ }
+
mRemoteService.onActivityLifecycleEvent(event);
}
+ private void createSystemUIUnderlay(int sessionId, ActivityId activityId) {
+ if (mMaster.debug) Slog.d(mTag, "createSystemUIUnderlay: " + sessionId);
+ // TODO: b/403422950 migrate to aidl when available
+ Intent intent = new Intent(ACTION_CREATE_UNDERLAY);
+ intent.putExtra("dataSessionId", sessionId);
+ intent.putExtra("timestamp", System.currentTimeMillis());
+ intent.putExtra("activityId", activityId);
+ getContext().sendBroadcast(intent);
+ }
+
+ private void destroySystemUIUnderlay(int sessionId, ActivityId activityId) {
+ if (mMaster.debug) Slog.d(mTag, "destroySystemUIUnderlay: " + sessionId);
+ // TODO: b/403422950 migrate to aidl when available
+ Intent intent = new Intent(ACTION_DESTROY_UNDERLAY);
+ intent.putExtra("dataSessionId", sessionId);
+ intent.putExtra("timestamp", System.currentTimeMillis());
+ intent.putExtra("activityId", activityId);
+ getContext().sendBroadcast(intent);
+ }
+
@Override
protected void dumpLocked(String prefix, PrintWriter pw) {
super.dumpLocked(prefix, pw);
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java
index 73ed97f..d1a5ba0 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java
@@ -48,6 +48,10 @@
private static final String TAG = ContentCaptureServerSession.class.getSimpleName();
+ /**
+ * The {@link Activity#getActivityToken()}.
+ * Note, not the {@link Activity#getShareableActivityToken()}.
+ */
final IBinder mActivityToken;
private final ContentCapturePerUserService mService;
@@ -73,6 +77,8 @@
private final Object mLock;
+ private final ActivityId mActivityId;
+
public final ComponentName appComponentName;
ContentCaptureServerSession(@NonNull Object lock, @NonNull IBinder activityToken,
@@ -86,6 +92,7 @@
mService = service;
mId = sessionId;
mUid = uid;
+ mActivityId = activityId;
mContentCaptureContext = new ContentCaptureContext(/* clientContext= */ null,
activityId, appComponentName, displayId, activityToken, flags);
mSessionStateReceiver = sessionStateReceiver;
@@ -98,12 +105,21 @@
/**
* Returns whether this session is for the given activity.
+ *
+ * @param activityToken the {@link Activity#getActivityToken()} from the activity.
*/
boolean isActivitySession(@NonNull IBinder activityToken) {
return mActivityToken.equals(activityToken);
}
/**
+ * Returns whether this session is for the given activity.
+ */
+ boolean isActivitySession(@NonNull ActivityId activityId) {
+ return mActivityId.equals(activityId);
+ }
+
+ /**
* Notifies the {@link ContentCaptureService} that the service started.
*/
@GuardedBy("mLock")
@@ -213,6 +229,10 @@
/* binder= */ null);
}
+ int getSessionId() {
+ return mId;
+ }
+
/**
* Called when the session client binder object died - typically when its process was killed
* and the activity was not properly destroyed.
diff --git a/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java b/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java
index a1d39fa..8f989c2 100644
--- a/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java
+++ b/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java
@@ -53,6 +53,9 @@
import android.content.pm.ResolveInfo;
import android.graphics.Bitmap;
import android.media.AudioManager;
+import android.media.projection.IMediaProjection;
+import android.media.projection.IMediaProjectionManager;
+import android.media.projection.MediaProjectionManager;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
@@ -70,6 +73,7 @@
import android.provider.Settings;
import android.util.Log;
import android.util.Slog;
+import android.view.Display;
import android.view.IWindowManager;
import android.window.ScreenCapture.ScreenshotHardwareBuffer;
@@ -380,6 +384,13 @@
launchIntent.putExtra(ContextualSearchManager.EXTRA_SCREENSHOT, bm.asShared());
}
}
+
+ IMediaProjection mediaProjection = getMediaProjection(csUid, csPackage);
+ if (mediaProjection != null) {
+ launchIntent.putExtra(MediaProjectionManager.EXTRA_MEDIA_PROJECTION,
+ mediaProjection.asBinder());
+ }
+
launchIntent.putExtra(ContextualSearchManager.EXTRA_IS_MANAGED_PROFILE_VISIBLE,
isManagedProfileVisible);
// Only put the list of visible package names if assist data is allowed
@@ -390,6 +401,24 @@
return launchIntent;
}
+ private IMediaProjection getMediaProjection(int uid, String packageName) {
+ if (Flags.contextualSearchMediaProjection()) {
+
+ return Binder.withCleanCallingIdentity(() -> {
+ IBinder binder = ServiceManager.getService(Context.MEDIA_PROJECTION_SERVICE);
+ IMediaProjectionManager mediaProjectionManager =
+ IMediaProjectionManager.Stub.asInterface(binder);
+ IMediaProjection mediaProjection = mediaProjectionManager.createProjection(uid,
+ packageName,
+ MediaProjectionManager.TYPE_SCREEN_CAPTURE, false, Display.DEFAULT_DISPLAY);
+ mediaProjection.setRecordingOverlay(true);
+ return mediaProjection;
+ });
+ } else {
+ return null;
+ }
+ }
+
@RequiresPermission(android.Manifest.permission.START_TASKS_FROM_RECENTS)
private int invokeContextualSearchIntent(Intent launchIntent, final int userId) {
// Contextual search starts with a frozen screen - so we launch without
@@ -549,9 +578,12 @@
issueToken();
Bundle bundle = new Bundle();
bundle.putParcelable(ContextualSearchManager.EXTRA_TOKEN, mToken);
+
// We get take the screenshot with the system server's identity because the system
// server has READ_FRAME_BUFFER permission to get the screenshot.
final int callingUid = Binder.getCallingUid();
+ IMediaProjection mediaProjection = getMediaProjection(callingUid,
+ getContextualSearchPackageName());
Binder.withCleanCallingIdentity(() -> {
final ScreenshotHardwareBuffer shb =
mWmInternal.takeContextualSearchScreenshot(
@@ -563,6 +595,11 @@
bundle.putBoolean(ContextualSearchManager.EXTRA_FLAG_SECURE_FOUND,
shb.containsSecureLayers());
}
+
+ if (mediaProjection != null) {
+ bundle.putBinder(MediaProjectionManager.EXTRA_MEDIA_PROJECTION,
+ mediaProjection.asBinder());
+ }
try {
callback.onResult(
new ContextualSearchState(null, null, bundle));
diff --git a/services/core/java/android/os/BatteryStatsInternal.java b/services/core/java/android/os/BatteryStatsInternal.java
index 289935a..f6dcc13 100644
--- a/services/core/java/android/os/BatteryStatsInternal.java
+++ b/services/core/java/android/os/BatteryStatsInternal.java
@@ -19,12 +19,8 @@
import android.annotation.IntDef;
import android.net.Network;
-import com.android.internal.os.BinderCallsStats;
-import com.android.server.power.stats.SystemServerCpuThreadReader.SystemServiceCpuThreadTimes;
-
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import java.util.Collection;
import java.util.List;
/**
@@ -67,9 +63,6 @@
*/
public abstract String[] getMobileIfaces();
- /** Returns CPU times for system server thread groups. */
- public abstract SystemServiceCpuThreadTimes getSystemServiceCpuThreadTimes();
-
/**
* Returns BatteryUsageStats, which contains power attribution data on a per-subsystem
* and per-UID basis.
@@ -109,17 +102,6 @@
public abstract void noteCpuWakingBluetoothProxyPacket(int uid, long elapsedMillis);
/**
- * Informs battery stats of binder stats for the given work source UID.
- */
- public abstract void noteBinderCallStats(int workSourceUid, long incrementalBinderCallCount,
- Collection<BinderCallsStats.CallStat> callStats);
-
- /**
- * Informs battery stats of native thread IDs of threads taking incoming binder calls.
- */
- public abstract void noteBinderThreadNativeIds(int[] binderThreadNativeTids);
-
- /**
* Reports a sound trigger recognition event that may have woken up the CPU.
* @param elapsedMillis The time when the event happened in the elapsed timebase.
* @param uid The uid that requested this trigger.
diff --git a/services/core/java/com/android/server/BinderCallsStatsService.java b/services/core/java/com/android/server/BinderCallsStatsService.java
index 416b36f..06a3d2f 100644
--- a/services/core/java/com/android/server/BinderCallsStatsService.java
+++ b/services/core/java/com/android/server/BinderCallsStatsService.java
@@ -36,7 +36,6 @@
import android.content.pm.PackageManager.NameNotFoundException;
import android.database.ContentObserver;
import android.net.Uri;
-import android.os.BatteryStatsInternal;
import android.os.Binder;
import android.os.ParcelFileDescriptor;
import android.os.Process;
@@ -59,7 +58,6 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.Collection;
import java.util.List;
public class BinderCallsStatsService extends Binder {
@@ -288,23 +286,6 @@
CachedDeviceState.Readonly.class);
mBinderCallsStats.setDeviceState(deviceState);
- if (!com.android.server.power.optimization.Flags.disableSystemServicePowerAttr()) {
- BatteryStatsInternal batteryStatsInternal = getLocalService(
- BatteryStatsInternal.class);
- mBinderCallsStats.setCallStatsObserver(new BinderInternal.CallStatsObserver() {
- @Override
- public void noteCallStats(int workSourceUid, long incrementalCallCount,
- Collection<BinderCallsStats.CallStat> callStats) {
- batteryStatsInternal.noteBinderCallStats(workSourceUid,
- incrementalCallCount, callStats);
- }
-
- @Override
- public void noteBinderThreadNativeIds(int[] binderThreadNativeTids) {
- batteryStatsInternal.noteBinderThreadNativeIds(binderThreadNativeTids);
- }
- });
- }
// It needs to be called before mService.systemReady to make sure the observer is
// initialized before installing it.
mWorkSourceProvider.systemReady(getContext());
diff --git a/services/core/java/com/android/server/SystemServerInitThreadPool.java b/services/core/java/com/android/server/SystemServerInitThreadPool.java
index 73d8384..6492931 100644
--- a/services/core/java/com/android/server/SystemServerInitThreadPool.java
+++ b/services/core/java/com/android/server/SystemServerInitThreadPool.java
@@ -32,8 +32,10 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
+import java.util.concurrent.Callable;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
@@ -72,26 +74,39 @@
"system-server-init-thread", Process.THREAD_PRIORITY_FOREGROUND);
}
- /**
- * Submits a task for execution.
- *
- * @throws IllegalStateException if it hasn't been started or has been shut down already.
- */
- public static @NonNull Future<?> submit(@NonNull Runnable runnable,
- @NonNull String description) {
- Objects.requireNonNull(description, "description cannot be null");
-
+ private static SystemServerInitThreadPool getInstance() {
SystemServerInitThreadPool instance;
synchronized (LOCK) {
Preconditions.checkState(sInstance != null, "Cannot get " + TAG
+ " - it has been shut down");
instance = sInstance;
}
-
- return instance.submitTask(runnable, description);
+ return instance;
}
- private @NonNull Future<?> submitTask(@NonNull Runnable runnable,
+ /**
+ * Submits a task for execution and returns a Future that returns null on success.
+ *
+ * @throws IllegalStateException if it hasn't been started or has been shut down already.
+ */
+ public static @NonNull Future<?> submit(@NonNull Runnable runnable,
+ @NonNull String description) {
+ Objects.requireNonNull(description, "description cannot be null");
+ return getInstance().submitTask(Executors.callable(runnable), description);
+ }
+
+ /**
+ * Submits a task for execution and returns a Future containing the Callable's result.
+ *
+ * @throws IllegalStateException if it hasn't been started or has been shut down already.
+ */
+ public static <T> @NonNull Future<T> submit(@NonNull Callable<T> callable,
+ @NonNull String description) {
+ Objects.requireNonNull(description, "description cannot be null");
+ return getInstance().submitTask(callable, description);
+ }
+
+ private <T> @NonNull Future<T> submitTask(@NonNull Callable<T> callable,
@NonNull String description) {
synchronized (mPendingTasks) {
Preconditions.checkState(!mShutDown, TAG + " already shut down");
@@ -103,8 +118,9 @@
if (IS_DEBUGGABLE) {
Slog.d(TAG, "Started executing " + description);
}
+ T result = null;
try {
- runnable.run();
+ result = callable.call();
} catch (RuntimeException e) {
Slog.e(TAG, "Failure in " + description + ": " + e, e);
traceLog.traceEnd();
@@ -117,6 +133,7 @@
Slog.d(TAG, "Finished executing " + description);
}
traceLog.traceEnd();
+ return result;
});
}
diff --git a/services/core/java/com/android/server/TradeInModeService.java b/services/core/java/com/android/server/TradeInModeService.java
index f17a23c..b9dd770 100644
--- a/services/core/java/com/android/server/TradeInModeService.java
+++ b/services/core/java/com/android/server/TradeInModeService.java
@@ -34,12 +34,15 @@
import android.os.ITradeInMode;
import android.os.RemoteException;
import android.os.SystemProperties;
+import android.os.RemoteException;
+import android.view.SurfaceControl;
import android.provider.Settings;
import android.provider.Settings.SettingNotFoundException;
import android.service.persistentdata.PersistentDataBlockManager;
import android.util.Slog;
import com.android.server.health.HealthServiceWrapper;
+import com.android.server.display.DisplayControl;
import java.io.FileWriter;
import java.io.IOException;
@@ -249,6 +252,20 @@
@Override
@RequiresPermission(android.Manifest.permission.ENTER_TRADE_IN_MODE)
+ public int getScreenPartStatus() throws RemoteException {
+ SurfaceControl b = new SurfaceControl.Builder().setName("TIM").build();
+ // loop through all displayId to find id of internal display
+ for (long physicalDisplayId : DisplayControl.getPhysicalDisplayIds()) {
+ SurfaceControl.StaticDisplayInfo info = b.getStaticDisplayInfo(physicalDisplayId);
+ if (info.isInternal) {
+ return info.screenPartStatus;
+ }
+ }
+ return 0;
+ }
+
+ @Override
+ @RequiresPermission(android.Manifest.permission.ENTER_TRADE_IN_MODE)
public int getHingeCount() throws RemoteException {
android.hardware.health.HingeInfo[] info = getHealthService().getHingeInfo();
return (info == null) ? 0 : info.length;
diff --git a/services/core/java/com/android/server/adb/AdbService.java b/services/core/java/com/android/server/adb/AdbService.java
index c338a1e..4186e46 100644
--- a/services/core/java/com/android/server/adb/AdbService.java
+++ b/services/core/java/com/android/server/adb/AdbService.java
@@ -145,18 +145,6 @@
public void notifyKeyFilesUpdated() {
mDebuggingManager.notifyKeyFilesUpdated();
}
-
- @Override
- public void startAdbdForTransport(byte transportType) {
- FgThread.getHandler().sendMessage(obtainMessage(
- AdbService::setAdbdEnabledForTransport, AdbService.this, true, transportType));
- }
-
- @Override
- public void stopAdbdForTransport(byte transportType) {
- FgThread.getHandler().sendMessage(obtainMessage(
- AdbService::setAdbdEnabledForTransport, AdbService.this, false, transportType));
- }
}
private void registerContentObservers() {
@@ -406,19 +394,6 @@
}
}
- private void setAdbdEnabledForTransport(boolean enable, byte transportType) {
- if (transportType == AdbTransportType.USB) {
- mIsAdbUsbEnabled = enable;
- } else if (transportType == AdbTransportType.WIFI) {
- mIsAdbWifiEnabled = enable;
- }
- if (enable) {
- startAdbd();
- } else {
- stopAdbd();
- }
- }
-
private WifiManager.MulticastLock mAdbMulticastLock = null;
private void acquireMulticastLock() {
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index c278ffb4..63a968b 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -3727,7 +3727,7 @@
mShortFGSAnrTimer.discard(sr);
return;
}
- mShortFGSAnrTimer.accept(sr);
+ mShortFGSAnrTimer.accept(sr, tr);
final String message = "Short FGS ANR'ed: " + sr;
if (DEBUG_SHORT_SERVICE) {
@@ -5999,9 +5999,11 @@
if (clientApp == hostApp) {
policy = DEFAULT_SERVICE_NO_BUMP_BIND_POLICY_FLAG;
} else if (clientApp.isCached()) {
- policy = DEFAULT_SERVICE_NO_BUMP_BIND_POLICY_FLAG;
- if (clientApp.isFreezable()) {
- policy |= SERVICE_BIND_OOMADJ_POLICY_FREEZE_CALLER;
+ if (!Flags.cpuTimeCapabilityBasedFreezePolicy()) {
+ policy = DEFAULT_SERVICE_NO_BUMP_BIND_POLICY_FLAG;
+ if (clientApp.isFreezable()) {
+ policy |= SERVICE_BIND_OOMADJ_POLICY_FREEZE_CALLER;
+ }
}
}
if ((policy & SERVICE_BIND_OOMADJ_POLICY_SKIP_OOM_UPDATE_ON_CONNECT) == 0) {
@@ -6013,6 +6015,12 @@
policy = DEFAULT_SERVICE_NO_BUMP_BIND_POLICY_FLAG;
}
}
+ if (Flags.cpuTimeCapabilityBasedFreezePolicy()) {
+ // Non cached processes can possibly be frozen, always check their freezability.
+ if (clientApp.isFreezable()) {
+ policy |= SERVICE_BIND_OOMADJ_POLICY_FREEZE_CALLER;
+ }
+ }
}
return policy;
}
@@ -7566,7 +7574,6 @@
}
}
if (timeout != null && mAm.mProcessList.isInLruListLOSP(proc)) {
- final AutoCloseable timer = mActiveServiceAnrTimer.accept(proc);
Slog.w(TAG, "Timeout executing service: " + timeout);
StringWriter sw = new StringWriter();
PrintWriter pw = new FastPrintWriter(sw, false, 1024);
@@ -7579,7 +7586,8 @@
LAST_ANR_LIFETIME_DURATION_MSECS);
long waitedMillis = now - timeout.executingStart;
timeoutRecord = TimeoutRecord.forServiceExec(timeout.shortInstanceName,
- waitedMillis).setExpiredTimer(timer);
+ waitedMillis);
+ mActiveServiceAnrTimer.accept(proc, timeoutRecord);
} else {
mActiveServiceAnrTimer.discard(proc);
final long delay = psr.shouldExecServicesFg()
@@ -7623,7 +7631,7 @@
return;
}
- mServiceFGAnrTimer.accept(r);
+ mServiceFGAnrTimer.accept(r, timeoutRecord);
if (DEBUG_BACKGROUND_CHECK) {
Slog.i(TAG, "Service foreground-required timeout for " + r);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 038338d..3dd8657 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -3455,9 +3455,7 @@
doLowMem = false;
}
if (doOomAdj) {
- if (Flags.migrateFullOomadjUpdates()) {
- app.forEachConnectionHost((host) -> enqueueOomAdjTargetLocked(host));
- }
+ app.forEachConnectionHost((host) -> enqueueOomAdjTargetLocked(host));
}
EventLogTags.writeAmProcDied(app.userId, pid, app.processName, setAdj, setProcState);
@@ -3466,11 +3464,7 @@
handleAppDiedLocked(app, pid, false, true, fromBinderDied);
if (doOomAdj) {
- if (Flags.migrateFullOomadjUpdates()) {
- updateOomAdjPendingTargetsLocked(OOM_ADJ_REASON_PROCESS_END);
- } else {
- updateOomAdjLocked(OOM_ADJ_REASON_PROCESS_END);
- }
+ updateOomAdjPendingTargetsLocked(OOM_ADJ_REASON_PROCESS_END);
}
if (doLowMem) {
mAppProfiler.doLowMemReportIfNeededLocked(app);
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 6f30abc..79c1a658 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -284,6 +284,8 @@
return runFreeze(pw, true);
case "unfreeze":
return runFreeze(pw, false);
+ case "isfrozen":
+ return isFrozen(pw);
case "instrument":
getOutPrintWriter().println("Error: must be invoked through 'am instrument'.");
return -1;
@@ -1348,6 +1350,17 @@
return 0;
}
+ @NeverCompile
+ int isFrozen(PrintWriter pw) throws RemoteException {
+ ProcessRecord proc = getProcessFromShell();
+ if (proc == null) {
+ return -1;
+ }
+ boolean frozen = android.os.Process.isProcessFrozen(proc.mPid, proc.uid);
+ pw.println(frozen ? "true" : "false");
+ return 0;
+ }
+
/**
* Parses from the shell the pid or process name and provides the corresponding
* {@link ProcessRecord}.
@@ -4462,6 +4475,8 @@
pw.println(" may be either a process name or pid. Options are:");
pw.println(" --sticky: persists the unfrozen state for the process lifetime or");
pw.println(" until a freeze is triggered via shell");
+ pw.println(" isfrozen <PROCESS>");
+ pw.println(" Print the frozen status of the process (true or false)");
pw.println(" instrument [-r] [-e <NAME> <VALUE>] [-p <FILE>] [-w]");
pw.println(" [--user <USER_ID> | current]");
pw.println(" [--no-hidden-api-checks [--no-test-api-access]]");
diff --git a/services/core/java/com/android/server/am/ApplicationThreadDeferred.java b/services/core/java/com/android/server/am/ApplicationThreadDeferred.java
index 193edd2..60ab8af 100644
--- a/services/core/java/com/android/server/am/ApplicationThreadDeferred.java
+++ b/services/core/java/com/android/server/am/ApplicationThreadDeferred.java
@@ -42,12 +42,6 @@
static final String TAG = TAG_WITH_CLASS_NAME ? "ApplicationThreadDeferred" : TAG_AM;
- // The flag that enables the deferral behavior of this class. If the flag is disabled then
- // the class behaves exactly like an ApplicationThreadFilter.
- private static boolean deferBindersWhenPaused() {
- return Flags.deferBindersWhenPaused();
- }
-
// The list of notifications that may be deferred.
private static final int CLEAR_DNS_CACHE = 0;
private static final int UPDATE_TIME_ZONE = 1;
@@ -84,19 +78,14 @@
@GuardedBy("mLock")
private final boolean[] mPending = new boolean[NOTIFICATION_COUNT];
- // When true, binder calls to paused processes will be deferred until the process is unpaused.
- private final boolean mDefer;
-
// The base thread, because Delegator does not expose it.
private final IApplicationThread mBase;
- /** Create an instance with a base thread and a deferral enable flag. */
- @VisibleForTesting
- public ApplicationThreadDeferred(IApplicationThread thread, boolean defer) {
+ /** Create an instance with a base thread. */
+ public ApplicationThreadDeferred(IApplicationThread thread) {
super(thread);
mBase = thread;
- mDefer = defer;
mOperations[CLEAR_DNS_CACHE] = () -> { super.clearDnsCache(); };
mOperations[UPDATE_TIME_ZONE] = () -> { super.updateTimeZone(); };
@@ -104,11 +93,6 @@
mOperations[UPDATE_HTTP_PROXY] = () -> { super.updateHttpProxy(); };
}
- /** Create an instance with a base flag, using the system deferral enable flag. */
- public ApplicationThreadDeferred(IApplicationThread thread) {
- this(thread, deferBindersWhenPaused());
- }
-
/**
* Return the implementation's value of asBinder(). super.asBinder() is not a real Binder
* object.
@@ -155,7 +139,7 @@
*/
private void execute(@NotificationType int tag) throws RemoteException {
synchronized (mLock) {
- if (mPaused && mDefer) {
+ if (mPaused) {
mPending[tag] = true;
return;
}
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 8fde9bb..3ee4343 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -106,7 +106,6 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IBatteryStats;
-import com.android.internal.os.BinderCallsStats;
import com.android.internal.os.Clock;
import com.android.internal.os.CpuScalingPolicies;
import com.android.internal.os.CpuScalingPolicyReader;
@@ -117,7 +116,6 @@
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.ParseUtils;
-import com.android.internal.util.function.pooled.PooledLambda;
import com.android.modules.utils.build.SdkLevel;
import com.android.net.module.util.NetworkCapabilitiesUtils;
import com.android.server.LocalServices;
@@ -134,7 +132,6 @@
import com.android.server.power.stats.PowerStatsScheduler;
import com.android.server.power.stats.PowerStatsStore;
import com.android.server.power.stats.PowerStatsUidResolver;
-import com.android.server.power.stats.SystemServerCpuThreadReader.SystemServiceCpuThreadTimes;
import com.android.server.power.stats.processor.MultiStatePowerAttributor;
import com.android.server.power.stats.wakeups.CpuWakeupStats;
@@ -150,7 +147,6 @@
import java.nio.charset.CodingErrorAction;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
-import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
@@ -395,10 +391,6 @@
mStats.setExternalStatsSyncLocked(mWorker);
mStats.setRadioScanningTimeoutLocked(mContext.getResources().getInteger(
com.android.internal.R.integer.config_radioScanningTimeout) * 1000L);
- if (!Flags.disableSystemServicePowerAttr()) {
- mStats.startTrackingSystemServerCpuTime();
- }
-
mPowerStatsStore = new PowerStatsStore(systemDir, mHandler);
mPowerAttributor = new MultiStatePowerAttributor(mContext, mPowerStatsStore, mPowerProfile,
mCpuScalingPolicies, () -> mStats.getBatteryCapacity());
@@ -643,11 +635,6 @@
}
@Override
- public SystemServiceCpuThreadTimes getSystemServiceCpuThreadTimes() {
- return mStats.getSystemServiceCpuThreadTimes();
- }
-
- @Override
public List<BatteryUsageStats> getBatteryUsageStats(List<BatteryUsageStatsQuery> queries) {
return BatteryStatsService.this.getBatteryUsageStats(queries);
}
@@ -697,23 +684,6 @@
}
@Override
- public void noteBinderCallStats(int workSourceUid, long incrementatCallCount,
- Collection<BinderCallsStats.CallStat> callStats) {
- synchronized (BatteryStatsService.this.mLock) {
- mHandler.sendMessage(PooledLambda.obtainMessage(
- mStats::noteBinderCallStats, workSourceUid, incrementatCallCount, callStats,
- SystemClock.elapsedRealtime(), SystemClock.uptimeMillis()));
- }
- }
-
- @Override
- public void noteBinderThreadNativeIds(int[] binderThreadNativeTids) {
- synchronized (BatteryStatsService.this.mLock) {
- mStats.noteBinderThreadNativeIds(binderThreadNativeTids);
- }
- }
-
- @Override
public void noteWakingSoundTrigger(long elapsedMillis, int uid) {
noteCpuWakingActivity(CPU_WAKEUP_SUBSYSTEM_SOUND_TRIGGER, elapsedMillis, uid);
}
@@ -2904,27 +2874,23 @@
final int chargeFullUAh, final long chargeTimeToFullSeconds) {
super.setBatteryState_enforcePermission();
- synchronized (mLock) {
- final long elapsedRealtime = SystemClock.elapsedRealtime();
- final long uptime = SystemClock.uptimeMillis();
- final long currentTime = System.currentTimeMillis();
- final boolean onBattery = BatteryStatsImpl.isOnBattery(plugType, status);
- final boolean batteryStateChanged;
+ final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
+ final long currentTime = System.currentTimeMillis();
+ final boolean onBattery = BatteryStatsImpl.isOnBattery(plugType, status);
+ mHandler.post(() -> {
+ boolean batteryStateChanged;
synchronized (mStats) {
batteryStateChanged = mStats.isOnBattery() != onBattery;
+ mStats.setBatteryStateLocked(status, health, plugType, level, temp, volt,
+ chargeUAh, chargeFullUAh, chargeTimeToFullSeconds,
+ elapsedRealtime, uptime, currentTime);
}
- mHandler.post(() -> {
- synchronized (mStats) {
- mStats.setBatteryStateLocked(status, health, plugType, level, temp, volt,
- chargeUAh, chargeFullUAh, chargeTimeToFullSeconds,
- elapsedRealtime, uptime,currentTime);
- }
- if (batteryStateChanged) {
- mWorker.scheduleSync("battery-state",
- BatteryExternalStatsWorker.UPDATE_ALL);
- }
- });
- }
+ if (batteryStateChanged) {
+ mWorker.scheduleSync("battery-state",
+ BatteryExternalStatsWorker.UPDATE_ALL);
+ }
+ });
}
@Override
diff --git a/services/core/java/com/android/server/am/BroadcastController.java b/services/core/java/com/android/server/am/BroadcastController.java
index ee94130..a26b31472 100644
--- a/services/core/java/com/android/server/am/BroadcastController.java
+++ b/services/core/java/com/android/server/am/BroadcastController.java
@@ -491,15 +491,10 @@
// provider that needs to lock mProviderMap in ActivityThread
// and also it may need to wait application response, so we
// cannot lock ActivityManagerService here.
- final int match;
- if (Flags.avoidResolvingType()) {
- match = filter.match(intent.getAction(), broadcast.resolvedDataType,
- intent.getScheme(), intent.getData(), intent.getCategories(),
- TAG, false /* supportsWildcards */, null /* ignoreActions */,
- intent.getExtras());
- } else {
- match = filter.match(resolver, intent, true, TAG);
- }
+ final int match = filter.match(intent.getAction(), broadcast.resolvedDataType,
+ intent.getScheme(), intent.getData(), intent.getCategories(),
+ TAG, false /* supportsWildcards */, null /* ignoreActions */,
+ intent.getExtras());
if (match >= 0) {
if (allSticky == null) {
allSticky = new ArrayList<>();
diff --git a/services/core/java/com/android/server/am/BroadcastProcessQueue.java b/services/core/java/com/android/server/am/BroadcastProcessQueue.java
index c0fe738..f493747 100644
--- a/services/core/java/com/android/server/am/BroadcastProcessQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastProcessQueue.java
@@ -106,14 +106,6 @@
*/
private boolean mTimeoutScheduled;
- /**
- * Snapshotted value of {@link ProcessRecord#getCpuDelayTime()}, typically
- * used when deciding if we should extend the soft ANR timeout.
- *
- * Required when Flags.anrTimerServiceEnabled is false.
- */
- long lastCpuDelayTime;
-
/**
* Snapshotted value of {@link ProcessStateRecord#getCurProcState()} before
* dispatching the current broadcast to the receiver in this process.
diff --git a/services/core/java/com/android/server/am/BroadcastQueueImpl.java b/services/core/java/com/android/server/am/BroadcastQueueImpl.java
index 7ab87f9..487963b 100644
--- a/services/core/java/com/android/server/am/BroadcastQueueImpl.java
+++ b/services/core/java/com/android/server/am/BroadcastQueueImpl.java
@@ -282,10 +282,6 @@
private static final int MSG_PROCESS_FREEZABLE_CHANGED = 6;
private static final int MSG_UID_STATE_CHANGED = 7;
- // Required when Flags.anrTimerServiceEnabled is false. This constant should be deleted if and
- // when the flag is fused on.
- private static final int MSG_DELIVERY_TIMEOUT_SOFT = 8;
-
private void enqueueUpdateRunningList() {
mLocalHandler.removeMessages(MSG_UPDATE_RUNNING_LIST);
mLocalHandler.sendEmptyMessage(MSG_UPDATE_RUNNING_LIST);
@@ -299,14 +295,6 @@
updateRunningList();
return true;
}
- // Required when Flags.anrTimerServiceEnabled is false. This case should be deleted if
- // and when the flag is fused on.
- case MSG_DELIVERY_TIMEOUT_SOFT: {
- synchronized (mService) {
- deliveryTimeoutSoftLocked((BroadcastProcessQueue) msg.obj, msg.arg1);
- return true;
- }
- }
case MSG_DELIVERY_TIMEOUT: {
deliveryTimeout((BroadcastProcessQueue) msg.obj);
return true;
@@ -1296,13 +1284,7 @@
@GuardedBy("mService")
private void startDeliveryTimeoutLocked(@NonNull BroadcastProcessQueue queue,
int softTimeoutMillis) {
- if (mAnrTimer.serviceEnabled()) {
- mAnrTimer.start(queue, softTimeoutMillis);
- } else {
- queue.lastCpuDelayTime = queue.app.getCpuDelayTime();
- mLocalHandler.sendMessageDelayed(Message.obtain(mLocalHandler,
- MSG_DELIVERY_TIMEOUT_SOFT, softTimeoutMillis, 0, queue), softTimeoutMillis);
- }
+ mAnrTimer.start(queue, softTimeoutMillis);
}
// Required when Flags.anrTimerServiceEnabled is false. This function can be replaced with a
@@ -1310,26 +1292,6 @@
@GuardedBy("mService")
private void cancelDeliveryTimeoutLocked(@NonNull BroadcastProcessQueue queue) {
mAnrTimer.cancel(queue);
- if (!mAnrTimer.serviceEnabled()) {
- mLocalHandler.removeMessages(MSG_DELIVERY_TIMEOUT_SOFT, queue);
- }
- }
-
- // Required when Flags.anrTimerServiceEnabled is false. This function can be deleted entirely
- // if and when the flag is fused on.
- @GuardedBy("mService")
- private void deliveryTimeoutSoftLocked(@NonNull BroadcastProcessQueue queue,
- int softTimeoutMillis) {
- if (queue.app != null) {
- // Instead of immediately triggering an ANR, extend the timeout by
- // the amount of time the process was runnable-but-waiting; we're
- // only willing to do this once before triggering an hard ANR
- final long cpuDelayTime = queue.app.getCpuDelayTime() - queue.lastCpuDelayTime;
- final long hardTimeoutMillis = MathUtils.constrain(cpuDelayTime, 0, softTimeoutMillis);
- mAnrTimer.start(queue, hardTimeoutMillis);
- } else {
- deliveryTimeoutLocked(queue);
- }
}
private void deliveryTimeout(@NonNull BroadcastProcessQueue queue) {
@@ -1469,11 +1431,11 @@
if (deliveryState == BroadcastRecord.DELIVERY_TIMEOUT) {
r.anrCount++;
if (app != null && !app.isDebugging()) {
- final AutoCloseable timer = mAnrTimer.accept(queue);
final String packageName = getReceiverPackageName(receiver);
final String className = getReceiverClassName(receiver);
TimeoutRecord tr = TimeoutRecord.forBroadcastReceiver(r.intent, packageName,
- className).setExpiredTimer(timer);
+ className);
+ mAnrTimer.accept(queue, tr);
mService.appNotResponding(queue.app, tr);
} else {
mAnrTimer.discard(queue);
diff --git a/services/core/java/com/android/server/am/OWNERS b/services/core/java/com/android/server/am/OWNERS
index 54adb18..4963f59 100644
--- a/services/core/java/com/android/server/am/OWNERS
+++ b/services/core/java/com/android/server/am/OWNERS
@@ -3,16 +3,12 @@
per-file ActiveServices.java = file:/ACTIVITY_MANAGER_OWNERS
per-file ProcessList.java = file:/ACTIVITY_MANAGER_OWNERS
per-file ActivityThread.java = file:/ACTIVITY_MANAGER_OWNERS
-per-file ProcessRecord.java = file:/ACTIVITY_MANAGER_OWNERS
+per-file Process*Record.java = file:/ACTIVITY_MANAGER_OWNERS
per-file SystemServer.java = file:/ACTIVITY_MANAGER_OWNERS
per-file ServiceRecord.java = file:/ACTIVITY_MANAGER_OWNERS
per-file AppProfiler.java = file:/ACTIVITY_MANAGER_OWNERS
-per-file ProcessStateRecord.java = file:/ACTIVITY_MANAGER_OWNERS
-per-file ProcessServiceRecord.java = file:/ACTIVITY_MANAGER_OWNERS
per-file ForegroundServiceTypeLoggerModule.java = file:/ACTIVITY_MANAGER_OWNERS
per-file AppRestrictionController.java = file:/ACTIVITY_MANAGER_OWNERS
-per-file ProcessErrorStateRecord.java = file:/ACTIVITY_MANAGER_OWNERS
-per-file ProcessProfileRecord.java = file:/ACTIVITY_MANAGER_OWNERS
per-file ConnectionRecord.java = file:/ACTIVITY_MANAGER_OWNERS
per-file UidRecord.java = file:/ACTIVITY_MANAGER_OWNERS
per-file IntentBindRecord.java = file:/ACTIVITY_MANAGER_OWNERS
@@ -61,7 +57,7 @@
per-file ProcessStateController.java = file:/OOM_ADJUSTER_OWNERS
# Miscellaneous
-per-file SettingsToPropertiesMapper.java = omakoto@google.com, yamasani@google.com, dzshen@google.com, zhidou@google.com
+per-file SettingsToPropertiesMapper.java = omakoto@google.com, yamasani@google.com, dzshen@google.com, zhidou@google.com, marybethfair@google.com
per-file CarUserSwitchingDialog.java = file:platform/packages/services/Car:/OWNERS
# Activity Security
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 5d223a2..28bcbd9 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -419,8 +419,7 @@
/**
* The oom score a client needs to be to raise a service with UI out of cache.
*/
- protected static final int CACHING_UI_SERVICE_CLIENT_ADJ_THRESHOLD =
- Flags.raiseBoundUiServiceThreshold() ? SERVICE_ADJ : PERCEPTIBLE_APP_ADJ;
+ protected static final int CACHING_UI_SERVICE_CLIENT_ADJ_THRESHOLD = SERVICE_ADJ;
static final long PERCEPTIBLE_TASK_TIMEOUT_MILLIS = 5 * 60 * 1000;
@@ -502,9 +501,7 @@
final int group = msg.what;
final ProcessRecord app = (ProcessRecord) msg.obj;
setProcessGroup(app.getPid(), group, app.processName);
- if (Flags.phantomProcessesFix()) {
- mService.mPhantomProcessList.setProcessGroupForPhantomProcessOfApp(app, group);
- }
+ mService.mPhantomProcessList.setProcessGroupForPhantomProcessOfApp(app, group);
return true;
});
mTmpUidRecords = new ActiveUids(service, false);
@@ -2391,14 +2388,10 @@
final UidRecord uidRec = mActiveUids.get(uid);
if (uidRec != null && uidRec.isCurAllowListed() != onAllowlist) {
uidRec.setCurAllowListed(onAllowlist);
- if (Flags.migrateFullOomadjUpdates()) {
- for (int i = uidRec.getNumOfProcs() - 1; i >= 0; i--) {
- enqueueOomAdjTargetLocked(uidRec.getProcessRecordByIndex(i));
- }
- updateOomAdjPendingTargetsLocked(OOM_ADJ_REASON_ALLOWLIST);
- } else {
- updateOomAdjLSP(OOM_ADJ_REASON_ALLOWLIST);
+ for (int i = uidRec.getNumOfProcs() - 1; i >= 0; i--) {
+ enqueueOomAdjTargetLocked(uidRec.getProcessRecordByIndex(i));
}
+ updateOomAdjPendingTargetsLocked(OOM_ADJ_REASON_ALLOWLIST);
}
}
@@ -2640,14 +2633,10 @@
& ALL_CPU_TIME_CAPABILITIES) != 0) {
// The connection might grant CPU capability to the service.
needDryRun = true;
- } else if (Flags.unfreezeBindPolicyFix()
- && cr.hasFlag(Context.BIND_WAIVE_PRIORITY
- | Context.BIND_ALLOW_OOM_MANAGEMENT)) {
+ } else if (cr.hasFlag(Context.BIND_WAIVE_PRIORITY | Context.BIND_ALLOW_OOM_MANAGEMENT)) {
// These bind flags can grant the shouldNotFreeze state to the service.
needDryRun = true;
- } else if (Flags.unfreezeBindPolicyFix()
- && client.mOptRecord.shouldNotFreeze()
- && !app.mOptRecord.shouldNotFreeze()) {
+ } else if (client.mOptRecord.shouldNotFreeze() && !app.mOptRecord.shouldNotFreeze()) {
// The shouldNotFreeze state can be propagated and needs to be checked.
needDryRun = true;
}
@@ -2677,13 +2666,9 @@
&& (app.getSetCapability() & client.getSetCapability())
!= PROCESS_CAPABILITY_NONE) {
return true;
- } else if (Flags.unfreezeBindPolicyFix()
- && cr.hasFlag(Context.BIND_WAIVE_PRIORITY
- | Context.BIND_ALLOW_OOM_MANAGEMENT)) {
+ } else if (cr.hasFlag(Context.BIND_WAIVE_PRIORITY | Context.BIND_ALLOW_OOM_MANAGEMENT)) {
return true;
- } else if (Flags.unfreezeBindPolicyFix()
- && app.mOptRecord.shouldNotFreeze()
- && client.mOptRecord.shouldNotFreeze()) {
+ } else if (app.mOptRecord.shouldNotFreeze() && client.mOptRecord.shouldNotFreeze()) {
// Process has shouldNotFreeze and it could have gotten it from the client.
return true;
} else if (Flags.cpuTimeCapabilityBasedFreezePolicy()
@@ -2705,9 +2690,7 @@
needDryRun = true;
} else if (app.getSetProcState() > client.getSetProcState()) {
needDryRun = true;
- } else if (Flags.unfreezeBindPolicyFix()
- && client.mOptRecord.shouldNotFreeze()
- && !app.mOptRecord.shouldNotFreeze()) {
+ } else if (client.mOptRecord.shouldNotFreeze() && !app.mOptRecord.shouldNotFreeze()) {
needDryRun = true;
} else if (Flags.cpuTimeCapabilityBasedFreezePolicy()
&& (client.getSetCapability() & ~app.getSetCapability()
@@ -2732,9 +2715,7 @@
return true;
} else if (app.getSetProcState() >= client.getSetProcState()) {
return true;
- } else if (Flags.unfreezeBindPolicyFix()
- && app.mOptRecord.shouldNotFreeze()
- && client.mOptRecord.shouldNotFreeze()) {
+ } else if (app.mOptRecord.shouldNotFreeze() && client.mOptRecord.shouldNotFreeze()) {
// Process has shouldNotFreeze and it could have gotten it from the client.
return true;
} else if (Flags.cpuTimeCapabilityBasedFreezePolicy()
@@ -2760,9 +2741,6 @@
@GuardedBy("mService")
protected void maybeSetProcessFollowUpUpdateLocked(ProcessRecord proc,
long updateUptimeMs, long now) {
- if (!Flags.followUpOomadjUpdates()) {
- return;
- }
if (updateUptimeMs <= now) {
// Time sensitive period has already passed. No need to schedule a follow up.
return;
diff --git a/services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java b/services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java
index 980c1f5..4e28eed 100644
--- a/services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java
+++ b/services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java
@@ -338,11 +338,7 @@
boolean setShouldNotFreeze(boolean shouldNotFreeze, boolean dryRun,
@ShouldNotFreezeReason int reason, int adjSeq) {
if (dryRun) {
- if (Flags.unfreezeBindPolicyFix()) {
- return mShouldNotFreeze != shouldNotFreeze;
- } else {
- return mFrozen && !shouldNotFreeze;
- }
+ return mShouldNotFreeze != shouldNotFreeze;
}
if (Flags.traceUpdateAppFreezeStateLsp()) {
if (shouldNotFreeze) {
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 88111e3..6d97a43 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -1139,70 +1139,77 @@
return totalProcessLimit/2;
}
- private static String buildOomTag(String prefix, String compactPrefix, String space, int val,
+ private static String buildOomTag(String prefix, String compactPrefix, int val,
int base, boolean compact) {
final int diff = val - base;
+ if (compact && (diff == 0)) {
+ return compactPrefix;
+ }
+
+ final StringBuilder formattedPrefix = new StringBuilder(prefix);
+ for (int i = 0; i < (5 - prefix.length()); i++) {
+ formattedPrefix.append(" ");
+ }
if (diff == 0) {
- if (compact) {
- return compactPrefix;
- }
- if (space == null) return prefix;
- return prefix + space;
+ formattedPrefix.append(" ");
+ return formattedPrefix.toString();
}
+
if (diff < 10) {
- return prefix + (compact ? "+" : "+ ") + Integer.toString(diff);
+ return compact ? prefix : formattedPrefix.toString()
+ + (compact ? "+" : "+ ") + Integer.toString(diff);
}
- return prefix + "+" + Integer.toString(diff);
+ return formattedPrefix.toString() + "+" + Integer.toString(diff);
}
public static String makeOomAdjString(int setAdj, boolean compact) {
if (setAdj >= ProcessList.CACHED_APP_MIN_ADJ) {
- return buildOomTag("cch", "cch", " ", setAdj,
+ return buildOomTag("cch", "cch", setAdj,
ProcessList.CACHED_APP_MIN_ADJ, compact);
} else if (setAdj >= ProcessList.SERVICE_B_ADJ) {
- return buildOomTag("svcb ", "svcb", null, setAdj,
+ return buildOomTag("svcb", "svcb", setAdj,
ProcessList.SERVICE_B_ADJ, compact);
} else if (setAdj >= ProcessList.PREVIOUS_APP_ADJ) {
- return buildOomTag("prev ", "prev", null, setAdj,
+ return buildOomTag("prev", "prev", setAdj,
ProcessList.PREVIOUS_APP_ADJ, compact);
} else if (setAdj >= ProcessList.HOME_APP_ADJ) {
- return buildOomTag("home ", "home", null, setAdj,
+ return buildOomTag("home", "home", setAdj,
ProcessList.HOME_APP_ADJ, compact);
} else if (setAdj >= ProcessList.SERVICE_ADJ) {
- return buildOomTag("svc ", "svc", null, setAdj,
+ return buildOomTag("svc", "svc", setAdj,
ProcessList.SERVICE_ADJ, compact);
} else if (setAdj >= ProcessList.HEAVY_WEIGHT_APP_ADJ) {
- return buildOomTag("hvy ", "hvy", null, setAdj,
+ return buildOomTag("hvy", "hvy", setAdj,
ProcessList.HEAVY_WEIGHT_APP_ADJ, compact);
} else if (setAdj >= ProcessList.BACKUP_APP_ADJ) {
- return buildOomTag("bkup ", "bkup", null, setAdj,
+ return buildOomTag("bkup", "bkup", setAdj,
ProcessList.BACKUP_APP_ADJ, compact);
} else if (setAdj >= ProcessList.PERCEPTIBLE_LOW_APP_ADJ) {
- return buildOomTag("prcl ", "prcl", null, setAdj,
+ return buildOomTag("prcl", "prcl", setAdj,
ProcessList.PERCEPTIBLE_LOW_APP_ADJ, compact);
} else if (setAdj >= ProcessList.PERCEPTIBLE_MEDIUM_APP_ADJ) {
- return buildOomTag("prcm ", "prcm", null, setAdj,
+ return buildOomTag("prcm", "prcm", setAdj,
ProcessList.PERCEPTIBLE_MEDIUM_APP_ADJ, compact);
} else if (setAdj >= ProcessList.PERCEPTIBLE_APP_ADJ) {
- return buildOomTag("prcp ", "prcp", null, setAdj,
+ return buildOomTag("prcp", "prcp", setAdj,
ProcessList.PERCEPTIBLE_APP_ADJ, compact);
} else if (setAdj >= ProcessList.VISIBLE_APP_ADJ) {
- return buildOomTag("vis", "vis", " ", setAdj,
+ return buildOomTag("vis", "vis", setAdj,
ProcessList.VISIBLE_APP_ADJ, compact);
} else if (setAdj >= ProcessList.FOREGROUND_APP_ADJ) {
- return buildOomTag("fg ", "fg ", " ", setAdj,
+ return buildOomTag("fg", "fg ", setAdj,
ProcessList.FOREGROUND_APP_ADJ, compact);
} else if (setAdj >= ProcessList.PERSISTENT_SERVICE_ADJ) {
- return buildOomTag("psvc ", "psvc", null, setAdj,
+ return buildOomTag("psvc", "psvc", setAdj,
ProcessList.PERSISTENT_SERVICE_ADJ, compact);
} else if (setAdj >= ProcessList.PERSISTENT_PROC_ADJ) {
- return buildOomTag("pers ", "pers", null, setAdj,
+ return buildOomTag("pers", "pers", setAdj,
ProcessList.PERSISTENT_PROC_ADJ, compact);
} else if (setAdj >= ProcessList.SYSTEM_ADJ) {
- return buildOomTag("sys ", "sys", null, setAdj,
+ return buildOomTag("sys", "sys", setAdj,
ProcessList.SYSTEM_ADJ, compact);
} else if (setAdj >= ProcessList.NATIVE_ADJ) {
- return buildOomTag("ntv ", "ntv", null, setAdj,
+ return buildOomTag("ntv", "ntv", setAdj,
ProcessList.NATIVE_ADJ, compact);
} else {
return Integer.toString(setAdj);
@@ -4823,7 +4830,9 @@
pw.print(r.isPersistent() ? persistentLabel : normalLabel);
pw.print(" #");
int num = (origList.size() - 1) - list.get(i).second;
- if (num < 10) pw.print(' ');
+ if (list.size() >= 1000 && num < 1000) pw.print(' ');
+ if (list.size() >= 100 && num < 100) pw.print(' ');
+ if (list.size() >= 10 && num < 10) pw.print(' ');
pw.print(num);
pw.print(": ");
pw.print(oomAdj);
diff --git a/services/core/java/com/android/server/am/ProcessServiceRecord.java b/services/core/java/com/android/server/am/ProcessServiceRecord.java
index 14d3fbc..851611e 100644
--- a/services/core/java/com/android/server/am/ProcessServiceRecord.java
+++ b/services/core/java/com/android/server/am/ProcessServiceRecord.java
@@ -393,7 +393,7 @@
adj = ProcessList.PERCEPTIBLE_APP_ADJ;
} else if (adj < ProcessList.PERCEPTIBLE_LOW_APP_ADJ) {
adj = ProcessList.PERCEPTIBLE_LOW_APP_ADJ;
- } else if (Flags.addModifyRawOomAdjServiceLevel() && adj < ProcessList.SERVICE_ADJ) {
+ } else if (adj < ProcessList.SERVICE_ADJ) {
adj = ProcessList.SERVICE_ADJ;
} else if (adj < ProcessList.CACHED_APP_MIN_ADJ) {
adj = ProcessList.CACHED_APP_MIN_ADJ;
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 3174452..a8a9882 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -203,6 +203,7 @@
static final int COMPLETE_USER_SWITCH_MSG = 130;
static final int USER_COMPLETED_EVENT_MSG = 140;
static final int SCHEDULED_STOP_BACKGROUND_USER_MSG = 150;
+ static final int USER_SWITCHING_DIALOG_ANIMATION_TIMEOUT_MSG = 160;
private static final int NO_ARG2 = 0;
@@ -1983,8 +1984,56 @@
return false;
}
+ boolean needStart = false;
+ boolean updateUmState = false;
+ UserState uss;
+
+ // If the user we are switching to is not currently started, then
+ // we need to start it now.
+ t.traceBegin("updateStartedUserArrayStarting");
+ synchronized (mLock) {
+ uss = mStartedUsers.get(userId);
+ if (uss == null) {
+ uss = new UserState(UserHandle.of(userId));
+ uss.mUnlockProgress.addListener(new UserProgressListener());
+ mStartedUsers.put(userId, uss);
+ updateStartedUserArrayLU();
+ needStart = true;
+ updateUmState = true;
+ } else if (uss.state == UserState.STATE_SHUTDOWN
+ || mDoNotAbortShutdownUserIds.contains(userId)) {
+ Slogf.i(TAG, "User #" + userId
+ + " is shutting down - will start after full shutdown");
+ mPendingUserStarts.add(new PendingUserStart(userId, userStartMode,
+ unlockListener));
+ t.traceEnd(); // updateStartedUserArrayStarting
+ return true;
+ }
+ }
+
+ // No matter what, the fact that we're requested to start the user (even if it is
+ // already running) puts it towards the end of the mUserLru list.
+ addUserToUserLru(userId);
+ if (android.multiuser.Flags.scheduleStopOfBackgroundUser()) {
+ mHandler.removeEqualMessages(SCHEDULED_STOP_BACKGROUND_USER_MSG,
+ Integer.valueOf(userId));
+ }
+
+ if (unlockListener != null) {
+ uss.mUnlockProgress.addListener(unlockListener);
+ }
+ t.traceEnd(); // updateStartedUserArrayStarting
+
+ if (updateUmState) {
+ t.traceBegin("setUserState");
+ mInjector.getUserManagerInternal().setUserState(userId, uss.state);
+ t.traceEnd();
+ }
+
+ UserState finalUss = uss;
+ boolean finalNeedStart = needStart;
final Runnable continueStartUserInternal = () -> continueStartUserInternal(userInfo,
- oldUserId, userStartMode, unlockListener, callingUid, callingPid);
+ oldUserId, userStartMode, finalUss, finalNeedStart, callingUid, callingPid);
if (foreground) {
mHandler.post(() -> dispatchOnBeforeUserSwitching(userId, () ->
mHandler.post(continueStartUserInternal)));
@@ -1999,56 +2048,11 @@
}
private void continueStartUserInternal(UserInfo userInfo, int oldUserId, int userStartMode,
- IProgressListener unlockListener, int callingUid, int callingPid) {
+ UserState uss, boolean needStart, int callingUid, int callingPid) {
final TimingsTraceAndSlog t = new TimingsTraceAndSlog();
final boolean foreground = userStartMode == USER_START_MODE_FOREGROUND;
final int userId = userInfo.id;
- boolean needStart = false;
- boolean updateUmState = false;
- UserState uss;
-
- // If the user we are switching to is not currently started, then
- // we need to start it now.
- t.traceBegin("updateStartedUserArrayStarting");
- synchronized (mLock) {
- uss = mStartedUsers.get(userId);
- if (uss == null) {
- uss = new UserState(UserHandle.of(userId));
- uss.mUnlockProgress.addListener(new UserProgressListener());
- mStartedUsers.put(userId, uss);
- updateStartedUserArrayLU();
- needStart = true;
- updateUmState = true;
- } else if (uss.state == UserState.STATE_SHUTDOWN
- || mDoNotAbortShutdownUserIds.contains(userId)) {
- Slogf.i(TAG, "User #" + userId
- + " is shutting down - will start after full shutdown");
- mPendingUserStarts.add(new PendingUserStart(userId, userStartMode,
- unlockListener));
- t.traceEnd(); // updateStartedUserArrayStarting
- return;
- }
- }
-
- // No matter what, the fact that we're requested to start the user (even if it is
- // already running) puts it towards the end of the mUserLru list.
- addUserToUserLru(userId);
- if (android.multiuser.Flags.scheduleStopOfBackgroundUser()) {
- mHandler.removeEqualMessages(SCHEDULED_STOP_BACKGROUND_USER_MSG,
- Integer.valueOf(userId));
- }
-
- if (unlockListener != null) {
- uss.mUnlockProgress.addListener(unlockListener);
- }
- t.traceEnd(); // updateStartedUserArrayStarting
-
- if (updateUmState) {
- t.traceBegin("setUserState");
- mInjector.getUserManagerInternal().setUserState(userId, uss.state);
- t.traceEnd();
- }
t.traceBegin("updateConfigurationAndProfileIds");
if (foreground) {
// Make sure the old user is no longer considering the display to be on.
diff --git a/services/core/java/com/android/server/am/UserSwitchingDialog.java b/services/core/java/com/android/server/am/UserSwitchingDialog.java
index f4e733a..a1cc9dc 100644
--- a/services/core/java/com/android/server/am/UserSwitchingDialog.java
+++ b/services/core/java/com/android/server/am/UserSwitchingDialog.java
@@ -16,6 +16,8 @@
package com.android.server.am;
+import static com.android.server.am.UserController.USER_SWITCHING_DIALOG_ANIMATION_TIMEOUT_MSG;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
@@ -302,14 +304,14 @@
final AtomicBoolean isFirst = new AtomicBoolean(true);
final Runnable onAnimationEndOrTimeout = () -> {
if (isFirst.getAndSet(false)) {
- mHandler.removeCallbacksAndMessages(null);
+ mHandler.removeMessages(USER_SWITCHING_DIALOG_ANIMATION_TIMEOUT_MSG);
onAnimationEnd.run();
}
};
mHandler.postDelayed(() -> {
Slog.w(TAG, name + " animation not completed in " + ANIMATION_TIMEOUT_MS + " ms");
onAnimationEndOrTimeout.run();
- }, ANIMATION_TIMEOUT_MS);
+ }, USER_SWITCHING_DIALOG_ANIMATION_TIMEOUT_MSG, ANIMATION_TIMEOUT_MS);
return onAnimationEndOrTimeout;
}
diff --git a/services/core/java/com/android/server/am/flags.aconfig b/services/core/java/com/android/server/am/flags.aconfig
index ac22975..7025a03 100644
--- a/services/core/java/com/android/server/am/flags.aconfig
+++ b/services/core/java/com/android/server/am/flags.aconfig
@@ -2,14 +2,6 @@
container: "system"
flag {
- name: "oomadjuster_correctness_rewrite"
- namespace: "backstage_power"
- description: "Utilize new OomAdjuster implementation"
- bug: "298055811"
- is_fixed_read_only: true
-}
-
-flag {
name: "fgs_abuse_detection"
namespace: "backstage_power"
description: "Detect abusive FGS behavior for certain types (camera, mic, media, location)."
@@ -79,17 +71,6 @@
flag {
namespace: "backstage_power"
- name: "avoid_resolving_type"
- description: "Avoid resolving data type for sticky broadcasts"
- bug: "323817802"
- is_fixed_read_only: true
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
- namespace: "backstage_power"
name: "trace_receiver_registration"
description: "Add tracing for broadcast receiver registration and un-registration"
bug: "336385821"
@@ -100,16 +81,6 @@
}
flag {
- name: "migrate_full_oomadj_updates"
- namespace: "backstage_power"
- description: "Migrate full updates to partial updates where possible"
- bug: "324915545"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
name: "skip_unimportant_connections"
namespace: "backstage_power"
description: "Avoid OomAdjuster calculations for connections that won't change importance"
@@ -144,16 +115,6 @@
}
flag {
- name: "follow_up_oomadj_updates"
- namespace: "backstage_power"
- description: "Schedule follow up OomAdjuster updates for time sensitive states."
- bug: "333450932"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
name: "collect_logcat_on_run_synchronously"
namespace: "stability"
description: "Allow logcat collection on synchronous dropbox collection"
@@ -170,14 +131,6 @@
}
flag {
- name: "defer_binders_when_paused"
- namespace: "system_performance"
- is_fixed_read_only: true
- description: "Defer submitting binder calls to paused processes."
- bug: "327038797"
-}
-
-flag {
name: "log_broadcast_sent_event"
namespace: "backstage_power"
description: "Log the broadcast send event to Statsd"
@@ -220,16 +173,6 @@
}
flag {
- name: "unfreeze_bind_policy_fix"
- namespace: "backstage_power"
- description: "Make sure shouldNotFreeze state change correctly triggers updates."
- bug: "375691778"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
name: "oomadjuster_prev_laddering"
namespace: "system_performance"
is_fixed_read_only: true
@@ -246,16 +189,6 @@
}
flag {
- name: "phantom_processes_fix"
- namespace: "backstage_power"
- description: "Make sure setProcessGroupForPhantomProcessOfApp deals with phantom processes properly"
- bug: "375058190"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
name: "cpu_time_capability_based_freeze_policy"
namespace: "backstage_power"
description: "Use PROCESS_CAPABILITY_CPU_TIME and PROCESS_CAPABILITY_IMPLICIT_CPU_TIME to control process freeze state."
@@ -276,16 +209,6 @@
}
flag {
- name: "add_modify_raw_oom_adj_service_level"
- namespace: "backstage_power"
- description: "Add a SERVICE_ADJ level to the modifyRawOomAdj method"
- bug: "374810368"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
name: "app_start_info_isolated_process"
namespace: "system_performance"
description: "Adjust handling of isolated process records to be discarded."
@@ -313,17 +236,6 @@
}
flag {
- name: "raise_bound_ui_service_threshold"
- namespace: "backstage_power"
- is_fixed_read_only: true
- description: "Raise the threshold OomAdjuster will drop a service with UI to cached."
- bug: "391691057"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
name: "lower_sms_oom_importance"
namespace: "backstage_power"
description: "Lower messaging app process oom importance to PERCEPTIBLE_APP_ADJ + 1."
@@ -349,4 +261,14 @@
metadata {
purpose: PURPOSE_BUGFIX
}
-}
\ No newline at end of file
+}
+
+flag {
+ name: "syncmanager_off_main_thread"
+ namespace: "backstage_power"
+ description: "Move broadcast receivers off the main thread to reduce ANR chances"
+ bug: "409177829"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/services/core/java/com/android/server/appbinding/AppBindingConstants.java b/services/core/java/com/android/server/appbinding/AppBindingConstants.java
index 561cc0e..f63955b 100644
--- a/services/core/java/com/android/server/appbinding/AppBindingConstants.java
+++ b/services/core/java/com/android/server/appbinding/AppBindingConstants.java
@@ -46,6 +46,12 @@
private static final String SMS_APP_BIND_FLAGS_KEY =
"sms_app_bind_flags";
+ private static final String SUPERVISION_APP_SERVICE_ENABLED_KEY =
+ "supervision_app_service_enabled";
+
+ private static final String SUPERVISION_APP_SERVICE_BIND_FLAGS_KEY =
+ "supervision_app_service_bind_flags";
+
public final String sourceSettings;
/**
@@ -79,6 +85,12 @@
*/
public final int SMS_APP_BIND_FLAGS;
+ /** Whether to actually bind to the supervision app service. (Feature flag) */
+ public final boolean SUPERVISION_APP_SERVICE_ENABLED;
+
+ /** Extra binding flags for supervision app service. */
+ public final int SUPERVISION_APP_SERVICE_BIND_FLAGS;
+
private AppBindingConstants(String settings) {
sourceSettings = settings;
@@ -113,6 +125,14 @@
Context.BIND_NOT_VISIBLE | Context.BIND_FOREGROUND_SERVICE);
}
+ boolean supervisionAppServiceEnabled =
+ parser.getBoolean(SUPERVISION_APP_SERVICE_ENABLED_KEY, true);
+
+ int supervisionAppServiceBindFlags =
+ parser.getInt(
+ SUPERVISION_APP_SERVICE_BIND_FLAGS_KEY,
+ Context.BIND_NOT_VISIBLE | Context.BIND_FOREGROUND_SERVICE);
+
long serviceStableConnectionThresholdSec = parser.getLong(
SERVICE_STABLE_CONNECTION_THRESHOLD_SEC_KEY, TimeUnit.MINUTES.toSeconds(2));
@@ -133,6 +153,8 @@
SERVICE_STABLE_CONNECTION_THRESHOLD_SEC = serviceStableConnectionThresholdSec;
SMS_SERVICE_ENABLED = smsServiceEnabled;
SMS_APP_BIND_FLAGS = smsAppBindFlags;
+ SUPERVISION_APP_SERVICE_ENABLED = supervisionAppServiceEnabled;
+ SUPERVISION_APP_SERVICE_BIND_FLAGS = supervisionAppServiceBindFlags;
}
/**
@@ -173,5 +195,13 @@
pw.print(prefix);
pw.print(" SMS_APP_BIND_FLAGS: 0x");
pw.println(Integer.toHexString(SMS_APP_BIND_FLAGS));
+
+ pw.print(prefix);
+ pw.print(" SUPERVISION_APP_SERVICE_ENABLED: ");
+ pw.println(SUPERVISION_APP_SERVICE_ENABLED);
+
+ pw.print(prefix);
+ pw.print(" SUPERVISION_APP_SERVICE_BIND_FLAGS: 0x");
+ pw.println(Integer.toHexString(SUPERVISION_APP_SERVICE_BIND_FLAGS));
}
}
diff --git a/services/core/java/com/android/server/appbinding/AppBindingService.java b/services/core/java/com/android/server/appbinding/AppBindingService.java
index 6ccb3ee..61f6156 100644
--- a/services/core/java/com/android/server/appbinding/AppBindingService.java
+++ b/services/core/java/com/android/server/appbinding/AppBindingService.java
@@ -19,6 +19,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.AppGlobals;
+import android.app.supervision.flags.Flags;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentResolver;
@@ -49,6 +50,7 @@
import com.android.server.am.PersistentConnection;
import com.android.server.appbinding.finders.AppServiceFinder;
import com.android.server.appbinding.finders.CarrierMessagingClientServiceFinder;
+import com.android.server.appbinding.finders.SupervisionAppServiceFinder;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -149,6 +151,9 @@
mHandler = BackgroundThread.getHandler();
mApps.add(new CarrierMessagingClientServiceFinder(context, this::onAppChanged, mHandler));
+ if (Flags.enableSupervisionAppService()) {
+ mApps.add(new SupervisionAppServiceFinder(context, this::onAppChanged, mHandler));
+ }
// Initialize with the default value to make it non-null.
mConstants = AppBindingConstants.initializeFromString("");
diff --git a/services/core/java/com/android/server/appbinding/finders/SupervisionAppServiceFinder.java b/services/core/java/com/android/server/appbinding/finders/SupervisionAppServiceFinder.java
new file mode 100644
index 0000000..8a8360b
--- /dev/null
+++ b/services/core/java/com/android/server/appbinding/finders/SupervisionAppServiceFinder.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.appbinding.finders;
+
+import android.app.role.OnRoleHoldersChangedListener;
+import android.app.role.RoleManager;
+import android.app.supervision.ISupervisionAppService;
+import android.app.supervision.SupervisionAppService;
+import android.app.supervision.flags.Flags;
+import android.content.Context;
+import android.content.pm.ServiceInfo;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.UserHandle;
+import android.text.TextUtils;
+import android.util.Slog;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.internal.os.BackgroundThread;
+import com.android.internal.util.CollectionUtils;
+import com.android.server.appbinding.AppBindingConstants;
+
+import java.util.function.BiConsumer;
+
+/** Finds the @{link SupervisionAppService} implementation within the supervision app. */
+public class SupervisionAppServiceFinder
+ extends AppServiceFinder<SupervisionAppService, ISupervisionAppService> {
+
+ private final RoleManager mRoleManager;
+
+ public SupervisionAppServiceFinder(
+ Context context,
+ BiConsumer<AppServiceFinder, Integer> listener,
+ Handler callbackHandler) {
+ super(context, listener, callbackHandler);
+ mRoleManager = context.getSystemService(RoleManager.class);
+ }
+
+ @Override
+ protected boolean isEnabled(AppBindingConstants constants) {
+ return constants.SUPERVISION_APP_SERVICE_ENABLED && Flags.enableSupervisionAppService();
+ }
+
+ @NonNull
+ @Override
+ public String getAppDescription() {
+ return "[Supervision app]";
+ }
+
+ @Override
+ protected Class<SupervisionAppService> getServiceClass() {
+ return SupervisionAppService.class;
+ }
+
+ @Override
+ public ISupervisionAppService asInterface(IBinder obj) {
+ return ISupervisionAppService.Stub.asInterface(obj);
+ }
+
+ @Nullable
+ @Override
+ public String getTargetPackage(int userId) {
+ final String ret =
+ CollectionUtils.firstOrNull(
+ mRoleManager.getRoleHoldersAsUser(
+ RoleManager.ROLE_SYSTEM_SUPERVISION, UserHandle.of(userId)));
+
+ if (DEBUG) {
+ Slog.d(TAG, "getTargetPackage()=" + ret);
+ }
+
+ return ret;
+ }
+
+ @NonNull
+ @Override
+ protected String getServiceAction() {
+ return SupervisionAppService.ACTION_BIND_SUPERVISION_APP_SERVICE;
+ }
+
+ @NonNull
+ @Override
+ protected String getServicePermission() {
+ return "android.permission.BIND_SUPERVISION_APP_SERVICE";
+ }
+
+ @Override
+ public void startMonitoring() {
+ mRoleManager.addOnRoleHoldersChangedListenerAsUser(
+ BackgroundThread.getExecutor(), mRoleHolderChangedListener, UserHandle.ALL);
+ }
+
+ @Override
+ protected String validateService(ServiceInfo service) {
+ final String packageName = service.packageName;
+ final String process = service.processName;
+
+ if (process == null || TextUtils.equals(packageName, process)) {
+ return "Service must not run on the main process";
+ }
+ return null; // Null means accept this service.
+ }
+
+ @Override
+ public int getBindFlags(AppBindingConstants constants) {
+ return constants.SUPERVISION_APP_SERVICE_BIND_FLAGS;
+ }
+
+ private final OnRoleHoldersChangedListener mRoleHolderChangedListener =
+ (role, user) -> {
+ if (RoleManager.ROLE_SYSTEM_SUPERVISION.equals(role)) {
+ mListener.accept(SupervisionAppServiceFinder.this, user.getIdentifier());
+ }
+ };
+}
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 3e2eeec..dd22385 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -7014,6 +7014,7 @@
offsetHistory_enforcePermission();
// Must not hold the appops lock
mHistoricalRegistry.offsetHistory(offsetMillis);
+ mHistoricalRegistry.offsetDiscreteHistory(offsetMillis);
}
@android.annotation.EnforcePermission(android.Manifest.permission.MANAGE_APPOPS)
diff --git a/services/core/java/com/android/server/appop/HistoricalRegistryInterface.java b/services/core/java/com/android/server/appop/HistoricalRegistryInterface.java
index b04299a..4a8ef92 100644
--- a/services/core/java/com/android/server/appop/HistoricalRegistryInterface.java
+++ b/services/core/java/com/android/server/appop/HistoricalRegistryInterface.java
@@ -135,6 +135,11 @@
*/
void offsetHistory(long offsetMillis);
+ /**
+ * Offsets discrete history by the given duration.
+ */
+ void offsetDiscreteHistory(long offsetMillis);
+
/**
* Retrieve historical app op stats for a period form disk.
*/
diff --git a/services/core/java/com/android/server/appop/HistoricalRegistrySql.java b/services/core/java/com/android/server/appop/HistoricalRegistrySql.java
index c171471..64ff70e 100644
--- a/services/core/java/com/android/server/appop/HistoricalRegistrySql.java
+++ b/services/core/java/com/android/server/appop/HistoricalRegistrySql.java
@@ -131,6 +131,11 @@
}
@Override
+ public void offsetDiscreteHistory(long offsetMillis) {
+
+ }
+
+ @Override
public void getHistoricalOpsFromDiskRaw(int uid, @Nullable String packageName,
@Nullable String attributionTag, @Nullable String[] opNames, int historyFlags,
int filter, long beginTimeMillis, long endTimeMillis, int flags,
diff --git a/services/core/java/com/android/server/appop/LegacyHistoricalRegistry.java b/services/core/java/com/android/server/appop/LegacyHistoricalRegistry.java
index 4ccbdf2..47a5c63 100644
--- a/services/core/java/com/android/server/appop/LegacyHistoricalRegistry.java
+++ b/services/core/java/com/android/server/appop/LegacyHistoricalRegistry.java
@@ -619,7 +619,11 @@
mPersistence.persistHistoricalOpsDLocked(history);
}
}
- mDiscreteRegistry.offsetHistory(offsetMillis);
+ }
+
+ @Override
+ public void offsetDiscreteHistory(long offsetMillis) {
+ mDiscreteRegistry.offsetHistory(offsetMillis);
}
@Override
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index b50e6df..d0416163 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -361,15 +361,6 @@
+ ", uid: " + attributionSource.getUid());
}
- if (device == null) {
- synchronized (mDeviceStateLock) {
- CommunicationRouteClient client =
- getCommunicationRouteClientForUid(attributionSource.getUid());
- if (client == null) {
- return false;
- }
- }
- }
synchronized (mCommunicationDeviceLock) {
mCommunicationDeviceUpdateCount++;
AudioDeviceAttributes deviceAttr =
@@ -2065,7 +2056,7 @@
Log.e(TAG, "mCommunicationDeviceUpdateCount already 0 in"
+ " MSG_L_SET_COMMUNICATION_DEVICE_FOR_CLIENT");
}
- mCommunicationDeviceLock.notify();
+ mCommunicationDeviceLock.notifyAll();
}
break;
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 88cd02b..058a35d 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -43,6 +43,7 @@
import static android.media.AudioDeviceVolumeManager.DEVICE_VOLUME_BEHAVIOR_UNSET;
import static android.media.AudioDeviceVolumeManager.DEVICE_VOLUME_BEHAVIOR_VARIABLE;
import static android.media.AudioManager.AUDIO_DEVICE_CATEGORY_HEADPHONES;
+import static android.media.AudioManager.FLAG_ABSOLUTE_VOLUME;
import static android.media.AudioManager.RINGER_MODE_NORMAL;
import static android.media.AudioManager.RINGER_MODE_SILENT;
import static android.media.AudioManager.RINGER_MODE_VIBRATE;
@@ -53,6 +54,7 @@
import static android.media.audio.Flags.concurrentAudioRecordBypassPermission;
import static android.media.audio.Flags.featureSpatialAudioHeadtrackingLowLatency;
import static android.media.audio.Flags.focusFreezeTestApi;
+import static android.media.audio.Flags.registerVolumeCallbackApiHardening;
import static android.media.audio.Flags.roForegroundAudioControl;
import static android.media.audio.Flags.scoManagedByAudio;
import static android.media.audio.Flags.unifyAbsoluteVolumeManagement;
@@ -68,8 +70,8 @@
import static com.android.media.audio.Flags.alarmMinVolumeZero;
import static com.android.media.audio.Flags.asDeviceConnectionFailure;
import static com.android.media.audio.Flags.audioserverPermissions;
-import static com.android.media.audio.Flags.disablePrescaleAbsoluteVolume;
import static com.android.media.audio.Flags.deferWearPermissionUpdates;
+import static com.android.media.audio.Flags.disablePrescaleAbsoluteVolume;
import static com.android.media.audio.Flags.equalScoLeaVcIndexRange;
import static com.android.media.audio.Flags.optimizeBtDeviceSwitch;
import static com.android.media.audio.Flags.replaceStreamBtSco;
@@ -4590,11 +4592,13 @@
/** @see AudioManager#registerVolumeGroupCallback(executor, callback) */
public void registerAudioVolumeCallback(IAudioVolumeChangeDispatcher callback) {
+ checkCallingAudioSettingsPrivilegedPermission();
mAudioVolumeChangeHandler.registerListener(callback);
}
/** @see AudioManager#unregisterVolumeGroupCallback(callback) */
public void unregisterAudioVolumeCallback(IAudioVolumeChangeDispatcher callback) {
+ checkCallingAudioSettingsPrivilegedPermission();
mAudioVolumeChangeHandler.unregisterListener(callback);
}
@@ -4690,21 +4694,13 @@
public void setDeviceVolume(@NonNull VolumeInfo vi, @NonNull AudioDeviceAttributes ada,
@NonNull String callingPackage) {
super.setDeviceVolume_enforcePermission();
- Objects.requireNonNull(vi);
Objects.requireNonNull(ada);
Objects.requireNonNull(callingPackage);
- if (!vi.hasStreamType()) {
- Log.e(TAG, "Unsupported non-stream type based VolumeInfo", new Exception());
+ if (!isVolumeInfoValid(vi)) {
return;
}
- int index = vi.getVolumeIndex();
- if (index == VolumeInfo.INDEX_NOT_SET && !vi.hasMuteCommand()) {
- throw new IllegalArgumentException(
- "changing device volume requires a volume index or mute command");
- }
-
// force a cache clear to force reevaluating stream type to audio device selection
// that can interfere with the sending of the VOLUME_CHANGED_ACTION intent
mAudioSystem.clearRoutingCache();
@@ -4721,30 +4717,95 @@
(unifyAbsoluteVolumeManagement() ? currDevAttr.equalTypeAddress(ada)
: currDevAttr.getInternalType() == ada.getInternalType()) || (vss == null);
- AudioService.sVolumeLogger.enqueue(new DeviceVolumeEvent(streamType, index, ada,
- currDevAttr.getInternalType(), callingPackage, skipping));
+ AudioService.sVolumeLogger.enqueue(
+ new DeviceVolumeEvent(streamType, vi.getVolumeIndex(), ada,
+ currDevAttr.getInternalType(), callingPackage, skipping));
if (skipping) {
// setDeviceVolume was called on a device currently being used or stream state is null
return;
}
- // TODO handle unmuting of current audio device
+ setDeviceVolumeInt(vi, vss, ada, callingPackage, /*flags=*/0, /*changeMute=*/false,
+ "setDeviceVolume");
+ }
+
+ @Override
+ @android.annotation.EnforcePermission(anyOf = {MODIFY_AUDIO_SETTINGS_PRIVILEGED,
+ BLUETOOTH_PRIVILEGED})
+ /** @see AudioDeviceVolumeManager#notifyAbsoluteVolumeChanged(VolumeInfo, AudioDeviceAttributes)
+ * Part of service interface, check permissions and parameters here
+ * Note calling package is for logging purposes only, not to be trusted
+ */
+ public void notifyAbsoluteVolumeChanged(@NonNull VolumeInfo vi,
+ @NonNull AudioDeviceAttributes ada,
+ @NonNull String callingPackage) {
+ super.notifyAbsoluteVolumeChanged_enforcePermission();
+ Objects.requireNonNull(ada);
+ Objects.requireNonNull(callingPackage);
+
+ if (!isVolumeInfoValid(vi)) {
+ return;
+ }
+ if (!isAbsoluteVolumeDevice(ada.getInternalType())) {
+ Slog.e(TAG, "notifyAbsoluteVolumeChanged(): device " + ada
+ + " is not registered as an absolute volume device");
+ return;
+ }
+
+ int streamType = replaceBtScoStreamWithVoiceCall(vi.getStreamType(),
+ "notifyAbsoluteVolumeChanged");
+
+ final VolumeStreamState vss = getVssForStream(streamType);
+ if (vss == null) {
+ Slog.e(TAG, "No VolumeStreamState for stream type " + streamType);
+ return;
+ }
+
+ final int currDev = getDeviceForStream(streamType);
+
+ AudioService.sVolumeLogger.enqueue(
+ new DeviceVolumeEvent(streamType, vi.getVolumeIndex(), ada, callingPackage,
+ currDev == ada.getInternalType()));
+
+ setDeviceVolumeInt(vi, vss, ada, callingPackage,
+ FLAG_ABSOLUTE_VOLUME, currDev == ada.getInternalType(),
+ "notifyAbsoluteVolumeChanged");
+ }
+
+ private static boolean isVolumeInfoValid(VolumeInfo vi) {
+ Objects.requireNonNull(vi);
+
+ // TODO(b/409634289): for now we only allow stream volume info commands, this needs to be
+ // adapted for volume groups.
+ if (!vi.hasStreamType()) {
+ Log.e(TAG, "Unsupported non-stream type based VolumeInfo", new Exception());
+ return false;
+ }
+
+ int index = vi.getVolumeIndex();
+ if (index == VolumeInfo.INDEX_NOT_SET && !vi.hasMuteCommand()) {
+ throw new IllegalArgumentException(
+ "changing device volume requires a volume index or mute command");
+ }
+ return true;
+ }
+
+ private void setDeviceVolumeInt(VolumeInfo vi, VolumeStreamState vss, AudioDeviceAttributes ada,
+ String callingPackage, int flags, boolean changeMute, String caller) {
+ int streamType = vss.getStreamType();
+ int index = vi.getVolumeIndex();
// if a stream is not muted but the VolumeInfo is for muting, set the volume index
// for the device to min volume
if (vi.hasMuteCommand() && vi.isMuted() && !isStreamMute(streamType)) {
setStreamVolumeWithAttributionInt(streamType,
vss.getMinIndex(),
- /*flags*/ 0,
+ flags,
ada, callingPackage, null,
- //TODO handle unmuting of current audio device
- false /*canChangeMuteAndUpdateController*/);
+ changeMute);
return;
}
- AudioService.sVolumeLogger.enqueueAndLog("setDeviceVolume" + " from:" + callingPackage
- + " " + vi + " " + ada, EventLogger.Event.ALOGI, TAG);
-
if (vi.getMinVolumeIndex() == VolumeInfo.INDEX_NOT_SET
|| vi.getMaxVolumeIndex() == VolumeInfo.INDEX_NOT_SET) {
// assume index meant to be in stream type range, validate
@@ -4763,9 +4824,9 @@
/*dstMin*/ min, /*dstMax*/ max);
}
}
- setStreamVolumeWithAttributionInt(streamType, index, /*flags*/ 0,
+ setStreamVolumeWithAttributionInt(streamType, index, flags,
ada, callingPackage, null,
- false /*canChangeMuteAndUpdateController*/);
+ changeMute);
}
/** Retain API for unsupported app usage */
@@ -5183,6 +5244,8 @@
+ optimizeBtDeviceSwitch());
pw.println("\tandroid.media.audio.unifyAbsoluteVolumeManagement:"
+ unifyAbsoluteVolumeManagement());
+ pw.println("\tandroid.media.audio.Flags.registerVolumeCallbackApiHardening:"
+ + registerVolumeCallbackApiHardening());
}
private void dumpAudioMode(PrintWriter pw) {
@@ -8084,6 +8147,17 @@
== PackageManager.PERMISSION_GRANTED;
}
+ private void checkCallingAudioSettingsPrivilegedPermission() {
+ if (!registerVolumeCallbackApiHardening()) {
+ return;
+ }
+ if (mContext.checkCallingPermission(MODIFY_AUDIO_SETTINGS_PRIVILEGED)
+ == PackageManager.PERMISSION_GRANTED) {
+ return;
+ }
+ throw new SecurityException("Missing MODIFY_AUDIO_SETTINGS_PRIVILEGED permission");
+ }
+
/**
* Minimum attenuation that can be set for alarms over speaker by an application that
* doesn't have the MODIFY_AUDIO_SETTINGS permission.
@@ -13909,14 +13983,12 @@
if (app == null) {
return AudioManager.ERROR;
}
- if (android.media.audiopolicy.Flags.audioMixOwnership()) {
- for (AudioMix mix : policyConfig.getMixes()) {
- if (!app.getMixes().contains(mix)) {
- Slog.e(TAG,
- "removeMixForPolicy attempted to unregister AudioMix(es) not "
- + "belonging to the AudioPolicy");
- return AudioManager.ERROR;
- }
+ for (AudioMix mix : policyConfig.getMixes()) {
+ if (!app.getMixes().contains(mix)) {
+ Slog.e(TAG,
+ "removeMixForPolicy attempted to unregister AudioMix(es) not "
+ + "belonging to the AudioPolicy");
+ return AudioManager.ERROR;
}
}
return app.removeMixes(policyConfig.getMixes()) == AudioSystem.SUCCESS
@@ -14717,12 +14789,8 @@
}
final long identity = Binder.clearCallingIdentity();
try {
- if (android.media.audiopolicy.Flags.audioMixOwnership()) {
- synchronized (mMixes) {
- removeMixes(new ArrayList(mMixes));
- }
- } else {
- mAudioSystem.registerPolicyMixes(mMixes, false);
+ synchronized (mMixes) {
+ removeMixes(new ArrayList(mMixes));
}
} finally {
Binder.restoreCallingIdentity(identity);
@@ -14767,20 +14835,16 @@
int addMixes(@NonNull ArrayList<AudioMix> mixes) {
synchronized (mMixes) {
- if (android.media.audiopolicy.Flags.audioMixOwnership()) {
- for (AudioMix mix : mixes) {
- setMixRegistration(mix);
- mix.setVirtualDeviceId(mAttributionSource.getDeviceId());
- }
-
- int result = mAudioSystem.registerPolicyMixes(mixes, true);
- if (result == AudioSystem.SUCCESS) {
- this.add(mixes);
- }
- return result;
+ for (AudioMix mix : mixes) {
+ setMixRegistration(mix);
+ mix.setVirtualDeviceId(mAttributionSource.getDeviceId());
}
- this.add(mixes);
- return mAudioSystem.registerPolicyMixes(mixes, true);
+
+ int result = mAudioSystem.registerPolicyMixes(mixes, true);
+ if (result == AudioSystem.SUCCESS) {
+ this.add(mixes);
+ }
+ return result;
}
}
diff --git a/services/core/java/com/android/server/audio/AudioServiceEvents.java b/services/core/java/com/android/server/audio/AudioServiceEvents.java
index 1b5f0e5..9076fa8 100644
--- a/services/core/java/com/android/server/audio/AudioServiceEvents.java
+++ b/services/core/java/com/android/server/audio/AudioServiceEvents.java
@@ -174,16 +174,22 @@
}
static final class DeviceVolumeEvent extends EventLogger.Event {
+ static final int DEV_VOL_SET_DEVICE_VOLUME = 0;
+ static final int DEV_VOL_NOTIFY_ABSOLUTE_VOLUME_CHANGED = 1;
+
+ final int mEvent;
final int mStream;
final int mVolIndex;
final String mDeviceNativeType;
final String mDeviceAddress;
final String mCaller;
- final int mDeviceForStream;
- final boolean mSkipped;
+ int mDeviceForStream;
+ boolean mSkipped;
+ boolean mIsCurDevice;
DeviceVolumeEvent(int streamType, int index, @NonNull AudioDeviceAttributes device,
int deviceForStream, String callingPackage, boolean skipped) {
+ mEvent = DEV_VOL_SET_DEVICE_VOLUME;
mStream = streamType;
mVolIndex = index;
mDeviceNativeType = "0x" + Integer.toHexString(device.getInternalType());
@@ -203,20 +209,57 @@
.record();
}
+ DeviceVolumeEvent(int streamType, int index, @NonNull AudioDeviceAttributes device,
+ String callingPackage, boolean isCurDevice) {
+ mEvent = DEV_VOL_NOTIFY_ABSOLUTE_VOLUME_CHANGED;
+ mStream = streamType;
+ mVolIndex = index;
+ mDeviceNativeType = "0x" + Integer.toHexString(device.getInternalType());
+ mDeviceAddress = device.getAddress();
+ mCaller = callingPackage;
+ mIsCurDevice = isCurDevice;
+ // log metrics
+ new MediaMetrics.Item(MediaMetrics.Name.AUDIO_VOLUME_EVENT)
+ .set(MediaMetrics.Property.EVENT, "notifyAbsoluteVolumeChanged")
+ .set(MediaMetrics.Property.STREAM_TYPE,
+ AudioSystem.streamToString(mStream))
+ .set(MediaMetrics.Property.INDEX, mVolIndex)
+ .set(MediaMetrics.Property.DEVICE, mDeviceNativeType)
+ .set(MediaMetrics.Property.ADDRESS, mDeviceAddress)
+ .set(MediaMetrics.Property.CALLING_PACKAGE, mCaller)
+ .record();
+ }
+
@Override
public String eventToString() {
- final StringBuilder sb = new StringBuilder("setDeviceVolume(stream:")
- .append(AudioSystem.streamToString(mStream))
- .append(" index:").append(mVolIndex)
- .append(" device:").append(mDeviceNativeType)
- .append(" addr:").append(mDeviceAddress)
- .append(") from ").append(mCaller);
- if (mSkipped) {
- sb.append(" skipped [device in use]");
- } else {
- sb.append(" currDevForStream:Ox").append(Integer.toHexString(mDeviceForStream));
+ switch(mEvent) {
+ case DEV_VOL_SET_DEVICE_VOLUME: {
+ final StringBuilder sb = new StringBuilder("setDeviceVolume")
+ .append("(stream:").append(AudioSystem.streamToString(mStream))
+ .append(" index:").append(mVolIndex)
+ .append(" device:").append(mDeviceNativeType)
+ .append(" addr:").append(mDeviceAddress)
+ .append(") from ").append(mCaller);
+ if (mSkipped) {
+ sb.append(" skipped [device in use]");
+ } else {
+ sb.append(" currDevForStream:Ox").append(
+ Integer.toHexString(mDeviceForStream));
+ }
+ return sb.toString();
+ }
+ case DEV_VOL_NOTIFY_ABSOLUTE_VOLUME_CHANGED: {
+ final StringBuilder sb = new StringBuilder("notifyAbsoluteVolumeChanged")
+ .append("(stream:").append(AudioSystem.streamToString(mStream))
+ .append(" index:").append(mVolIndex)
+ .append(" device:").append(mDeviceNativeType)
+ .append(" addr:").append(mDeviceAddress)
+ .append("[").append(mIsCurDevice ? "curr dev]" : "not curr dev]")
+ .append(") from ").append(mCaller);
+ return sb.toString();
+ }
+ default: return "wrong event";
}
- return sb.toString();
}
}
diff --git a/services/core/java/com/android/server/biometrics/AuthSession.java b/services/core/java/com/android/server/biometrics/AuthSession.java
index 6eade1c..4033718 100644
--- a/services/core/java/com/android/server/biometrics/AuthSession.java
+++ b/services/core/java/com/android/server/biometrics/AuthSession.java
@@ -438,7 +438,8 @@
*/
boolean onErrorReceived(int sensorId, int cookie, @BiometricConstants.Errors int error,
int vendorCode) throws RemoteException {
- Slog.d(TAG, "onErrorReceived sensor: " + sensorId + " error: " + error);
+ Slog.d(TAG, "onErrorReceived sensor: " + sensorId + " error: " + error
+ + " state: " + mState);
if (!containsCookie(cookie)) {
Slog.e(TAG, "Unknown/expired cookie: " + cookie);
@@ -510,6 +511,7 @@
mState = STATE_SHOWING_DEVICE_CREDENTIAL;
mStatusBarService.onBiometricError(modality, error, vendorCode);
} else if (error == BiometricConstants.BIOMETRIC_ERROR_CANCELED) {
+ cancelAllSensors();
mStatusBarService.hideAuthenticationDialog(mRequestId);
// TODO: If multiple authenticators are simultaneously running, this will
// need to be modified. Send the error to the client here, instead of doing
diff --git a/services/core/java/com/android/server/biometrics/Utils.java b/services/core/java/com/android/server/biometrics/Utils.java
index c1f8e2e..c820303 100644
--- a/services/core/java/com/android/server/biometrics/Utils.java
+++ b/services/core/java/com/android/server/biometrics/Utils.java
@@ -617,20 +617,31 @@
/**
* Checks if a client package is running in the background.
*
+ * @param activityTaskManager Task manager which provides the list of clients running.
* @param clientPackage The name of the package to be checked.
* @return Whether the client package is running in background
*/
- public static boolean isBackground(String clientPackage) {
+ public static boolean isBackground(ActivityTaskManager activityTaskManager,
+ String clientPackage) {
Slog.v(TAG, "Checking if the authenticating is in background,"
+ " clientPackage:" + clientPackage);
final List<ActivityManager.RunningTaskInfo> tasks =
- ActivityTaskManager.getInstance().getTasks(Integer.MAX_VALUE);
+ activityTaskManager.getTasks(Integer.MAX_VALUE);
if (tasks == null || tasks.isEmpty()) {
Slog.d(TAG, "No running tasks reported");
return true;
}
+ //Allow auth for top activity even if it is not visible
+ final ActivityManager.RunningTaskInfo topTaskInfo = tasks.getFirst();
+ if (topTaskInfo != null && topTaskInfo.topActivity != null) {
+ final String topPackage = topTaskInfo.topActivity.getPackageName();
+ if (topPackage.contentEquals(clientPackage)) {
+ return false;
+ }
+ }
+
for (ActivityManager.RunningTaskInfo taskInfo : tasks) {
final ComponentName topActivity = taskInfo.topActivity;
if (topActivity != null) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
index 8a98585..7da25d3 100644
--- a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
@@ -208,7 +208,7 @@
if (!mAllowBackgroundAuthentication && authenticated
&& !Utils.isKeyguard(getContext(), getOwnerString())
&& !Utils.isSystem(getContext(), getOwnerString())) {
- isBackgroundAuth = Utils.isBackground(getOwnerString());
+ isBackgroundAuth = Utils.isBackground(mActivityTaskManager, getOwnerString());
}
// Fail authentication if we can't confirm the client activity is on top.
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
index 6cce722..8b2ccf2 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
@@ -16,7 +16,6 @@
package com.android.server.biometrics.sensors.face.aidl;
-import static android.adaptiveauth.Flags.reportBiometricAuthAttempts;
import static android.hardware.biometrics.BiometricFaceConstants.FACE_ACQUIRED_NOT_DETECTED;
import static android.hardware.biometrics.BiometricFaceConstants.FACE_ACQUIRED_SENSOR_DIRTY;
import static android.hardware.biometrics.BiometricFaceConstants.FACE_ACQUIRED_UNKNOWN;
@@ -270,18 +269,16 @@
0 /* vendorError */,
getTargetUserId()));
- if (reportBiometricAuthAttempts()) {
- if (authenticated) {
- mAuthenticationStateListeners.onAuthenticationSucceeded(
- new AuthenticationSucceededInfo.Builder(BiometricSourceType.FACE,
- getRequestReason(), mIsStrongBiometric, getTargetUserId()).build()
- );
- } else {
- mAuthenticationStateListeners.onAuthenticationFailed(
- new AuthenticationFailedInfo.Builder(BiometricSourceType.FACE,
- getRequestReason(), getTargetUserId()).build()
- );
- }
+ if (authenticated) {
+ mAuthenticationStateListeners.onAuthenticationSucceeded(
+ new AuthenticationSucceededInfo.Builder(BiometricSourceType.FACE,
+ getRequestReason(), mIsStrongBiometric, getTargetUserId()).build()
+ );
+ } else {
+ mAuthenticationStateListeners.onAuthenticationFailed(
+ new AuthenticationFailedInfo.Builder(BiometricSourceType.FACE,
+ getRequestReason(), getTargetUserId()).build()
+ );
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
index 944e85c..47e7a6e 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
@@ -157,8 +157,8 @@
continue; // Keyguard is always allowed
}
- if (Utils.isBackground(client.getOwnerString())
- && !client.isAlreadyDone()) {
+ if (Utils.isBackground(ActivityTaskManager.getInstance(),
+ client.getOwnerString()) && !client.isAlreadyDone()) {
Slog.e(getTag(), "Stopping background authentication,"
+ " currentClient: " + client);
mFaceSensors.valueAt(i).getScheduler().cancelAuthenticationOrDetection(
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
index dee4b4f..cd864eb 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
@@ -16,7 +16,6 @@
package com.android.server.biometrics.sensors.fingerprint.aidl;
-import static android.adaptiveauth.Flags.reportBiometricAuthAttempts;
import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_VENDOR;
import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_VENDOR_BASE;
import static android.hardware.fingerprint.FingerprintManager.getAcquiredString;
@@ -182,23 +181,19 @@
if (authenticated) {
mState = STATE_STOPPED;
mSensorOverlays.hide(getSensorId());
- if (reportBiometricAuthAttempts()) {
- mAuthenticationStateListeners.onAuthenticationSucceeded(
- new AuthenticationSucceededInfo.Builder(BiometricSourceType.FINGERPRINT,
- getRequestReason(), mIsStrongBiometric, getTargetUserId()).build()
- );
- }
+ mAuthenticationStateListeners.onAuthenticationSucceeded(
+ new AuthenticationSucceededInfo.Builder(BiometricSourceType.FINGERPRINT,
+ getRequestReason(), mIsStrongBiometric, getTargetUserId()).build()
+ );
mAuthenticationStateListeners.onAuthenticationStopped(new AuthenticationStoppedInfo
.Builder(BiometricSourceType.FINGERPRINT, getRequestReason()).build()
);
} else {
mState = STATE_STARTED_PAUSED_ATTEMPTED;
- if (reportBiometricAuthAttempts()) {
- mAuthenticationStateListeners.onAuthenticationFailed(new AuthenticationFailedInfo
- .Builder(BiometricSourceType.FINGERPRINT, getRequestReason(),
- getTargetUserId()).build()
- );
- }
+ mAuthenticationStateListeners.onAuthenticationFailed(new AuthenticationFailedInfo
+ .Builder(BiometricSourceType.FINGERPRINT, getRequestReason(),
+ getTargetUserId()).build()
+ );
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
index c18d925..84d2a46 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
@@ -153,8 +153,8 @@
continue; // Keyguard is always allowed
}
- if (Utils.isBackground(client.getOwnerString())
- && !client.isAlreadyDone()) {
+ if (Utils.isBackground(ActivityTaskManager.getInstance(),
+ client.getOwnerString()) && !client.isAlreadyDone()) {
Slog.e(getTag(), "Stopping background authentication,"
+ " currentClient: " + client);
mFingerprintSensors.valueAt(i).getScheduler()
diff --git a/services/core/java/com/android/server/clipboard/OWNERS b/services/core/java/com/android/server/clipboard/OWNERS
index 4ca4b80..8586e05 100644
--- a/services/core/java/com/android/server/clipboard/OWNERS
+++ b/services/core/java/com/android/server/clipboard/OWNERS
@@ -1,3 +1,7 @@
+# Bug component: 137825
+
+include platform/frameworks/base:/core/java/android/permission/OWNERS
+
per-file EmulatorClipboardMonitor.java = bohu@google.com,rkir@google.com
olilan@google.com
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index 2d387ea..af80bab 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -244,6 +244,7 @@
private final NotificationManager mNotificationMgr;
private final IBatteryStats mBatteryStats;
private JobScheduler mJobScheduler;
+ private volatile boolean mJobSchedulerInitialized;
private SyncStorageEngine mSyncStorageEngine;
@@ -574,10 +575,22 @@
mSyncStorageEngine.setJobAttributionFixed(allSyncsAttributed);
}
- private synchronized void verifyJobScheduler() {
- if (mJobScheduler != null) {
- return;
+ private void verifyJobScheduler() {
+ if (mJobSchedulerInitialized) return;
+ // Initialize JobScheduler
+ synchronized (this) {
+ if (initializeJobScheduler()) {
+ mJobSchedulerInitialized = true;
+ }
}
+ }
+
+ /**
+ * Initialize JobScheduler connection and load initial sync jobs.
+ * @return true if successfully initialized
+ */
+ @GuardedBy("this")
+ private boolean initializeJobScheduler() {
final long token = Binder.clearCallingIdentity();
try {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
@@ -625,6 +638,8 @@
} finally {
Binder.restoreCallingIdentity(token);
}
+
+ return mJobScheduler != null;
}
/**
@@ -712,7 +727,13 @@
mAppCloningDeviceConfigHelper = AppCloningDeviceConfigHelper.getInstance(context);
IntentFilter intentFilter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
- context.registerReceiver(mConnectivityIntentReceiver, intentFilter);
+
+ if (com.android.server.am.Flags.syncmanagerOffMainThread()) {
+ context.registerReceiver(mConnectivityIntentReceiver, intentFilter, null,
+ mSyncHandler);
+ } else {
+ context.registerReceiver(mConnectivityIntentReceiver, intentFilter);
+ }
intentFilter = new IntentFilter(Intent.ACTION_SHUTDOWN);
intentFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
@@ -723,7 +744,8 @@
intentFilter.addAction(Intent.ACTION_USER_UNLOCKED);
intentFilter.addAction(Intent.ACTION_USER_STOPPED);
mContext.registerReceiverAsUser(
- mUserIntentReceiver, UserHandle.ALL, intentFilter, null, null);
+ mUserIntentReceiver, UserHandle.ALL, intentFilter, null,
+ com.android.server.am.Flags.syncmanagerOffMainThread() ? mSyncHandler : null);
mPackageMonitor = new PackageMonitorImpl();
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index c41b8db..f6b4bc7f 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -572,14 +572,13 @@
public boolean setBrightnessConfiguration(BrightnessConfiguration configuration,
boolean shouldResetShortTermModel) {
- if (mBrightnessMappingStrategyMap.get(AUTO_BRIGHTNESS_MODE_DEFAULT)
- .setBrightnessConfiguration(configuration)) {
- if (!isInIdleMode() && shouldResetShortTermModel) {
- resetShortTermModel();
- }
- return true;
+ boolean changed = mBrightnessMappingStrategyMap.get(AUTO_BRIGHTNESS_MODE_DEFAULT)
+ .setBrightnessConfiguration(configuration);
+ if (!isInIdleMode() && shouldResetShortTermModel) {
+ resetShortTermModel();
+ changed = true;
}
- return false;
+ return changed;
}
/**
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 8943691..ff9d20d 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -171,7 +171,6 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.display.BrightnessSynchronizer;
-import com.android.internal.foldables.FoldGracePeriodProvider;
import com.android.internal.foldables.FoldLockSettingAvailabilityProvider;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.ArrayUtils;
@@ -646,7 +645,7 @@
mUiHandler = UiThread.getHandler();
mDisplayDeviceRepo = new DisplayDeviceRepository(mSyncRoot, mPersistentDataStore);
mLogicalDisplayMapper = new LogicalDisplayMapper(mContext,
- foldSettingProvider, new FoldGracePeriodProvider(),
+ foldSettingProvider,
mDisplayDeviceRepo, new LogicalDisplayListener(), mSyncRoot, mHandler, mFlags);
mDisplayModeDirector = new DisplayModeDirector(
context, mHandler, mFlags, mDisplayDeviceConfigProvider);
@@ -2082,9 +2081,15 @@
: projection.getLaunchCookie().binder;
int taskId = projection.getTaskId();
if (taskWindowContainerToken == null) {
- // Record a particular display.
- session = ContentRecordingSession.createDisplaySession(
- virtualDisplayConfig.getDisplayIdToMirror());
+ if (projection.isRecordingOverlay()) {
+ // Record an overlay session.
+ session = ContentRecordingSession.createOverlaySession(
+ virtualDisplayConfig.getDisplayIdToMirror(), callingUid);
+ } else {
+ // Record a particular display.
+ session = ContentRecordingSession.createDisplaySession(
+ virtualDisplayConfig.getDisplayIdToMirror());
+ }
} else {
// Record a single task indicated by the launch cookie.
session = ContentRecordingSession.createTaskSession(
@@ -3504,6 +3509,7 @@
viewport.valid = true;
viewport.displayId = displayId;
viewport.isActive = Display.isActiveState(info.state);
+ viewport.densityDpi = info.densityDpi;
}
private void updateViewportPowerStateLocked(LogicalDisplay display) {
diff --git a/services/core/java/com/android/server/display/LogicalDisplayMapper.java b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
index 35a41f7..40838e6 100644
--- a/services/core/java/com/android/server/display/LogicalDisplayMapper.java
+++ b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
@@ -50,7 +50,6 @@
import android.view.DisplayInfo;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.foldables.FoldGracePeriodProvider;
import com.android.server.LocalServices;
import com.android.server.display.feature.DisplayManagerFlags;
import com.android.server.display.layout.DisplayIdProducer;
@@ -172,7 +171,6 @@
private final DisplayManagerService.SyncRoot mSyncRoot;
private final LogicalDisplayMapperHandler mHandler;
private final FoldSettingProvider mFoldSettingProvider;
- private final FoldGracePeriodProvider mFoldGracePeriodProvider;
private final PowerManager mPowerManager;
/**
@@ -223,11 +221,10 @@
private final DisplayGroupAllocator mDisplayGroupAllocator;
LogicalDisplayMapper(@NonNull Context context, FoldSettingProvider foldSettingProvider,
- FoldGracePeriodProvider foldGracePeriodProvider,
@NonNull DisplayDeviceRepository repo,
@NonNull Listener listener, @NonNull DisplayManagerService.SyncRoot syncRoot,
@NonNull Handler handler, DisplayManagerFlags flags) {
- this(context, foldSettingProvider, foldGracePeriodProvider, repo, listener, syncRoot,
+ this(context, foldSettingProvider, repo, listener, syncRoot,
handler,
new DeviceStateToLayoutMap((isDefault) -> isDefault ? DEFAULT_DISPLAY
: sNextNonDefaultDisplayId++, flags), flags,
@@ -235,7 +232,6 @@
}
LogicalDisplayMapper(@NonNull Context context, FoldSettingProvider foldSettingProvider,
- FoldGracePeriodProvider foldGracePeriodProvider,
@NonNull DisplayDeviceRepository repo,
@NonNull Listener listener, @NonNull DisplayManagerService.SyncRoot syncRoot,
@NonNull Handler handler, @NonNull DeviceStateToLayoutMap deviceStateToLayoutMap,
@@ -249,7 +245,6 @@
mDisplayDeviceRepo = repo;
mListener = listener;
mFoldSettingProvider = foldSettingProvider;
- mFoldGracePeriodProvider = foldGracePeriodProvider;
mSingleDisplayDemoMode = SystemProperties.getBoolean("persist.demo.singledisplay", false);
mSupportsConcurrentInternalDisplays = context.getResources().getBoolean(
com.android.internal.R.bool.config_supportsConcurrentInternalDisplays);
@@ -1394,9 +1389,8 @@
* the value of `Continue using app on fold` setting
*/
private boolean shouldStayAwakeOnFold() {
- return mFoldSettingProvider.shouldStayAwakeOnFold() || (
- mFoldSettingProvider.shouldSelectiveStayAwakeOnFold()
- && mFoldGracePeriodProvider.isEnabled());
+ return mFoldSettingProvider.shouldStayAwakeOnFold()
+ || mFoldSettingProvider.shouldSelectiveStayAwakeOnFold();
}
private String displayEventToString(int msg) {
diff --git a/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java b/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java
index ef936de..8911d181 100644
--- a/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java
+++ b/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java
@@ -178,7 +178,8 @@
int targetDisplayState = strategySelectionRequest.getTargetDisplayState();
DisplayPowerRequest displayPowerRequest = strategySelectionRequest
.getDisplayPowerRequest();
- setAllowAutoBrightnessWhileDozing(strategySelectionRequest.getDisplayOffloadSession());
+ setAllowAutoBrightnessWhileDozing(strategySelectionRequest.getDisplayOffloadSession(),
+ displayPowerRequest.useNormalBrightnessForDoze);
if (targetDisplayState == Display.STATE_OFF) {
displayBrightnessStrategy = mScreenOffBrightnessStrategy;
} else if (shouldUseDozeBrightnessStrategy(displayPowerRequest, targetDisplayState)) {
@@ -307,9 +308,10 @@
@VisibleForTesting
void setAllowAutoBrightnessWhileDozing(
- DisplayManagerInternal.DisplayOffloadSession displayOffloadSession) {
+ DisplayManagerInternal.DisplayOffloadSession displayOffloadSession,
+ boolean useNormalBrightnessForDoze) {
mAllowAutoBrightnessWhileDozing = mAllowAutoBrightnessWhileDozingConfig;
- if (mDisplayManagerFlags.isDisplayOffloadEnabled()
+ if (!useNormalBrightnessForDoze && mDisplayManagerFlags.isDisplayOffloadEnabled()
&& displayOffloadSession != null) {
mAllowAutoBrightnessWhileDozing &= displayOffloadSession.allowAutoBrightnessInDoze();
}
diff --git a/services/core/java/com/android/server/flags/services.aconfig b/services/core/java/com/android/server/flags/services.aconfig
index 7e5c1bc..ca1c142 100644
--- a/services/core/java/com/android/server/flags/services.aconfig
+++ b/services/core/java/com/android/server/flags/services.aconfig
@@ -2,13 +2,6 @@
container: "system"
flag {
- namespace: "wear_frameworks"
- name: "enable_odp_feature_guard"
- description: "Enable guard based on system feature to prevent OnDevicePersonalization service from starting on form factors."
- bug: "322249125"
-}
-
-flag {
namespace: "input"
name: "new_bugreport_keyboard_shortcut"
description: "Enable META+CTRL+BACKSPACE keyboard shortcut for taking a bug report"
diff --git a/services/core/java/com/android/server/grammaticalinflection/OWNERS b/services/core/java/com/android/server/grammaticalinflection/OWNERS
index 41d079e..5c121ff 100644
--- a/services/core/java/com/android/server/grammaticalinflection/OWNERS
+++ b/services/core/java/com/android/server/grammaticalinflection/OWNERS
@@ -1,5 +1,4 @@
# Bug template url: https://b.corp.google.com/issues/new?component=1082762&template=1601534
allenwtsu@google.com
-goldmanj@google.com
calvinpan@google.com
zoeychen@google.com
diff --git a/services/core/java/com/android/server/health/HealthServiceWrapperAidl.java b/services/core/java/com/android/server/health/HealthServiceWrapperAidl.java
index eb5eeaf..db4a717 100644
--- a/services/core/java/com/android/server/health/HealthServiceWrapperAidl.java
+++ b/services/core/java/com/android/server/health/HealthServiceWrapperAidl.java
@@ -16,8 +16,6 @@
package com.android.server.health;
-import static android.os.Flags.batteryPartStatusApi;
-
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.hardware.health.BatteryHealthData;
@@ -157,16 +155,12 @@
prop.setLong(healthData.batteryStateOfHealth);
break;
case BatteryManager.BATTERY_PROPERTY_SERIAL_NUMBER:
- if (batteryPartStatusApi()) {
- healthData = service.getBatteryHealthData();
- prop.setString(healthData.batterySerialNumber);
- }
+ healthData = service.getBatteryHealthData();
+ prop.setString(healthData.batterySerialNumber);
break;
case BatteryManager.BATTERY_PROPERTY_PART_STATUS:
- if (batteryPartStatusApi()) {
- healthData = service.getBatteryHealthData();
- prop.setLong(healthData.batteryPartStatus);
- }
+ healthData = service.getBatteryHealthData();
+ prop.setLong(healthData.batteryPartStatus);
break;
}
} catch (UnsupportedOperationException e) {
diff --git a/services/core/java/com/android/server/infra/AbstractMasterSystemService.java b/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
index 8ab32f5..88d0273 100644
--- a/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
+++ b/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
@@ -29,6 +29,9 @@
import android.net.Uri;
import android.os.Binder;
import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Process;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
@@ -194,6 +197,10 @@
private final MyPackageMonitor mPackageMonitor;
+ private static HandlerThread sPackageMonitorHandlerThread;
+
+ private static final Object sPackageMonitorLock = new Object();
+
/**
* Default constructor.
*
@@ -290,10 +297,27 @@
}
});
}
+
mPackageMonitor = new MyPackageMonitor(/* supportsPackageRestartQuery */ true);
startTrackingPackageChanges();
}
+ private void startTrackingPackageChanges() {
+ synchronized (sPackageMonitorLock) {
+ if (sPackageMonitorHandlerThread == null) {
+ sPackageMonitorHandlerThread = new HandlerThread(mTag + "PkgMonitorThread",
+ Process.THREAD_PRIORITY_BACKGROUND);
+ // Start a new dedicated background thread for listening to package changes
+ try {
+ sPackageMonitorHandlerThread.start();
+ } catch (IllegalThreadStateException e) {
+ Slog.w(mTag, "Thread is already started: " + e.getMessage());
+ }
+ }
+ }
+ mPackageMonitor.startTrackingPackageChanges(sPackageMonitorHandlerThread.getLooper());
+ }
+
@Override // from SystemService
public void onBootPhase(int phase) {
if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
@@ -993,6 +1017,11 @@
super(supportsPackageRestartQuery);
}
+ public void startTrackingPackageChanges(Looper looper) {
+ // package changes
+ this.register(getContext(), looper, UserHandle.ALL, true);
+ }
+
@Override
public void onPackageUpdateStarted(@NonNull String packageName, int uid) {
if (verbose) Slog.v(mTag, "onPackageUpdateStarted(): " + packageName);
@@ -1242,11 +1271,6 @@
}
}
- private void startTrackingPackageChanges() {
- // package changes
- mPackageMonitor.register(getContext(), null, UserHandle.ALL, true);
- }
-
@GuardedBy("mLock")
@SuppressWarnings("GuardedBy") // ErrorProne requires this.mLock for
// handleServiceRemovedMultiModeLocked which is the same
diff --git a/services/core/java/com/android/server/input/InputManagerInternal.java b/services/core/java/com/android/server/input/InputManagerInternal.java
index 2660db4..e4d8c55 100644
--- a/services/core/java/com/android/server/input/InputManagerInternal.java
+++ b/services/core/java/com/android/server/input/InputManagerInternal.java
@@ -123,18 +123,6 @@
public abstract PointF getCursorPosition(int displayId);
/**
- * Set whether all pointer scaling, including linear scaling based on the
- * user's pointer speed setting, should be enabled or disabled for mice.
- *
- * Note that this only affects pointer movements from mice (that is, pointing devices which send
- * relative motions, including trackballs and pointing sticks), not from other pointer devices
- * such as touchpads and styluses.
- *
- * Scaling is enabled by default on new displays until it is explicitly disabled.
- */
- public abstract void setMouseScalingEnabled(boolean enabled, int displayId);
-
- /**
* Sets the eligibility of windows on a given display for pointer capture. If a display is
* marked ineligible, requests to enable pointer capture for windows on that display will be
* ignored.
@@ -158,13 +146,6 @@
*/
public abstract void notifyInputMethodConnectionActive(boolean connectionIsActive);
- /**
- * Notify user id changes to input.
- *
- * TODO(b/362473586): Cleanup after input shifts to Lifecycle with user change callbacks
- */
- public abstract void setCurrentUser(@UserIdInt int newUserId);
-
/** Callback interface for notifications relating to the lid switch. */
public interface LidSwitchCallback {
/**
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 96b1d89..fa06356 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -34,8 +34,6 @@
import android.annotation.EnforcePermission;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.PermissionManuallyEnforced;
-import android.annotation.RequiresPermission;
import android.annotation.SuppressLint;
import android.annotation.UserIdInt;
import android.app.ActivityManagerInternal;
@@ -47,7 +45,6 @@
import android.content.IntentFilter;
import android.content.PermissionChecker;
import android.content.pm.PackageManager;
-import android.content.pm.PackageManagerInternal;
import android.graphics.PixelFormat;
import android.graphics.PointF;
import android.hardware.SensorPrivacyManager;
@@ -1428,7 +1425,7 @@
mNative.setPointerSpeed(speed);
}
- private void setMouseScalingEnabled(boolean enabled, int displayId) {
+ private void setMouseScalingEnabledInternal(boolean enabled, int displayId) {
updateAdditionalDisplayInputProperties(displayId,
properties -> properties.mouseScalingEnabled = enabled);
}
@@ -3120,57 +3117,29 @@
lockedModifierState);
}
- /**
- * Enforces the caller contains the necessary permission to manage key gestures.
- */
- @RequiresPermission(Manifest.permission.MANAGE_KEY_GESTURES)
- private void enforceManageKeyGesturePermission() {
- // TODO(b/361567988): Use @EnforcePermission to enforce permission once flag guarding the
- // permission is rolled out
- String systemUIPackage = mContext.getString(R.string.config_systemUi);
- PackageManagerInternal pm = LocalServices.getService(PackageManagerInternal.class);
- if (pm != null) {
- int systemUIAppId = UserHandle.getAppId(
- pm.getPackageUid(systemUIPackage, PackageManager.MATCH_SYSTEM_ONLY,
- UserHandle.USER_SYSTEM));
- if (UserHandle.getCallingAppId() == systemUIAppId) {
- return;
- }
- }
- if (mContext.checkCallingOrSelfPermission(
- Manifest.permission.MANAGE_KEY_GESTURES) == PackageManager.PERMISSION_GRANTED) {
- return;
- }
-
- String message = "Managing Key Gestures requires the following permission: "
- + Manifest.permission.MANAGE_KEY_GESTURES;
- throw new SecurityException(message);
- }
-
-
@Override
- @PermissionManuallyEnforced
+ @EnforcePermission(Manifest.permission.MANAGE_KEY_GESTURES)
public void registerKeyGestureEventListener(@NonNull IKeyGestureEventListener listener) {
- enforceManageKeyGesturePermission();
+ super.registerKeyGestureEventListener_enforcePermission();
Objects.requireNonNull(listener);
mKeyGestureController.registerKeyGestureEventListener(listener, Binder.getCallingPid());
}
@Override
- @PermissionManuallyEnforced
+ @EnforcePermission(Manifest.permission.MANAGE_KEY_GESTURES)
public void unregisterKeyGestureEventListener(@NonNull IKeyGestureEventListener listener) {
- enforceManageKeyGesturePermission();
+ super.unregisterKeyGestureEventListener_enforcePermission();
Objects.requireNonNull(listener);
mKeyGestureController.unregisterKeyGestureEventListener(listener, Binder.getCallingPid());
}
@Override
- @PermissionManuallyEnforced
+ @EnforcePermission(Manifest.permission.MANAGE_KEY_GESTURES)
public void registerKeyGestureHandler(int[] keyGesturesToHandle,
@NonNull IKeyGestureHandler handler) {
- enforceManageKeyGesturePermission();
+ super.registerKeyGestureHandler_enforcePermission();
Objects.requireNonNull(handler);
Objects.requireNonNull(keyGesturesToHandle);
@@ -3179,48 +3148,48 @@
}
@Override
- @PermissionManuallyEnforced
+ @EnforcePermission(Manifest.permission.MANAGE_KEY_GESTURES)
public void unregisterKeyGestureHandler(@NonNull IKeyGestureHandler handler) {
- enforceManageKeyGesturePermission();
+ super.unregisterKeyGestureHandler_enforcePermission();
Objects.requireNonNull(handler);
mKeyGestureController.unregisterKeyGestureHandler(handler, Binder.getCallingPid());
}
@Override
- @PermissionManuallyEnforced
+ @EnforcePermission(Manifest.permission.MANAGE_KEY_GESTURES)
public AidlInputGestureData getInputGesture(@UserIdInt int userId,
@NonNull AidlInputGestureData.Trigger trigger) {
- enforceManageKeyGesturePermission();
+ super.getInputGesture_enforcePermission();
Objects.requireNonNull(trigger);
return mKeyGestureController.getInputGesture(userId, trigger);
}
@Override
- @PermissionManuallyEnforced
+ @EnforcePermission(Manifest.permission.MANAGE_KEY_GESTURES)
public int addCustomInputGesture(@UserIdInt int userId,
@NonNull AidlInputGestureData inputGestureData) {
- enforceManageKeyGesturePermission();
+ super.addCustomInputGesture_enforcePermission();
Objects.requireNonNull(inputGestureData);
return mKeyGestureController.addCustomInputGesture(userId, inputGestureData);
}
@Override
- @PermissionManuallyEnforced
+ @EnforcePermission(Manifest.permission.MANAGE_KEY_GESTURES)
public int removeCustomInputGesture(@UserIdInt int userId,
@NonNull AidlInputGestureData inputGestureData) {
- enforceManageKeyGesturePermission();
+ super.removeCustomInputGesture_enforcePermission();
Objects.requireNonNull(inputGestureData);
return mKeyGestureController.removeCustomInputGesture(userId, inputGestureData);
}
@Override
- @PermissionManuallyEnforced
+ @EnforcePermission(Manifest.permission.MANAGE_KEY_GESTURES)
public void removeAllCustomInputGestures(@UserIdInt int userId, int tag) {
- enforceManageKeyGesturePermission();
+ super.removeAllCustomInputGestures_enforcePermission();
mKeyGestureController.removeAllCustomInputGestures(userId, InputGestureData.Filter.of(tag));
}
@@ -3241,6 +3210,19 @@
mNative.resetLockedModifierState();
}
+ @Override // Binder call
+ public void setMouseScalingEnabled(boolean enabled, int displayId) {
+ if (!checkCallingPermission(
+ Manifest.permission.SET_POINTER_SPEED,
+ "setMouseScalingEnabled()",
+ true /*checkInstrumentationSource*/)) {
+ throw new SecurityException(
+ "The SET_POINTER_SPEED permission is required to override mouse scaling.");
+ }
+
+ setMouseScalingEnabledInternal(enabled, displayId);
+ }
+
private void onUserSwitching(@NonNull SystemService.TargetUser from,
@NonNull SystemService.TargetUser to) {
if (DEBUG) {
@@ -3719,11 +3701,6 @@
}
@Override
- public void setMouseScalingEnabled(boolean enabled, int displayId) {
- InputManagerService.this.setMouseScalingEnabled(enabled, displayId);
- }
-
- @Override
public void setDisplayEligibilityForPointerCapture(int displayId, boolean isEligible) {
InputManagerService.this.setDisplayEligibilityForPointerCapture(displayId, isEligible);
}
@@ -3835,11 +3812,6 @@
}
@Override
- public void setCurrentUser(@UserIdInt int newUserId) {
- mHandler.obtainMessage(MSG_CURRENT_USER_CHANGED, newUserId).sendToTarget();
- }
-
- @Override
public void registerShortcutKey(long shortcutCode, IShortcutService shortcutKeyReceiver)
throws RemoteException {
mKeyGestureController.registerShortcutKey(shortcutCode, shortcutKeyReceiver);
diff --git a/services/core/java/com/android/server/policy/KeyCombinationManager.java b/services/core/java/com/android/server/input/KeyCombinationManager.java
similarity index 99%
rename from services/core/java/com/android/server/policy/KeyCombinationManager.java
rename to services/core/java/com/android/server/input/KeyCombinationManager.java
index 1b98dd1..874ba20 100644
--- a/services/core/java/com/android/server/policy/KeyCombinationManager.java
+++ b/services/core/java/com/android/server/input/KeyCombinationManager.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.server.policy;
+package com.android.server.input;
import static android.view.KeyEvent.KEYCODE_POWER;
@@ -33,7 +33,7 @@
/**
* Handles a mapping of two keys combination.
*/
-public class KeyCombinationManager {
+final class KeyCombinationManager {
private static final String TAG = "KeyCombinationManager";
// Store the received down time of keycode.
diff --git a/services/core/java/com/android/server/input/KeyGestureController.java b/services/core/java/com/android/server/input/KeyGestureController.java
index b069a87..8da0e46 100644
--- a/services/core/java/com/android/server/input/KeyGestureController.java
+++ b/services/core/java/com/android/server/input/KeyGestureController.java
@@ -43,7 +43,6 @@
import android.hardware.input.IKeyGestureHandler;
import android.hardware.input.InputGestureData;
import android.hardware.input.InputManager;
-import android.hardware.input.InputSettings;
import android.hardware.input.KeyGestureEvent;
import android.os.Handler;
import android.os.IBinder;
@@ -72,7 +71,6 @@
import com.android.internal.policy.IShortcutService;
import com.android.server.LocalServices;
import com.android.server.pm.UserManagerInternal;
-import com.android.server.policy.KeyCombinationManager;
import org.xmlpull.v1.XmlPullParserException;
@@ -243,9 +241,6 @@
}
private void initKeyCombinationRules() {
- if (!InputSettings.doesKeyGestureEventHandlerSupportMultiKeyGestures()) {
- return;
- }
// TODO(b/358569822): Handle Power, Back key properly since key combination gesture is
// captured here and rest of the Power, Back key behaviors are handled in PWM
final boolean screenshotChordEnabled = mContext.getResources().getBoolean(
@@ -458,31 +453,16 @@
private void initKeyGestures() {
InputManager im = Objects.requireNonNull(mContext.getSystemService(InputManager.class));
im.registerKeyGestureEventHandler(
- List.of(KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT_CHORD),
- (event, focusedToken) -> {
- if (event.getKeyGestureType()
- == KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT_CHORD) {
- if (event.getAction() == KeyGestureEvent.ACTION_GESTURE_START) {
- mHandler.removeMessages(MSG_ACCESSIBILITY_SHORTCUT);
- mHandler.sendMessageDelayed(
- mHandler.obtainMessage(MSG_ACCESSIBILITY_SHORTCUT),
- getAccessibilityShortcutTimeout());
- } else {
- mHandler.removeMessages(MSG_ACCESSIBILITY_SHORTCUT);
- }
- } else {
- Log.w(TAG, "Received a key gesture " + event
- + " that was not registered by this handler");
- }
- });
+ List.of(KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT_CHORD,
+ KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT),
+ new LocalKeyGestureEventHandler());
}
public boolean interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
if (mVisibleBackgroundUsersEnabled && shouldIgnoreKeyEventForVisibleBackgroundUser(event)) {
return false;
}
- if (InputSettings.doesKeyGestureEventHandlerSupportMultiKeyGestures()
- && (event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
+ if ((event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
final boolean interactive = (policyFlags & FLAG_INTERACTIVE) != 0;
final boolean isDefaultDisplayOn = isDefaultDisplayOn();
return mKeyCombinationManager.interceptKey(event, interactive && isDefaultDisplayOn);
@@ -518,18 +498,16 @@
final long keyConsumed = -1;
final long keyNotConsumed = 0;
- if (InputSettings.doesKeyGestureEventHandlerSupportMultiKeyGestures()) {
- if (mKeyCombinationManager.isKeyConsumed(event)) {
- return keyConsumed;
- }
+ if (mKeyCombinationManager.isKeyConsumed(event)) {
+ return keyConsumed;
+ }
- if ((flags & KeyEvent.FLAG_FALLBACK) == 0) {
- final long now = SystemClock.uptimeMillis();
- final long interceptTimeout = mKeyCombinationManager.getKeyInterceptTimeout(
- keyCode);
- if (now < interceptTimeout) {
- return interceptTimeout - now;
- }
+ if ((flags & KeyEvent.FLAG_FALLBACK) == 0) {
+ final long now = SystemClock.uptimeMillis();
+ final long interceptTimeout = mKeyCombinationManager.getKeyInterceptTimeout(
+ keyCode);
+ if (now < interceptTimeout) {
+ return interceptTimeout - now;
}
}
@@ -917,7 +895,9 @@
break;
case KeyEvent.KEYCODE_Z:
if (down && KeyEvent.metaStateHasModifiers(metaState,
- KeyEvent.META_CTRL_ON | KeyEvent.META_ALT_ON)) {
+ KeyEvent.META_CTRL_ON | KeyEvent.META_ALT_ON)
+ && mAccessibilityShortcutController.isAccessibilityShortcutAvailable(
+ mWindowManagerCallbacks.isKeyguardLocked(DEFAULT_DISPLAY))) {
// Intercept the Accessibility keychord (CTRL + ALT + Z) for keyboard users.
handleKeyGesture(deviceId, new int[]{keyCode},
KeyEvent.META_CTRL_ON | KeyEvent.META_ALT_ON,
@@ -1531,4 +1511,33 @@
return new AccessibilityShortcutController(context, handler, UserHandle.USER_SYSTEM);
}
}
+
+ private class LocalKeyGestureEventHandler implements InputManager.KeyGestureEventHandler {
+
+ @Override
+ public void handleKeyGestureEvent(@NonNull KeyGestureEvent event,
+ @Nullable IBinder focusedToken) {
+ final boolean complete = event.getAction() == KeyGestureEvent.ACTION_GESTURE_COMPLETE
+ && !event.isCancelled();
+ final boolean start = event.getAction() == KeyGestureEvent.ACTION_GESTURE_START;
+ switch (event.getKeyGestureType()) {
+ case KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT_CHORD:
+ mHandler.removeMessages(MSG_ACCESSIBILITY_SHORTCUT);
+ if (start) {
+ mHandler.sendMessageDelayed(
+ mHandler.obtainMessage(MSG_ACCESSIBILITY_SHORTCUT),
+ getAccessibilityShortcutTimeout());
+ }
+ break;
+ case KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT:
+ if (complete) {
+ mHandler.sendMessage(mHandler.obtainMessage(MSG_ACCESSIBILITY_SHORTCUT));
+ }
+ break;
+ default:
+ Log.w(TAG, "Received a key gesture " + event
+ + " that was not registered by this handler");
+ }
+ }
+ }
}
diff --git a/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java b/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java
index 1a4ead2..4aa14d9 100644
--- a/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java
+++ b/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java
@@ -18,21 +18,16 @@
import static android.view.inputmethod.ImeTracker.DEBUG_IME_VISIBILITY;
-import static com.android.internal.inputmethod.SoftInputShowHideReason.REMOVE_IME_SCREENSHOT_FROM_IMMS;
-import static com.android.internal.inputmethod.SoftInputShowHideReason.SHOW_IME_SCREENSHOT_FROM_IMMS;
import static com.android.server.EventLogTags.IMF_HIDE_IME;
import static com.android.server.EventLogTags.IMF_SHOW_IME;
import static com.android.server.inputmethod.ImeProtoLogGroup.IME_VISIBILITY_APPLIER_DEBUG;
import static com.android.server.inputmethod.ImeVisibilityStateComputer.STATE_HIDE_IME;
import static com.android.server.inputmethod.ImeVisibilityStateComputer.STATE_HIDE_IME_EXPLICIT;
import static com.android.server.inputmethod.ImeVisibilityStateComputer.STATE_HIDE_IME_NOT_ALWAYS;
-import static com.android.server.inputmethod.ImeVisibilityStateComputer.STATE_REMOVE_IME_SNAPSHOT;
import static com.android.server.inputmethod.ImeVisibilityStateComputer.STATE_SHOW_IME;
import static com.android.server.inputmethod.ImeVisibilityStateComputer.STATE_SHOW_IME_IMPLICIT;
-import static com.android.server.inputmethod.ImeVisibilityStateComputer.STATE_SHOW_IME_SNAPSHOT;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.os.IBinder;
import android.os.ResultReceiver;
@@ -44,6 +39,7 @@
import android.view.inputmethod.InputMethodManager;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.inputmethod.InputMethodDebug;
import com.android.internal.inputmethod.SoftInputShowHideReason;
import com.android.internal.protolog.ProtoLog;
@@ -164,7 +160,7 @@
* @param userId the target user when applying the IME visibility state
*/
@GuardedBy("ImfLock.class")
- void applyImeVisibility(IBinder windowToken, @Nullable ImeTracker.Token statsToken,
+ void applyImeVisibility(IBinder windowToken, @NonNull ImeTracker.Token statsToken,
@ImeVisibilityStateComputer.VisibilityState int state,
@SoftInputShowHideReason int reason, @UserIdInt int userId) {
final var userData = mService.getUserData(userId);
@@ -224,50 +220,65 @@
null /* resultReceiver */, reason, userId);
}
break;
- case STATE_SHOW_IME_SNAPSHOT:
- showImeScreenshot(windowToken, displayIdToShowIme, userId);
- break;
- case STATE_REMOVE_IME_SNAPSHOT:
- removeImeScreenshot(displayIdToShowIme, userId);
- break;
default:
throw new IllegalArgumentException("Invalid IME visibility state: " + state);
}
}
/**
- * Shows the IME screenshot and attach it to the given IME target window.
+ * Applies the IME screenshot visibility on the given IME target window.
*
- * @param imeTarget the token of a window to show the IME screenshot
- * @param displayId the unique id to identify the display
- * @param userId the target user when when showing the IME screenshot
- * @return {@code true} if success, {@code false} otherwise
+ * @param imeTarget the token of the IME target window.
+ * @param show whether to show or remove the screenshot.
+ * @param userId the ID of the user to apply the screenshot visibility for.
*/
@GuardedBy("ImfLock.class")
- boolean showImeScreenshot(@NonNull IBinder imeTarget, int displayId,
- @UserIdInt int userId) {
+ void applyImeScreenshotVisibility(IBinder imeTarget, boolean show, @UserIdInt int userId) {
+ final var userData = mService.getUserData(userId);
+ final var bindingController = userData.mBindingController;
+ final int displayId = bindingController.getDisplayIdToShowIme();
+ if (show) {
+ showImeScreenshot(imeTarget, displayId, userId);
+ } else {
+ removeImeScreenshot(imeTarget, displayId, userId);
+ }
+ }
+
+ /**
+ * Shows the IME screenshot and attaches it to the given IME target window.
+ *
+ * @param imeTarget the token of the IME target window.
+ * @param displayId the ID of the display to show the screenshot on.
+ * @param userId the ID of the user to show the screenshot for.
+ * @return {@code true} if successful, {@code false} otherwise.
+ */
+ @VisibleForTesting
+ @GuardedBy("ImfLock.class")
+ boolean showImeScreenshot(IBinder imeTarget, int displayId, @UserIdInt int userId) {
if (mImeTargetVisibilityPolicy.showImeScreenshot(imeTarget, displayId)) {
mService.onShowHideSoftInputRequested(false /* show */, imeTarget,
- SHOW_IME_SCREENSHOT_FROM_IMMS, null /* statsToken */, userId);
+ SoftInputShowHideReason.SHOW_IME_SCREENSHOT_FROM_IMMS, null /* statsToken */,
+ userId);
return true;
}
return false;
}
/**
- * Removes the IME screenshot on the given display.
+ * Removes the IME screenshot from the given display.
*
- * @param displayId the target display of showing IME screenshot
- * @param userId the target user of showing IME screenshot
- * @return {@code true} if success, {@code false} otherwise
+ * @param imeTarget the token of the IME target window.
+ * @param displayId the ID of the display to remove the screenshot from.
+ * @param userId the ID of the user to remove the screenshot for.
+ * @return {@code true} if successful, {@code false} otherwise.
*/
+ @VisibleForTesting
@GuardedBy("ImfLock.class")
- boolean removeImeScreenshot(int displayId, @UserIdInt int userId) {
- final var userData = mService.getUserData(userId);
+ boolean removeImeScreenshot(IBinder imeTarget, int displayId, @UserIdInt int userId) {
if (mImeTargetVisibilityPolicy.removeImeScreenshot(displayId)) {
- mService.onShowHideSoftInputRequested(false /* show */,
- userData.mImeBindingState.mFocusedWindow,
- REMOVE_IME_SCREENSHOT_FROM_IMMS, null /* statsToken */, userId);
+ mService.onShowHideSoftInputRequested(false /* show */, imeTarget,
+ SoftInputShowHideReason.REMOVE_IME_SCREENSHOT_FROM_IMMS, null /* statsToken */,
+ userId);
return true;
}
return false;
diff --git a/services/core/java/com/android/server/inputmethod/IInputMethodManagerImpl.java b/services/core/java/com/android/server/inputmethod/IInputMethodManagerImpl.java
index 15f186b0..74b20a0 100644
--- a/services/core/java/com/android/server/inputmethod/IInputMethodManagerImpl.java
+++ b/services/core/java/com/android/server/inputmethod/IInputMethodManagerImpl.java
@@ -128,8 +128,9 @@
void startInputOrWindowGainedFocusAsync(
@StartInputReason int startInputReason, IInputMethodClient client,
IBinder windowToken, @StartInputFlags int startInputFlags,
- @WindowManager.LayoutParams.SoftInputModeFlags int softInputMode, int windowFlags,
- @Nullable EditorInfo editorInfo, IRemoteInputConnection inputConnection,
+ @WindowManager.LayoutParams.SoftInputModeFlags int softInputMode,
+ @WindowManager.LayoutParams.Flags int windowFlags, @Nullable EditorInfo editorInfo,
+ IRemoteInputConnection inputConnection,
IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
int unverifiedTargetSdkVersion, @UserIdInt int userId,
@NonNull ImeOnBackInvokedDispatcher imeDispatcher, boolean imeRequestedVisible,
@@ -138,8 +139,9 @@
InputBindResult startInputOrWindowGainedFocus(
@StartInputReason int startInputReason, IInputMethodClient client,
IBinder windowToken, @StartInputFlags int startInputFlags,
- @WindowManager.LayoutParams.SoftInputModeFlags int softInputMode, int windowFlags,
- @Nullable EditorInfo editorInfo, IRemoteInputConnection inputConnection,
+ @WindowManager.LayoutParams.SoftInputModeFlags int softInputMode,
+ @WindowManager.LayoutParams.Flags int windowFlags, @Nullable EditorInfo editorInfo,
+ IRemoteInputConnection inputConnection,
IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
int unverifiedTargetSdkVersion, @UserIdInt int userId,
@NonNull ImeOnBackInvokedDispatcher imeDispatcher, boolean imeRequestedVisible);
@@ -320,7 +322,7 @@
@StartInputReason int startInputReason, IInputMethodClient client, IBinder windowToken,
@StartInputFlags int startInputFlags,
@WindowManager.LayoutParams.SoftInputModeFlags int softInputMode,
- int windowFlags, @Nullable EditorInfo editorInfo,
+ @WindowManager.LayoutParams.Flags int windowFlags, @Nullable EditorInfo editorInfo,
IRemoteInputConnection inputConnection,
IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
int unverifiedTargetSdkVersion, @UserIdInt int userId,
@@ -336,7 +338,7 @@
IInputMethodClient client, IBinder windowToken,
@StartInputFlags int startInputFlags,
@WindowManager.LayoutParams.SoftInputModeFlags int softInputMode,
- int windowFlags, @Nullable EditorInfo editorInfo,
+ @WindowManager.LayoutParams.Flags int windowFlags, @Nullable EditorInfo editorInfo,
IRemoteInputConnection inputConnection,
IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
int unverifiedTargetSdkVersion, @UserIdInt int userId,
diff --git a/services/core/java/com/android/server/inputmethod/ImeBindingState.java b/services/core/java/com/android/server/inputmethod/ImeBindingState.java
index adfd991..41313a3 100644
--- a/services/core/java/com/android/server/inputmethod/ImeBindingState.java
+++ b/services/core/java/com/android/server/inputmethod/ImeBindingState.java
@@ -43,9 +43,9 @@
final int mUserId;
/**
- * The last window token that we confirmed to be focused. This is always updated upon reports
- * from the input method client. If the window state is already changed before the report is
- * handled, this field just keeps the last value.
+ * The token of the last window that we confirmed to be focused. This is always updated upon
+ * reports from the input method client. If the window state is already changed before the
+ * report is handled, this field just keeps the last value.
*/
@Nullable
final IBinder mFocusedWindow;
diff --git a/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java b/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java
index 2c07a31..3321787 100644
--- a/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java
+++ b/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java
@@ -30,8 +30,6 @@
import static android.view.WindowManager.LayoutParams.SoftInputModeFlags;
import static com.android.internal.inputmethod.InputMethodDebug.softInputModeToString;
-import static com.android.internal.inputmethod.SoftInputShowHideReason.REMOVE_IME_SCREENSHOT_FROM_IMMS;
-import static com.android.internal.inputmethod.SoftInputShowHideReason.SHOW_IME_SCREENSHOT_FROM_IMMS;
import static com.android.server.inputmethod.ImeProtoLogGroup.IME_VIS_STATE_COMPUTER_DEBUG;
import static com.android.server.inputmethod.InputMethodManagerService.computeImeDisplayIdForTarget;
@@ -87,7 +85,7 @@
/**
* A map used to track the requested IME target window and its state. The key represents the
- * token of the window and the value is the corresponding IME window state.
+ * token of the window and the value is the corresponding IME target window state.
*/
@GuardedBy("ImfLock.class")
private final WeakHashMap<IBinder, ImeTargetWindowState> mRequestWindowStateMap =
@@ -119,8 +117,7 @@
private boolean mInputShown;
/**
- * Set if we called
- * {@link com.android.server.wm.ImeTargetVisibilityPolicy#showImeScreenshot(IBinder, int)}.
+ * Set if we called {@link com.android.server.wm.ImeTargetVisibilityPolicy#showImeScreenshot}.
*/
@GuardedBy("ImfLock.class")
private boolean mRequestedImeScreenshot;
@@ -134,9 +131,9 @@
private IBinder mCurVisibleImeInputTarget;
/**
- * The last window token that we confirmed that IME started talking to. This is always updated
- * upon reports from the input method. If the window state is already changed before the report
- * is handled, this field just keeps the last value.
+ * The token of the last window that we confirmed that IME started talking to. This is always
+ * updated upon reports from the input method. If the window state is already changed before
+ * the report is handled, this field just keeps the last value.
*/
@GuardedBy("ImfLock.class")
@Nullable
@@ -157,17 +154,11 @@
/** State to handle showing the IME window with making the overlay window behind it. */
public static final int STATE_SHOW_IME_BEHIND_OVERLAY = 3;
- /** State to handle showing an IME preview surface during the app was loosing the IME focus */
- public static final int STATE_SHOW_IME_SNAPSHOT = 4;
+ public static final int STATE_HIDE_IME_EXPLICIT = 4;
- public static final int STATE_HIDE_IME_EXPLICIT = 5;
+ public static final int STATE_HIDE_IME_NOT_ALWAYS = 5;
- public static final int STATE_HIDE_IME_NOT_ALWAYS = 6;
-
- public static final int STATE_SHOW_IME_IMPLICIT = 7;
-
- /** State to handle removing an IME preview surface when necessary. */
- public static final int STATE_REMOVE_IME_SNAPSHOT = 8;
+ public static final int STATE_SHOW_IME_IMPLICIT = 6;
@IntDef({
STATE_INVALID,
@@ -175,11 +166,9 @@
STATE_SHOW_IME,
STATE_SHOW_IME_ABOVE_OVERLAY,
STATE_SHOW_IME_BEHIND_OVERLAY,
- STATE_SHOW_IME_SNAPSHOT,
STATE_HIDE_IME_EXPLICIT,
STATE_HIDE_IME_NOT_ALWAYS,
STATE_SHOW_IME_IMPLICIT,
- STATE_REMOVE_IME_SNAPSHOT,
})
@interface VisibilityState {}
@@ -393,26 +382,28 @@
ImeTargetWindowState getOrCreateWindowState(IBinder windowToken) {
ImeTargetWindowState state = mRequestWindowStateMap.get(windowToken);
if (state == null) {
- state = new ImeTargetWindowState(SOFT_INPUT_STATE_UNSPECIFIED, 0, false, false, false);
+ state = new ImeTargetWindowState(SOFT_INPUT_STATE_UNSPECIFIED, 0 /* windowFlags */,
+ false /* imeFocusChanged */, false /* hasFocusedEditor */,
+ false /* isStartInputByWindowGainFocus */, TOOL_TYPE_UNKNOWN);
}
return state;
}
@GuardedBy("ImfLock.class")
- ImeTargetWindowState getWindowStateOrNull(IBinder windowToken) {
- ImeTargetWindowState state = mRequestWindowStateMap.get(windowToken);
- return state;
+ @Nullable
+ ImeTargetWindowState getWindowStateOrNull(@Nullable IBinder windowToken) {
+ return mRequestWindowStateMap.get(windowToken);
}
@GuardedBy("ImfLock.class")
- void setWindowState(IBinder windowToken, @NonNull ImeTargetWindowState newState) {
+ void setWindowState(@NonNull IBinder windowToken, @NonNull ImeTargetWindowState newState) {
final ImeTargetWindowState state = mRequestWindowStateMap.get(windowToken);
if (state != null && newState.hasEditorFocused() && (
- newState.mToolType != MotionEvent.TOOL_TYPE_STYLUS
+ newState.getToolType() != MotionEvent.TOOL_TYPE_STYLUS
|| Flags.refactorInsetsController())) {
// Inherit the last requested IME visible state when the target window is still
// focused with an editor.
- newState.setRequestedImeVisible(state.mRequestedImeVisible);
+ newState.setRequestedImeVisible(state.isRequestedImeVisible());
}
setWindowStateInner(windowToken, newState);
}
@@ -442,11 +433,25 @@
}
}
+ /**
+ * Computes the IME visibility state from the given IME target window state.
+ *
+ * @param state the state of the IME target window.
+ * @param allowVisible whether the soft input modes
+ * {@link WindowManager.LayoutParams#SOFT_INPUT_STATE_VISIBLE} and
+ * {@link WindowManager.LayoutParams#SOFT_INPUT_STATE_ALWAYS_VISIBLE}
+ * are allowed.
+ * @param imeRequestedVisible whether the IME target window has the IME insets type in the
+ * requestedVisibleTypes (see
+ * {@link InputMethodManager#hasViewImeRequestedVisible}).
+ */
@GuardedBy("ImfLock.class")
- ImeVisibilityResult computeState(ImeTargetWindowState state, boolean allowVisible,
+ @Nullable
+ ImeVisibilityResult computeState(@NonNull ImeTargetWindowState state, boolean allowVisible,
boolean imeRequestedVisible) {
// TODO: Output the request IME visibility state according to the requested window state
- final int softInputVisibility = state.mSoftInputModeState & SOFT_INPUT_MASK_STATE;
+ final var softInputMode = state.getSoftInputModeState();
+ final int softInputVisibility = softInputMode & SOFT_INPUT_MASK_STATE;
// Should we auto-show the IME even if the caller has not
// specified what should be done with it?
// We only do this automatically if the window can resize
@@ -455,12 +460,12 @@
// by the IME) or if running on a large screen where there
// is more room for the target window + IME.
final boolean doAutoShow =
- (state.mSoftInputModeState & WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST)
+ (softInputMode & WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST)
== WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE
|| mService.mRes.getConfiguration().isLayoutSizeAtLeast(
Configuration.SCREENLAYOUT_SIZE_LARGE);
- final boolean isForwardNavigation = (state.mSoftInputModeState
- & WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) != 0;
+ final boolean isForwardNavigation =
+ (softInputMode & WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) != 0;
// We shows the IME when the system allows the IME focused target window to restore the
// IME visibility (e.g. switching to the app task when last time the IME is visible).
@@ -508,7 +513,7 @@
// Do nothing but preserving the last IME requested visibility state.
final ImeTargetWindowState lastState = getWindowStateOrNull(mLastImeTargetWindow);
if (lastState != null) {
- state.setRequestedImeVisible(lastState.mRequestedImeVisible);
+ state.setRequestedImeVisible(lastState.isRequestedImeVisible());
}
break;
case WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN:
@@ -570,7 +575,7 @@
// to rely on this behavior to hide the IME when the editor no longer has focus
// To maintain compatibility, we are now hiding the IME when we don't have
// an editor upon refocusing a window.
- if (state.isStartInputByGainFocus()) {
+ if (state.isStartInputByWindowGainFocus()) {
ProtoLog.v(IME_VIS_STATE_COMPUTER_DEBUG,
"Same window without editor will hide input");
return new ImeVisibilityResult(STATE_HIDE_IME_EXPLICIT,
@@ -578,7 +583,7 @@
}
}
if (!state.hasEditorFocused() && (mInputShown || (Flags.refactorInsetsController()
- && imeRequestedVisible)) && state.isStartInputByGainFocus()
+ && imeRequestedVisible)) && state.isStartInputByWindowGainFocus()
&& mService.mInputMethodDeviceConfigs.shouldHideImeWhenNoEditorFocus()) {
// Hide the soft-keyboard when the system do nothing for softInputModeState
// of the window being gained focus without an editor. This behavior benefits
@@ -597,17 +602,26 @@
return null;
}
+ /**
+ * Checks if we should show or hide the IME screenshot, based on the given IME target window and
+ * the interactive state.
+ *
+ * @param windowToken the token of the {@link ImeTargetWindowState} to check.
+ * @param interactive whether the system is currently interactive.
+ * @return {@code true} if the screenshot should be shown, {@code false} if it should be hidden,
+ * or {@code null} if it should remain unchanged.
+ */
+ @Nullable
@GuardedBy("ImfLock.class")
- ImeVisibilityResult onInteractiveChanged(IBinder windowToken, boolean interactive) {
+ Boolean shouldShowImeScreenshot(IBinder windowToken, boolean interactive) {
final ImeTargetWindowState state = getWindowStateOrNull(windowToken);
if (state != null && state.isRequestedImeVisible() && mInputShown && !interactive) {
mRequestedImeScreenshot = true;
- return new ImeVisibilityResult(STATE_SHOW_IME_SNAPSHOT, SHOW_IME_SCREENSHOT_FROM_IMMS);
+ return true;
}
if (interactive && mRequestedImeScreenshot) {
mRequestedImeScreenshot = false;
- return new ImeVisibilityResult(STATE_REMOVE_IME_SNAPSHOT,
- REMOVE_IME_SCREENSHOT_FROM_IMMS);
+ return false;
}
return null;
}
@@ -760,56 +774,59 @@
}
/**
- * A class that represents the current state of the IME target window.
+ * State information about an IME target window (i.e. a window for which we started an
+ * input connection). One of the IME target windows is set as the current
+ * {@link com.android.server.wm.DisplayContent#mImeInputTarget}.
*/
static class ImeTargetWindowState {
- ImeTargetWindowState(@SoftInputModeFlags int softInputModeState, int windowFlags,
- boolean imeFocusChanged, boolean hasFocusedEditor,
- boolean isStartInputByGainFocus) {
- this(softInputModeState, windowFlags, imeFocusChanged, hasFocusedEditor,
- isStartInputByGainFocus, TOOL_TYPE_UNKNOWN);
- }
-
- ImeTargetWindowState(@SoftInputModeFlags int softInputModeState, int windowFlags,
- boolean imeFocusChanged, boolean hasFocusedEditor,
- boolean isStartInputByGainFocus, @MotionEvent.ToolType int toolType) {
+ ImeTargetWindowState(@SoftInputModeFlags int softInputModeState,
+ @WindowManager.LayoutParams.Flags int windowFlags, boolean imeFocusChanged,
+ boolean hasFocusedEditor, boolean isStartInputByWindowGainFocus,
+ @MotionEvent.ToolType int toolType) {
mSoftInputModeState = softInputModeState;
mWindowFlags = windowFlags;
mImeFocusChanged = imeFocusChanged;
mHasFocusedEditor = hasFocusedEditor;
- mIsStartInputByGainFocus = isStartInputByGainFocus;
+ mIsStartInputByWindowGainFocus = isStartInputByWindowGainFocus;
mToolType = toolType;
}
- /**
- * Visibility state for this window. By default no state has been specified.
- */
- private final @SoftInputModeFlags int mSoftInputModeState;
+ /** {@link WindowManager.LayoutParams#softInputMode} of the IME target window. */
+ @SoftInputModeFlags
+ private final int mSoftInputModeState;
+ /** Window flags of the IME target window. */
+ @WindowManager.LayoutParams.Flags
private final int mWindowFlags;
- /**
- * {@link MotionEvent#getToolType(int)} that was used to click editor.
- */
- private final int mToolType;
-
- /**
- * {@code true} means the IME focus changed from the previous window, {@code false}
- * otherwise.
- */
+ /** Whether the IME focus changed from the previous window. */
private final boolean mImeFocusChanged;
/**
- * {@code true} when the window has focused an editor, {@code false} otherwise.
+ * Whether the window has a focused view that is a text editor.
+ *
+ * @see android.view.View#onCheckIsTextEditor
*/
private final boolean mHasFocusedEditor;
- private final boolean mIsStartInputByGainFocus;
+ /**
+ * Whether this became the IME target (started an input connection) due to the window
+ * gaining input focus.
+ *
+ * @see com.android.internal.inputmethod.StartInputFlags#WINDOW_GAINED_FOCUS
+ */
+ private final boolean mIsStartInputByWindowGainFocus;
/**
- * Set if the client has asked for the input method to be shown.
+ * The type of tool that was used to click editor.
+ *
+ * @see MotionEvent#getToolType
*/
+ @MotionEvent.ToolType
+ private final int mToolType;
+
+ /** Whether the client of the window requested the IME to be visible. */
@GuardedBy("ImfLock.class")
private boolean mRequestedImeVisible;
@@ -827,6 +844,18 @@
private int mImeDisplayId = DEFAULT_DISPLAY;
@AnyThread
+ @SoftInputModeFlags
+ int getSoftInputModeState() {
+ return mSoftInputModeState;
+ }
+
+ @AnyThread
+ @WindowManager.LayoutParams.Flags
+ int getWindowFlags() {
+ return mWindowFlags;
+ }
+
+ @AnyThread
boolean hasImeFocusChanged() {
return mImeFocusChanged;
}
@@ -837,36 +866,17 @@
}
@AnyThread
- boolean isStartInputByGainFocus() {
- return mIsStartInputByGainFocus;
+ boolean isStartInputByWindowGainFocus() {
+ return mIsStartInputByWindowGainFocus;
}
@AnyThread
- int getSoftInputModeState() {
- return mSoftInputModeState;
- }
-
- @AnyThread
- int getWindowFlags() {
- return mWindowFlags;
- }
-
- @AnyThread
+ @MotionEvent.ToolType
int getToolType() {
return mToolType;
}
@GuardedBy("ImfLock.class")
- private void setImeDisplayId(int imeDisplayId) {
- mImeDisplayId = imeDisplayId;
- }
-
- @GuardedBy("ImfLock.class")
- int getImeDisplayId() {
- return mImeDisplayId;
- }
-
- @GuardedBy("ImfLock.class")
private void setRequestedImeVisible(boolean requestedImeVisible) {
mRequestedImeVisible = requestedImeVisible;
}
@@ -877,7 +887,7 @@
}
@GuardedBy("ImfLock.class")
- void setRequestImeToken(IBinder token) {
+ void setRequestImeToken(@NonNull IBinder token) {
mRequestImeToken = token;
}
@@ -886,15 +896,28 @@
return mRequestImeToken;
}
+ @GuardedBy("ImfLock.class")
+ private void setImeDisplayId(int imeDisplayId) {
+ mImeDisplayId = imeDisplayId;
+ }
+
+ @GuardedBy("ImfLock.class")
+ int getImeDisplayId() {
+ return mImeDisplayId;
+ }
+
@Override
public String toString() {
- return "ImeTargetWindowState{ imeToken " + mRequestImeToken
- + " imeFocusChanged " + mImeFocusChanged
- + " hasEditorFocused " + mHasFocusedEditor
- + " requestedImeVisible " + mRequestedImeVisible
- + " imeDisplayId " + mImeDisplayId
+ return "ImeTargetWindowState{"
+ " softInputModeState " + softInputModeToString(mSoftInputModeState)
- + " isStartInputByGainFocus " + mIsStartInputByGainFocus
+ + " windowFlags: " + Integer.toHexString(mWindowFlags)
+ + " imeFocusChanged " + mImeFocusChanged
+ + " hasFocusedEditor " + mHasFocusedEditor
+ + " isStartInputByWindowGainFocus " + mIsStartInputByWindowGainFocus
+ + " toolType: " + mToolType
+ + " requestedImeVisible " + mRequestedImeVisible
+ + " requestImeToken " + mRequestImeToken
+ + " imeDisplayId " + mImeDisplayId
+ "}";
}
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 06e66e6..f124102 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -462,8 +462,6 @@
@Nullable
private StatusBarManagerInternal mStatusBarManagerInternal;
- @SharedByAllUsersField
- private boolean mShowOngoingImeSwitcherForPhones;
@GuardedBy("ImfLock.class")
@MultiUserUnawareField
private final HandwritingModeController mHwController;
@@ -617,6 +615,12 @@
private final CopyOnWriteArrayList<InputMethodListListener> mInputMethodListListeners =
new CopyOnWriteArrayList<>();
+ /**
+ * Mapping of startInput token to IME target window token. This is set before dispatching
+ * the startInput to the IME (in {@link #attachNewInputLocked}), and read when the IME replied
+ * to it (in {@link #reportStartInputLocked}). When read, it is reported as the new IME target
+ * in WindowManagerService.
+ */
@GuardedBy("ImfLock.class")
@SharedByAllUsersField
private final WeakHashMap<IBinder, IBinder> mImeTargetWindowMap = new WeakHashMap<>();
@@ -1269,8 +1273,6 @@
mSlotIme = mContext.getString(com.android.internal.R.string.status_bar_ime);
- mShowOngoingImeSwitcherForPhones = false;
-
ProtoLog.init(ImeProtoLogGroup.values());
mCurrentImeUserId = mActivityManagerInternal.getCurrentUserId();
@@ -1490,9 +1492,7 @@
final var bindingController = getInputMethodBindingController(currentImeUserId);
updateSystemUiLocked(bindingController.getImeWindowVis(),
bindingController.getBackDisposition(), currentImeUserId);
- mShowOngoingImeSwitcherForPhones = mRes.getBoolean(
- com.android.internal.R.bool.show_ongoing_ime_switcher);
- if (mShowOngoingImeSwitcherForPhones) {
+ if (!Flags.imeSwitcherRevamp()) {
mWindowManagerInternal.setOnHardKeyboardStatusChangeListener(available -> {
mHandler.obtainMessage(MSG_HARD_KEYBOARD_SWITCH_CHANGED,
available ? 1 : 0, 0 /* unused */).sendToTarget();
@@ -1901,9 +1901,9 @@
void onUnbindCurrentMethodByReset(@UserIdInt int userId) {
final var userData = getUserData(userId);
final var visibilityStateComputer = userData.mVisibilityStateComputer;
- final ImeTargetWindowState winState = visibilityStateComputer.getWindowStateOrNull(
+ final ImeTargetWindowState targetWindowState = visibilityStateComputer.getWindowStateOrNull(
userData.mImeBindingState.mFocusedWindow);
- if (winState != null && !winState.isRequestedImeVisible()
+ if (targetWindowState != null && !targetWindowState.isRequestedImeVisible()
&& !visibilityStateComputer.isInputShown()) {
// Normally, the focus window will apply the IME visibility state to
// WindowManager when the IME has applied it. But it would be too late when
@@ -1924,9 +1924,9 @@
private boolean isShowRequestedForCurrentWindow(@UserIdInt int userId) {
final var userData = getUserData(userId);
final var visibilityStateComputer = userData.mVisibilityStateComputer;
- final ImeTargetWindowState state = visibilityStateComputer.getWindowStateOrNull(
+ final ImeTargetWindowState targetWindowState = visibilityStateComputer.getWindowStateOrNull(
userData.mImeBindingState.mFocusedWindow);
- return state != null && state.isRequestedImeVisible();
+ return targetWindowState != null && targetWindowState.isRequestedImeVisible();
}
@GuardedBy("ImfLock.class")
@@ -1940,8 +1940,9 @@
userData.mBoundToMethod = true;
}
- final boolean restarting = !initial;
final Binder startInputToken = new Binder();
+ mImeTargetWindowMap.put(startInputToken, userData.mImeBindingState.mFocusedWindow);
+ final boolean restarting = !initial;
final StartInputInfo info = new StartInputInfo(userId,
bindingController.getCurToken(), bindingController.getCurTokenDisplayId(),
bindingController.getCurId(), startInputReason,
@@ -1950,7 +1951,6 @@
userData.mImeBindingState.mFocusedWindow, userData.mCurEditorInfo,
userData.mImeBindingState.mFocusedWindowSoftInputMode,
bindingController.getSequenceNumber());
- mImeTargetWindowMap.put(startInputToken, userData.mImeBindingState.mFocusedWindow);
mStartInputHistory.addEntry(info);
// Seems that PackageManagerInternal#grantImplicitAccess() doesn't handle cross-user
@@ -2074,14 +2074,14 @@
// Compute the final shown display ID with validated cs.selfReportedDisplayId for this
// session & other conditions.
- ImeTargetWindowState winState = visibilityStateComputer.getWindowStateOrNull(
+ final ImeTargetWindowState targetWindowState = visibilityStateComputer.getWindowStateOrNull(
userData.mImeBindingState.mFocusedWindow);
- if (winState == null) {
+ if (targetWindowState == null) {
return InputBindResult.NOT_IME_TARGET_WINDOW;
}
final int csDisplayId = cs.mSelfReportedDisplayId;
bindingController.setDisplayIdToShowIme(
- visibilityStateComputer.computeImeDisplayId(winState, csDisplayId));
+ visibilityStateComputer.computeImeDisplayId(targetWindowState, csDisplayId));
// Potentially override the selected input method if the new display belongs to a virtual
// device with a custom IME.
@@ -2717,7 +2717,6 @@
@GuardedBy("ImfLock.class")
private boolean shouldShowImeSwitcherLocked(@ImeWindowVisibility int visibility,
@UserIdInt int userId) {
- if (!mShowOngoingImeSwitcherForPhones) return false;
// When the IME switcher dialog is shown, the IME switcher button should be hidden.
// TODO(b/305849394): Make mMenuController multi-user aware.
final boolean switcherMenuShowing = Flags.imeSwitcherRevamp()
@@ -2848,12 +2847,11 @@
@BinderThread
@GuardedBy("ImfLock.class")
private void reportStartInputLocked(IBinder startInputToken, @NonNull UserData userData) {
- final IBinder targetWindow = mImeTargetWindowMap.get(startInputToken);
- if (targetWindow != null) {
- mWindowManagerInternal.updateInputMethodTargetWindow(targetWindow);
+ final IBinder targetWindowToken = mImeTargetWindowMap.get(startInputToken);
+ if (targetWindowToken != null) {
+ mWindowManagerInternal.updateImeTargetWindow(targetWindowToken);
}
- final var visibilityStateComputer = userData.mVisibilityStateComputer;
- visibilityStateComputer.setLastImeTargetWindow(targetWindow);
+ userData.mVisibilityStateComputer.setLastImeTargetWindow(targetWindowToken);
}
@GuardedBy("ImfLock.class")
@@ -3533,14 +3531,8 @@
final IInputMethodInvoker curMethod = bindingController.getCurMethod();
ImeTracker.forLogging().onCancelled(userData.mCurStatsToken,
ImeTracker.PHASE_SERVER_WAIT_IME);
- final boolean readyToDispatchToIme;
- if (Flags.deferShowSoftInputUntilSessionCreation()) {
- readyToDispatchToIme =
- curMethod != null && userData.mCurClient != null
- && userData.mCurClient.mCurSession != null;
- } else {
- readyToDispatchToIme = curMethod != null;
- }
+ final boolean readyToDispatchToIme = curMethod != null && userData.mCurClient != null
+ && userData.mCurClient.mCurSession != null;
if (readyToDispatchToIme) {
ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_SERVER_HAS_IME);
userData.mCurStatsToken = null;
@@ -3715,7 +3707,13 @@
return shouldHideSoftInput;
}
- private boolean isImeClientFocused(IBinder windowToken, ClientState cs) {
+ /**
+ * Checks whether the specified IME client has IME focus or not.
+ *
+ * @param windowToken the token of the IME client window.
+ * @param cs the IME client state.
+ */
+ private boolean isImeClientFocused(IBinder windowToken, @NonNull ClientState cs) {
final int imeClientFocus = mWindowManagerInternal.hasInputMethodClientFocus(
windowToken, cs.mUid, cs.mPid, cs.mSelfReportedDisplayId);
return imeClientFocus == WindowManagerInternal.ImeClientFocusResult.HAS_IME_FOCUS;
@@ -3727,7 +3725,7 @@
public void startInputOrWindowGainedFocusAsync(
@StartInputReason int startInputReason, IInputMethodClient client, IBinder windowToken,
@StartInputFlags int startInputFlags, @SoftInputModeFlags int softInputMode,
- int windowFlags, @Nullable EditorInfo editorInfo,
+ @WindowManager.LayoutParams.Flags int windowFlags, @Nullable EditorInfo editorInfo,
IRemoteInputConnection inputConnection,
IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
int unverifiedTargetSdkVersion, @UserIdInt int userId,
@@ -3741,7 +3739,7 @@
public InputBindResult startInputOrWindowGainedFocus(
@StartInputReason int startInputReason, IInputMethodClient client, IBinder windowToken,
@StartInputFlags int startInputFlags, @SoftInputModeFlags int softInputMode,
- int windowFlags, @Nullable EditorInfo editorInfo,
+ @WindowManager.LayoutParams.Flags int windowFlags, @Nullable EditorInfo editorInfo,
IRemoteInputConnection inputConnection,
IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
int unverifiedTargetSdkVersion, @UserIdInt int userId,
@@ -3812,7 +3810,7 @@
return InputBindResult.INVALID_USER;
}
- // Ensure that caller's focused window and display parameters are allowd to
+ // Ensure that caller's focused window and display parameters are allowed to
// display input method.
final int imeClientFocus = mWindowManagerInternal.hasInputMethodClientFocus(
windowToken, cs.mUid, cs.mPid, cs.mSelfReportedDisplayId);
@@ -3902,7 +3900,8 @@
private InputBindResult startInputOrWindowGainedFocusInternalLocked(
@StartInputReason int startInputReason, IInputMethodClient client,
@NonNull IBinder windowToken, @StartInputFlags int startInputFlags,
- @SoftInputModeFlags int softInputMode, int windowFlags, EditorInfo editorInfo,
+ @SoftInputModeFlags int softInputMode,
+ @WindowManager.LayoutParams.Flags int windowFlags, EditorInfo editorInfo,
IRemoteInputConnection inputContext,
@Nullable IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
int unverifiedTargetSdkVersion, @NonNull InputMethodBindingController bindingController,
@@ -3930,18 +3929,18 @@
final var userData = getUserData(userId);
final boolean sameWindowFocused = userData.mImeBindingState.mFocusedWindow == windowToken;
final boolean isTextEditor = (startInputFlags & StartInputFlags.IS_TEXT_EDITOR) != 0;
- final boolean startInputByWinGainedFocus =
+ final boolean isStartInputByWindowGainFocus =
(startInputFlags & StartInputFlags.WINDOW_GAINED_FOCUS) != 0;
final int toolType = editorInfo != null
? editorInfo.getInitialToolType() : MotionEvent.TOOL_TYPE_UNKNOWN;
- // Init the focused window state (e.g. whether the editor has focused or IME focus has
+ // Init the IME target window state (e.g. whether there is a focused editor or IME focus has
// changed from another window).
- final ImeTargetWindowState windowState = new ImeTargetWindowState(
- softInputMode, windowFlags, !sameWindowFocused, isTextEditor,
- startInputByWinGainedFocus, toolType);
+ final var targetWindowState = new ImeTargetWindowState(softInputMode, windowFlags,
+ !sameWindowFocused /* imeFocusChanged */, isTextEditor /* hasFocusedEditor */,
+ isStartInputByWindowGainFocus, toolType);
final var visibilityStateComputer = userData.mVisibilityStateComputer;
- visibilityStateComputer.setWindowState(windowToken, windowState);
+ visibilityStateComputer.setWindowState(windowToken, targetWindowState);
if (sameWindowFocused && isTextEditor) {
ProtoLog.v(IMMS_DEBUG,
@@ -3960,8 +3959,8 @@
null, null, null, null, -1, false);
}
- userData.mImeBindingState = new ImeBindingState(bindingController.getUserId(), windowToken,
- softInputMode, cs, editorInfo);
+ userData.mImeBindingState = new ImeBindingState(bindingController.getUserId(),
+ windowToken /* focusedWindow */, softInputMode, cs, editorInfo);
mFocusedWindowPerceptible.put(windowToken, true);
// We want to start input before showing the IME, but after closing
@@ -3971,11 +3970,12 @@
boolean didStart = false;
InputBindResult res = null;
- final ImeVisibilityResult imeVisRes = visibilityStateComputer.computeState(windowState,
+ final ImeVisibilityResult imeVisRes = visibilityStateComputer.computeState(
+ targetWindowState,
isSoftInputModeStateVisibleAllowed(unverifiedTargetSdkVersion, startInputFlags),
imeRequestedVisible);
if (imeVisRes != null) {
- boolean isShow = false;
+ final boolean isShow;
switch (imeVisRes.getReason()) {
case SoftInputShowHideReason.SHOW_RESTORE_IME_VISIBILITY:
case SoftInputShowHideReason.SHOW_AUTO_EDITOR_FORWARD_NAV:
@@ -3991,6 +3991,9 @@
didStart = true;
}
break;
+ default:
+ isShow = false;
+ break;
}
final var statsToken = createStatsTokenForFocusedClient(isShow, imeVisRes.getReason(),
userId);
@@ -5047,9 +5050,10 @@
// --------------------------------------------------------------
case MSG_HARD_KEYBOARD_SWITCH_CHANGED:
- if (!Flags.imeSwitcherRevamp()) {
- mMenuController.handleHardKeyboardStatusChange(msg.arg1 == 1);
+ if (Flags.imeSwitcherRevamp()) {
+ return true;
}
+ mMenuController.handleHardKeyboardStatusChange(msg.arg1 == 1);
synchronized (ImfLock.class) {
sendOnNavButtonFlagsChangedToAllImesLocked();
}
@@ -5173,16 +5177,14 @@
}
if (mImePlatformCompatUtils.shouldUseSetInteractiveProtocol(
bindingController.getCurMethodUid())) {
- // Handle IME visibility when interactive changed before finishing the input to
- // ensure we preserve the last state as possible.
+ // Apply IME screenshot visibility before notifying the client, as it could dismiss
+ // the IME.
final var visibilityStateComputer = userData.mVisibilityStateComputer;
- final ImeVisibilityResult imeVisRes = visibilityStateComputer.onInteractiveChanged(
+ final Boolean showScreenshot = visibilityStateComputer.shouldShowImeScreenshot(
userData.mImeBindingState.mFocusedWindow, interactive);
- if (imeVisRes != null) {
- // Pass in a null statsToken as the IME snapshot is not tracked by ImeTracker.
- mVisibilityApplier.applyImeVisibility(userData.mImeBindingState.mFocusedWindow,
- null /* statsToken */, imeVisRes.getState(), imeVisRes.getReason(),
- userId);
+ if (showScreenshot != null) {
+ mVisibilityApplier.applyImeScreenshotVisibility(
+ userData.mImeBindingState.mFocusedWindow, showScreenshot, userId);
}
// Eligible IME processes use new "setInteractive" protocol.
userData.mCurClient.mClient.setInteractive(mIsInteractive,
diff --git a/services/core/java/com/android/server/inputmethod/ZeroJankProxy.java b/services/core/java/com/android/server/inputmethod/ZeroJankProxy.java
index 12c1d9c..0ba769a 100644
--- a/services/core/java/com/android/server/inputmethod/ZeroJankProxy.java
+++ b/services/core/java/com/android/server/inputmethod/ZeroJankProxy.java
@@ -230,7 +230,7 @@
IInputMethodClient client, IBinder windowToken,
@StartInputFlags int startInputFlags,
@WindowManager.LayoutParams.SoftInputModeFlags int softInputMode,
- int windowFlags, @Nullable EditorInfo editorInfo,
+ @WindowManager.LayoutParams.Flags int windowFlags, @Nullable EditorInfo editorInfo,
IRemoteInputConnection inputConnection,
IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
int unverifiedTargetSdkVersion, @UserIdInt int userId,
@@ -265,7 +265,7 @@
IInputMethodClient client, IBinder windowToken,
@StartInputFlags int startInputFlags,
@WindowManager.LayoutParams.SoftInputModeFlags int softInputMode,
- int windowFlags, @Nullable EditorInfo editorInfo,
+ @WindowManager.LayoutParams.Flags int windowFlags, @Nullable EditorInfo editorInfo,
IRemoteInputConnection inputConnection,
IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
int unverifiedTargetSdkVersion, @UserIdInt int userId,
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 d7d0eb4..3b1ac2d 100644
--- a/services/core/java/com/android/server/location/gnss/GnssNetworkConnectivityHandler.java
+++ b/services/core/java/com/android/server/location/gnss/GnssNetworkConnectivityHandler.java
@@ -20,7 +20,6 @@
import android.annotation.RequiresPermission;
import android.content.Context;
-import android.location.flags.Flags;
import android.net.ConnectivityManager;
import android.net.LinkAddress;
import android.net.LinkProperties;
@@ -203,12 +202,8 @@
SubscriptionManager subManager = mContext.getSystemService(SubscriptionManager.class);
if (subManager != null) {
- if (Flags.subscriptionsChangedListenerThread()) {
- subManager.addOnSubscriptionsChangedListener(FgThread.getExecutor(),
- mOnSubscriptionsChangeListener);
- } else {
- subManager.addOnSubscriptionsChangedListener(mOnSubscriptionsChangeListener);
- }
+ subManager.addOnSubscriptionsChangedListener(FgThread.getExecutor(),
+ mOnSubscriptionsChangeListener);
}
PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
@@ -652,13 +647,11 @@
mSuplConnectivityCallback,
mHandler,
SUPL_NETWORK_REQUEST_TIMEOUT_MILLIS);
- if (Flags.releaseSuplConnectionOnTimeout()) {
- // Schedule to release the SUPL connection after timeout
- mHandler.removeCallbacksAndMessages(mSuplConnectionReleaseOnTimeoutToken);
- mHandler.postDelayed(() -> handleReleaseSuplConnection(GPS_RELEASE_AGPS_DATA_CONN),
- mSuplConnectionReleaseOnTimeoutToken,
- SUPL_CONNECTION_TIMEOUT_MILLIS);
- }
+ // Schedule to release the SUPL connection after timeout
+ mHandler.removeCallbacksAndMessages(mSuplConnectionReleaseOnTimeoutToken);
+ mHandler.postDelayed(() -> handleReleaseSuplConnection(GPS_RELEASE_AGPS_DATA_CONN),
+ mSuplConnectionReleaseOnTimeoutToken,
+ SUPL_CONNECTION_TIMEOUT_MILLIS);
} catch (RuntimeException e) {
Log.e(TAG, "Failed to request network.", e);
mSuplConnectivityCallback = null;
@@ -689,10 +682,8 @@
Log.d(TAG, message);
}
- if (Flags.releaseSuplConnectionOnTimeout()) {
- // Remove pending task to avoid releasing an incorrect connection
- mHandler.removeCallbacksAndMessages(mSuplConnectionReleaseOnTimeoutToken);
- }
+ // Remove pending task to avoid releasing an incorrect connection
+ mHandler.removeCallbacksAndMessages(mSuplConnectionReleaseOnTimeoutToken);
if (mAGpsDataConnectionState == AGPS_DATA_CONNECTION_CLOSED) {
return;
}
diff --git a/services/core/java/com/android/server/location/gnss/NetworkTimeHelper.java b/services/core/java/com/android/server/location/gnss/NetworkTimeHelper.java
index 494ea77..0ad02bf 100644
--- a/services/core/java/com/android/server/location/gnss/NetworkTimeHelper.java
+++ b/services/core/java/com/android/server/location/gnss/NetworkTimeHelper.java
@@ -19,7 +19,6 @@
import android.annotation.ElapsedRealtimeLong;
import android.annotation.NonNull;
import android.content.Context;
-import android.location.flags.Flags;
import android.os.Looper;
import java.io.PrintWriter;
@@ -56,13 +55,7 @@
static NetworkTimeHelper create(
@NonNull Context context, @NonNull Looper looper,
@NonNull InjectTimeCallback injectTimeCallback) {
- if (!Flags.useLegacyNtpTime()) {
- TimeDetectorNetworkTimeHelper.Environment environment =
- new TimeDetectorNetworkTimeHelper.EnvironmentImpl(looper);
- return new TimeDetectorNetworkTimeHelper(environment, injectTimeCallback);
- } else {
- return new NtpNetworkTimeHelper(context, looper, injectTimeCallback);
- }
+ return new NtpNetworkTimeHelper(context, looper, injectTimeCallback);
}
/**
diff --git a/services/core/java/com/android/server/media/AudioManagerRouteController.java b/services/core/java/com/android/server/media/AudioManagerRouteController.java
index 14f870b..07c95e9 100644
--- a/services/core/java/com/android/server/media/AudioManagerRouteController.java
+++ b/services/core/java/com/android/server/media/AudioManagerRouteController.java
@@ -32,6 +32,7 @@
import android.media.AudioDeviceInfo;
import android.media.AudioManager;
import android.media.MediaRoute2Info;
+import android.media.RoutingSessionInfo;
import android.media.audio.Flags;
import android.media.audiopolicy.AudioProductStrategy;
import android.os.Handler;
@@ -232,6 +233,17 @@
}
@Override
+ public @RoutingSessionInfo.ReleaseType int getSessionReleaseType() {
+ // TODO(b/385672684): impl get session release type
+ return RoutingSessionInfo.RELEASE_UNSUPPORTED;
+ }
+
+ @Override
+ public void releaseRoutingSession() {
+ // TODO(b/385672684): impl release system session
+ }
+
+ @Override
@NonNull
public synchronized MediaRoute2Info getSelectedRoute() {
return mSelectedRoute;
diff --git a/services/core/java/com/android/server/media/DeviceRouteController.java b/services/core/java/com/android/server/media/DeviceRouteController.java
index 0a4cbcd..63d46a5 100644
--- a/services/core/java/com/android/server/media/DeviceRouteController.java
+++ b/services/core/java/com/android/server/media/DeviceRouteController.java
@@ -26,6 +26,7 @@
import android.media.AudioManager;
import android.media.IAudioService;
import android.media.MediaRoute2Info;
+import android.media.RoutingSessionInfo;
import android.media.audiopolicy.AudioProductStrategy;
import android.os.Looper;
import android.os.ServiceManager;
@@ -147,6 +148,13 @@
*/
void stop();
+ /** Returns the release type of current session. */
+ @RoutingSessionInfo.ReleaseType
+ int getSessionReleaseType();
+
+ /** Releases the routing session. */
+ void releaseRoutingSession();
+
/**
* Interface for receiving events when device route has changed.
*/
diff --git a/services/core/java/com/android/server/media/LegacyDeviceRouteController.java b/services/core/java/com/android/server/media/LegacyDeviceRouteController.java
index 1e8ebca..d1cc549 100644
--- a/services/core/java/com/android/server/media/LegacyDeviceRouteController.java
+++ b/services/core/java/com/android/server/media/LegacyDeviceRouteController.java
@@ -34,6 +34,7 @@
import android.media.IAudioRoutesObserver;
import android.media.IAudioService;
import android.media.MediaRoute2Info;
+import android.media.RoutingSessionInfo;
import android.os.RemoteException;
import android.os.UserHandle;
import android.util.Slog;
@@ -117,6 +118,16 @@
}
@Override
+ public @RoutingSessionInfo.ReleaseType int getSessionReleaseType() {
+ return RoutingSessionInfo.RELEASE_UNSUPPORTED;
+ }
+
+ @Override
+ public void releaseRoutingSession() {
+ // Nothing to do.
+ }
+
+ @Override
@NonNull
public synchronized MediaRoute2Info getSelectedRoute() {
return mDeviceRoute;
diff --git a/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java b/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java
index 96e4539..3c9d4d0 100644
--- a/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java
+++ b/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java
@@ -78,7 +78,6 @@
private Connection mActiveConnection;
private boolean mConnectionReady;
- private boolean mIsManagerScanning;
private RouteDiscoveryPreference mLastDiscoveryPreference = null;
private boolean mLastDiscoveryPreferenceIncludesThisPackage = false;
@@ -119,13 +118,6 @@
mHandler = new Handler(looper);
}
- public void setManagerScanning(boolean managerScanning) {
- if (mIsManagerScanning != managerScanning) {
- mIsManagerScanning = managerScanning;
- updateBinding();
- }
- }
-
@Override
public void requestCreateSession(
long requestId,
@@ -328,16 +320,23 @@
}
}
+ /**
+ * Returns true if we should bind to the corresponding provider.
+ *
+ * <p>The reasons to bind are:
+ *
+ * <ul>
+ * <li>There's an active scan and this provider supports system media routing.
+ * <li>There's an active routing session against this provider.
+ * <li>There's a non-empty {@link RouteDiscoveryPreference#getPreferredFeatures()} that's
+ * relevant to this provider (that is, this provider is public, or the app that owns this
+ * provider is one of the apps populating the active discovery preference).
+ * </ul>
+ */
private boolean shouldBind() {
if (!mRunning) {
return false;
}
- // We bind if any manager is scanning (regardless of whether an app is scanning) to give
- // the opportunity for providers to publish routing sessions that were established
- // directly between the app and the provider (typically via AndroidX MediaRouter). See
- // b/176774510#comment20 for more information.
- boolean bindDueToManagerScan =
- mIsManagerScanning && !Flags.enablePreventionOfManagerScansWhenNoAppsScan();
// We also bind if this provider supports system media routing, because even if an app
// doesn't have any registered discovery preference, we should still be able to route their
// system media.
@@ -353,7 +352,6 @@
}
if (!getSessionInfos().isEmpty()
|| bindDueToOngoingSystemMediaRoutingSessions
- || bindDueToManagerScan
|| bindDueToSystemMediaRoutingSupport) {
return true;
}
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index e84d41a..db97ae0 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -3963,8 +3963,6 @@
activeRouterRecords = getIndividuallyActiveRouters(service, allRouterRecords);
}
- updateManagerScanningForProviders(areManagersScanning);
-
Set<String> activelyScanningPackages = new HashSet<>();
RouteDiscoveryPreference newPreference =
buildCompositeDiscoveryPreference(
@@ -4038,10 +4036,8 @@
preferredFeatures.addAll(preference.getPreferredFeatures());
boolean isRouterRecordActivelyScanning =
- Flags.enablePreventionOfManagerScansWhenNoAppsScan()
- ? (activeRouterRecord.isActivelyScanning() || shouldForceActiveScan)
- && !preference.getPreferredFeatures().isEmpty()
- : activeRouterRecord.isActivelyScanning();
+ (activeRouterRecord.isActivelyScanning() || shouldForceActiveScan)
+ && !preference.getPreferredFeatures().isEmpty();
if (isRouterRecordActivelyScanning) {
activeScan = true;
@@ -4053,15 +4049,6 @@
.build();
}
- private void updateManagerScanningForProviders(boolean isManagerScanning) {
- for (MediaRoute2Provider provider : mRouteProviders) {
- if (provider instanceof MediaRoute2ProviderServiceProxy) {
- ((MediaRoute2ProviderServiceProxy) provider)
- .setManagerScanning(isManagerScanning);
- }
- }
- }
-
@NonNull
private static List<RouterRecord> getIndividuallyActiveRouters(
MediaRouter2ServiceImpl service, List<RouterRecord> allRouterRecords) {
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index 58d2401..01c94df 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -1265,8 +1265,10 @@
if (metadata == null) {
return null;
}
- MediaMetadata.Builder metadataBuilder = new MediaMetadata.Builder(metadata);
- for (String key: ART_URIS) {
+
+ // Find all URIs that need to be sanitized
+ List<String> urisToSanitize = new ArrayList<>();
+ for (String key : ART_URIS) {
String uriString = metadata.getString(key);
if (TextUtils.isEmpty(uriString)) {
continue;
@@ -1282,9 +1284,20 @@
Intent.FLAG_GRANT_READ_URI_PERMISSION,
ContentProvider.getUserIdFromUri(uri, getUserId()));
} catch (SecurityException e) {
- metadataBuilder.putString(key, null);
+ urisToSanitize.add(key);
}
}
+
+ // Avoid creating a Builder (and copying the metadata) if there are no URIs to
+ // sanitize.
+ if (urisToSanitize.isEmpty()) {
+ return metadata;
+ }
+
+ MediaMetadata.Builder metadataBuilder = new MediaMetadata.Builder(metadata);
+ for (String key : urisToSanitize) {
+ metadataBuilder.putString(key, null);
+ }
MediaMetadata sanitizedMetadata = metadataBuilder.build();
// sanitizedMetadata.size() guarantees that the underlying bundle is unparceled
// before we set it to prevent concurrent reads from throwing an
diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
index 90fbc66..10de736 100644
--- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
@@ -223,7 +223,9 @@
@Override
public void releaseSession(long requestId, String sessionId) {
- // Do nothing
+ if (Flags.enableOutputSwitcherPersonalAudioSharing()) {
+ mDeviceRouteController.releaseRoutingSession();
+ }
}
@Override
@@ -404,6 +406,10 @@
oldSessionInfo.getTransferInitiatorPackageName());
}
+ if (Flags.enableOutputSwitcherPersonalAudioSharing()) {
+ builder.setReleaseType(mDeviceRouteController.getSessionReleaseType());
+ }
+
return builder.setProviderId(mUniqueId).build();
}
}
@@ -541,6 +547,10 @@
}
}
+ if (Flags.enableOutputSwitcherPersonalAudioSharing()) {
+ builder.setReleaseType(mDeviceRouteController.getSessionReleaseType());
+ }
+
builder.setTransferReason(transferReason)
.setTransferInitiator(
transferInitiatorUserHandle, transferInitiatorPackageName);
@@ -570,6 +580,7 @@
.setTransferInitiator(
newSessionInfo.getTransferInitiatorUserHandle(),
newSessionInfo.getTransferInitiatorPackageName())
+ .setReleaseType(newSessionInfo.getReleaseType())
.build();
return true;
}
diff --git a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
index 34a6cb9..accbe77 100644
--- a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
+++ b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
@@ -776,7 +776,7 @@
int type,
boolean isPermanentGrant,
int displayId) {
- if (mContext.checkCallingPermission(MANAGE_MEDIA_PROJECTION)
+ if (mContext.checkCallingOrSelfPermission(MANAGE_MEDIA_PROJECTION)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Requires MANAGE_MEDIA_PROJECTION in order to grant "
+ "projection permission");
@@ -1092,6 +1092,7 @@
private boolean mRestoreSystemAlertWindow;
private int mTaskId = -1;
private LaunchCookie mLaunchCookie = null;
+ private boolean mIsRecordingOverlay = false;
// Count of number of times IMediaProjection#start is invoked.
private int mCountStarts = 0;
@@ -1321,6 +1322,23 @@
@android.annotation.EnforcePermission(android.Manifest.permission.MANAGE_MEDIA_PROJECTION)
@Override // Binder call
+ public void setRecordingOverlay(boolean isRecordingOverlay) {
+ setRecordingOverlay_enforcePermission();
+ if (!Flags.recordingOverlay()) {
+ return;
+ }
+
+ Binder.withCleanCallingIdentity(() -> {
+ String contextualSearchPackage = mContext.getResources().getString(
+ R.string.config_defaultContextualSearchPackageName);
+ if (packageName.equals(contextualSearchPackage)) {
+ mIsRecordingOverlay = isRecordingOverlay;
+ }
+ });
+ }
+
+ @android.annotation.EnforcePermission(android.Manifest.permission.MANAGE_MEDIA_PROJECTION)
+ @Override // Binder call
public LaunchCookie getLaunchCookie() {
getLaunchCookie_enforcePermission();
return mLaunchCookie;
@@ -1333,6 +1351,13 @@
return mTaskId;
}
+ @android.annotation.EnforcePermission(android.Manifest.permission.MANAGE_MEDIA_PROJECTION)
+ @Override // Binder call
+ public boolean isRecordingOverlay() {
+ isRecordingOverlay_enforcePermission();
+ return mIsRecordingOverlay;
+ }
+
@Override // Binder call
public int getDisplayId() {
return mDisplayId;
diff --git a/services/core/java/com/android/server/media/projection/MediaProjectionMetricsLogger.java b/services/core/java/com/android/server/media/projection/MediaProjectionMetricsLogger.java
index 8e5c016..dd52740 100644
--- a/services/core/java/com/android/server/media/projection/MediaProjectionMetricsLogger.java
+++ b/services/core/java/com/android/server/media/projection/MediaProjectionMetricsLogger.java
@@ -20,6 +20,7 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.view.ContentRecordingSession.RECORD_CONTENT_BELOW_OVERLAY;
import static android.view.ContentRecordingSession.RECORD_CONTENT_DISPLAY;
import static android.view.ContentRecordingSession.RECORD_CONTENT_TASK;
@@ -49,6 +50,7 @@
import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_TARGET_CHANGED__TARGET_CHANGE_TYPE__TARGET_CHANGE_WINDOWING_MODE;
import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_TARGET_CHANGED__TARGET_TYPE__TARGET_TYPE_APP_TASK;
import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_TARGET_CHANGED__TARGET_TYPE__TARGET_TYPE_DISPLAY;
+import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_TARGET_CHANGED__TARGET_TYPE__TARGET_TYPE_OVERLAY;
import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_TARGET_CHANGED__TARGET_TYPE__TARGET_TYPE_UNKNOWN;
import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_TARGET_CHANGED__TARGET_WINDOWING_MODE__WINDOWING_MODE_FREEFORM;
import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_TARGET_CHANGED__TARGET_WINDOWING_MODE__WINDOWING_MODE_FULLSCREEN;
@@ -288,6 +290,8 @@
return switch (recordContentType) {
case RECORD_CONTENT_DISPLAY ->
MEDIA_PROJECTION_TARGET_CHANGED__TARGET_TYPE__TARGET_TYPE_DISPLAY;
+ case RECORD_CONTENT_BELOW_OVERLAY ->
+ MEDIA_PROJECTION_TARGET_CHANGED__TARGET_TYPE__TARGET_TYPE_OVERLAY;
case RECORD_CONTENT_TASK ->
MEDIA_PROJECTION_TARGET_CHANGED__TARGET_TYPE__TARGET_TYPE_APP_TASK;
default -> MEDIA_PROJECTION_TARGET_CHANGED__TARGET_TYPE__TARGET_TYPE_UNKNOWN;
diff --git a/services/core/java/com/android/server/media/quality/MediaQualityService.java b/services/core/java/com/android/server/media/quality/MediaQualityService.java
index 21cbc66..912d647 100644
--- a/services/core/java/com/android/server/media/quality/MediaQualityService.java
+++ b/services/core/java/com/android/server/media/quality/MediaQualityService.java
@@ -42,6 +42,7 @@
import android.hardware.tv.mediaquality.SoundParameters;
import android.hardware.tv.mediaquality.StreamStatus;
import android.hardware.tv.mediaquality.VendorParamCapability;
+import android.hardware.tv.mediaquality.VendorParameterIdentifier;
import android.media.quality.ActiveProcessingPicture;
import android.media.quality.AmbientBacklightEvent;
import android.media.quality.AmbientBacklightMetadata;
@@ -105,6 +106,7 @@
private static final String PICTURE_PROFILE_PREFERENCE = "picture_profile_preference";
private static final String SOUND_PROFILE_PREFERENCE = "sound_profile_preference";
private static final String COMMA_DELIMITER = ",";
+ private static final String DEFAULT_PICTURE_PROFILE_ID = "default_picture_profile_id";
private final Context mContext;
private final MediaQualityDbHelper mMediaQualityDbHelper;
private final BiMap<Long, String> mPictureProfileTempIdMap;
@@ -276,53 +278,52 @@
@GuardedBy("mPictureProfileLock")
@Override
public void createPictureProfile(PictureProfile pp, int userId) {
- mHandler.post(
- () -> {
- if ((pp.getPackageName() != null && !pp.getPackageName().isEmpty()
- && !incomingPackageEqualsCallingUidPackage(pp.getPackageName()))
- && !hasGlobalPictureQualityServicePermission()) {
- mMqManagerNotifier.notifyOnPictureProfileError(null,
- PictureProfile.ERROR_NO_PERMISSION,
- Binder.getCallingUid(), Binder.getCallingPid());
- }
+ int callingUid = Binder.getCallingUid();
+ int callingPid = Binder.getCallingPid();
+ mHandler.post(() -> {
+ if ((pp.getPackageName() != null && !pp.getPackageName().isEmpty()
+ && !incomingPackageEqualsUidPackage(pp.getPackageName(), callingUid))
+ && !hasGlobalPictureQualityServicePermission(callingUid, callingPid)) {
+ mMqManagerNotifier.notifyOnPictureProfileError(
+ null, PictureProfile.ERROR_NO_PERMISSION, callingUid, callingPid);
+ }
- synchronized (mPictureProfileLock) {
- SQLiteDatabase db = mMediaQualityDbHelper.getWritableDatabase();
+ synchronized (mPictureProfileLock) {
+ SQLiteDatabase db = mMediaQualityDbHelper.getWritableDatabase();
- ContentValues values = MediaQualityUtils.getContentValues(null,
- pp.getProfileType(),
- pp.getName(),
- pp.getPackageName() == null || pp.getPackageName().isEmpty()
- ? getPackageOfCallingUid() : pp.getPackageName(),
- pp.getInputId(),
- pp.getParameters());
+ ContentValues values = MediaQualityUtils.getContentValues(null,
+ pp.getProfileType(), pp.getName(),
+ pp.getPackageName() == null || pp.getPackageName().isEmpty()
+ ? getPackageOfUid(callingUid)
+ : pp.getPackageName(),
+ pp.getInputId(), pp.getParameters());
- // id is auto-generated by SQLite upon successful insertion of row
- Long id = db.insert(mMediaQualityDbHelper.PICTURE_QUALITY_TABLE_NAME,
- null, values);
- MediaQualityUtils.populateTempIdMap(mPictureProfileTempIdMap, id);
- String value = mPictureProfileTempIdMap.getValue(id);
- pp.setProfileId(value);
- mMqManagerNotifier.notifyOnPictureProfileAdded(value, pp,
- Binder.getCallingUid(), Binder.getCallingPid());
- if (isPackageDefaultPictureProfile(pp)) {
- mPackageDefaultPictureProfileHandleMap.put(
- pp.getPackageName(), pp.getHandle().getId());
- }
- }
+ // id is auto-generated by SQLite upon successful insertion of row
+ Long id = db.insert(
+ mMediaQualityDbHelper.PICTURE_QUALITY_TABLE_NAME, null, values);
+ MediaQualityUtils.populateTempIdMap(mPictureProfileTempIdMap, id);
+ String value = mPictureProfileTempIdMap.getValue(id);
+ pp.setProfileId(value);
+ mMqManagerNotifier.notifyOnPictureProfileAdded(
+ value, pp, callingUid, callingPid);
+ if (isPackageDefaultPictureProfile(pp)) {
+ mPackageDefaultPictureProfileHandleMap.put(
+ pp.getPackageName(), pp.getHandle().getId());
}
- );
+ }
+ });
}
@GuardedBy("mPictureProfileLock")
@Override
public void updatePictureProfile(String id, PictureProfile pp, int userId) {
+ int callingUid = Binder.getCallingUid();
+ int callingPid = Binder.getCallingPid();
mHandler.post(() -> {
Long dbId = mPictureProfileTempIdMap.getKey(id);
- if (!hasPermissionToUpdatePictureProfile(dbId, pp)) {
- mMqManagerNotifier.notifyOnPictureProfileError(id,
- PictureProfile.ERROR_NO_PERMISSION,
- Binder.getCallingUid(), Binder.getCallingPid());
+ if (!hasPermissionToUpdatePictureProfile(dbId, pp, callingUid)) {
+ mMqManagerNotifier.notifyOnPictureProfileError(
+ id, PictureProfile.ERROR_NO_PERMISSION, callingUid, callingPid);
}
synchronized (mPictureProfileLock) {
ContentValues values = MediaQualityUtils.getContentValues(dbId,
@@ -331,8 +332,8 @@
pp.getPackageName(),
pp.getInputId(),
pp.getParameters());
- updateDatabaseOnPictureProfileAndNotifyManagerAndHal(values,
- pp.getParameters());
+ updateDatabaseOnPictureProfileAndNotifyManager(
+ values, pp.getParameters(), callingUid, callingPid, true);
if (isPackageDefaultPictureProfile(pp)) {
mPackageDefaultPictureProfileHandleMap.put(
pp.getPackageName(), pp.getHandle().getId());
@@ -341,26 +342,28 @@
});
}
- private boolean hasPermissionToUpdatePictureProfile(Long dbId, PictureProfile toUpdate) {
+ private boolean hasPermissionToUpdatePictureProfile(
+ Long dbId, PictureProfile toUpdate, int uid) {
PictureProfile fromDb = mMqDatabaseUtils.getPictureProfile(dbId);
return fromDb.getProfileType() == toUpdate.getProfileType()
&& fromDb.getPackageName().equals(toUpdate.getPackageName())
&& fromDb.getName().equals(toUpdate.getName())
- && fromDb.getName().equals(getPackageOfCallingUid());
+ && fromDb.getName().equals(getPackageOfUid(uid));
}
@GuardedBy("mPictureProfileLock")
@Override
public void removePictureProfile(String id, int userId) {
+ int callingUid = Binder.getCallingUid();
+ int callingPid = Binder.getCallingPid();
mHandler.post(() -> {
synchronized (mPictureProfileLock) {
Long dbId = mPictureProfileTempIdMap.getKey(id);
PictureProfile toDelete = mMqDatabaseUtils.getPictureProfile(dbId);
- if (!hasPermissionToRemovePictureProfile(toDelete)) {
- mMqManagerNotifier.notifyOnPictureProfileError(id,
- PictureProfile.ERROR_NO_PERMISSION,
- Binder.getCallingUid(), Binder.getCallingPid());
+ if (!hasPermissionToRemovePictureProfile(toDelete, callingUid)) {
+ mMqManagerNotifier.notifyOnPictureProfileError(
+ id, PictureProfile.ERROR_NO_PERMISSION, callingUid, callingPid);
}
if (dbId != null) {
@@ -371,14 +374,12 @@
selection, selectionArgs);
if (result == 0) {
mMqManagerNotifier.notifyOnPictureProfileError(id,
- PictureProfile.ERROR_INVALID_ARGUMENT,
- Binder.getCallingUid(), Binder.getCallingPid());
+ PictureProfile.ERROR_INVALID_ARGUMENT, callingUid, callingPid);
} else {
mMqManagerNotifier.notifyOnPictureProfileRemoved(
- mPictureProfileTempIdMap.getValue(dbId), toDelete,
- Binder.getCallingUid(), Binder.getCallingPid());
+ mPictureProfileTempIdMap.getValue(dbId), toDelete, callingUid,
+ callingPid);
mPictureProfileTempIdMap.remove(dbId);
- mHalNotifier.notifyHalOnPictureProfileChange(dbId, null);
}
}
@@ -390,9 +391,9 @@
});
}
- private boolean hasPermissionToRemovePictureProfile(PictureProfile toDelete) {
+ private boolean hasPermissionToRemovePictureProfile(PictureProfile toDelete, int uid) {
if (toDelete != null) {
- return toDelete.getName().equalsIgnoreCase(getPackageOfCallingUid());
+ return toDelete.getName().equalsIgnoreCase(getPackageOfUid(uid));
}
return false;
}
@@ -401,10 +402,12 @@
@Override
public PictureProfile getPictureProfile(int type, String name, boolean includeParams,
int userId) {
+ int callingUid = Binder.getCallingUid();
String selection = BaseParameters.PARAMETER_TYPE + " = ? AND "
+ BaseParameters.PARAMETER_NAME + " = ? AND "
+ BaseParameters.PARAMETER_PACKAGE + " = ?";
- String[] selectionArguments = {Integer.toString(type), name, getPackageOfCallingUid()};
+ String[] selectionArguments = {
+ Integer.toString(type), name, getPackageOfUid(callingUid)};
synchronized (mPictureProfileLock) {
try (
@@ -435,10 +438,17 @@
@Override
public List<PictureProfile> getPictureProfilesByPackage(
String packageName, boolean includeParams, int userId) {
- if (!hasGlobalPictureQualityServicePermission()) {
- mMqManagerNotifier.notifyOnPictureProfileError(null,
- PictureProfile.ERROR_NO_PERMISSION,
- Binder.getCallingUid(), Binder.getCallingPid());
+ int callingUid = Binder.getCallingUid();
+ int callingPid = Binder.getCallingPid();
+ return getPictureProfilesByPackage(
+ packageName, includeParams, userId, callingUid, callingPid);
+ }
+
+ private List<PictureProfile> getPictureProfilesByPackage(
+ String packageName, boolean includeParams, int userId, int uid, int pid) {
+ if (!hasGlobalPictureQualityServicePermission(uid, pid)) {
+ mMqManagerNotifier.notifyOnPictureProfileError(
+ null, PictureProfile.ERROR_NO_PERMISSION, uid, pid);
}
synchronized (mPictureProfileLock) {
@@ -453,26 +463,54 @@
@GuardedBy("mPictureProfileLock")
@Override
public List<PictureProfile> getAvailablePictureProfiles(boolean includeParams, int userId) {
- String packageName = getPackageOfCallingUid();
+ int callingUid = BinderService.getCallingUid();
+ int callingPid = BinderService.getCallingPid();
+ String packageName = getPackageOfUid(callingUid);
if (packageName != null) {
- return getPictureProfilesByPackage(packageName, includeParams, userId);
+ return getPictureProfilesByPackage(
+ packageName, includeParams, userId, callingUid, callingPid);
}
return new ArrayList<>();
}
@GuardedBy("mPictureProfileLock")
@Override
+ public PictureProfile getDefaultPictureProfile() {
+ int callingUid = Binder.getCallingUid();
+ int callingPid = Binder.getCallingPid();
+ if (!hasGlobalPictureQualityServicePermission(callingUid, callingPid)) {
+ mMqManagerNotifier.notifyOnPictureProfileError(
+ null, PictureProfile.ERROR_NO_PERMISSION, callingUid, callingPid);
+ }
+ Long defaultPictureProfileId = mPictureProfileSharedPreference.getLong(
+ DEFAULT_PICTURE_PROFILE_ID,
+ -1
+ );
+ if (defaultPictureProfileId != -1) {
+ return mMqDatabaseUtils.getPictureProfile(defaultPictureProfileId);
+ }
+ return null;
+ }
+
+ @GuardedBy("mPictureProfileLock")
+ @Override
public boolean setDefaultPictureProfile(String profileId, int userId) {
- if (!hasGlobalPictureQualityServicePermission()) {
- mMqManagerNotifier.notifyOnPictureProfileError(profileId,
- PictureProfile.ERROR_NO_PERMISSION,
- Binder.getCallingUid(), Binder.getCallingPid());
+ int callingUid = Binder.getCallingUid();
+ int callingPid = Binder.getCallingPid();
+ if (!hasGlobalPictureQualityServicePermission(callingUid, callingPid)) {
+ mMqManagerNotifier.notifyOnPictureProfileError(
+ profileId, PictureProfile.ERROR_NO_PERMISSION, callingUid, callingPid);
}
Long longId = mPictureProfileTempIdMap.getKey(profileId);
if (longId == null) {
return false;
}
+
+ SharedPreferences.Editor editor = mPictureProfileSharedPreference.edit();
+ editor.putLong(DEFAULT_PICTURE_PROFILE_ID, longId);
+ editor.apply();
+
PictureProfile pictureProfile = mMqDatabaseUtils.getPictureProfile(longId);
PersistableBundle params = pictureProfile.getParameters();
@@ -504,10 +542,11 @@
@GuardedBy("mPictureProfileLock")
@Override
public List<String> getPictureProfilePackageNames(int userId) {
- if (!hasGlobalPictureQualityServicePermission()) {
- mMqManagerNotifier.notifyOnPictureProfileError(null,
- PictureProfile.ERROR_NO_PERMISSION,
- Binder.getCallingUid(), Binder.getCallingPid());
+ int callingUid = Binder.getCallingUid();
+ int callingPid = Binder.getCallingPid();
+ if (!hasGlobalPictureQualityServicePermission(callingUid, callingPid)) {
+ mMqManagerNotifier.notifyOnPictureProfileError(
+ null, PictureProfile.ERROR_NO_PERMISSION, callingUid, callingPid);
}
String [] column = {BaseParameters.PARAMETER_PACKAGE};
synchronized (mPictureProfileLock) {
@@ -542,6 +581,9 @@
public long getPictureProfileHandleValue(String id, int userId) {
synchronized (mPictureProfileLock) {
Long value = mPictureProfileTempIdMap.getKey(id);
+ if (value != null) {
+ mHalNotifier.notifyHalOnPictureProfileChange(value, null);
+ }
return value != null ? value : -1;
}
}
@@ -549,11 +591,15 @@
@GuardedBy("mPictureProfileLock")
@Override
public long getDefaultPictureProfileHandleValue(int userId) {
+ int callingUid = Binder.getCallingUid();
synchronized (mPictureProfileLock) {
- String packageName = getPackageOfCallingUid();
+ String packageName = getPackageOfUid(callingUid);
Long value = null;
if (packageName != null) {
value = mPackageDefaultPictureProfileHandleMap.get(packageName);
+ if (value != null) {
+ mHalNotifier.notifyHalOnPictureProfileChange(value, null);
+ }
}
return value != null ? value : -1;
}
@@ -570,10 +616,11 @@
public long getPictureProfileForTvInput(String inputId, int userId) {
// TODO: cache profiles
- if (!hasGlobalPictureQualityServicePermission()) {
- mMqManagerNotifier.notifyOnPictureProfileError(null,
- PictureProfile.ERROR_NO_PERMISSION,
- Binder.getCallingUid(), Binder.getCallingPid());
+ int callingUid = Binder.getCallingUid();
+ int callingPid = Binder.getCallingPid();
+ if (!hasGlobalPictureQualityServicePermission(callingUid, callingPid)) {
+ mMqManagerNotifier.notifyOnPictureProfileError(
+ null, PictureProfile.ERROR_NO_PERMISSION, callingUid, callingPid);
}
String[] columns = {BaseParameters.PARAMETER_ID};
String selection = BaseParameters.PARAMETER_TYPE + " = ? AND "
@@ -623,24 +670,25 @@
@GuardedBy("mSoundProfileLock")
@Override
public void createSoundProfile(SoundProfile sp, int userId) {
+ int callingUid = Binder.getCallingUid();
+ int callingPid = Binder.getCallingPid();
mHandler.post(() -> {
if ((sp.getPackageName() != null && !sp.getPackageName().isEmpty()
- && !incomingPackageEqualsCallingUidPackage(sp.getPackageName()))
- && !hasGlobalSoundQualityServicePermission()) {
- mMqManagerNotifier.notifyOnSoundProfileError(null, SoundProfile.ERROR_NO_PERMISSION,
- Binder.getCallingUid(), Binder.getCallingPid());
+ && !incomingPackageEqualsUidPackage(sp.getPackageName(), callingUid))
+ && !hasGlobalSoundQualityServicePermission(callingUid, callingPid)) {
+ mMqManagerNotifier.notifyOnSoundProfileError(
+ null, SoundProfile.ERROR_NO_PERMISSION, callingUid, callingPid);
}
synchronized (mSoundProfileLock) {
SQLiteDatabase db = mMediaQualityDbHelper.getWritableDatabase();
ContentValues values = MediaQualityUtils.getContentValues(null,
- sp.getProfileType(),
- sp.getName(),
+ sp.getProfileType(), sp.getName(),
sp.getPackageName() == null || sp.getPackageName().isEmpty()
- ? getPackageOfCallingUid() : sp.getPackageName(),
- sp.getInputId(),
- sp.getParameters());
+ ? getPackageOfUid(callingUid)
+ : sp.getPackageName(),
+ sp.getInputId(), sp.getParameters());
// id is auto-generated by SQLite upon successful insertion of row
Long id = db.insert(mMediaQualityDbHelper.SOUND_QUALITY_TABLE_NAME,
@@ -648,8 +696,7 @@
MediaQualityUtils.populateTempIdMap(mSoundProfileTempIdMap, id);
String value = mSoundProfileTempIdMap.getValue(id);
sp.setProfileId(value);
- mMqManagerNotifier.notifyOnSoundProfileAdded(value, sp, Binder.getCallingUid(),
- Binder.getCallingPid());
+ mMqManagerNotifier.notifyOnSoundProfileAdded(value, sp, callingUid, callingPid);
}
});
}
@@ -657,12 +704,13 @@
@GuardedBy("mSoundProfileLock")
@Override
public void updateSoundProfile(String id, SoundProfile sp, int userId) {
+ int callingUid = Binder.getCallingUid();
+ int callingPid = Binder.getCallingPid();
mHandler.post(() -> {
Long dbId = mSoundProfileTempIdMap.getKey(id);
- if (!hasPermissionToUpdateSoundProfile(dbId, sp)) {
- mMqManagerNotifier.notifyOnSoundProfileError(id,
- SoundProfile.ERROR_NO_PERMISSION,
- Binder.getCallingUid(), Binder.getCallingPid());
+ if (!hasPermissionToUpdateSoundProfile(dbId, sp, callingUid)) {
+ mMqManagerNotifier.notifyOnSoundProfileError(
+ id, SoundProfile.ERROR_NO_PERMISSION, callingUid, callingPid);
}
synchronized (mSoundProfileLock) {
@@ -673,30 +721,32 @@
sp.getInputId(),
sp.getParameters());
- updateDatabaseOnSoundProfileAndNotifyManagerAndHal(values, sp.getParameters());
+ updateDatabaseOnSoundProfileAndNotifyManager(
+ values, sp.getParameters(), callingUid, callingPid, true);
}
});
}
- private boolean hasPermissionToUpdateSoundProfile(Long dbId, SoundProfile sp) {
+ private boolean hasPermissionToUpdateSoundProfile(Long dbId, SoundProfile sp, int uid) {
SoundProfile fromDb = mMqDatabaseUtils.getSoundProfile(dbId);
return fromDb.getProfileType() == sp.getProfileType()
&& fromDb.getPackageName().equals(sp.getPackageName())
&& fromDb.getName().equals(sp.getName())
- && fromDb.getName().equals(getPackageOfCallingUid());
+ && fromDb.getName().equals(getPackageOfUid(uid));
}
@GuardedBy("mSoundProfileLock")
@Override
public void removeSoundProfile(String id, int userId) {
+ int callingUid = Binder.getCallingUid();
+ int callingPid = Binder.getCallingPid();
mHandler.post(() -> {
synchronized (mSoundProfileLock) {
Long dbId = mSoundProfileTempIdMap.getKey(id);
SoundProfile toDelete = mMqDatabaseUtils.getSoundProfile(dbId);
- if (!hasPermissionToRemoveSoundProfile(toDelete)) {
- mMqManagerNotifier.notifyOnSoundProfileError(id,
- SoundProfile.ERROR_NO_PERMISSION,
- Binder.getCallingUid(), Binder.getCallingPid());
+ if (!hasPermissionToRemoveSoundProfile(toDelete, callingUid)) {
+ mMqManagerNotifier.notifyOnSoundProfileError(
+ id, SoundProfile.ERROR_NO_PERMISSION, callingUid, callingPid);
}
if (dbId != null) {
SQLiteDatabase db = mMediaQualityDbHelper.getWritableDatabase();
@@ -707,23 +757,21 @@
selectionArgs);
if (result == 0) {
mMqManagerNotifier.notifyOnSoundProfileError(id,
- SoundProfile.ERROR_INVALID_ARGUMENT,
- Binder.getCallingUid(), Binder.getCallingPid());
+ SoundProfile.ERROR_INVALID_ARGUMENT, callingUid, callingPid);
} else {
mMqManagerNotifier.notifyOnSoundProfileRemoved(
- mSoundProfileTempIdMap.getValue(dbId), toDelete,
- Binder.getCallingUid(), Binder.getCallingPid());
+ mSoundProfileTempIdMap.getValue(dbId), toDelete, callingUid,
+ callingPid);
mSoundProfileTempIdMap.remove(dbId);
- mHalNotifier.notifyHalOnSoundProfileChange(dbId, null);
}
}
}
});
}
- private boolean hasPermissionToRemoveSoundProfile(SoundProfile toDelete) {
+ private boolean hasPermissionToRemoveSoundProfile(SoundProfile toDelete, int uid) {
if (toDelete != null) {
- return toDelete.getName().equalsIgnoreCase(getPackageOfCallingUid());
+ return toDelete.getName().equalsIgnoreCase(getPackageOfUid(uid));
}
return false;
}
@@ -732,10 +780,12 @@
@Override
public SoundProfile getSoundProfile(int type, String name, boolean includeParams,
int userId) {
+ int callingUid = Binder.getCallingUid();
+
String selection = BaseParameters.PARAMETER_TYPE + " = ? AND "
+ BaseParameters.PARAMETER_NAME + " = ? AND "
+ BaseParameters.PARAMETER_PACKAGE + " = ?";
- String[] selectionArguments = {String.valueOf(type), name, getPackageOfCallingUid()};
+ String[] selectionArguments = {String.valueOf(type), name, getPackageOfUid(callingUid)};
synchronized (mSoundProfileLock) {
try (
@@ -766,9 +816,17 @@
@Override
public List<SoundProfile> getSoundProfilesByPackage(
String packageName, boolean includeParams, int userId) {
- if (!hasGlobalSoundQualityServicePermission()) {
- mMqManagerNotifier.notifyOnSoundProfileError(null, SoundProfile.ERROR_NO_PERMISSION,
- Binder.getCallingUid(), Binder.getCallingPid());
+ int callingUid = Binder.getCallingUid();
+ int callingPid = Binder.getCallingPid();
+ return getSoundProfilesByPackage(
+ packageName, includeParams, userId, callingUid, callingPid);
+ }
+
+ private List<SoundProfile> getSoundProfilesByPackage(
+ String packageName, boolean includeParams, int userId, int uid, int pid) {
+ if (!hasGlobalSoundQualityServicePermission(uid, pid)) {
+ mMqManagerNotifier.notifyOnSoundProfileError(
+ null, SoundProfile.ERROR_NO_PERMISSION, uid, pid);
}
synchronized (mSoundProfileLock) {
@@ -783,9 +841,12 @@
@GuardedBy("mSoundProfileLock")
@Override
public List<SoundProfile> getAvailableSoundProfiles(boolean includeParams, int userId) {
- String packageName = getPackageOfCallingUid();
+ int callingUid = BinderService.getCallingUid();
+ int callingPid = BinderService.getCallingPid();
+ String packageName = getPackageOfUid(callingUid);
if (packageName != null) {
- return getSoundProfilesByPackage(packageName, includeParams, userId);
+ return getSoundProfilesByPackage(
+ packageName, includeParams, userId, callingUid, callingPid);
}
return new ArrayList<>();
}
@@ -793,10 +854,11 @@
@GuardedBy("mSoundProfileLock")
@Override
public boolean setDefaultSoundProfile(String profileId, int userId) {
- if (!hasGlobalSoundQualityServicePermission()) {
- mMqManagerNotifier.notifyOnSoundProfileError(profileId,
- SoundProfile.ERROR_NO_PERMISSION,
- Binder.getCallingUid(), Binder.getCallingPid());
+ int callingUid = Binder.getCallingUid();
+ int callingPid = Binder.getCallingPid();
+ if (!hasGlobalSoundQualityServicePermission(callingUid, callingPid)) {
+ mMqManagerNotifier.notifyOnSoundProfileError(
+ profileId, SoundProfile.ERROR_NO_PERMISSION, callingUid, callingPid);
}
Long longId = mSoundProfileTempIdMap.getKey(profileId);
@@ -832,9 +894,11 @@
@GuardedBy("mSoundProfileLock")
@Override
public List<String> getSoundProfilePackageNames(int userId) {
- if (!hasGlobalSoundQualityServicePermission()) {
- mMqManagerNotifier.notifyOnSoundProfileError(null, SoundProfile.ERROR_NO_PERMISSION,
- Binder.getCallingUid(), Binder.getCallingPid());
+ int callingUid = Binder.getCallingUid();
+ int callingPid = Binder.getCallingPid();
+ if (!hasGlobalSoundQualityServicePermission(callingUid, callingPid)) {
+ mMqManagerNotifier.notifyOnSoundProfileError(
+ null, SoundProfile.ERROR_NO_PERMISSION, callingUid, callingPid);
}
String [] column = {BaseParameters.PARAMETER_NAME};
@@ -849,65 +913,67 @@
}
}
- private String getPackageOfCallingUid() {
- String[] packageNames = mPackageManager.getPackagesForUid(
- Binder.getCallingUid());
+ private String getPackageOfUid(int uid) {
+ String[] packageNames = mPackageManager.getPackagesForUid(uid);
if (packageNames != null && packageNames.length == 1 && !packageNames[0].isEmpty()) {
return packageNames[0];
}
return null;
}
- private boolean incomingPackageEqualsCallingUidPackage(String incomingPackage) {
- return incomingPackage.equalsIgnoreCase(getPackageOfCallingUid());
+ private boolean incomingPackageEqualsUidPackage(String incomingPackage, int uid) {
+ return incomingPackage.equalsIgnoreCase(getPackageOfUid(uid));
}
- private boolean hasGlobalPictureQualityServicePermission() {
- return mContext.checkCallingPermission(
- android.Manifest.permission.MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE)
+ private boolean hasGlobalPictureQualityServicePermission(int uid, int pid) {
+ return mContext.checkPermission(
+ android.Manifest.permission.MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE, pid,
+ uid)
== PackageManager.PERMISSION_GRANTED;
}
- private boolean hasGlobalSoundQualityServicePermission() {
- return mContext.checkCallingPermission(
- android.Manifest.permission.MANAGE_GLOBAL_SOUND_QUALITY_SERVICE)
+ private boolean hasGlobalSoundQualityServicePermission(int uid, int pid) {
+ return mContext.checkPermission(
+ android.Manifest.permission.MANAGE_GLOBAL_SOUND_QUALITY_SERVICE, pid,
+ uid)
== PackageManager.PERMISSION_GRANTED;
}
- private boolean hasReadColorZonesPermission() {
- return mContext.checkCallingPermission(
- android.Manifest.permission.READ_COLOR_ZONES)
+ private boolean hasReadColorZonesPermission(int uid, int pid) {
+ return mContext.checkPermission(android.Manifest.permission.READ_COLOR_ZONES, pid, uid)
== PackageManager.PERMISSION_GRANTED;
}
@Override
public void registerPictureProfileCallback(final IPictureProfileCallback callback) {
- int callingPid = Binder.getCallingPid();
int callingUid = Binder.getCallingUid();
+ int callingPid = Binder.getCallingPid();
- UserState userState = getOrCreateUserState(Binder.getCallingUid());
+ UserState userState = getOrCreateUserState(UserHandle.USER_SYSTEM);
userState.mPictureProfileCallbackPidUidMap.put(callback,
Pair.create(callingPid, callingUid));
+ userState.mPictureProfileCallbacks.register(callback);
}
@Override
public void registerSoundProfileCallback(final ISoundProfileCallback callback) {
- int callingPid = Binder.getCallingPid();
int callingUid = Binder.getCallingUid();
+ int callingPid = Binder.getCallingPid();
- UserState userState = getOrCreateUserState(Binder.getCallingUid());
+ UserState userState = getOrCreateUserState(UserHandle.USER_SYSTEM);
userState.mSoundProfileCallbackPidUidMap.put(callback,
Pair.create(callingPid, callingUid));
+ userState.mSoundProfileCallbacks.register(callback);
}
@Override
public void registerActiveProcessingPictureListener(
final IActiveProcessingPictureListener l) {
- int callingPid = Binder.getCallingPid();
int callingUid = Binder.getCallingUid();
+ int callingPid = Binder.getCallingPid();
- UserState userState = getOrCreateUserState(Binder.getCallingUid());
- String packageName = getPackageOfCallingUid();
+ UserState userState = getOrCreateUserState(UserHandle.USER_SYSTEM);
+ String packageName = getPackageOfUid(callingUid);
userState.mActiveProcessingPictureListenerMap.put(l,
new ActiveProcessingPictureListenerInfo(callingUid, callingPid, packageName));
}
@@ -917,12 +983,14 @@
if (DEBUG) {
Slogf.d(TAG, "registerAmbientBacklightCallback");
}
+ int callingUid = Binder.getCallingUid();
+ int callingPid = Binder.getCallingPid();
- if (!hasReadColorZonesPermission()) {
+ if (!hasReadColorZonesPermission(callingUid, callingPid)) {
//TODO: error handling
}
- String callingPackageName = getPackageOfCallingUid();
+ String callingPackageName = getPackageOfUid(callingUid);
synchronized (mCallbackRecords) {
AmbientBacklightCallbackRecord record = mCallbackRecords.get(callingPackageName);
@@ -940,11 +1008,13 @@
}
public void unregisterAmbientBacklightCallback(IAmbientBacklightCallback callback) {
+ int callingUid = Binder.getCallingUid();
+ int callingPid = Binder.getCallingPid();
if (DEBUG) {
Slogf.d(TAG, "unregisterAmbientBacklightCallback");
}
- if (!hasReadColorZonesPermission()) {
+ if (!hasReadColorZonesPermission(callingUid, callingPid)) {
//TODO: error handling
}
@@ -967,7 +1037,10 @@
Slogf.d(TAG, "setAmbientBacklightSettings " + settings);
}
- if (!hasReadColorZonesPermission()) {
+ int callingUid = Binder.getCallingUid();
+ int callingPid = Binder.getCallingPid();
+
+ if (!hasReadColorZonesPermission(callingUid, callingPid)) {
//TODO: error handling
}
@@ -975,7 +1048,7 @@
if (mMediaQuality != null) {
android.hardware.tv.mediaquality.AmbientBacklightSettings halSettings =
new android.hardware.tv.mediaquality.AmbientBacklightSettings();
- halSettings.uid = Binder.getCallingUid();
+ halSettings.uid = callingUid;
halSettings.source = (byte) settings.getSource();
halSettings.maxFramerate = settings.getMaxFps();
halSettings.colorFormat = (byte) settings.getColorFormat();
@@ -987,7 +1060,7 @@
mMediaQuality.setAmbientBacklightDetector(halSettings);
mHalAmbientBacklightCallback.setAmbientBacklightClientPackageName(
- getPackageOfCallingUid());
+ getPackageOfUid(callingUid));
if (DEBUG) {
Slogf.d(TAG, "set ambient settings package: " + halSettings.uid);
@@ -1001,10 +1074,12 @@
@GuardedBy("mAmbientBacklightLock")
@Override
public void setAmbientBacklightEnabled(boolean enabled, int userId) {
+ int callingUid = Binder.getCallingUid();
+ int callingPid = Binder.getCallingPid();
if (DEBUG) {
Slogf.d(TAG, "setAmbientBacklightEnabled " + enabled);
}
- if (!hasReadColorZonesPermission()) {
+ if (!hasReadColorZonesPermission(callingUid, callingPid)) {
//TODO: error handling
}
try {
@@ -1029,10 +1104,45 @@
Slog.e(TAG, "Failed to get parameter capabilities", e);
}
- return getListParameterCapability(caps);
+ //Handle vendor parameter capability.
+ MediaQualityUtils.getVendorParamsByRemovePreDefineParams(names);
+ int namesCount = names.size();
+ VendorParamCapability[] vendorParamCapabilities =
+ new VendorParamCapability[namesCount];
+ if (!names.isEmpty()) {
+ List<VendorParameterIdentifier> vendorParamIdentifiersList = new ArrayList<>();
+ for (String name: names) {
+ DefaultExtension vendorParamCapDefaultExtension = new DefaultExtension();
+ Parcel vendorParamCapParcel = Parcel.obtain();
+ vendorParamCapParcel.writeString(name);
+ vendorParamCapDefaultExtension.bytes = vendorParamCapParcel.marshall();
+
+ VendorParameterIdentifier vendorParamIdentifier =
+ new VendorParameterIdentifier();
+ vendorParamIdentifier.identifier.setParcelable(vendorParamCapDefaultExtension);
+ vendorParamIdentifiersList.add(vendorParamIdentifier);
+ vendorParamCapParcel.recycle();
+ }
+
+ VendorParameterIdentifier[] vendorParamIdentifierArray =
+ new VendorParameterIdentifier[namesCount];
+ for (int i = 0; i < namesCount; i++) {
+ vendorParamIdentifierArray[i] = vendorParamIdentifiersList.get(i);
+ }
+
+ try {
+ mMediaQuality.getVendorParamCaps(
+ vendorParamIdentifierArray, vendorParamCapabilities);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to get vendor parameter capabilities", e);
+ }
+ }
+
+ return getParameterCapabilityList(caps, vendorParamCapabilities);
}
- private List<ParameterCapability> getListParameterCapability(ParamCapability[] caps) {
+ private List<ParameterCapability> getParameterCapabilityList(
+ ParamCapability[] caps, VendorParamCapability[] vendorParamCaps) {
List<ParameterCapability> pcList = new ArrayList<>();
if (caps != null) {
@@ -1048,16 +1158,38 @@
}
}
+ if (vendorParamCaps != null) {
+ for (VendorParamCapability vpcHal : vendorParamCaps) {
+ if (vpcHal != null) {
+ String name = MediaQualityUtils.getVendorParameterName(vpcHal);
+ boolean isSupported = vpcHal.isSupported;
+ // The default value for VendorParamCapability in HAL is IntValue = 0,
+ // LongValue = 1, DoubleValue = 2, StringValue = 3. The default value for
+ // ParameterCapability in the framework is None = 0, IntValue = 1,
+ // LongValue = 2, DoubleValue = 3, StringValue = 4. So +1 here to map the
+ // default value in HAL with framework.
+ int type = vpcHal.defaultValue
+ == null ? 0 : vpcHal.defaultValue.getTag() + 1;
+ Bundle paramRangeBundle = MediaQualityUtils.convertToCaps(
+ type, vpcHal.range);
+ MediaQualityUtils.convertToVendorCaps(vpcHal, paramRangeBundle);
+ pcList.add(new ParameterCapability(
+ name, isSupported, type, paramRangeBundle));
+ }
+ }
+ }
+
return pcList;
}
@GuardedBy("mPictureProfileLock")
@Override
public List<String> getPictureProfileAllowList(int userId) {
- if (!hasGlobalPictureQualityServicePermission()) {
- mMqManagerNotifier.notifyOnPictureProfileError(null,
- PictureProfile.ERROR_NO_PERMISSION,
- Binder.getCallingUid(), Binder.getCallingPid());
+ int callingUid = Binder.getCallingUid();
+ int callingPid = Binder.getCallingPid();
+ if (!hasGlobalPictureQualityServicePermission(callingUid, callingPid)) {
+ mMqManagerNotifier.notifyOnPictureProfileError(
+ null, PictureProfile.ERROR_NO_PERMISSION, callingUid, callingPid);
}
String allowlist = mPictureProfileSharedPreference.getString(ALLOWLIST, null);
if (allowlist != null) {
@@ -1070,11 +1202,12 @@
@GuardedBy("mPictureProfileLock")
@Override
public void setPictureProfileAllowList(List<String> packages, int userId) {
+ int callingUid = Binder.getCallingUid();
+ int callingPid = Binder.getCallingPid();
mHandler.post(() -> {
- if (!hasGlobalPictureQualityServicePermission()) {
- mMqManagerNotifier.notifyOnPictureProfileError(null,
- PictureProfile.ERROR_NO_PERMISSION,
- Binder.getCallingUid(), Binder.getCallingPid());
+ if (!hasGlobalPictureQualityServicePermission(callingUid, callingPid)) {
+ mMqManagerNotifier.notifyOnPictureProfileError(
+ null, PictureProfile.ERROR_NO_PERMISSION, callingUid, callingPid);
}
SharedPreferences.Editor editor = mPictureProfileSharedPreference.edit();
editor.putString(ALLOWLIST, String.join(COMMA_DELIMITER, packages));
@@ -1085,9 +1218,11 @@
@GuardedBy("mSoundProfileLock")
@Override
public List<String> getSoundProfileAllowList(int userId) {
- if (!hasGlobalSoundQualityServicePermission()) {
- mMqManagerNotifier.notifyOnSoundProfileError(null, SoundProfile.ERROR_NO_PERMISSION,
- Binder.getCallingUid(), Binder.getCallingPid());
+ int callingUid = Binder.getCallingUid();
+ int callingPid = Binder.getCallingPid();
+ if (!hasGlobalSoundQualityServicePermission(callingUid, callingPid)) {
+ mMqManagerNotifier.notifyOnSoundProfileError(
+ null, SoundProfile.ERROR_NO_PERMISSION, callingUid, callingPid);
}
String allowlist = mSoundProfileSharedPreference.getString(ALLOWLIST, null);
if (allowlist != null) {
@@ -1100,11 +1235,12 @@
@GuardedBy("mSoundProfileLock")
@Override
public void setSoundProfileAllowList(List<String> packages, int userId) {
+ int callingUid = Binder.getCallingUid();
+ int callingPid = Binder.getCallingPid();
mHandler.post(() -> {
- if (!hasGlobalSoundQualityServicePermission()) {
- mMqManagerNotifier.notifyOnSoundProfileError(null,
- SoundProfile.ERROR_NO_PERMISSION,
- Binder.getCallingUid(), Binder.getCallingPid());
+ if (!hasGlobalSoundQualityServicePermission(callingUid, callingPid)) {
+ mMqManagerNotifier.notifyOnSoundProfileError(
+ null, SoundProfile.ERROR_NO_PERMISSION, callingUid, callingPid);
}
SharedPreferences.Editor editor = mSoundProfileSharedPreference.edit();
editor.putString(ALLOWLIST, String.join(COMMA_DELIMITER, packages));
@@ -1120,11 +1256,12 @@
@GuardedBy("mPictureProfileLock")
@Override
public void setAutoPictureQualityEnabled(boolean enabled, int userId) {
+ int callingUid = Binder.getCallingUid();
+ int callingPid = Binder.getCallingPid();
mHandler.post(() -> {
- if (!hasGlobalPictureQualityServicePermission()) {
- mMqManagerNotifier.notifyOnPictureProfileError(null,
- PictureProfile.ERROR_NO_PERMISSION,
- Binder.getCallingUid(), Binder.getCallingPid());
+ if (!hasGlobalPictureQualityServicePermission(callingUid, callingPid)) {
+ mMqManagerNotifier.notifyOnPictureProfileError(
+ null, PictureProfile.ERROR_NO_PERMISSION, callingUid, callingPid);
}
synchronized (mPictureProfileLock) {
try {
@@ -1160,11 +1297,12 @@
@GuardedBy("mPictureProfileLock")
@Override
public void setSuperResolutionEnabled(boolean enabled, int userId) {
+ int callingUid = Binder.getCallingUid();
+ int callingPid = Binder.getCallingPid();
mHandler.post(() -> {
- if (!hasGlobalPictureQualityServicePermission()) {
- mMqManagerNotifier.notifyOnPictureProfileError(null,
- PictureProfile.ERROR_NO_PERMISSION,
- Binder.getCallingUid(), Binder.getCallingPid());
+ if (!hasGlobalPictureQualityServicePermission(callingUid, callingPid)) {
+ mMqManagerNotifier.notifyOnPictureProfileError(
+ null, PictureProfile.ERROR_NO_PERMISSION, callingUid, callingPid);
}
synchronized (mPictureProfileLock) {
try {
@@ -1200,11 +1338,12 @@
@GuardedBy("mSoundProfileLock")
@Override
public void setAutoSoundQualityEnabled(boolean enabled, int userId) {
+ int callingUid = Binder.getCallingUid();
+ int callingPid = Binder.getCallingPid();
mHandler.post(() -> {
- if (!hasGlobalSoundQualityServicePermission()) {
- mMqManagerNotifier.notifyOnSoundProfileError(null,
- SoundProfile.ERROR_NO_PERMISSION,
- Binder.getCallingUid(), Binder.getCallingPid());
+ if (!hasGlobalSoundQualityServicePermission(callingUid, callingPid)) {
+ mMqManagerNotifier.notifyOnSoundProfileError(
+ null, SoundProfile.ERROR_NO_PERMISSION, callingUid, callingPid);
}
synchronized (mSoundProfileLock) {
@@ -1246,6 +1385,8 @@
}
public void updatePictureProfileFromHal(Long dbId, PersistableBundle bundle) {
+ int callingUid = Binder.getCallingUid();
+ int callingPid = Binder.getCallingPid();
ContentValues values = MediaQualityUtils.getContentValues(dbId,
null,
null,
@@ -1253,22 +1394,26 @@
null,
bundle);
- updateDatabaseOnPictureProfileAndNotifyManagerAndHal(values, bundle);
+ updateDatabaseOnPictureProfileAndNotifyManager(
+ values, bundle, callingUid, callingPid, false);
}
- public void updateDatabaseOnPictureProfileAndNotifyManagerAndHal(ContentValues values,
- PersistableBundle bundle) {
+ public void updateDatabaseOnPictureProfileAndNotifyManager(
+ ContentValues values, PersistableBundle bundle, int uid, int pid, boolean notifyHal) {
SQLiteDatabase db = mMediaQualityDbHelper.getWritableDatabase();
db.replace(mMediaQualityDbHelper.PICTURE_QUALITY_TABLE_NAME,
null, values);
Long dbId = values.getAsLong(BaseParameters.PARAMETER_ID);
mMqManagerNotifier.notifyOnPictureProfileUpdated(mPictureProfileTempIdMap.getValue(dbId),
- mMqDatabaseUtils.getPictureProfile(dbId), Binder.getCallingUid(),
- Binder.getCallingPid());
- mHalNotifier.notifyHalOnPictureProfileChange(dbId, bundle);
+ mMqDatabaseUtils.getPictureProfile(dbId), uid, pid);
+ if (notifyHal) {
+ mHalNotifier.notifyHalOnPictureProfileChange(dbId, bundle);
+ }
}
public void updateSoundProfileFromHal(Long dbId, PersistableBundle bundle) {
+ int callingUid = Binder.getCallingUid();
+ int callingPid = Binder.getCallingPid();
ContentValues values = MediaQualityUtils.getContentValues(dbId,
null,
null,
@@ -1276,19 +1421,21 @@
null,
bundle);
- updateDatabaseOnSoundProfileAndNotifyManagerAndHal(values, bundle);
+ updateDatabaseOnSoundProfileAndNotifyManager(values, bundle, callingUid,
+ callingPid, false);
}
- public void updateDatabaseOnSoundProfileAndNotifyManagerAndHal(ContentValues values,
- PersistableBundle bundle) {
+ public void updateDatabaseOnSoundProfileAndNotifyManager(
+ ContentValues values, PersistableBundle bundle, int uid, int pid, boolean notifyHal) {
SQLiteDatabase db = mMediaQualityDbHelper.getWritableDatabase();
db.replace(mMediaQualityDbHelper.SOUND_QUALITY_TABLE_NAME,
null, values);
Long dbId = values.getAsLong(BaseParameters.PARAMETER_ID);
mMqManagerNotifier.notifyOnSoundProfileUpdated(mSoundProfileTempIdMap.getValue(dbId),
- mMqDatabaseUtils.getSoundProfile(dbId), Binder.getCallingUid(),
- Binder.getCallingPid());
- mHalNotifier.notifyHalOnSoundProfileChange(dbId, bundle);
+ mMqDatabaseUtils.getSoundProfile(dbId), uid, pid);
+ if (notifyHal) {
+ mHalNotifier.notifyHalOnSoundProfileChange(dbId, bundle);
+ }
}
private class MediaQualityManagerPictureProfileCallbackList extends
@@ -1517,17 +1664,8 @@
}
private void notifyOnPictureProfileParameterCapabilitiesChanged(Long profileId,
- ParamCapability[] caps, int uid, int pid) {
+ List<ParameterCapability> paramCaps, int uid, int pid) {
String uuid = mPictureProfileTempIdMap.getValue(profileId);
- List<ParameterCapability> paramCaps = new ArrayList<>();
- for (ParamCapability cap: caps) {
- String name = MediaQualityUtils.getParameterName(cap.name);
- boolean isSupported = cap.isSupported;
- int type = cap.defaultValue == null ? 0 : cap.defaultValue.getTag() + 1;
- Bundle bundle = MediaQualityUtils.convertToCaps(type, cap.range);
-
- paramCaps.add(new ParameterCapability(name, isSupported, type, bundle));
- }
notifyPictureProfileHelper(ProfileModes.PARAMETER_CAPABILITY_CHANGED, uuid,
null, null, paramCaps , uid, pid);
}
@@ -1802,14 +1940,41 @@
@Override
public void onParamCapabilityChanged(long pictureProfileId, ParamCapability[] caps)
throws RemoteException {
+ List<ParameterCapability> paramCaps = new ArrayList<>();
+ for (ParamCapability cap: caps) {
+ String name = MediaQualityUtils.getParameterName(cap.name);
+ boolean isSupported = cap.isSupported;
+ //Reason for +1: please see getListParameterCapability()
+ int type = cap.defaultValue == null ? 0 : cap.defaultValue.getTag() + 1;
+ Bundle bundle = MediaQualityUtils.convertToCaps(type, cap.range);
+
+ paramCaps.add(new ParameterCapability(name, isSupported, type, bundle));
+ }
mMqManagerNotifier.notifyOnPictureProfileParameterCapabilitiesChanged(
- pictureProfileId, caps, Binder.getCallingUid(), Binder.getCallingPid());
+ pictureProfileId, paramCaps, Binder.getCallingUid(), Binder.getCallingPid());
}
@Override
public void onVendorParamCapabilityChanged(long pictureProfileId,
VendorParamCapability[] caps) throws RemoteException {
- // TODO
+ List<ParameterCapability> vendorParamCaps = new ArrayList<>();
+ for (VendorParamCapability vpcHal: caps) {
+ String name = MediaQualityUtils.getVendorParameterName(vpcHal);
+ boolean isSupported = vpcHal.isSupported;
+ //Reason for +1: please see getListParameterCapability()
+ int type = vpcHal.defaultValue
+ == null ? 0 : vpcHal.defaultValue.getTag() + 1;
+ Bundle paramRangeBundle = MediaQualityUtils.convertToCaps(
+ type, vpcHal.range);
+ MediaQualityUtils.convertToVendorCaps(vpcHal, paramRangeBundle);
+ vendorParamCaps.add(new ParameterCapability(
+ name, isSupported, type, paramRangeBundle));
+ }
+ mMqManagerNotifier.notifyOnPictureProfileParameterCapabilitiesChanged(
+ pictureProfileId,
+ vendorParamCaps,
+ Binder.getCallingUid(),
+ Binder.getCallingPid());
}
@Override
@@ -1829,82 +1994,89 @@
// get from map if exists
PictureProfile previous = mHandleToPictureProfile.get(profileHandle);
if (previous == null) {
+ Slog.d(TAG, "Previous profile not in the map");
// get from DB if not exists
previous = mMqDatabaseUtils.getPictureProfile(profileHandle);
if (previous == null) {
+ Slog.d(TAG, "Previous profile not in the database");
return;
}
}
String[] arr = splitNameAndStatus(previous.getName());
String profileName = arr[0];
String profileStatus = arr[1];
- if (status == StreamStatus.HDR10) {
- if (isHdr(profileStatus)) {
- // already HDR
+ if (status != StreamStatus.SDR) {
+ // TODO: merge SDR handling
+ if (isSameStatus(profileStatus, status)) {
+ Slog.d(TAG, "The current status is the same as new status");
return;
}
- if (isSdr(profileStatus)) {
- // SDR to HDR
- String selection = BaseParameters.PARAMETER_TYPE + " = ? AND "
- + BaseParameters.PARAMETER_PACKAGE + " = ? AND "
- + BaseParameters.PARAMETER_NAME + " = ?";
- String[] selectionArguments = {
- Integer.toString(previous.getProfileType()),
- previous.getPackageName(),
- profileName + "/" + PictureProfile.STATUS_HDR
- };
- List<PictureProfile> list =
- mMqDatabaseUtils.getPictureProfilesBasedOnConditions(
- MediaQualityUtils.getMediaProfileColumns(true),
- selection,
- selectionArguments);
- if (list.isEmpty()) {
- // HDR profile not found
- return;
- }
- PictureProfile current = list.get(0);
- mHandleToPictureProfile.put(profileHandle, current);
- mCurrentPictureHandleToOriginal.put(
- current.getHandle().getId(), profileHandle);
- mHalNotifier.notifyHalOnPictureProfileChange(profileHandle,
- current.getParameters());
-
- }
- } else if (status == StreamStatus.SDR) {
- if (isSdr(profileStatus)) {
- // already SDR
+ // to new status
+ String newStatus = toPictureProfileStatus(status);
+ if (newStatus.isEmpty()) {
+ Slog.d(TAG, "new status is not a supported status");
return;
}
- if (isHdr(profileStatus)) {
- // HDR to SDR
- String selection = BaseParameters.PARAMETER_TYPE + " = ? AND "
- + BaseParameters.PARAMETER_PACKAGE + " = ? AND ("
- + BaseParameters.PARAMETER_NAME + " = ? OR "
- + BaseParameters.PARAMETER_NAME + " = ?)";
- String[] selectionArguments = {
- Integer.toString(previous.getProfileType()),
- previous.getPackageName(),
- profileName,
- profileName + "/" + PictureProfile.STATUS_SDR
- };
- List<PictureProfile> list =
- mMqDatabaseUtils.getPictureProfilesBasedOnConditions(
- MediaQualityUtils.getMediaProfileColumns(true),
- selection,
- selectionArguments);
- if (list.isEmpty()) {
- // SDR profile not found
- return;
- }
- PictureProfile current = list.get(0);
- mHandleToPictureProfile.put(profileHandle, current);
- mCurrentPictureHandleToOriginal.put(
- current.getHandle().getId(), profileHandle);
-
- mHalNotifier.notifyHalOnPictureProfileChange(profileHandle,
- current.getParameters());
+ Slog.d(TAG, "The new status is " + newStatus);
+ String selection = BaseParameters.PARAMETER_TYPE + " = ? AND "
+ + BaseParameters.PARAMETER_PACKAGE + " = ? AND "
+ + BaseParameters.PARAMETER_NAME + " = ?";
+ String[] selectionArguments = {
+ Integer.toString(previous.getProfileType()),
+ previous.getPackageName(),
+ profileName + "/" + newStatus
+ };
+ List<PictureProfile> list =
+ mMqDatabaseUtils.getPictureProfilesBasedOnConditions(
+ MediaQualityUtils.getMediaProfileColumns(true),
+ selection,
+ selectionArguments);
+ if (list.isEmpty()) {
+ Slog.d(TAG, "Picture profile not found for status: " + newStatus);
+ return;
}
+ PictureProfile current = list.get(0);
+ mHandleToPictureProfile.put(profileHandle, current);
+ mCurrentPictureHandleToOriginal.put(
+ current.getHandle().getId(), profileHandle);
+
+ mHalNotifier.notifyHalOnPictureProfileChange(profileHandle,
+ current.getParameters());
+ } else {
+ // handle SDR status
+ if (isSdr(profileStatus)) {
+ Slog.d(TAG, "Current status is already SDR");
+ return;
+ }
+
+ // to SDR
+ String selection = BaseParameters.PARAMETER_TYPE + " = ? AND "
+ + BaseParameters.PARAMETER_PACKAGE + " = ? AND ("
+ + BaseParameters.PARAMETER_NAME + " = ? OR "
+ + BaseParameters.PARAMETER_NAME + " = ?)";
+ String[] selectionArguments = {
+ Integer.toString(previous.getProfileType()),
+ previous.getPackageName(),
+ profileName,
+ profileName + "/" + PictureProfile.STATUS_SDR
+ };
+ List<PictureProfile> list =
+ mMqDatabaseUtils.getPictureProfilesBasedOnConditions(
+ MediaQualityUtils.getMediaProfileColumns(true),
+ selection,
+ selectionArguments);
+ if (list.isEmpty()) {
+ Slog.d(TAG, "SDR profile not found");
+ return;
+ }
+ PictureProfile current = list.get(0);
+ mHandleToPictureProfile.put(profileHandle, current);
+ mCurrentPictureHandleToOriginal.put(
+ current.getHandle().getId(), profileHandle);
+
+ mHalNotifier.notifyHalOnPictureProfileChange(profileHandle,
+ current.getParameters());
}
}
});
@@ -1925,13 +2097,57 @@
}
+ private String toPictureProfileStatus(int halStatus) {
+ switch (halStatus) {
+ case StreamStatus.SDR:
+ return PictureProfile.STATUS_SDR;
+ case StreamStatus.HDR10:
+ return PictureProfile.STATUS_HDR10;
+ case StreamStatus.TCH:
+ return PictureProfile.STATUS_TCH;
+ case StreamStatus.DOLBYVISION:
+ return PictureProfile.STATUS_DOLBY_VISION;
+ case StreamStatus.HLG:
+ return PictureProfile.STATUS_HLG;
+ case StreamStatus.HDR10PLUS:
+ return PictureProfile.STATUS_HDR10_PLUS;
+ case StreamStatus.HDRVIVID:
+ return PictureProfile.STATUS_HDR_VIVID;
+ case StreamStatus.IMAXSDR:
+ return PictureProfile.STATUS_IMAX_SDR;
+ case StreamStatus.IMAXHDR10:
+ return PictureProfile.STATUS_IMAX_HDR10;
+ case StreamStatus.IMAXHDR10PLUS:
+ return PictureProfile.STATUS_IMAX_HDR10_PLUS;
+ case StreamStatus.FMMSDR:
+ return PictureProfile.STATUS_FMM_SDR;
+ case StreamStatus.FMMHDR10:
+ return PictureProfile.STATUS_FMM_HDR10;
+ case StreamStatus.FMMHDR10PLUS:
+ return PictureProfile.STATUS_FMM_HDR10_PLUS;
+ case StreamStatus.FMMHLG:
+ return PictureProfile.STATUS_FMM_HLG;
+ case StreamStatus.FMMDOLBY:
+ return PictureProfile.STATUS_FMM_DOLBY;
+ case StreamStatus.FMMTCH:
+ return PictureProfile.STATUS_FMM_TCH;
+ case StreamStatus.FMMHDRVIVID:
+ return PictureProfile.STATUS_FMM_HDR_VIVID;
+ default:
+ return "";
+ }
+ }
+
private boolean isSdr(@NonNull String profileStatus) {
return profileStatus.equals(PictureProfile.STATUS_SDR)
|| profileStatus.isEmpty();
}
- private boolean isHdr(@NonNull String profileStatus) {
- return profileStatus.equals(PictureProfile.STATUS_HDR);
+ private boolean isSameStatus(@NonNull String profileStatus, int halStatus) {
+ if (halStatus == StreamStatus.SDR) {
+ return isSdr(profileStatus);
+ }
+ return toPictureProfileStatus(halStatus).equals(profileStatus);
}
@Override
diff --git a/services/core/java/com/android/server/media/quality/MediaQualityUtils.java b/services/core/java/com/android/server/media/quality/MediaQualityUtils.java
index f58bc98..cb803c4 100644
--- a/services/core/java/com/android/server/media/quality/MediaQualityUtils.java
+++ b/services/core/java/com/android/server/media/quality/MediaQualityUtils.java
@@ -18,6 +18,7 @@
import android.content.ContentValues;
import android.database.Cursor;
+import android.hardware.audio.effect.DefaultExtension;
import android.hardware.tv.mediaquality.ColorRange;
import android.hardware.tv.mediaquality.ColorSpace;
import android.hardware.tv.mediaquality.ColorTemperature;
@@ -31,6 +32,7 @@
import android.hardware.tv.mediaquality.PictureQualityEventType;
import android.hardware.tv.mediaquality.QualityLevel;
import android.hardware.tv.mediaquality.SoundParameter;
+import android.hardware.tv.mediaquality.VendorParamCapability;
import android.media.quality.MediaQualityContract;
import android.media.quality.MediaQualityContract.BaseParameters;
import android.media.quality.MediaQualityContract.PictureQuality;
@@ -41,6 +43,7 @@
import android.media.quality.SoundProfile;
import android.media.quality.SoundProfileHandle;
import android.os.Bundle;
+import android.os.Parcel;
import android.os.PersistableBundle;
import android.util.Log;
@@ -54,6 +57,8 @@
import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
import java.util.UUID;
/**
@@ -72,6 +77,105 @@
SOUND_PROFILE_HANDLE_NONE.id = -10000;
}
+ private static final Set<String> PREDEFINED_NAMES = new HashSet<>(Arrays.asList(
+ PictureQuality.PARAMETER_BRIGHTNESS,
+ PictureQuality.PARAMETER_CONTRAST,
+ PictureQuality.PARAMETER_SHARPNESS,
+ PictureQuality.PARAMETER_SATURATION,
+ PictureQuality.PARAMETER_HUE,
+ PictureQuality.PARAMETER_COLOR_TUNER_BRIGHTNESS,
+ PictureQuality.PARAMETER_COLOR_TUNER_SATURATION,
+ PictureQuality.PARAMETER_COLOR_TUNER_HUE,
+ PictureQuality.PARAMETER_COLOR_TUNER_RED_OFFSET,
+ PictureQuality.PARAMETER_COLOR_TUNER_GREEN_OFFSET,
+ PictureQuality.PARAMETER_COLOR_TUNER_BLUE_OFFSET,
+ PictureQuality.PARAMETER_COLOR_TUNER_RED_GAIN,
+ PictureQuality.PARAMETER_COLOR_TUNER_GREEN_GAIN,
+ PictureQuality.PARAMETER_COLOR_TUNER_BLUE_GAIN,
+ PictureQuality.PARAMETER_NOISE_REDUCTION,
+ PictureQuality.PARAMETER_MPEG_NOISE_REDUCTION,
+ PictureQuality.PARAMETER_FLESH_TONE,
+ PictureQuality.PARAMETER_DECONTOUR,
+ PictureQuality.PARAMETER_DYNAMIC_LUMA_CONTROL,
+ PictureQuality.PARAMETER_FILM_MODE,
+ PictureQuality.PARAMETER_BLACK_STRETCH,
+ PictureQuality.PARAMETER_BLUE_STRETCH,
+ PictureQuality.PARAMETER_COLOR_TUNE,
+ PictureQuality.PARAMETER_COLOR_TEMPERATURE,
+ PictureQuality.PARAMETER_GLOBAL_DIMMING,
+ PictureQuality.PARAMETER_AUTO_PICTURE_QUALITY_ENABLED,
+ PictureQuality.PARAMETER_AUTO_SUPER_RESOLUTION_ENABLED,
+ PictureQuality.PARAMETER_LEVEL_RANGE,
+ PictureQuality.PARAMETER_GAMUT_MAPPING,
+ PictureQuality.PARAMETER_PC_MODE,
+ PictureQuality.PARAMETER_LOW_LATENCY,
+ PictureQuality.PARAMETER_VRR,
+ PictureQuality.PARAMETER_CVRR,
+ PictureQuality.PARAMETER_HDMI_RGB_RANGE,
+ PictureQuality.PARAMETER_COLOR_SPACE,
+ PictureQuality.PARAMETER_PANEL_INIT_MAX_LUMINCE_VALID,
+ PictureQuality.PARAMETER_GAMMA,
+ PictureQuality.PARAMETER_COLOR_TEMPERATURE_RED_GAIN,
+ PictureQuality.PARAMETER_COLOR_TEMPERATURE_GREEN_GAIN,
+ PictureQuality.PARAMETER_COLOR_TEMPERATURE_BLUE_GAIN,
+ PictureQuality.PARAMETER_COLOR_TEMPERATURE_RED_OFFSET,
+ PictureQuality.PARAMETER_COLOR_TEMPERATURE_GREEN_OFFSET,
+ PictureQuality.PARAMETER_COLOR_TEMPERATURE_BLUE_OFFSET,
+ PictureQuality.PARAMETER_ELEVEN_POINT_RED,
+ PictureQuality.PARAMETER_ELEVEN_POINT_GREEN,
+ PictureQuality.PARAMETER_ELEVEN_POINT_BLUE,
+ PictureQuality.PARAMETER_LOW_BLUE_LIGHT,
+ PictureQuality.PARAMETER_LD_MODE,
+ PictureQuality.PARAMETER_OSD_RED_GAIN,
+ PictureQuality.PARAMETER_OSD_GREEN_GAIN,
+ PictureQuality.PARAMETER_OSD_BLUE_GAIN,
+ PictureQuality.PARAMETER_OSD_RED_OFFSET,
+ PictureQuality.PARAMETER_OSD_GREEN_OFFSET,
+ PictureQuality.PARAMETER_OSD_BLUE_OFFSET,
+ PictureQuality.PARAMETER_OSD_HUE,
+ PictureQuality.PARAMETER_OSD_SATURATION,
+ PictureQuality.PARAMETER_OSD_CONTRAST,
+ PictureQuality.PARAMETER_COLOR_TUNER_SWITCH,
+ PictureQuality.PARAMETER_COLOR_TUNER_HUE_RED,
+ PictureQuality.PARAMETER_COLOR_TUNER_HUE_GREEN,
+ PictureQuality.PARAMETER_COLOR_TUNER_HUE_BLUE,
+ PictureQuality.PARAMETER_COLOR_TUNER_HUE_CYAN,
+ PictureQuality.PARAMETER_COLOR_TUNER_HUE_MAGENTA,
+ PictureQuality.PARAMETER_COLOR_TUNER_HUE_YELLOW,
+ PictureQuality.PARAMETER_COLOR_TUNER_HUE_FLESH,
+ PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_RED,
+ PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_GREEN,
+ PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_BLUE,
+ PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_CYAN,
+ PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_MAGENTA,
+ PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_YELLOW,
+ PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_FLESH,
+ PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_RED,
+ PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_GREEN,
+ PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_BLUE,
+ PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_CYAN,
+ PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_MAGENTA,
+ PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_YELLOW,
+ PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_FLESH,
+ SoundQuality.PARAMETER_BALANCE,
+ SoundQuality.PARAMETER_BASS,
+ SoundQuality.PARAMETER_TREBLE,
+ SoundQuality.PARAMETER_SURROUND_SOUND,
+ SoundQuality.PARAMETER_EQUALIZER_DETAIL,
+ SoundQuality.PARAMETER_SPEAKERS,
+ SoundQuality.PARAMETER_SPEAKERS_DELAY_MILLIS,
+ SoundQuality.PARAMETER_EARC,
+ SoundQuality.PARAMETER_AUTO_VOLUME_CONTROL,
+ SoundQuality.PARAMETER_DOWN_MIX_MODE,
+ SoundQuality.PARAMETER_DTS_DRC,
+ SoundQuality.PARAMETER_DOLBY_AUDIO_PROCESSING,
+ SoundQuality.PARAMETER_DIALOGUE_ENHANCER,
+ SoundQuality.PARAMETER_DTS_VIRTUAL_X,
+ SoundQuality.PARAMETER_DIGITAL_OUTPUT_DELAY_MILLIS,
+ SoundQuality.PARAMETER_DIGITAL_OUTPUT_MODE,
+ SoundQuality.PARAMETER_SOUND_STYLE
+ ));
+
/**
* Convert PictureParameter List to PersistableBundle.
*/
@@ -1369,9 +1473,6 @@
if (nameMap.contains(PictureQuality.PARAMETER_BRIGHTNESS)) {
bytes.add(ParameterName.BRIGHTNESS);
}
- if (nameMap.contains(PictureQuality.PARAMETER_BRIGHTNESS)) {
- bytes.add(ParameterName.BRIGHTNESS);
- }
if (nameMap.contains(PictureQuality.PARAMETER_CONTRAST)) {
bytes.add(ParameterName.CONTRAST);
}
@@ -1384,21 +1485,12 @@
if (nameMap.contains(PictureQuality.PARAMETER_HUE)) {
bytes.add(ParameterName.HUE);
}
- if (nameMap.contains(PictureQuality.PARAMETER_BRIGHTNESS)) {
- bytes.add(ParameterName.BRIGHTNESS);
- }
if (nameMap.contains(PictureQuality.PARAMETER_COLOR_TUNER_BRIGHTNESS)) {
bytes.add(ParameterName.COLOR_TUNER_BRIGHTNESS);
}
- if (nameMap.contains(PictureQuality.PARAMETER_SATURATION)) {
- bytes.add(ParameterName.SATURATION);
- }
if (nameMap.contains(PictureQuality.PARAMETER_COLOR_TUNER_SATURATION)) {
bytes.add(ParameterName.COLOR_TUNER_SATURATION);
}
- if (nameMap.contains(PictureQuality.PARAMETER_HUE)) {
- bytes.add(ParameterName.HUE);
- }
if (nameMap.contains(PictureQuality.PARAMETER_COLOR_TUNER_HUE)) {
bytes.add(ParameterName.COLOR_TUNER_HUE);
}
@@ -1438,6 +1530,9 @@
if (nameMap.contains(PictureQuality.PARAMETER_FILM_MODE)) {
bytes.add(ParameterName.FILM_MODE);
}
+ if (nameMap.contains(PictureQuality.PARAMETER_BLACK_STRETCH)) {
+ bytes.add(ParameterName.BLACK_STRETCH);
+ }
if (nameMap.contains(PictureQuality.PARAMETER_BLUE_STRETCH)) {
bytes.add(ParameterName.BLUE_STRETCH);
}
@@ -1486,6 +1581,15 @@
if (nameMap.contains(PictureQuality.PARAMETER_GAMMA)) {
bytes.add(ParameterName.GAMMA);
}
+ if (nameMap.contains(PictureQuality.PARAMETER_COLOR_TEMPERATURE_RED_GAIN)) {
+ bytes.add(ParameterName.COLOR_TEMPERATURE_RED_GAIN);
+ }
+ if (nameMap.contains(PictureQuality.PARAMETER_COLOR_TEMPERATURE_GREEN_GAIN)) {
+ bytes.add(ParameterName.COLOR_TEMPERATURE_GREEN_GAIN);
+ }
+ if (nameMap.contains(PictureQuality.PARAMETER_COLOR_TEMPERATURE_BLUE_GAIN)) {
+ bytes.add(ParameterName.COLOR_TEMPERATURE_BLUE_GAIN);
+ }
if (nameMap.contains(PictureQuality.PARAMETER_COLOR_TEMPERATURE_RED_OFFSET)) {
bytes.add(ParameterName.COLOR_TEMPERATURE_RED_OFFSET);
}
@@ -1665,6 +1769,17 @@
}
/**
+ * Remove the pre-defined parameters, the parameters that are left in the list are vendor
+ * parameters.
+ */
+ public static void getVendorParamsByRemovePreDefineParams(List<String> names) {
+ if (names == null) {
+ return;
+ }
+ names.removeAll(PREDEFINED_NAMES);
+ }
+
+ /**
* Get Parameter Name based on byte.
*/
public static String getParameterName(byte pn) {
@@ -1842,6 +1957,21 @@
}
/**
+ * Get vendor parameter name.
+ */
+ public static String getVendorParameterName(VendorParamCapability vpcHal) {
+ byte[] vendorParamCapByteArray = Objects.requireNonNull(
+ vpcHal.identifier.identifier.getParcelable(DefaultExtension.class)).bytes;
+ Parcel vendorParamNameParcel = Parcel.obtain();
+ vendorParamNameParcel.unmarshall(
+ vendorParamCapByteArray, 0, vendorParamCapByteArray.length);
+ vendorParamNameParcel.setDataPosition(0);
+ String name = vendorParamNameParcel.readString();
+ vendorParamNameParcel.recycle();
+ return name;
+ }
+
+ /**
* Convert ParameterRange to a Bundle.
*/
public static Bundle convertToCaps(int type, ParameterRange range) {
@@ -1865,6 +1995,29 @@
return bundle;
}
+ /**
+ * Retrieve the vendor parameter capability from the HAL and stores in the bundle.
+ * @param vpcHal vendor param capability from the HAL. Contains information about the param
+ * Identifier, is supported, default value and range.
+ * @param paramRangeBundle bundle that will contains vendor param defined values.
+ */
+ public static void convertToVendorCaps(VendorParamCapability vpcHal, Bundle paramRangeBundle) {
+ byte[] vendorParamCapRangeByteArray = Objects.requireNonNull(
+ vpcHal.range.vendorDefinedValues.getParcelable(
+ DefaultExtension.class)).bytes;
+ Parcel vendorParamCapRangeParcel = Parcel.obtain();
+ vendorParamCapRangeParcel.unmarshall(
+ vendorParamCapRangeByteArray, 0, vendorParamCapRangeByteArray.length);
+ vendorParamCapRangeParcel.setDataPosition(0);
+ int vendorDefinedValuesSize = vendorParamCapRangeParcel.readInt();
+ vendorParamCapRangeParcel.setDataPosition(0);
+ String[] vendorDefinedValues = new String[vendorDefinedValuesSize];
+ vendorParamCapRangeParcel.readStringArray(vendorDefinedValues);
+ //TODO: Handle int, long and double array
+ paramRangeBundle.putStringArray(ParameterCapability.CAPABILITY_ENUM, vendorDefinedValues);
+ vendorParamCapRangeParcel.recycle();
+ }
+
private static String getTempId(BiMap<Long, String> map, Cursor cursor) {
int colIndex = cursor.getColumnIndex(BaseParameters.PARAMETER_ID);
Long dbId = colIndex != -1 ? cursor.getLong(colIndex) : null;
diff --git a/services/core/java/com/android/server/notification/GroupHelper.java b/services/core/java/com/android/server/notification/GroupHelper.java
index 3f4df1d..404deaa 100644
--- a/services/core/java/com/android/server/notification/GroupHelper.java
+++ b/services/core/java/com/android/server/notification/GroupHelper.java
@@ -362,13 +362,9 @@
AUTOGROUP_KEY, Integer.MAX_VALUE, attr);
}
for (String keyToGroup : notificationsToGroup) {
- if (android.app.Flags.checkAutogroupBeforePost()) {
- if (keyToGroup.equals(sbn.getKey())) {
- // Autogrouping for the provided notification is to be done synchronously.
- sbnToBeAutogrouped = true;
- } else {
- mCallback.addAutoGroup(keyToGroup, AUTOGROUP_KEY, /*requestSort=*/true);
- }
+ if (keyToGroup.equals(sbn.getKey())) {
+ // Autogrouping for the provided notification is to be done synchronously.
+ sbnToBeAutogrouped = true;
} else {
mCallback.addAutoGroup(keyToGroup, AUTOGROUP_KEY, /*requestSort=*/true);
}
@@ -647,16 +643,12 @@
// add notification to aggregate group
for (String keyToGroup : ungrouped.keySet()) {
- if (android.app.Flags.checkAutogroupBeforePost()) {
- if (keyToGroup.equals(record.getKey())) {
- // Autogrouping for the posted notification is to be done synchronously.
- sbnToBeAutogrouped = true;
- } else {
- mCallback.addAutoGroup(keyToGroup, fullAggregateGroupKey.toString(),
- true);
- }
+ if (keyToGroup.equals(record.getKey())) {
+ // Autogrouping for the posted notification is to be done synchronously.
+ sbnToBeAutogrouped = true;
} else {
- mCallback.addAutoGroup(keyToGroup, fullAggregateGroupKey.toString(), true);
+ mCallback.addAutoGroup(keyToGroup, fullAggregateGroupKey.toString(),
+ true);
}
}
diff --git a/services/core/java/com/android/server/notification/NotificationChannelExtractor.java b/services/core/java/com/android/server/notification/NotificationChannelExtractor.java
index 18bccd8..2d9a821 100644
--- a/services/core/java/com/android/server/notification/NotificationChannelExtractor.java
+++ b/services/core/java/com/android/server/notification/NotificationChannelExtractor.java
@@ -15,9 +15,6 @@
*/
package com.android.server.notification;
-import static android.app.Flags.restrictAudioAttributesAlarm;
-import static android.app.Flags.restrictAudioAttributesCall;
-import static android.app.Flags.restrictAudioAttributesMedia;
import static android.app.Notification.CATEGORY_ALARM;
import static android.media.AudioAttributes.USAGE_NOTIFICATION;
@@ -76,40 +73,34 @@
record.getSbn().getShortcutId(), true, false);
record.updateNotificationChannel(updatedChannel);
- if (restrictAudioAttributesCall() || restrictAudioAttributesAlarm()
- || restrictAudioAttributesMedia()) {
- AudioAttributes attributes = record.getChannel().getAudioAttributes();
- if (attributes == null) {
- if (DBG) Slog.d(TAG, "missing AudioAttributes");
- return null;
- }
+ AudioAttributes attributes = record.getChannel().getAudioAttributes();
+ if (attributes == null) {
+ if (DBG) Slog.d(TAG, "missing AudioAttributes");
+ return null;
+ }
- boolean updateAttributes = false;
- if (restrictAudioAttributesCall()
- && !record.getNotification().isStyle(Notification.CallStyle.class)
- && attributes.getUsage() == AudioAttributes.USAGE_NOTIFICATION_RINGTONE) {
- updateAttributes = true;
- }
- if (restrictAudioAttributesAlarm()
- && !CATEGORY_ALARM.equals(record.getNotification().category)
- && attributes.getUsage() == AudioAttributes.USAGE_ALARM) {
- updateAttributes = true;
- }
+ boolean updateAttributes = false;
+ if (!record.getNotification().isStyle(Notification.CallStyle.class)
+ && attributes.getUsage() == AudioAttributes.USAGE_NOTIFICATION_RINGTONE) {
+ updateAttributes = true;
+ }
+ if (!CATEGORY_ALARM.equals(record.getNotification().category)
+ && attributes.getUsage() == AudioAttributes.USAGE_ALARM) {
+ updateAttributes = true;
+ }
- if (restrictAudioAttributesMedia()
- && (attributes.getUsage() == AudioAttributes.USAGE_UNKNOWN
- || attributes.getUsage() == AudioAttributes.USAGE_MEDIA)) {
- updateAttributes = true;
- }
+ if (attributes.getUsage() == AudioAttributes.USAGE_UNKNOWN
+ || attributes.getUsage() == AudioAttributes.USAGE_MEDIA) {
+ updateAttributes = true;
+ }
- if (updateAttributes) {
- reportAudioAttributesChanged(record.getUid());
- NotificationChannel clone = record.getChannel().copy();
- clone.setSound(clone.getSound(), new AudioAttributes.Builder(attributes)
- .setUsage(USAGE_NOTIFICATION)
- .build());
- record.updateNotificationChannel(clone);
- }
+ if (updateAttributes) {
+ reportAudioAttributesChanged(record.getUid());
+ NotificationChannel clone = record.getChannel().copy();
+ clone.setSound(clone.getSound(), new AudioAttributes.Builder(attributes)
+ .setUsage(USAGE_NOTIFICATION)
+ .build());
+ record.updateNotificationChannel(clone);
}
return null;
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 89a06ae..d4684fa 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -2043,7 +2043,6 @@
}
@Override
- @FlaggedApi(Flags.FLAG_ALL_NOTIFS_NEED_TTL)
public void timeoutNotification(String key) {
boolean foundNotification = false;
int uid = 0;
@@ -2163,41 +2162,6 @@
}
};
- private final BroadcastReceiver mNotificationTimeoutReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- if (action == null) {
- return;
- }
- if (ACTION_NOTIFICATION_TIMEOUT.equals(action)) {
- final NotificationRecord record;
- // TODO: b/323013410 - Record should be cloned instead of used directly.
- synchronized (mNotificationLock) {
- record = findNotificationByKeyLocked(intent.getStringExtra(EXTRA_KEY));
- }
- if (record != null) {
- if (lifetimeExtensionRefactor()) {
- cancelNotification(record.getSbn().getUid(),
- record.getSbn().getInitialPid(),
- record.getSbn().getPackageName(), record.getSbn().getTag(),
- record.getSbn().getId(), 0,
- FLAG_FOREGROUND_SERVICE | FLAG_USER_INITIATED_JOB
- | FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY,
- true, record.getUserId(), REASON_TIMEOUT, null);
- } else {
- cancelNotification(record.getSbn().getUid(),
- record.getSbn().getInitialPid(),
- record.getSbn().getPackageName(), record.getSbn().getTag(),
- record.getSbn().getId(), 0,
- FLAG_FOREGROUND_SERVICE | FLAG_USER_INITIATED_JOB,
- true, record.getUserId(), REASON_TIMEOUT, null);
- }
- }
- }
- }
- };
-
private final BroadcastReceiver mPackageIntentReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -2835,9 +2799,7 @@
mSnoozeHelper = snoozeHelper;
mGroupHelper = groupHelper;
mHistoryManager = historyManager;
- if (Flags.allNotifsNeedTtl()) {
- mTtlHelper = new TimeToLiveHelper(mNotificationManagerPrivate, getContext());
- }
+ mTtlHelper = new TimeToLiveHelper(mNotificationManagerPrivate, getContext());
// This is a ManagedServices object that keeps track of the listeners.
mListeners = notificationListeners;
@@ -2926,13 +2888,6 @@
getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL, sdFilter, null,
null);
- if (!Flags.allNotifsNeedTtl()) {
- IntentFilter timeoutFilter = new IntentFilter(ACTION_NOTIFICATION_TIMEOUT);
- timeoutFilter.addDataScheme(SCHEME_TIMEOUT);
- getContext().registerReceiver(mNotificationTimeoutReceiver, timeoutFilter,
- Context.RECEIVER_EXPORTED_UNAUDITED);
- }
-
IntentFilter settingsRestoredFilter = new IntentFilter(Intent.ACTION_SETTING_RESTORED);
getContext().registerReceiver(mRestoreReceiver, settingsRestoredFilter);
@@ -2966,14 +2921,8 @@
if (mPackageIntentReceiver != null) {
getContext().unregisterReceiver(mPackageIntentReceiver);
}
- if (Flags.allNotifsNeedTtl()) {
- if (mTtlHelper != null) {
- mTtlHelper.destroy();
- }
- } else {
- if (mNotificationTimeoutReceiver != null) {
- getContext().unregisterReceiver(mNotificationTimeoutReceiver);
- }
+ if (mTtlHelper != null) {
+ mTtlHelper.destroy();
}
if (mRestoreReceiver != null) {
getContext().unregisterReceiver(mRestoreReceiver);
@@ -7213,13 +7162,11 @@
}
if (allow != mLockScreenAllowSecureNotifications) {
mLockScreenAllowSecureNotifications = allow;
- if (android.app.Flags.keyguardPrivateNotifications()) {
- getContext().sendBroadcast(
- new Intent(ACTION_KEYGUARD_PRIVATE_NOTIFICATIONS_CHANGED)
- .putExtra(EXTRA_KM_PRIVATE_NOTIFS_ALLOWED,
- mLockScreenAllowSecureNotifications),
- STATUS_BAR_SERVICE);
- }
+ getContext().sendBroadcast(
+ new Intent(ACTION_KEYGUARD_PRIVATE_NOTIFICATIONS_CHANGED)
+ .putExtra(EXTRA_KM_PRIVATE_NOTIFS_ALLOWED,
+ mLockScreenAllowSecureNotifications),
+ STATUS_BAR_SERVICE);
handleSavePolicyFile();
}
@@ -7526,7 +7473,7 @@
addAutoGroupAdjustment(r, groupName);
EventLogTags.writeNotificationAutogrouped(key);
- if (!android.app.Flags.checkAutogroupBeforePost() || requestSort) {
+ if (requestSort) {
mRankingHandler.requestSort();
}
@@ -8023,10 +7970,8 @@
pw.println("\n Usage Stats:");
mUsageStats.dump(pw, " ", filter);
- if (Flags.allNotifsNeedTtl()) {
- pw.println("\n TimeToLive alarms:");
- mTtlHelper.dump(pw, " ");
- }
+ pw.println("\n TimeToLive alarms:");
+ mTtlHelper.dump(pw, " ");
}
if (notificationForceGrouping()) {
@@ -8862,10 +8807,8 @@
// Remote views? Are they too big?
checkRemoteViews(pkg, tag, id, notification);
- if (Flags.allNotifsNeedTtl()) {
- if (notification.getTimeoutAfter() == 0) {
- notification.setTimeoutAfter(NOTIFICATION_TTL);
- }
+ if (notification.getTimeoutAfter() == 0) {
+ notification.setTimeoutAfter(NOTIFICATION_TTL);
}
if (notificationForceGrouping()) {
@@ -9219,7 +9162,7 @@
return false;
}
- if (Flags.rejectOldNotifications() && n.hasAppProvidedWhen() && n.getWhen() > 0
+ if (n.hasAppProvidedWhen() && n.getWhen() > 0
&& (System.currentTimeMillis() - n.getWhen()) > NOTIFICATION_MAX_AGE_AT_POST) {
Slog.d(TAG, "Ignored enqueue for old " + n.getWhen() + " notification " + r.getKey());
mUsageStats.registerTooOldBlocked(r);
@@ -9681,11 +9624,7 @@
}
mEnqueuedNotifications.add(r);
- if (Flags.allNotifsNeedTtl()) {
- mTtlHelper.scheduleTimeoutLocked(r, SystemClock.elapsedRealtime());
- } else {
- scheduleTimeoutLocked(r);
- }
+ mTtlHelper.scheduleTimeoutLocked(r, SystemClock.elapsedRealtime());
final StatusBarNotification n = r.getSbn();
if (DBG) Slog.d(TAG, "EnqueueNotificationRunnable.run for: " + n.getKey());
@@ -9894,50 +9833,47 @@
// Posts the notification if it has a small icon, and potentially autogroup
// the new notification.
- if (android.app.Flags.checkAutogroupBeforePost()) {
- if (notification.getSmallIcon() != null && !isCritical(r)) {
- StatusBarNotification oldSbn = (old != null) ? old.getSbn() : null;
- if (oldSbn == null || !Objects.equals(oldSbn.getGroup(), n.getGroup())
- || !Objects.equals(oldSbn.getNotification().getGroup(),
- n.getNotification().getGroup())
- || oldSbn.getNotification().flags
- != n.getNotification().flags
- || !old.getChannel().getId().equals(r.getChannel().getId())) {
- synchronized (mNotificationLock) {
- final String autogroupName =
- notificationForceGrouping() ?
- GroupHelper.getFullAggregateGroupKey(r)
- : GroupHelper.AUTOGROUP_KEY;
- boolean willBeAutogrouped =
- mGroupHelper.onNotificationPosted(r,
- hasAutoGroupSummaryLocked(r));
- if (willBeAutogrouped) {
- // The newly posted notification will be autogrouped, but
- // was not autogrouped onPost, to avoid an unnecessary sort.
- // We add the autogroup key to the notification without a
- // sort here, and it'll be sorted below with extractSignals.
- addAutogroupKeyLocked(key,
- autogroupName, /*requestSort=*/false);
- } else {
- if (notificationForceGrouping()) {
- // Wait 3 seconds so that the app has a chance to post
- // a group summary or children (complete a group)
- mHandler.postDelayed(() -> {
- synchronized (mNotificationLock) {
- NotificationRecord record =
- mNotificationsByKey.get(key);
- if (record != null) {
- mGroupHelper.onNotificationPostedWithDelay(
- record, mNotificationList,
- mSummaryByGroupKey);
- }
- }
- }, key, DELAY_FORCE_REGROUP_TIME);
- }
+ if (notification.getSmallIcon() != null && !isCritical(r)) {
+ StatusBarNotification oldSbn = (old != null) ? old.getSbn() : null;
+ if (oldSbn == null || !Objects.equals(oldSbn.getGroup(), n.getGroup())
+ || !Objects.equals(oldSbn.getNotification().getGroup(),
+ n.getNotification().getGroup())
+ || oldSbn.getNotification().flags
+ != n.getNotification().flags
+ || !old.getChannel().getId().equals(r.getChannel().getId())) {
+ synchronized (mNotificationLock) {
+ final String autogroupName =
+ notificationForceGrouping()
+ ? GroupHelper.getFullAggregateGroupKey(r)
+ : GroupHelper.AUTOGROUP_KEY;
+ boolean willBeAutogrouped =
+ mGroupHelper.onNotificationPosted(r,
+ hasAutoGroupSummaryLocked(r));
+ if (willBeAutogrouped) {
+ // The newly posted notification will be autogrouped, but
+ // was not autogrouped onPost, to avoid an unnecessary sort.
+ // We add the autogroup key to the notification without a
+ // sort here, and it'll be sorted below with extractSignals.
+ addAutogroupKeyLocked(key,
+ autogroupName, /*requestSort=*/false);
+ } else {
+ if (notificationForceGrouping()) {
+ // Wait 3 seconds so that the app has a chance to post
+ // a group summary or children (complete a group)
+ mHandler.postDelayed(() -> {
+ synchronized (mNotificationLock) {
+ NotificationRecord record =
+ mNotificationsByKey.get(key);
+ if (record != null) {
+ mGroupHelper.onNotificationPostedWithDelay(
+ record, mNotificationList,
+ mSummaryByGroupKey);
+ }
+ }
+ }, key, DELAY_FORCE_REGROUP_TIME);
}
-
}
- }
+ }
}
}
@@ -9960,37 +9896,6 @@
getGroupInstanceId(r.getSbn().getGroupKey()));
notifyListenersPostedAndLogLocked(r, old, mTracker, maybeReport);
posted = true;
-
- if (!android.app.Flags.checkAutogroupBeforePost()) {
- StatusBarNotification oldSbn = (old != null) ? old.getSbn() : null;
- if (oldSbn == null
- || !Objects.equals(oldSbn.getGroup(), n.getGroup())
- || oldSbn.getNotification().flags
- != n.getNotification().flags) {
- if (!isCritical(r)) {
- mHandler.post(() -> {
- synchronized (mNotificationLock) {
- mGroupHelper.onNotificationPosted(
- r, hasAutoGroupSummaryLocked(r));
- }
- });
-
- if (notificationForceGrouping()) {
- mHandler.postDelayed(() -> {
- synchronized (mNotificationLock) {
- NotificationRecord record =
- mNotificationsByKey.get(key);
- if (record != null) {
- mGroupHelper.onNotificationPostedWithDelay(
- record, mNotificationList,
- mSummaryByGroupKey);
- }
- }
- }, key, DELAY_FORCE_REGROUP_TIME);
- }
- }
- }
- }
} else {
Slog.e(TAG, "Not posting notification without small icon: " + notification);
if (old != null && !old.isCanceled) {
@@ -10328,27 +10233,6 @@
flags);
}
- @VisibleForTesting
- @GuardedBy("mNotificationLock")
- void scheduleTimeoutLocked(NotificationRecord record) {
- if (record.getNotification().getTimeoutAfter() > 0) {
- final PendingIntent pi = getNotificationTimeoutPendingIntent(
- record, PendingIntent.FLAG_UPDATE_CURRENT);
- mAlarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP,
- SystemClock.elapsedRealtime() + record.getNotification().getTimeoutAfter(), pi);
- }
- }
-
- @VisibleForTesting
- @GuardedBy("mNotificationLock")
- void cancelScheduledTimeoutLocked(NotificationRecord record) {
- final PendingIntent pi = getNotificationTimeoutPendingIntent(
- record, PendingIntent.FLAG_CANCEL_CURRENT);
- if (pi != null) {
- mAlarmManager.cancel(pi);
- }
- }
-
@GuardedBy("mToastQueue")
void showNextToastLocked(boolean lastToastWasTextRecord) {
if (mIsCurrentToastShown) {
@@ -10919,11 +10803,7 @@
int rank, int count, boolean wasPosted, String listenerName,
@ElapsedRealtimeLong long cancellationElapsedTimeMs) {
final String canceledKey = r.getKey();
- if (Flags.allNotifsNeedTtl()) {
- mTtlHelper.cancelScheduledTimeoutLocked(r);
- } else {
- cancelScheduledTimeoutLocked(r);
- }
+ mTtlHelper.cancelScheduledTimeoutLocked(r);
// Record caller.
recordCallerLocked(r);
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 676ae27..302e9782 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -15,9 +15,6 @@
*/
package com.android.server.notification;
-import static android.app.Flags.restrictAudioAttributesAlarm;
-import static android.app.Flags.restrictAudioAttributesCall;
-import static android.app.Flags.restrictAudioAttributesMedia;
import static android.app.Flags.sortSectionByTime;
import static android.app.NotificationChannel.USER_LOCKED_IMPORTANCE;
import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
@@ -1245,13 +1242,10 @@
calculateImportance();
calculateUserSentiment();
mVibration = calculateVibration();
- if (restrictAudioAttributesCall() || restrictAudioAttributesAlarm()
- || restrictAudioAttributesMedia()) {
- if (channel.getAudioAttributes() != null) {
- mAttributes = channel.getAudioAttributes();
- } else {
- mAttributes = Notification.AUDIO_ATTRIBUTES_DEFAULT;
- }
+ if (channel.getAudioAttributes() != null) {
+ mAttributes = channel.getAudioAttributes();
+ } else {
+ mAttributes = Notification.AUDIO_ATTRIBUTES_DEFAULT;
}
}
}
diff --git a/services/core/java/com/android/server/notification/NotificationShellCmd.java b/services/core/java/com/android/server/notification/NotificationShellCmd.java
index ffc7c7b..052828b 100644
--- a/services/core/java/com/android/server/notification/NotificationShellCmd.java
+++ b/services/core/java/com/android/server/notification/NotificationShellCmd.java
@@ -51,6 +51,8 @@
import android.text.TextUtils;
import android.util.Slog;
+import com.android.server.utils.Slogf;
+
import java.io.PrintWriter;
import java.net.URISyntaxException;
import java.util.Collections;
@@ -95,6 +97,7 @@
+ " -I|--large-icon <iconspec>\n"
+ " -S|--style <style> [styleargs]\n"
+ " -c|--content-intent <intentspec>\n"
+ + " -u|--user <user_id>"
+ "\n"
+ "styles: (default none)\n"
+ " bigtext\n"
@@ -494,6 +497,7 @@
final Notification.Builder builder = new Notification.Builder(context, CHANNEL_ID);
String opt;
+ int userId = UserHandle.USER_NULL;
boolean verbose = false;
Notification.BigPictureStyle bigPictureStyle = null;
Notification.BigTextStyle bigTextStyle = null;
@@ -660,6 +664,15 @@
}
messagingStyle.setConversationTitle(getNextArgRequired());
break;
+ case "-u":
+ case "--user":
+ String userArg = getNextArg();
+ userId = UserHandle.parseUserArg(userArg);
+ if (userId == UserHandle.USER_CURRENT) {
+ userId = ActivityManager.getCurrentUser();
+ }
+ Slogf.v(TAG, "userId: %s %s converted into %d", opt, userArg, userId);
+ break;
case "-h":
case "--help":
case "--wtf":
@@ -687,12 +700,16 @@
ensureChannel(callingPackage, callingUid);
+ if (userId == UserHandle.USER_NULL) {
+ userId = UserHandle.getUserId(callingUid);
+ Slogf.v(TAG, "set user id (%d) from calling uid (%d)", userId, callingUid);
+ }
final Notification n = builder.build();
- pw.println("posting:\n " + n);
- Slog.v("NotificationManager", "posting: " + n);
+ pw.printf("posting for user %d:\n %s\n", userId, n);
+ Slogf.v(TAG, "posting: %s for user %d", n, userId);
mBinderService.enqueueNotificationWithTag(callingPackage, callingPackage, tag,
- NOTIFICATION_ID, n, UserHandle.getUserId(callingUid));
+ NOTIFICATION_ID, n, userId);
if (verbose) {
NotificationRecord nr = mDirectService.findNotificationLocked(
diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java
index f06d6405..5dbef54 100644
--- a/services/core/java/com/android/server/notification/RankingHelper.java
+++ b/services/core/java/com/android/server/notification/RankingHelper.java
@@ -15,9 +15,6 @@
*/
package com.android.server.notification;
-import static android.app.Flags.restrictAudioAttributesAlarm;
-import static android.app.Flags.restrictAudioAttributesCall;
-import static android.app.Flags.restrictAudioAttributesMedia;
import static android.app.Flags.sortSectionByTime;
import static android.app.NotificationManager.IMPORTANCE_MIN;
import static android.text.TextUtils.formatSimple;
@@ -80,10 +77,7 @@
extractor.setConfig(config);
extractor.setZenHelper(zenHelper);
extractor.setGroupHelper(groupHelper);
- if (restrictAudioAttributesAlarm() || restrictAudioAttributesMedia()
- || restrictAudioAttributesCall()) {
- extractor.setCompatChangeLogger(platformCompat);
- }
+ extractor.setCompatChangeLogger(platformCompat);
mSignalExtractors[i] = extractor;
} catch (ClassNotFoundException e) {
Slog.w(TAG, "Couldn't find extractor " + extractorNames[i] + ".", e);
diff --git a/services/core/java/com/android/server/notification/TimeToLiveHelper.java b/services/core/java/com/android/server/notification/TimeToLiveHelper.java
index b053dfe..ee15b11 100644
--- a/services/core/java/com/android/server/notification/TimeToLiveHelper.java
+++ b/services/core/java/com/android/server/notification/TimeToLiveHelper.java
@@ -41,7 +41,6 @@
/**
* Handles canceling notifications when their time to live expires
*/
-@FlaggedApi(Flags.FLAG_ALL_NOTIFS_NEED_TTL)
public class TimeToLiveHelper {
private static final String TAG = TimeToLiveHelper.class.getSimpleName();
private static final String ACTION = "com.android.server.notification.TimeToLiveHelper";
diff --git a/services/core/java/com/android/server/notification/flags.aconfig b/services/core/java/com/android/server/notification/flags.aconfig
index 73c2551..42c8f19 100644
--- a/services/core/java/com/android/server/notification/flags.aconfig
+++ b/services/core/java/com/android/server/notification/flags.aconfig
@@ -82,13 +82,6 @@
}
flag {
- name: "all_notifs_need_ttl"
- namespace: "systemui"
- description: "This flag sets a TTL on all notifications that don't already have an app provided one"
- bug: "331967355"
-}
-
-flag {
name: "persist_incomplete_restore_data"
namespace: "systemui"
description: "Stores restore data for not-yet-installed pkgs for 48 hours"
@@ -123,13 +116,6 @@
}
flag {
- name: "reject_old_notifications"
- namespace: "systemui"
- description: "This flag does not allow notifications older than 2 weeks old to be posted"
- bug: "339833083"
-}
-
-flag {
name: "notification_minimalism"
namespace: "systemui"
description: "Minimize the notifications to show on the lockscreen."
diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java
index 458b46d..5af97806 100644
--- a/services/core/java/com/android/server/pm/ApexManager.java
+++ b/services/core/java/com/android/server/pm/ApexManager.java
@@ -886,32 +886,6 @@
waitForApexService().reserveSpaceForCompressedApex(infoList);
}
- private SigningDetails getSigningDetails(PackageInfo pkg) throws PackageManagerException {
- final int minSignatureScheme =
- ApkSignatureVerifier.getMinimumSignatureSchemeVersionForTargetSdk(
- pkg.applicationInfo.targetSdkVersion);
- final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
- final ParseResult<SigningDetails> result = ApkSignatureVerifier.verify(
- input, pkg.applicationInfo.sourceDir, minSignatureScheme);
- if (result.isError()) {
- throw new PackageManagerException(result.getErrorCode(), result.getErrorMessage(),
- result.getException());
- }
- return result.getResult();
- }
-
- private void checkApexSignature(PackageInfo existingApexPkg, PackageInfo newApexPkg)
- throws PackageManagerException {
- final SigningDetails existingSigningDetails = getSigningDetails(existingApexPkg);
- final SigningDetails newSigningDetails = getSigningDetails(newApexPkg);
- if (!newSigningDetails.checkCapability(existingSigningDetails,
- SigningDetails.CertCapabilities.INSTALLED_DATA)) {
- throw new PackageManagerException(PackageManager.INSTALL_FAILED_BAD_SIGNATURE,
- "APK container signature of " + newApexPkg.applicationInfo.sourceDir
- + " is not compatible with currently installed on device");
- }
- }
-
@Override
ApexInfo installPackage(File apexFile, boolean force)
throws PackageManagerException {
diff --git a/services/core/java/com/android/server/pm/AppsFilterBase.java b/services/core/java/com/android/server/pm/AppsFilterBase.java
index 98b7c96..721cc80 100644
--- a/services/core/java/com/android/server/pm/AppsFilterBase.java
+++ b/services/core/java/com/android/server/pm/AppsFilterBase.java
@@ -24,7 +24,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.content.pm.Flags;
import android.content.pm.SigningDetails;
import android.os.Binder;
import android.os.Handler;
@@ -320,8 +319,7 @@
}
private static boolean isQueryableBySdkSandbox(int callingUid, int targetUid) {
- return Flags.allowSdkSandboxQueryIntentActivities()
- && targetUid == Process.getAppUidForSdkSandboxUid(callingUid);
+ return targetUid == Process.getAppUidForSdkSandboxUid(callingUid);
}
/**
diff --git a/services/core/java/com/android/server/pm/BackgroundUserSoundNotifier.java b/services/core/java/com/android/server/pm/BackgroundUserSoundNotifier.java
index 77572e0..c3cee65 100644
--- a/services/core/java/com/android/server/pm/BackgroundUserSoundNotifier.java
+++ b/services/core/java/com/android/server/pm/BackgroundUserSoundNotifier.java
@@ -34,7 +34,6 @@
import android.media.AudioManager;
import android.media.AudioPlaybackConfiguration;
import android.media.audiopolicy.AudioPolicy;
-import android.multiuser.Flags;
import android.os.Looper;
import android.os.RemoteException;
import android.os.UserHandle;
@@ -156,22 +155,12 @@
@SuppressLint("MissingPermission")
@Override
public void onReceive(Context context, Intent intent) {
- if (Flags.multipleAlarmNotificationsSupport()) {
- if (!intent.hasExtra(EXTRA_NOTIFICATION_CLIENT_UID)) {
- return;
- }
- } else {
- if (mNotificationClientUid == -1) {
- return;
- }
+ if (!intent.hasExtra(EXTRA_NOTIFICATION_CLIENT_UID)) {
+ return;
}
- int clientUid;
- if (Flags.multipleAlarmNotificationsSupport()) {
- clientUid = intent.getIntExtra(EXTRA_NOTIFICATION_CLIENT_UID, -1);
- } else {
- clientUid = mNotificationClientUid;
- }
+ int clientUid = intent.getIntExtra(EXTRA_NOTIFICATION_CLIENT_UID, -1);
+
dismissNotification(clientUid);
if (DEBUG) {
@@ -191,11 +180,9 @@
}
activityManager.switchUser(userId);
}
- if (Flags.multipleAlarmNotificationsSupport()) {
- mNotificationClientUids.remove(clientUid);
- } else {
- mNotificationClientUid = -1;
- }
+
+ mNotificationClientUids.remove(clientUid);
+
}
};
@@ -255,11 +242,8 @@
+ ", displaying notification for current user "
+ foregroundContext.getUserId());
}
- if (Flags.multipleAlarmNotificationsSupport()) {
- mNotificationClientUids.add(afi.getClientUid());
- } else {
- mNotificationClientUid = afi.getClientUid();
- }
+
+ mNotificationClientUids.add(afi.getClientUid());
mNotificationManager.notifyAsUser(LOG_TAG, afi.getClientUid(),
createNotification(userInfo.name, foregroundContext, afi.getClientUid()),
@@ -285,11 +269,8 @@
}
dismissNotification(notificationClientUid);
- if (Flags.multipleAlarmNotificationsSupport()) {
- mNotificationClientUids.remove(notificationClientUid);
- } else {
- mNotificationClientUid = -1;
- }
+ mNotificationClientUids.remove(notificationClientUid);
+
}
}
@@ -371,10 +352,6 @@
}
private boolean isNotificationShown(int notificationClientUid) {
- if (Flags.multipleAlarmNotificationsSupport()) {
- return mNotificationClientUids.contains(notificationClientUid);
- } else {
- return mNotificationClientUid != -1;
- }
+ return mNotificationClientUids.contains(notificationClientUid);
}
}
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 6ba4098..49f660f 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -184,6 +184,7 @@
import com.android.server.pm.permission.PermissionManagerServiceInternal;
import com.android.server.pm.pkg.AndroidPackage;
import com.android.server.pm.pkg.PackageStateInternal;
+import com.android.server.pm.pkg.PackageUserStateInternal;
import com.android.server.pm.pkg.SharedLibraryWrapper;
import com.android.server.rollback.RollbackManagerInternal;
import com.android.server.utils.WatchedArrayMap;
@@ -2451,7 +2452,7 @@
mPm.mAppsFilter.getVisibilityAllowList(mPm.snapshotComputer(),
installRequest.getScannedPackageSetting(),
allUsers, mPm.mSettings.getPackagesLocked());
- if (installRequest.isInstallSystem()) {
+ if (installRequest.isInstallSystem() && oldPackage != null) {
// Remove existing system package
mRemovePackageHelper.removePackage(oldPackage, true);
if (!disableSystemPackageLPw(oldPackage)) {
@@ -2617,6 +2618,22 @@
}
// Clear any existing archive state.
mPm.mInstallerService.mPackageArchiver.clearArchiveState(ps, userId);
+
+ // Check for new user that was created at the same time of this installation
+ // and has no UserState if pkg is installed for first time.
+ final SparseArray<? extends PackageUserStateInternal> us = ps.getUserStates();
+ if (allUsers.length != us.size()) {
+ for (int i = 0; i < allUsers.length; i++) {
+ final int currentUserId = allUsers[i];
+ final PackageUserStateInternal pus = us.get(currentUserId);
+ if (pus == null) {
+ // If a user state doesn't exist, explicitly set the app to
+ // be not installed on the user, otherwise the default installed
+ // state on that user would become true, which is incorrect.
+ ps.setInstalled(false, currentUserId);
+ }
+ }
+ }
} else {
// The caller explicitly specified INSTALL_ALL_USERS flag.
// Thus, updating the settings to install the app for all users.
@@ -3159,11 +3176,9 @@
// code is loaded by a new Activity before ApplicationInfo changes have
// propagated to all application threads.
mPm.scheduleDeferredNoKillPostDelete(args);
- if (Flags.improveInstallDontKill()) {
- try (var installLock = mPm.mInstallLock.acquireLock()) {
- PackageManagerServiceUtils.linkFilesToOldDirs(mPm.mInstaller,
- packageName, pkgSetting.getPath(), pkgSetting.getOldPaths());
- }
+ try (var installLock = mPm.mInstallLock.acquireLock()) {
+ PackageManagerServiceUtils.linkFilesToOldDirs(mPm.mInstaller,
+ packageName, pkgSetting.getPath(), pkgSetting.getOldPaths());
}
} else {
mRemovePackageHelper.cleanUpResources(packageName, args.getCodeFile(),
@@ -4574,8 +4589,7 @@
}
}
- final long firstInstallTime = Flags.fixSystemAppsFirstInstallTime()
- ? System.currentTimeMillis() : 0;
+ final long firstInstallTime = System.currentTimeMillis();
final ScanResult scanResult = scanPackageNew(parsedPackage, parseFlags,
scanFlags | SCAN_UPDATE_SIGNATURE, firstInstallTime, user, null);
return new Pair<>(scanResult, shouldHideSystemApp);
diff --git a/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java b/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java
index 7229f07..273473b 100644
--- a/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java
+++ b/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java
@@ -30,7 +30,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.pm.ApplicationInfo;
-import android.content.pm.Flags;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Environment;
@@ -388,8 +387,8 @@
// Force the match for these cases
// 1. pkg.getTargetSdkVersion >= Build.VERSION_CODES.VANILLA_ICE_CREAM
// 2. cpuAbiOverride is null. If it is non-null, it is set via shell for testing
- final boolean forceMatch = Flags.forceMultiArchNativeLibsMatch()
- && pkg.getTargetSdkVersion() >= Build.VERSION_CODES.VANILLA_ICE_CREAM
+ final boolean forceMatch =
+ pkg.getTargetSdkVersion() >= Build.VERSION_CODES.VANILLA_ICE_CREAM
&& cpuAbiOverride == null;
String[] supported32BitAbis = forceMatch ? getNativelySupported32BitAbis()
diff --git a/services/core/java/com/android/server/pm/PackageFreezer.java b/services/core/java/com/android/server/pm/PackageFreezer.java
index d66eb81..b19349f 100644
--- a/services/core/java/com/android/server/pm/PackageFreezer.java
+++ b/services/core/java/com/android/server/pm/PackageFreezer.java
@@ -20,7 +20,6 @@
import android.annotation.Nullable;
import android.annotation.SpecialUsers.CanBeALL;
import android.annotation.UserIdInt;
-import android.content.pm.Flags;
import android.content.pm.PackageManager;
import dalvik.system.CloseGuard;
@@ -85,7 +84,7 @@
ps = mPm.mSettings.getPackageLPr(mPackageName);
}
if (ps != null) {
- if (waitAppKilled && Flags.waitApplicationKilled()) {
+ if (waitAppKilled) {
mPm.killApplicationSync(ps.getPackageName(), ps.getAppId(), userId, killReason,
exitInfoReason);
} else {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 4775e0f..e9e7be5 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -1309,15 +1309,13 @@
// If the feature flag is on, retain the old files for a day. Otherwise, delete the old
// files after a few seconds.
long deleteDelayMillis = DEFERRED_NO_KILL_POST_DELETE_DELAY_MS;
- if (Flags.improveInstallDontKill()) {
- deleteDelayMillis = Binder.withCleanCallingIdentity(() -> {
- return DeviceConfig.getLong(NAMESPACE_PACKAGE_MANAGER_SERVICE,
- /* name= */ PROPERTY_DEFERRED_NO_KILL_POST_DELETE_DELAY_MS_EXTENDED,
- /* defaultValue= */ DEFERRED_NO_KILL_POST_DELETE_DELAY_MS_EXTENDED);
- });
- Slog.w(TAG, "Delaying the deletion of <" + args.getCodePath() + "> by "
- + deleteDelayMillis + "ms or till the next reboot");
- }
+ deleteDelayMillis = Binder.withCleanCallingIdentity(() -> {
+ return DeviceConfig.getLong(NAMESPACE_PACKAGE_MANAGER_SERVICE,
+ /* name= */ PROPERTY_DEFERRED_NO_KILL_POST_DELETE_DELAY_MS_EXTENDED,
+ /* defaultValue= */ DEFERRED_NO_KILL_POST_DELETE_DELAY_MS_EXTENDED);
+ });
+ Slog.w(TAG, "Delaying the deletion of <" + args.getCodePath() + "> by "
+ + deleteDelayMillis + "ms or till the next reboot");
mHandler.sendMessageDelayed(message, deleteDelayMillis);
}
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index a902f5f..99673d7 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -853,7 +853,7 @@
usesStaticLibrariesVersions = other.usesStaticLibrariesVersions != null
? Arrays.copyOf(other.usesStaticLibrariesVersions,
other.usesStaticLibrariesVersions.length) : null;
- mUserStates.clear();
+
for (int i = 0; i < other.mUserStates.size(); i++) {
if (sealedSnapshot) {
mUserStates.put(other.mUserStates.keyAt(i),
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 5564948..e2b0c0b 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -1291,7 +1291,7 @@
pkgSetting.setLegacyNativeLibraryPath(legacyNativeLibraryPath);
}
pkgSetting.setPath(codePath);
- if (isDontKill && Flags.improveInstallDontKill()) {
+ if (isDontKill) {
// We retain old code paths for DONT_KILL installs. Keep a record of old paths until
// they are removed.
pkgSetting.addOldPath(oldCodePath);
@@ -4753,7 +4753,7 @@
ps.getPackageName()));
// Only system apps are initially installed.
ps.setInstalled(shouldReallyInstall, userHandle);
- if (Flags.fixSystemAppsFirstInstallTime() && shouldReallyInstall) {
+ if (shouldReallyInstall) {
ps.setFirstInstallTime(currentTimeMillis, userHandle);
}
diff --git a/services/core/java/com/android/server/pm/UserManagerInternal.java b/services/core/java/com/android/server/pm/UserManagerInternal.java
index dc2aeb0..5f7a13e 100644
--- a/services/core/java/com/android/server/pm/UserManagerInternal.java
+++ b/services/core/java/com/android/server/pm/UserManagerInternal.java
@@ -26,7 +26,6 @@
import android.content.pm.UserProperties;
import android.content.res.Resources;
import android.graphics.Bitmap;
-import android.multiuser.Flags;
import android.os.Bundle;
import android.os.UserManager;
import android.util.DebugUtils;
@@ -619,8 +618,7 @@
* users.
*/
public static boolean shouldShowNotificationForBackgroundUserSounds() {
- return Flags.addUiForSoundsFromBackgroundUsers() && Resources.getSystem().getBoolean(
- com.android.internal.R.bool.config_showNotificationForBackgroundUserAlarms)
- && UserManager.supportsMultipleUsers();
+ return UserManager.supportsMultipleUsers() && Resources.getSystem().getBoolean(
+ com.android.internal.R.bool.config_showNotificationForBackgroundUserAlarms);
}
}
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index ec252c6..c53c23b 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -290,6 +290,7 @@
private static final String USER_INFO_DIR = "system" + File.separator + "users";
private static final String USER_LIST_FILENAME = "userlist.xml";
+ private static final String USER_LIST_PERF_FILENAME = "user.list";
private static final String USER_PHOTO_FILENAME = "photo.png";
private static final String USER_PHOTO_FILENAME_TMP = USER_PHOTO_FILENAME + ".tmp";
@@ -377,6 +378,7 @@
private final File mUsersDir;
private final File mUserListFile;
+ private final File mPerfUserListFile;
private final IBinder mUserRestrictionToken = new Binder();
@@ -1084,6 +1086,7 @@
FileUtils.S_IRWXU | FileUtils.S_IRWXG | FileUtils.S_IROTH | FileUtils.S_IXOTH,
-1, -1);
mUserListFile = new File(mUsersDir, USER_LIST_FILENAME);
+ mPerfUserListFile = new File(mUsersDir, USER_LIST_PERF_FILENAME);
initDefaultGuestRestrictions();
readUserListLP();
sInstance = this;
@@ -1104,16 +1107,14 @@
* so that caches can start working.
*/
private static final void initPropertyInvalidatedCaches() {
- if (android.multiuser.Flags.cachesNotInvalidatedAtStartReadOnly()) {
- UserManager.invalidateIsUserUnlockedCache();
- UserManager.invalidateQuietModeEnabledCache();
- if (android.multiuser.Flags.cacheUserPropertiesCorrectlyReadOnly()) {
- UserManager.invalidateStaticUserProperties();
- UserManager.invalidateUserPropertiesCache();
- }
- UserManager.invalidateCacheOnUserListChange();
- UserManager.invalidateUserRestriction();
+ UserManager.invalidateIsUserUnlockedCache();
+ UserManager.invalidateQuietModeEnabledCache();
+ if (android.multiuser.Flags.cacheUserPropertiesCorrectlyReadOnly()) {
+ UserManager.invalidateStaticUserProperties();
+ UserManager.invalidateUserPropertiesCache();
}
+ UserManager.invalidateCacheOnUserListChange();
+ UserManager.invalidateUserRestriction();
}
void systemReady() {
@@ -2379,25 +2380,16 @@
* Returns a UserInfo object with the name filled in, for Owner and Guest, or the original
* if the name is already set.
*
- * Note: Currently, the resulting name can be null if a user was truly created with a null name.
+ * <p><b>Note:</b> Currently, the resulting name can be {@code null} if a user was truly created
+ * with a {@code null} name.
*/
- private UserInfo userWithName(UserInfo orig) {
+ @VisibleForTesting
+ @Nullable
+ UserInfo userWithName(@Nullable UserInfo orig) {
if (orig != null && orig.name == null) {
- String name = null;
- // TODO(b/407597096): refactor to use getName() instead
- if (orig.id == UserHandle.USER_SYSTEM) {
- if (DBG_ALLOCATION) {
- final int number = mUser0Allocations.incrementAndGet();
- Slog.w(LOG_TAG, "System user instantiated at least " + number + " times");
- }
- name = getOwnerName();
- } else if (orig.isMain()) {
- name = getOwnerName();
- } else if (orig.isGuest()) {
- name = getGuestName();
- }
+ String name = getName(orig, /* logUser0Allocations= */ true);
if (name != null) {
- final UserInfo withName = new UserInfo(orig);
+ UserInfo withName = new UserInfo(orig);
withName.name = name;
return withName;
}
@@ -2407,10 +2399,22 @@
@Nullable
String getName(UserInfo user) {
+ return getName(user, /* logUser0Allocations= */ false);
+ }
+
+ @Nullable
+ private String getName(UserInfo user, boolean logUser0Allocations) {
if (user.name != null) {
return user.name;
}
- if (user.id == UserHandle.USER_SYSTEM || user.isMain()) {
+ if (user.id == UserHandle.USER_SYSTEM) {
+ if (DBG_ALLOCATION && logUser0Allocations) {
+ int number = mUser0Allocations.incrementAndGet();
+ Slog.w(LOG_TAG, "System user instantiated at least " + number + " times");
+ }
+ return getOwnerName();
+ }
+ if (user.isMain()) {
return getOwnerName();
}
if (user.isGuest()) {
@@ -4561,11 +4565,15 @@
}
private ResilientAtomicFile getUserListFile() {
- File tempBackup = new File(mUserListFile.getParent(), mUserListFile.getName() + ".backup");
- File reserveCopy = new File(mUserListFile.getParent(),
- mUserListFile.getName() + ".reservecopy");
+ return getUserListFile(mUserListFile);
+ }
+
+ private ResilientAtomicFile getUserListFile(File filename) {
+ File tempBackup = new File(filename.getParent(), filename.getName() + ".backup");
+ File reserveCopy = new File(filename.getParent(),
+ filename.getName() + ".reservecopy");
int fileMode = FileUtils.S_IRWXU | FileUtils.S_IRWXG | FileUtils.S_IXOTH;
- return new ResilientAtomicFile(mUserListFile, tempBackup, reserveCopy, fileMode,
+ return new ResilientAtomicFile(filename, tempBackup, reserveCopy, fileMode,
"user list", (priority, msg) -> {
Slog.e(LOG_TAG, msg);
// Something went wrong, schedule full rewrite.
@@ -5003,11 +5011,9 @@
/** Returns the oldest Full Admin user, or null is if there none. */
private @Nullable UserInfo getEarliestCreatedFullUser() {
- // TODO(b/407597096): it could call with resolveNullNames=false, but that method was added
- // in a "pure refactoring" CL
- final List<UserInfo> users = getUsersInternal(/* excludePartial= */ true,
+ List<UserInfo> users = getUsersInternal(/* excludePartial= */ true,
/* excludeDying= */ true, /* excludePreCreated= */ true,
- /* resolveNullNames= */ true);
+ /* resolveNullNames= */ false);
UserInfo earliestUser = null;
long earliestCreationTime = Long.MAX_VALUE;
for (int i = 0; i < users.size(); i++) {
@@ -5064,11 +5070,13 @@
writeUserListLP();
}
- private String getOwnerName() {
+ @VisibleForTesting
+ String getOwnerName() {
return mOwnerName.get();
}
- private String getGuestName() {
+ @VisibleForTesting
+ String getGuestName() {
return mContext.getString(com.android.internal.R.string.guest_name);
}
@@ -5350,6 +5358,26 @@
file.failWrite(fos);
}
}
+
+ if (android.multiuser.Flags.perfettoMultiuserTable()) {
+ try (ResilientAtomicFile file = getUserListFile(mPerfUserListFile)) {
+ FileOutputStream fos = null;
+ try {
+ fos = file.startWrite();
+ synchronized (mUsersLock) {
+ for (int i = 0; i < mUsers.size(); i++) {
+ UserInfo user = mUsers.valueAt(i).info;
+ final String line = user.userType + " " + user.id + '\n';
+ fos.write(line.getBytes());
+ }
+ }
+ file.finishWrite(fos);
+ } catch (Exception e) {
+ Slog.e(LOG_TAG, "Error writing perf user list", e);
+ file.failWrite(fos);
+ }
+ }
+ }
}
@GuardedBy({"mPackagesLock"})
@@ -5972,9 +6000,8 @@
}
userInfo.partial = false;
- if (android.multiuser.Flags.invalidateCacheOnUsersChangedReadOnly()) {
- UserManager.invalidateCacheOnUserListChange();
- }
+ UserManager.invalidateCacheOnUserListChange();
+
synchronized (mPackagesLock) {
writeUserLP(userData);
}
@@ -6281,11 +6308,9 @@
/** Writes a UserInfo pulled atom for each user on the device. */
private int onPullAtom(int atomTag, List<StatsEvent> data) {
if (atomTag == FrameworkStatsLog.USER_INFO) {
- // TODO(b/407597096): it could call with resolveNullNames=false, but that method was
- // added in a "pure refactoring" CL
final List<UserInfo> users = getUsersInternal(/* excludePartial= */ true,
/* excludeDying= */ true, /* excludePreCreated= */ true,
- /* resolveNullNames= */ true);
+ /* resolveNullNames= */ false);
final int size = users.size();
if (size > 1) {
for (int idx = 0; idx < size; idx++) {
@@ -6573,9 +6598,7 @@
// on next startup, in case the runtime stops now before stopping and
// removing the user completely.
userData.info.partial = true;
- if (android.multiuser.Flags.invalidateCacheOnUsersChangedReadOnly()) {
- UserManager.invalidateCacheOnUserListChange();
- }
+ UserManager.invalidateCacheOnUserListChange();
// Mark it as disabled, so that it isn't returned any more when
// profiles are queried.
addUserInfoFlags(userData.info, UserInfo.FLAG_DISABLED);
@@ -7188,9 +7211,7 @@
*/
@GuardedBy("mUsersLock")
private void addUserDataLU(UserData userData) {
- if (android.multiuser.Flags.invalidateCacheOnUsersChangedReadOnly()) {
- UserManager.invalidateCacheOnUserListChange();
- }
+ UserManager.invalidateCacheOnUserListChange();
mUsers.put(userData.info.id, userData);
}
diff --git a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
index 5ca5fda..ada644e 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
@@ -423,8 +423,7 @@
if (ai.isArchived) {
ai.nonLocalizedLabel = state.getArchiveState().getActivityInfos().get(0).getTitle();
}
- if (!state.isInstalled() && !state.dataExists()
- && android.content.pm.Flags.nullableDataDir()) {
+ if (!state.isInstalled() && !state.dataExists()) {
// The data dir has been deleted
ai.dataDir = null;
}
@@ -1069,8 +1068,7 @@
return;
}
- if (!state.isInstalled() && !state.dataExists()
- && android.content.pm.Flags.nullableDataDir()) {
+ if (!state.isInstalled() && !state.dataExists()) {
// The data dir has been deleted
output.dataDir = null;
return;
@@ -1118,8 +1116,7 @@
return;
}
- if (!state.isInstalled() && !state.dataExists()
- && android.content.pm.Flags.nullableDataDir()) {
+ if (!state.isInstalled() && !state.dataExists()) {
// The data dir has been deleted
output.dataDir = null;
return;
@@ -1167,8 +1164,7 @@
}
if (!ps.getUserStateOrDefault(userId).isInstalled()
- && !ps.getUserStateOrDefault(userId).dataExists()
- && android.content.pm.Flags.nullableDataDir()) {
+ && !ps.getUserStateOrDefault(userId).dataExists()) {
// The app has been uninstalled for the user and the data dir has been deleted
return null;
}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 76dbf02..7afb6f7 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -43,8 +43,6 @@
import static android.view.Display.INVALID_DISPLAY;
import static android.view.Display.STATE_OFF;
import static android.view.KeyEvent.KEYCODE_BACK;
-import static android.view.KeyEvent.KEYCODE_DPAD_CENTER;
-import static android.view.KeyEvent.KEYCODE_DPAD_DOWN;
import static android.view.KeyEvent.KEYCODE_HOME;
import static android.view.KeyEvent.KEYCODE_POWER;
import static android.view.KeyEvent.KEYCODE_STEM_PRIMARY;
@@ -86,7 +84,6 @@
import static com.android.hardware.input.Flags.enableNew25q2Keycodes;
import static com.android.hardware.input.Flags.enableTalkbackAndMagnifierKeyGestures;
import static com.android.hardware.input.Flags.enableVoiceAccessKeyGestures;
-import static com.android.hardware.input.Flags.inputManagerLifecycleSupport;
import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.SCREENSHOT_KEYCHORD_DELAY;
import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVERED;
import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVER_ABSENT;
@@ -107,6 +104,7 @@
import static com.android.server.wm.WindowManagerPolicyProto.SCREEN_ON_FULLY;
import static com.android.server.wm.WindowManagerPolicyProto.WINDOW_MANAGER_DRAW_COMPLETE;
import static com.android.systemui.shared.Flags.enableLppAssistInvocationEffect;
+import static com.android.window.flags.Flags.delegateBackGestureToShell;
import android.accessibilityservice.AccessibilityService;
import android.annotation.Nullable;
@@ -149,7 +147,6 @@
import android.hardware.hdmi.HdmiPlaybackClient.OneTouchPlayCallback;
import android.hardware.input.AppLaunchData;
import android.hardware.input.InputManager;
-import android.hardware.input.InputSettings;
import android.hardware.input.KeyGestureEvent;
import android.media.AudioManager;
import android.media.AudioManagerInternal;
@@ -215,7 +212,6 @@
import android.widget.Toast;
import com.android.internal.R;
-import com.android.internal.accessibility.AccessibilityShortcutController;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.AssistUtils;
import com.android.internal.display.BrightnessUtils;
@@ -243,7 +239,6 @@
import com.android.server.input.InputManagerInternal;
import com.android.server.inputmethod.InputMethodManagerInternal;
import com.android.server.pm.UserManagerInternal;
-import com.android.server.policy.KeyCombinationManager.TwoKeysCombinationRule;
import com.android.server.policy.keyguard.KeyguardServiceDelegate;
import com.android.server.policy.keyguard.KeyguardServiceDelegate.DrawnListener;
import com.android.server.policy.keyguard.KeyguardStateMonitor.StateCallback;
@@ -321,11 +316,6 @@
static final int VERY_LONG_PRESS_POWER_NOTHING = 0;
static final int VERY_LONG_PRESS_POWER_GLOBAL_ACTIONS = 1;
- // must match: config_keyChordPowerVolumeUp in config.xml
- static final int POWER_VOLUME_UP_BEHAVIOR_NOTHING = 0;
- static final int POWER_VOLUME_UP_BEHAVIOR_MUTE = 1;
- static final int POWER_VOLUME_UP_BEHAVIOR_GLOBAL_ACTIONS = 2;
-
// must match: config_doublePressOnPowerBehavior in config.xml
// The config value can be overridden using Settings.Global.POWER_BUTTON_DOUBLE_PRESS and/or
// Settings.Global.POWER_BUTTON_TRIPLE_PRESS
@@ -494,9 +484,6 @@
/** If true, can use a keyboard shortcut to trigger a bugreport. */
boolean mEnableBugReportKeyboardShortcut = false;
- /** Controller that supports enabling an AccessibilityService by holding down the volume keys */
- private AccessibilityShortcutController mAccessibilityShortcutController;
-
private TalkbackShortcutController mTalkbackShortcutController;
private VoiceAccessShortcutController mVoiceAccessShortcutController;
@@ -599,7 +586,6 @@
int mLongPressOnBackBehavior;
int mShortPressOnSleepBehavior;
int mShortPressOnWindowBehavior;
- int mPowerVolUpBehavior;
boolean mStylusButtonsEnabled = true;
boolean mKidsModeEnabled;
boolean mHasSoftInput = false;
@@ -716,7 +702,6 @@
private int mPowerButtonSuppressionDelayMillis = POWER_BUTTON_SUPPRESSION_DELAY_DEFAULT_MILLIS;
- private KeyCombinationManager mKeyCombinationManager;
private SingleKeyGestureDetector mSingleKeyGestureDetector;
private GestureLauncherService mGestureLauncherService;
private ButtonOverridePermissionChecker mButtonOverridePermissionChecker;
@@ -739,9 +724,7 @@
private static final int MSG_LAUNCH_VOICE_ASSIST_WITH_WAKE_LOCK = 12;
private static final int MSG_SHOW_PICTURE_IN_PICTURE_MENU = 15;
private static final int MSG_SCREENSHOT_CHORD = 16;
- private static final int MSG_ACCESSIBILITY_SHORTCUT = 17;
private static final int MSG_BUGREPORT_TV = 18;
- private static final int MSG_ACCESSIBILITY_TV = 19;
private static final int MSG_DISPATCH_BACK_KEY_TO_AUTOFILL = 20;
private static final int MSG_SYSTEM_KEY_PRESS = 21;
private static final int MSG_HANDLE_ALL_APPS = 22;
@@ -801,17 +784,9 @@
case MSG_SHOW_PICTURE_IN_PICTURE_MENU:
showPictureInPictureMenuInternal();
break;
- case MSG_ACCESSIBILITY_SHORTCUT:
- accessibilityShortcutActivated();
- break;
case MSG_BUGREPORT_TV:
requestBugreportForTv();
break;
- case MSG_ACCESSIBILITY_TV:
- if (mAccessibilityShortcutController.isAccessibilityShortcutAvailable(false)) {
- accessibilityShortcutActivated();
- }
- break;
case MSG_DISPATCH_BACK_KEY_TO_AUTOFILL:
mAutofillManagerInternal.onBackKeyPressed();
break;
@@ -1081,8 +1056,7 @@
// If the power key has still not yet been handled, then detect short
// press, long press, or multi press and decide what to do.
mPowerKeyHandled = mPowerKeyHandled || hungUp
- || handledByPowerManager || isKeyGestureTriggered
- || mKeyCombinationManager.isPowerKeyIntercepted();
+ || handledByPowerManager || isKeyGestureTriggered;
if (!mPowerKeyHandled) {
if (!interactive) {
@@ -1558,10 +1532,6 @@
}
}
- private void accessibilityShortcutActivated() {
- mAccessibilityShortcutController.performAccessibilityShortcut();
- }
-
private void sleepPress() {
if (mShortPressOnSleepBehavior == SHORT_PRESS_SLEEP_GO_TO_SLEEP_AND_GO_HOME) {
launchHomeFromHotKey(DEFAULT_DISPLAY, false /* awakenDreams */,
@@ -1810,34 +1780,12 @@
pressDelay);
}
- private void interceptAccessibilityShortcutChord() {
- mHandler.removeMessages(MSG_ACCESSIBILITY_SHORTCUT);
- mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_ACCESSIBILITY_SHORTCUT),
- getAccessibilityShortcutTimeout());
- }
-
private void interceptRingerToggleChord() {
mHandler.removeMessages(MSG_RINGER_TOGGLE_CHORD);
mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_RINGER_TOGGLE_CHORD),
getRingerToggleChordDelay());
}
- private long getAccessibilityShortcutTimeout() {
- final ViewConfiguration config = ViewConfiguration.get(mContext);
- final boolean hasDialogShown = Settings.Secure.getIntForUser(mContext.getContentResolver(),
- Settings.Secure.ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 0, mCurrentUserId) != 0;
- final boolean skipTimeoutRestriction =
- Settings.Secure.getIntForUser(mContext.getContentResolver(),
- Settings.Secure.SKIP_ACCESSIBILITY_SHORTCUT_DIALOG_TIMEOUT_RESTRICTION, 0,
- mCurrentUserId) != 0;
-
- // If users manually set the volume key shortcut for any accessibility service, the
- // system would bypass the timeout restriction of the shortcut dialog.
- return hasDialogShown || skipTimeoutRestriction
- ? config.getAccessibilityShortcutKeyTimeoutAfterConfirmation()
- : config.getAccessibilityShortcutKeyTimeout();
- }
-
private long getScreenshotChordLongPressDelay() {
long delayMs = DeviceConfig.getLong(
DeviceConfig.NAMESPACE_SYSTEMUI, SCREENSHOT_KEYCHORD_DELAY,
@@ -1858,10 +1806,6 @@
mHandler.removeMessages(MSG_SCREENSHOT_CHORD);
}
- private void cancelPendingAccessibilityShortcutAction() {
- mHandler.removeMessages(MSG_ACCESSIBILITY_SHORTCUT);
- }
-
private void cancelPendingRingerToggleChordAction() {
mHandler.removeMessages(MSG_RINGER_TOGGLE_CHORD);
}
@@ -2279,11 +2223,6 @@
return Looper.myLooper();
}
- AccessibilityShortcutController getAccessibilityShortcutController(
- Context context, Handler handler, int initialUserId) {
- return new AccessibilityShortcutController(context, handler, initialUserId);
- }
-
Supplier<GlobalActions> getGlobalActionsFactory() {
return () -> new GlobalActions(mContext, mWindowManagerFuncs);
}
@@ -2356,8 +2295,6 @@
mHasFeatureLeanback = mPackageManager.hasSystemFeature(FEATURE_LEANBACK);
mHasFeatureAuto = mPackageManager.hasSystemFeature(FEATURE_AUTOMOTIVE);
mHasFeatureHdmiCec = mPackageManager.hasSystemFeature(FEATURE_HDMI_CEC);
- mAccessibilityShortcutController = injector.getAccessibilityShortcutController(
- mContext, new Handler(), mCurrentUserId);
mGlobalActionsFactory = injector.getGlobalActionsFactory();
mLockPatternUtils = injector.getLockPatternUtils();
mLogger = new MetricsLogger();
@@ -2564,159 +2501,12 @@
mTalkbackShortcutController = injector.getTalkbackShortcutController();
mVoiceAccessShortcutController = injector.getVoiceAccessShortcutController();
mWindowWakeUpPolicy = injector.getWindowWakeUpPolicy();
- initKeyCombinationRules();
initSingleKeyGestureRules(injector.getLooper());
initKeyGestures();
mButtonOverridePermissionChecker = injector.getButtonOverridePermissionChecker();
mSideFpsEventHandler = new SideFpsEventHandler(mContext, mHandler, mPowerManager);
}
- private void initKeyCombinationRules() {
- mKeyCombinationManager = new KeyCombinationManager(mHandler);
- if (InputSettings.doesKeyGestureEventHandlerSupportMultiKeyGestures()) {
- return;
- }
- final boolean screenshotChordEnabled = mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_enableScreenshotChord);
-
- if (screenshotChordEnabled) {
- mKeyCombinationManager.addRule(
- new TwoKeysCombinationRule(KEYCODE_VOLUME_DOWN, KEYCODE_POWER) {
- @Override
- public void execute() {
- mPowerKeyHandled = true;
- interceptScreenshotChord(
- SCREENSHOT_KEY_CHORD, getScreenshotChordLongPressDelay());
- }
- @Override
- public void cancel() {
- cancelPendingScreenshotChordAction();
- }
- });
-
- if (mHasFeatureWatch) {
- mKeyCombinationManager.addRule(
- new TwoKeysCombinationRule(KEYCODE_POWER, KEYCODE_STEM_PRIMARY) {
- @Override
- public void execute() {
- mPowerKeyHandled = true;
- interceptScreenshotChord(SCREENSHOT_KEY_CHORD,
- getScreenshotChordLongPressDelay());
- }
- @Override
- public void cancel() {
- cancelPendingScreenshotChordAction();
- }
- });
- }
- }
-
- mKeyCombinationManager.addRule(
- new TwoKeysCombinationRule(KEYCODE_VOLUME_DOWN, KEYCODE_VOLUME_UP) {
- @Override
- public boolean preCondition() {
- return mAccessibilityShortcutController
- .isAccessibilityShortcutAvailable(isKeyguardLocked());
- }
- @Override
- public void execute() {
- interceptAccessibilityShortcutChord();
- }
- @Override
- public void cancel() {
- cancelPendingAccessibilityShortcutAction();
- }
- });
-
- // Volume up + power can either be the "ringer toggle chord" or as another way to
- // launch GlobalActions. This behavior can change at runtime so we must check behavior
- // inside the TwoKeysCombinationRule.
- mKeyCombinationManager.addRule(
- new TwoKeysCombinationRule(KEYCODE_VOLUME_UP, KEYCODE_POWER) {
- @Override
- public boolean preCondition() {
- switch (mPowerVolUpBehavior) {
- case POWER_VOLUME_UP_BEHAVIOR_MUTE:
- return mRingerToggleChord != VOLUME_HUSH_OFF;
- default:
- return true;
- }
- }
- @Override
- public void execute() {
- switch (mPowerVolUpBehavior) {
- case POWER_VOLUME_UP_BEHAVIOR_MUTE:
- // no haptic feedback here since
- interceptRingerToggleChord();
- mPowerKeyHandled = true;
- break;
- case POWER_VOLUME_UP_BEHAVIOR_GLOBAL_ACTIONS:
- performHapticFeedback(
- HapticFeedbackConstants.LONG_PRESS_POWER_BUTTON,
- "Power + Volume Up - Global Actions");
- showGlobalActions();
- mPowerKeyHandled = true;
- break;
- default:
- break;
- }
- }
- @Override
- public void cancel() {
- switch (mPowerVolUpBehavior) {
- case POWER_VOLUME_UP_BEHAVIOR_MUTE:
- cancelPendingRingerToggleChordAction();
- break;
- case POWER_VOLUME_UP_BEHAVIOR_GLOBAL_ACTIONS:
- cancelGlobalActionsAction();
- break;
- }
- }
- });
-
- if (mHasFeatureLeanback) {
- mKeyCombinationManager.addRule(
- new TwoKeysCombinationRule(KEYCODE_BACK, KEYCODE_DPAD_DOWN) {
- @Override
- public void execute() {
- mBackKeyHandled = true;
- interceptAccessibilityGestureTv();
- }
- @Override
- public void cancel() {
- cancelAccessibilityGestureTv();
- }
- @Override
- public long getKeyInterceptDelayMs() {
- // Use a timeout of 0 to prevent additional latency in processing of
- // this key. This will potentially cause some unwanted UI actions if the
- // user does end up triggering the key combination later, but in most
- // cases, the user will simply hit a single key, and this will allow us
- // to process it without first waiting to see if the combination is
- // going to be triggered.
- return 0;
- }
- });
-
- mKeyCombinationManager.addRule(
- new TwoKeysCombinationRule(KEYCODE_DPAD_CENTER, KEYCODE_BACK) {
- @Override
- public void execute() {
- mBackKeyHandled = true;
- interceptBugreportGestureTv();
- }
- @Override
- public void cancel() {
- cancelBugreportGestureTv();
- }
- @Override
- public long getKeyInterceptDelayMs() {
- return 0;
- }
- });
- }
- }
-
/**
* Rule for single power key gesture.
*/
@@ -3118,11 +2908,6 @@
mContext.getResources().getInteger(
com.android.internal.R.integer.config_longPressOnPowerDurationMs));
- mPowerVolUpBehavior = Settings.Global.getInt(resolver,
- Settings.Global.KEY_CHORD_POWER_VOLUME_UP,
- mContext.getResources().getInteger(
- com.android.internal.R.integer.config_keyChordPowerVolumeUp));
-
mShortPressOnStemPrimaryBehavior = Settings.Global.getInt(resolver,
Settings.Global.STEM_PRIMARY_BUTTON_SHORT_PRESS,
mContext.getResources().getInteger(
@@ -3509,21 +3294,6 @@
+ keyguardOn() + " canceled=" + event.isCanceled());
}
- if (!InputSettings.doesKeyGestureEventHandlerSupportMultiKeyGestures()) {
- if (mKeyCombinationManager.isKeyConsumed(event)) {
- return keyConsumed;
- }
-
- if ((flags & KeyEvent.FLAG_FALLBACK) == 0) {
- final long now = SystemClock.uptimeMillis();
- final long interceptTimeout = mKeyCombinationManager.getKeyInterceptTimeout(
- keyCode);
- if (now < interceptTimeout) {
- return interceptTimeout - now;
- }
- }
- }
-
Set<Integer> consumedKeys = mConsumedKeysForDevice.get(deviceId);
if (consumedKeys == null) {
consumedKeys = new HashSet<>();
@@ -3618,7 +3388,6 @@
KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL,
KeyGestureEvent.KEY_GESTURE_TYPE_TAKE_SCREENSHOT,
KeyGestureEvent.KEY_GESTURE_TYPE_TRIGGER_BUG_REPORT,
- KeyGestureEvent.KEY_GESTURE_TYPE_BACK,
KeyGestureEvent.KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION,
KeyGestureEvent.KEY_GESTURE_TYPE_DESKTOP_MODE,
KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_LEFT,
@@ -3627,10 +3396,8 @@
KeyGestureEvent.KEY_GESTURE_TYPE_BRIGHTNESS_UP,
KeyGestureEvent.KEY_GESTURE_TYPE_BRIGHTNESS_DOWN,
KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS_SWITCHER,
- KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS,
KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SEARCH,
KeyGestureEvent.KEY_GESTURE_TYPE_LANGUAGE_SWITCH,
- KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT,
KeyGestureEvent.KEY_GESTURE_TYPE_CLOSE_ALL_DIALOGS,
KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION,
KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_DO_NOT_DISTURB,
@@ -3639,12 +3406,20 @@
KeyGestureEvent.KEY_GESTURE_TYPE_GLOBAL_ACTIONS,
KeyGestureEvent.KEY_GESTURE_TYPE_TV_TRIGGER_BUG_REPORT
));
+ if (!delegateBackGestureToShell()) {
+ supportedGestures.add(KeyGestureEvent.KEY_GESTURE_TYPE_BACK);
+ }
if (enableTalkbackAndMagnifierKeyGestures()) {
supportedGestures.add(KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_TALKBACK);
}
if (enableVoiceAccessKeyGestures()) {
supportedGestures.add(KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_VOICE_ACCESS);
}
+ if (!com.android.window.flags.Flags.enableKeyGestureHandlerForRecents()) {
+ // When enableKeyGestureHandlerForRecents is enabled, the event is handled in the
+ // recents app.
+ supportedGestures.add(KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS);
+ }
mInputManager.registerKeyGestureEventHandler(supportedGestures,
PhoneWindowManager.this::handleKeyGestureEvent);
}
@@ -3725,8 +3500,9 @@
}
break;
case KeyGestureEvent.KEY_GESTURE_TYPE_BACK:
- if (complete) {
- injectBackGesture(SystemClock.uptimeMillis());
+ if (!delegateBackGestureToShell() && complete) {
+ injectBackGesture(SystemClock.uptimeMillis(),
+ getTargetDisplayIdForKeyGestureEvent(event));
}
break;
case KeyGestureEvent.KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION:
@@ -3830,12 +3606,6 @@
cancelBugreportGestureTv();
}
break;
- case KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT:
- if (complete && mAccessibilityShortcutController.isAccessibilityShortcutAvailable(
- isKeyguardLocked())) {
- mHandler.sendMessage(mHandler.obtainMessage(MSG_ACCESSIBILITY_SHORTCUT));
- }
- break;
case KeyGestureEvent.KEY_GESTURE_TYPE_CLOSE_ALL_DIALOGS:
if (complete) {
mContext.closeSystemDialogs();
@@ -3955,21 +3725,20 @@
}
@SuppressLint("MissingPermission")
- private void injectBackGesture(long downtime) {
- if (mActivityTaskManagerInternal.requestBackGesture()) {
- return;
- }
+ private void injectBackGesture(long downtime, int displayId) {
// Create and inject down event
KeyEvent downEvent = new KeyEvent(downtime, downtime, KeyEvent.ACTION_DOWN,
KeyEvent.KEYCODE_BACK, 0 /* repeat */, 0 /* metaState */,
KeyCharacterMap.VIRTUAL_KEYBOARD, 0 /* scancode */,
KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY,
InputDevice.SOURCE_KEYBOARD);
+ downEvent.setDisplayId(displayId);
mInputManager.injectInputEvent(downEvent, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
// Create and inject up event
KeyEvent upEvent = KeyEvent.changeAction(downEvent, KeyEvent.ACTION_UP);
+ upEvent.setDisplayId(displayId);
mInputManager.injectInputEvent(upEvent, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
downEvent.recycle();
@@ -4031,20 +3800,6 @@
mHandler.removeMessages(MSG_BUGREPORT_TV);
}
- /**
- * TV only: recognizes a remote control gesture as Accessibility shortcut.
- * Shortcut: Long press (BACK + DPAD_DOWN)
- */
- private void interceptAccessibilityGestureTv() {
- mHandler.removeMessages(MSG_ACCESSIBILITY_TV);
- Message msg = Message.obtain(mHandler, MSG_ACCESSIBILITY_TV);
- msg.setAsynchronous(true);
- mHandler.sendMessageDelayed(msg, getAccessibilityShortcutTimeout());
- }
- private void cancelAccessibilityGestureTv() {
- mHandler.removeMessages(MSG_ACCESSIBILITY_TV);
- }
-
@VisibleForTesting
void requestBugreportForTv() {
try {
@@ -5126,13 +4881,6 @@
}
private void handleKeyGesture(KeyEvent event, boolean interactive, boolean defaultDisplayOn) {
- if (!InputSettings.doesKeyGestureEventHandlerSupportMultiKeyGestures()
- && mKeyCombinationManager.interceptKey(event, interactive)) {
- // handled by combo keys manager.
- mSingleKeyGestureDetector.reset();
- return;
- }
-
if (event.getKeyCode() == KEYCODE_POWER && event.getAction() == KeyEvent.ACTION_DOWN) {
mPowerKeyHandled = handleCameraGesture(event, interactive);
if (mPowerKeyHandled) {
@@ -6503,17 +6251,11 @@
if (mKeyguardDelegate != null) {
mKeyguardDelegate.setCurrentUser(newUserId);
}
- if (mAccessibilityShortcutController != null) {
- mAccessibilityShortcutController.setCurrentUser(newUserId);
- }
StatusBarManagerInternal statusBar = getStatusBarManagerInternal();
if (statusBar != null) {
statusBar.setCurrentUser(newUserId);
}
mModifierShortcutManager.setCurrentUser(UserHandle.of(newUserId));
- if (!inputManagerLifecycleSupport()) {
- mInputManagerInternal.setCurrentUser(newUserId);
- }
}
@Override
@@ -6592,8 +6334,6 @@
pw.print("mSupportShortPressPowerWhenDefaultDisplayOn=");
pw.println(mSupportShortPressPowerWhenDefaultDisplayOn);
pw.print(prefix);
- pw.print("mPowerVolUpBehavior=");
- pw.println(powerVolumeUpBehaviorToString(mPowerVolUpBehavior));
pw.print(prefix);
pw.print("mShortPressOnSleepBehavior=");
pw.println(shortPressOnSleepBehaviorToString(mShortPressOnSleepBehavior));
@@ -6647,7 +6387,6 @@
pw.print(prefix); pw.print("mKidsModeEnabled="); pw.println(mKidsModeEnabled);
mGlobalKeyManager.dump(prefix, pw);
- mKeyCombinationManager.dump(prefix, pw);
mSingleKeyGestureDetector.dump(prefix, pw);
mDeferredKeyActionExecutor.dump(prefix, pw);
@@ -6787,19 +6526,6 @@
}
}
- private static String powerVolumeUpBehaviorToString(int behavior) {
- switch (behavior) {
- case POWER_VOLUME_UP_BEHAVIOR_NOTHING:
- return "POWER_VOLUME_UP_BEHAVIOR_NOTHING";
- case POWER_VOLUME_UP_BEHAVIOR_MUTE:
- return "POWER_VOLUME_UP_BEHAVIOR_MUTE";
- case POWER_VOLUME_UP_BEHAVIOR_GLOBAL_ACTIONS:
- return "POWER_VOLUME_UP_BEHAVIOR_GLOBAL_ACTIONS";
- default:
- return Integer.toString(behavior);
- }
- }
-
private static String multiPressOnPowerBehaviorToString(int behavior) {
switch (behavior) {
case MULTI_PRESS_POWER_NOTHING:
diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java
index 417b5c7..c3c8a75 100644
--- a/services/core/java/com/android/server/power/Notifier.java
+++ b/services/core/java/com/android/server/power/Notifier.java
@@ -151,6 +151,7 @@
private final Vibrator mVibrator;
@NonNull private final WakeLockLog mPartialWakeLockLog;
@NonNull private final WakeLockLog mFullWakeLockLog;
+ @Nullable private final WakelockTracer mWakelockTracer;
private final DisplayManagerInternal mDisplayManagerInternal;
private final NotifierHandler mHandler;
@@ -256,6 +257,12 @@
mFullWakeLockLog = mInjector.getWakeLockLog(context);
mPartialWakeLockLog = mInjector.getWakeLockLog(context);
+ if (mFlags.isAppWakelockDataSourceEnabled()) {
+ mWakelockTracer = mInjector.getWakelockTracer(looper);
+ } else {
+ mWakelockTracer = null;
+ }
+
// Initialize interactive state for battery stats.
try {
mBatteryStats.noteInteractive(true);
@@ -306,7 +313,8 @@
+ ", ownerUid=" + ownerUid + ", ownerPid=" + ownerPid
+ ", workSource=" + workSource);
}
- logWakelockStateChanged(flags, tag, ownerUid, workSource, WakelockEventType.ACQUIRE);
+ logWakelockStateChanged(flags, tag, ownerUid, ownerPid, workSource,
+ WakelockEventType.ACQUIRE);
notifyWakeLockListener(callback, tag, true, ownerUid, ownerPid, flags, workSource,
packageName, historyTag);
if (!mFlags.improveWakelockLatency()) {
@@ -411,9 +419,10 @@
+ ", workSource=" + newWorkSource);
}
- logWakelockStateChanged(flags, tag, ownerUid, workSource, WakelockEventType.RELEASE);
- logWakelockStateChanged(
- newFlags, newTag, newOwnerUid, newWorkSource, WakelockEventType.ACQUIRE);
+ logWakelockStateChanged(flags, tag, ownerUid, ownerPid, workSource,
+ WakelockEventType.RELEASE);
+ logWakelockStateChanged(newFlags, newTag, newOwnerUid, newOwnerPid, newWorkSource,
+ WakelockEventType.ACQUIRE);
final boolean unimportantForLogging = newOwnerUid == Process.SYSTEM_UID
&& (newFlags & PowerManager.UNIMPORTANT_FOR_LOGGING) != 0;
@@ -460,7 +469,8 @@
+ ", ownerUid=" + ownerUid + ", ownerPid=" + ownerPid
+ ", workSource=" + workSource);
}
- logWakelockStateChanged(flags, tag, ownerUid, workSource, WakelockEventType.RELEASE);
+ logWakelockStateChanged(flags, tag, ownerUid, ownerPid, workSource,
+ WakelockEventType.RELEASE);
notifyWakeLockListener(callback, tag, false, ownerUid, ownerPid, flags, workSource,
packageName, historyTag);
if (!mFlags.improveWakelockLatency()) {
@@ -1484,8 +1494,15 @@
int flags,
String tag,
int ownerUid,
+ int ownerPid,
WorkSource workSource,
WakelockEventType eventType) {
+ if (mWakelockTracer != null) {
+ boolean isAcquire = eventType == WakelockEventType.ACQUIRE;
+ mWakelockTracer.onWakelockEvent(isAcquire, tag, ownerUid, ownerPid, flags,
+ workSource, mInjector.nanoTime());
+ }
+
if (mBatteryStatsInternal == null) {
return;
}
@@ -1525,11 +1542,21 @@
long currentTimeMillis();
/**
+ * Gets the current time in nanoseconds
+ */
+ long nanoTime();
+
+ /**
* Gets the WakeLockLog object
*/
@NonNull WakeLockLog getWakeLockLog(Context context);
/**
+ * Gets the WakelockTracer object
+ */
+ @Nullable WakelockTracer getWakelockTracer(Looper looper);
+
+ /**
* Gets the AppOpsManager system service
*/
AppOpsManager getAppOpsManager(Context context);
@@ -1548,11 +1575,21 @@
}
@Override
+ public long nanoTime() {
+ return System.nanoTime();
+ }
+
+ @Override
public @NonNull WakeLockLog getWakeLockLog(Context context) {
return new WakeLockLog(context);
}
@Override
+ public @Nullable WakelockTracer getWakelockTracer(Looper looper) {
+ return new WakelockTracer(looper);
+ }
+
+ @Override
public AppOpsManager getAppOpsManager(Context context) {
return context.getSystemService(AppOpsManager.class);
}
diff --git a/services/core/java/com/android/server/power/OWNERS b/services/core/java/com/android/server/power/OWNERS
index a1531a81..856916d 100644
--- a/services/core/java/com/android/server/power/OWNERS
+++ b/services/core/java/com/android/server/power/OWNERS
@@ -8,3 +8,4 @@
per-file ThermalManagerService.java=file:/THERMAL_OWNERS
per-file LowPowerStandbyController.java=qingxun@google.com
per-file LowPowerStandbyControllerInternal.java=qingxun@google.com
+per-file WakelockTracer.java=file:/PERFORMANCE_OWNERS
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index b10b2dc..7428546 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -123,7 +123,6 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IBatteryStats;
import com.android.internal.display.BrightnessSynchronizer;
-import com.android.internal.foldables.FoldGracePeriodProvider;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FrameworkStatsLog;
@@ -192,6 +191,9 @@
// Message: Sent when the processes frozen state changes
private static final int MSG_PROCESS_FROZEN_STATE_CHANGED = 7;
+ // Message: Sent when the policy wants to force disable wakelocks.
+ private static final int MSG_FORCE_DISABLE_WAKELOCKS = 8;
+
// Dirty bit: mWakeLocks changed
private static final int DIRTY_WAKE_LOCKS = 1 << 0;
// Dirty bit: mWakefulness changed
@@ -324,7 +326,6 @@
private final Context mContext;
private final ServiceThread mHandlerThread;
private final Handler mHandler;
- private final FoldGracePeriodProvider mFoldGracePeriodProvider;
private final AmbientDisplayConfiguration mAmbientDisplayConfiguration;
@Nullable
private final BatterySaverStateMachine mBatterySaverStateMachine;
@@ -742,6 +743,10 @@
// Whether to keep dreaming when the device is unplugging.
private boolean mKeepDreamingWhenUnplugging;
+ // Whether to force disable wakelocks.
+ @GuardedBy("mLock")
+ private boolean mForceDisableWakelocks;
+
@GuardedBy("mLock")
private ScreenTimeoutOverridePolicy mScreenTimeoutOverridePolicy;
@@ -1079,10 +1084,6 @@
return new InattentiveSleepWarningController();
}
- FoldGracePeriodProvider createFoldGracePeriodProvider() {
- return new FoldGracePeriodProvider();
- }
-
public SystemPropertiesWrapper createSystemPropertiesWrapper() {
return new SystemPropertiesWrapper() {
@Override
@@ -1228,7 +1229,6 @@
mHandler = injector.createHandler(mHandlerThread.getLooper(),
new PowerManagerHandlerCallback());
mConstants = new Constants(mHandler);
- mFoldGracePeriodProvider = injector.createFoldGracePeriodProvider();
mAmbientDisplayConfiguration = mInjector.createAmbientDisplayConfiguration(context);
mAmbientDisplaySuppressionController =
mInjector.createAmbientDisplaySuppressionController(
@@ -3536,6 +3536,7 @@
*/
private void handleSandman(int groupId) { // runs on handler thread
// Handle preconditions.
+ final boolean canDream;
final boolean startDreaming;
final int wakefulness;
synchronized (mLock) {
@@ -3546,8 +3547,9 @@
}
final PowerGroup powerGroup = mPowerGroups.get(groupId);
wakefulness = powerGroup.getWakefulnessLocked();
+ canDream = canDreamLocked(powerGroup);
if (powerGroup.isSandmanSummonedLocked() && powerGroup.isReadyLocked()) {
- startDreaming = canDreamLocked(powerGroup) || canDozeLocked(powerGroup);
+ startDreaming = canDream || canDozeLocked(powerGroup);
powerGroup.setSandmanSummonedLocked(/* isSandmanSummoned= */ false);
} else {
startDreaming = false;
@@ -3603,7 +3605,7 @@
// Determine whether the dream should continue.
long now = mClock.uptimeMillis();
if (wakefulness == WAKEFULNESS_DREAMING) {
- if (isDreaming && canDreamLocked(powerGroup)) {
+ if (isDreaming && canDream) {
if (mDreamsBatteryLevelDrainCutoffConfig >= 0
&& mDreamsBatteryLevelDrain > mDreamsBatteryLevelDrainCutoffConfig
&& !isBeingKeptAwakeLocked(powerGroup)) {
@@ -3668,7 +3670,6 @@
}
}
-
/**
* Returns true if the {@code groupId} is allowed to dream in its current state.
*/
@@ -3687,20 +3688,33 @@
| USER_ACTIVITY_SCREEN_DIM | USER_ACTIVITY_SCREEN_DREAM)) == 0) {
return false;
}
- if (!isBeingKeptAwakeLocked(powerGroup)) {
- if (!mIsPowered && !mDreamsEnabledOnBatteryConfig) {
- return false;
+
+ if (!mIsPowered && !mDreamsEnabledOnBatteryConfig) {
+ if (DEBUG) {
+ Slog.d(TAG, "Cannot dream because device is not powered");
}
- if (!mIsPowered
- && mDreamsBatteryLevelMinimumWhenNotPoweredConfig >= 0
- && mBatteryLevel < mDreamsBatteryLevelMinimumWhenNotPoweredConfig) {
- return false;
- }
- return !mIsPowered
- || mDreamsBatteryLevelMinimumWhenPoweredConfig < 0
- || mBatteryLevel >= mDreamsBatteryLevelMinimumWhenPoweredConfig;
+ return false;
}
- return true;
+
+ if (!mIsPowered
+ && mDreamsBatteryLevelMinimumWhenNotPoweredConfig >= 0
+ && mBatteryLevel < mDreamsBatteryLevelMinimumWhenNotPoweredConfig) {
+ if (DEBUG) {
+ Slog.d(TAG, "Cannot dream because device battery level is lower than required");
+ }
+ return false;
+ }
+
+ if (isBeingKeptAwakeLocked(powerGroup)) {
+ if (DEBUG) {
+ Slog.d(TAG, "Dream allowed because power group is being kept awake");
+ }
+ return true;
+ }
+
+ return !mIsPowered
+ || mDreamsBatteryLevelMinimumWhenPoweredConfig < 0
+ || mBatteryLevel >= mDreamsBatteryLevelMinimumWhenPoweredConfig;
}
/**
@@ -4392,6 +4406,15 @@
}
}
+ void setForceDisableWakelocksInternal(boolean force) {
+ synchronized (mLock) {
+ if (mFeatureFlags.isForceDisableWakelocksEnabled()) {
+ mForceDisableWakelocks = force;
+ updateWakeLockDisabledStatesLocked();
+ }
+ }
+ }
+
@GuardedBy("mLock")
private boolean doesIdleStateBlockWakeLocksLocked() {
return mDeviceIdleMode || (mLightDeviceIdleMode && disableWakelocksInLightIdle());
@@ -4470,6 +4493,10 @@
}
}
}
+ // Disable all PARTAIL_WAKE_LOCKS if mForceDisableWakelocks is true.
+ if (mForceDisableWakelocks) {
+ disabled = true;
+ }
return wakeLock.setDisabled(disabled);
} else if (mDisableScreenWakeLocksWhileCached && isScreenLock(wakeLock)) {
final int appid = UserHandle.getAppId(wakeLock.mOwnerUid);
@@ -5511,6 +5538,13 @@
case MSG_PROCESS_FROZEN_STATE_CHANGED:
handleProcessFrozenStateChange(msg.obj, msg.arg1);
break;
+ case MSG_FORCE_DISABLE_WAKELOCKS:
+ if (msg.arg1 == 1) {
+ setForceDisableWakelocksInternal(true);
+ } else {
+ setForceDisableWakelocksInternal(false);
+ }
+ break;
}
return true;
@@ -7337,20 +7371,14 @@
+ ") doesn't exist");
}
if ((flags & PowerManager.GO_TO_SLEEP_FLAG_SOFT_SLEEP) != 0) {
- if (mFoldGracePeriodProvider.isEnabled()) {
- if (!powerGroup.hasWakeLockKeepingScreenOnLocked()) {
- Slog.d(TAG, "Showing dismissible keyguard");
- mNotifier.showDismissibleKeyguard();
- } else {
- Slog.i(TAG, "There is a screen wake lock present: "
- + "sleep request will be ignored");
- }
- continue; // never actually goes to sleep for SOFT_SLEEP
+ if (!powerGroup.hasWakeLockKeepingScreenOnLocked()) {
+ Slog.d(TAG, "Showing dismissible keyguard");
+ mNotifier.showDismissibleKeyguard();
} else {
- if (powerGroup.hasWakeLockKeepingScreenOnLocked()) {
- continue;
- }
+ Slog.i(TAG, "There is a screen wake lock present: "
+ + "sleep request will be ignored");
}
+ continue; // never actually goes to sleep for SOFT_SLEEP
}
if (isNoDoze) {
sleepPowerGroupLocked(powerGroup, eventTime, reason, uid);
@@ -7565,6 +7593,14 @@
updateSettingsLocked();
}
}
+
+ @Override
+ public void setForceDisableWakelocks(boolean force) {
+ Slog.i(TAG, (force ? "Starting" : "Stopping") + " to force disable partial wakelocks");
+ Message msg = mHandler.obtainMessage(MSG_FORCE_DISABLE_WAKELOCKS,
+ force ? 1 : 0, 0 /*unused*/);
+ mHandler.sendMessageAtTime(msg, mClock.uptimeMillis());
+ }
}
/**
diff --git a/services/core/java/com/android/server/power/ScreenTimeoutOverridePolicy.java b/services/core/java/com/android/server/power/ScreenTimeoutOverridePolicy.java
index 8e08ce9..e698764 100644
--- a/services/core/java/com/android/server/power/ScreenTimeoutOverridePolicy.java
+++ b/services/core/java/com/android/server/power/ScreenTimeoutOverridePolicy.java
@@ -16,8 +16,6 @@
package com.android.server.power;
-import static android.os.PowerManagerInternal.WAKEFULNESS_AWAKE;
-
import static com.android.server.power.PowerManagerService.WAKE_LOCK_BUTTON_BRIGHT;
import static com.android.server.power.PowerManagerService.WAKE_LOCK_SCREEN_BRIGHT;
import static com.android.server.power.PowerManagerService.WAKE_LOCK_SCREEN_DIM;
@@ -26,9 +24,12 @@
import android.annotation.IntDef;
import android.content.Context;
import android.os.PowerManager;
+import android.os.PowerManagerInternal;
import android.util.IndentingPrintWriter;
import android.util.Slog;
+import androidx.annotation.NonNull;
+
import com.android.internal.annotations.VisibleForTesting;
import java.io.PrintWriter;
@@ -118,8 +119,8 @@
}
private PolicyCallback mPolicyCallback;
- ScreenTimeoutOverridePolicy(Context context, long minimumScreenOffTimeoutConfig,
- PolicyCallback callback) {
+ ScreenTimeoutOverridePolicy(@NonNull Context context, long minimumScreenOffTimeoutConfig,
+ @NonNull PolicyCallback callback) {
mScreenTimeoutOverrideConfig = context.getResources().getInteger(
com.android.internal.R.integer.config_screenTimeoutOverride);
if (mScreenTimeoutOverrideConfig < minimumScreenOffTimeoutConfig) {
@@ -195,7 +196,7 @@
return;
}
- if (globalWakefulness != WAKEFULNESS_AWAKE) {
+ if (!PowerManagerInternal.isInteractive(globalWakefulness)) {
releaseAllWakeLocks(RELEASE_REASON_NON_INTERACTIVE);
}
}
diff --git a/services/core/java/com/android/server/power/ThermalManagerService.java b/services/core/java/com/android/server/power/ThermalManagerService.java
index 0873366..7dcc4c7 100644
--- a/services/core/java/com/android/server/power/ThermalManagerService.java
+++ b/services/core/java/com/android/server/power/ThermalManagerService.java
@@ -184,12 +184,19 @@
public void onThresholdChanged(TemperatureThreshold threshold) {
final long token = Binder.clearCallingIdentity();
try {
- final HeadroomCallbackData data;
synchronized (mTemperatureWatcher.mSamples) {
if (DEBUG) {
Slog.d(TAG, "Updating skin threshold: " + threshold);
}
mTemperatureWatcher.updateTemperatureThresholdLocked(threshold, true);
+ }
+ synchronized (mLock) {
+ if (mThermalHeadroomListeners.getRegisteredCallbackCount() == 0) {
+ return;
+ }
+ }
+ final HeadroomCallbackData data;
+ synchronized (mTemperatureWatcher.mSamples) {
data = mTemperatureWatcher.getHeadroomCallbackDataLocked();
}
synchronized (mLock) {
@@ -347,6 +354,9 @@
@GuardedBy("mLock")
private void checkAndNotifyHeadroomListenersLocked(HeadroomCallbackData data, int type) {
+ if (mThermalHeadroomListeners.getRegisteredCallbackCount() == 0) {
+ return;
+ }
if (!data.isSignificantDifferentFrom(mLastHeadroomCallbackData)
&& System.currentTimeMillis()
< mLastHeadroomCallbackTimeMillis + HEADROOM_CALLBACK_MIN_INTERVAL_MILLIS) {
@@ -474,16 +484,18 @@
onTemperatureMapChangedLocked();
}
}
+ if (DEBUG) {
+ Slog.d(TAG, "Temperature changed: " + temperature);
+ }
if (sendCallback && Flags.allowThermalThresholdsCallback()
&& temperature.getType() == Temperature.TYPE_SKIN) {
+ synchronized (mLock) {
+ if (mThermalHeadroomListeners.getRegisteredCallbackCount() == 0) {
+ return;
+ }
+ }
final HeadroomCallbackData data;
synchronized (mTemperatureWatcher.mSamples) {
- if (DEBUG) {
- Slog.d(TAG, "Updating new temperature: " + temperature);
- }
- mTemperatureWatcher.updateTemperatureSampleLocked(System.currentTimeMillis(),
- temperature);
- mTemperatureWatcher.mCachedHeadrooms.clear();
data = mTemperatureWatcher.getHeadroomCallbackDataLocked();
}
synchronized (mLock) {
@@ -2078,19 +2090,24 @@
if (DEBUG) {
Slog.d(TAG, "Thermal HAL getCurrentTemperatures result: " + temperatures);
}
+ boolean samplesUpdated = false;
for (Temperature temperature : temperatures) {
- updateTemperatureSampleLocked(now, temperature);
+ samplesUpdated |= updateTemperatureSampleLocked(now, temperature);
}
- mCachedHeadrooms.clear();
+ if (samplesUpdated) {
+ mCachedHeadrooms.clear();
+ }
}
}
+ // returns whether the cache of samples was modified for the provided temperature, which
+ // can be used as the signal of whether the headroom cache should be cleared.
@GuardedBy("mSamples")
- private void updateTemperatureSampleLocked(long timeNow, Temperature temperature) {
+ private boolean updateTemperatureSampleLocked(long timeNow, Temperature temperature) {
// Filter out invalid temperatures. If this results in no values being stored at
- // all, the mSamples.empty() check in getForecast() will catch it.
+ // all, the mSamples.isEmpty() check in getForecast() will catch it.
if (Float.isNaN(temperature.getValue())) {
- return;
+ return false;
}
ArrayList<Sample> samples = mSamples.computeIfAbsent(temperature.getName(),
k -> new ArrayList<>(RING_BUFFER_SIZE));
@@ -2098,6 +2115,7 @@
samples.removeFirst();
}
samples.add(new Sample(timeNow, temperature.getValue()));
+ return true;
}
/**
@@ -2291,6 +2309,11 @@
maxNormalized = normalized;
}
}
+ if (DEBUG) {
+ Slog.d(TAG, "Headroom forecast in " + forecastSeconds + "s: " + maxNormalized
+ + " from thresholds:\n " + mSevereThresholds + " and samples:\n "
+ + mSamples);
+ }
if (noThresholdSampleCount == mSamples.size()) {
FrameworkStatsLog.write(FrameworkStatsLog.THERMAL_HEADROOM_CALLED,
/* uid= */ Binder.getCallingUid(),
diff --git a/services/core/java/com/android/server/power/WakelockTracer.java b/services/core/java/com/android/server/power/WakelockTracer.java
new file mode 100644
index 0000000..7822d4b
--- /dev/null
+++ b/services/core/java/com/android/server/power/WakelockTracer.java
@@ -0,0 +1,332 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.power;
+
+import static android.internal.perfetto.protos.AppWakelockConfig.AppWakelocksConfig.DROP_OWNER_PID;
+import static android.internal.perfetto.protos.AppWakelockConfig.AppWakelocksConfig.FILTER_DURATION_BELOW_MS;
+import static android.internal.perfetto.protos.AppWakelockConfig.AppWakelocksConfig.WRITE_DELAY_MS;
+import static android.internal.perfetto.protos.AppWakelockData.AppWakelockBundle.ENCODED_TS;
+import static android.internal.perfetto.protos.AppWakelockData.AppWakelockBundle.INTERN_ID;
+import static android.internal.perfetto.protos.AppWakelockData.AppWakelockInfo.FLAGS;
+import static android.internal.perfetto.protos.AppWakelockData.AppWakelockInfo.IID;
+import static android.internal.perfetto.protos.AppWakelockData.AppWakelockInfo.OWNER_PID;
+import static android.internal.perfetto.protos.AppWakelockData.AppWakelockInfo.OWNER_UID;
+import static android.internal.perfetto.protos.AppWakelockData.AppWakelockInfo.TAG;
+import static android.internal.perfetto.protos.AppWakelockData.AppWakelockInfo.WORK_UID;
+import static android.internal.perfetto.protos.DataSourceConfigOuterClass.DataSourceConfig.APP_WAKELOCKS_CONFIG;
+import static android.internal.perfetto.protos.InternedDataOuterClass.InternedData.APP_WAKELOCK_INFO;
+import static android.internal.perfetto.protos.TracePacketOuterClass.TracePacket.APP_WAKELOCK_BUNDLE;
+import static android.internal.perfetto.protos.TracePacketOuterClass.TracePacket.INTERNED_DATA;
+import static android.internal.perfetto.protos.TracePacketOuterClass.TracePacket.SEQUENCE_FLAGS;
+import static android.internal.perfetto.protos.TracePacketOuterClass.TracePacket.SEQ_INCREMENTAL_STATE_CLEARED;
+import static android.internal.perfetto.protos.TracePacketOuterClass.TracePacket.SEQ_NEEDS_INCREMENTAL_STATE;
+import static android.internal.perfetto.protos.TracePacketOuterClass.TracePacket.TIMESTAMP;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.os.WorkSource;
+import android.tracing.perfetto.CreateIncrementalStateArgs;
+import android.tracing.perfetto.DataSource;
+import android.tracing.perfetto.DataSourceInstance;
+import android.tracing.perfetto.DataSourceParams;
+import android.tracing.perfetto.FlushCallbackArguments;
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoOutputStream;
+import android.util.proto.WireTypeMismatchException;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+
+/** Records wakelock events using the Perfetto SDK. */
+final class WakelockTracer
+ extends DataSource<WakelockTracer.Instance, Void, WakelockTracer.IncrementalState> {
+ private final Handler mHandler;
+ private static final long NANOS_PER_MS = 1_000_000L;
+
+ WakelockTracer(Looper looper) {
+ this(looper, DataSourceParams.DEFAULTS);
+ }
+
+ WakelockTracer(Looper looper, DataSourceParams params) {
+ super("android.app_wakelocks");
+ mHandler = new Handler(looper);
+ if (params != null) {
+ register(params);
+ }
+ }
+
+ /**
+ * Creates a new instance of the Wakelock data source.
+ *
+ * @param stream A ProtoInputStream to read the tracing instance's config.
+ * @param instanceIdx The index of the instance being created.
+ * @return A new data source instance setup with the provided config.
+ */
+ @Override
+ public Instance createInstance(ProtoInputStream stream, int instanceIdx) {
+ return new Instance(this, instanceIdx, stream);
+ }
+
+ /**
+ * Creates a new incremental state object for the Perfetto data source.
+ *
+ * @return A new (clean) incremental state instance.
+ */
+ @Override
+ public IncrementalState createIncrementalState(CreateIncrementalStateArgs<Instance> args) {
+ return new IncrementalState();
+ }
+
+ /**
+ * Records a wakelock acquire or release event.
+ *
+ * Calls to onWakelockEvent are thread-safe. The mutations it applies to the instances
+ * are protected by the Perfetto mutex. All other accesses to instance state must be
+ * guarded by the Perfetto mutex.
+ *
+ * Calls to onWakelockEvent should be strictly sequenced (nanoTime should monotonically
+ * increase). This is accomplished in Notifier.java by enqueuing the changes on a handler
+ * (which is FIFO) while holding the PowerManagerService lock.
+ *
+ * @param acquired Whether or not this is a acquire or release event.
+ * @param tag The wakelock tag provided by the application.
+ * @param uid The uid of the process requesting the wakelock.
+ * @param pid The pid of the process requesting the wakelock.
+ * @param flags The wakelock flag bitmask (such as PowerManager.PARTIAL_WAKE_LOCK).
+ * @param ws The Worksource attached to the wakelock (or null if there is none).
+ * @param nanoTime The System.nanoTime() when the event occurred.
+ */
+ public void onWakelockEvent(
+ boolean acquired,
+ String tag,
+ int uid,
+ int pid,
+ int flags,
+ WorkSource ws,
+ long nanoTime) {
+ trace((ctx) -> {
+ try (Instance instance = ctx.getDataSourceInstanceLocked()) {
+ if (instance == null) return;
+ instance.onWakelockEvent(acquired, tag, uid, pid, flags, ws, nanoTime);
+ }
+ });
+ }
+
+ /** Writes all pending data for the given instance to the Perfetto backend. */
+ private void writeInstance(Instance target) {
+ trace((ctx) -> {
+ try (Instance instance = ctx.getDataSourceInstanceLocked()) {
+ if (instance != target) return;
+ instance.write(ctx.newTracePacket(), ctx.getIncrementalState(), System.nanoTime());
+ }
+ });
+ }
+
+ /** Wakelock identifies time and event-type independent attributes of a wakelock. */
+ private record Wakelock(String tag, int ownerUid, int ownerPid, int flags, int workUid) {}
+
+ /** WakelockEvent records an acquire or release of a wakelock. */
+ private record WakelockEvent(Wakelock lock, boolean acquired, long timestampNs) {}
+
+ /** InstanceConfig records the per-instance proto configuration. */
+ private record InstanceConfig(long thresholdMs, long delayMs, boolean dropPid) {}
+
+ /** IncrementalState tracks the interned wakelock info already written. */
+ public static class IncrementalState {
+ // Map from wakelock attributes to interned id.
+ public final Map<Wakelock, Integer> iids = new HashMap<Wakelock, Integer>();
+ }
+
+ /** Instance corresponds to a single trace data source in a session. */
+ public static class Instance extends DataSourceInstance {
+ private final InstanceConfig mConfig;
+ private final WakelockTracer mParent;
+
+ private ArrayList<WakelockEvent> mEvents = new ArrayList<WakelockEvent>();
+ private boolean mHasPendingWrite = false;
+
+ Instance(WakelockTracer dataSource, int index, ProtoInputStream stream) {
+ super(dataSource, index);
+ mConfig = parseConfig(stream);
+ mParent = dataSource;
+ }
+
+ @Override
+ public void onFlush(FlushCallbackArguments args) {
+ mParent.writeInstance(this);
+ }
+
+ /** Adds the event to the in-memory buffer. */
+ public void onWakelockEvent(
+ boolean acquired,
+ String tag,
+ int uid,
+ int pid,
+ int flags,
+ WorkSource ws,
+ long nanoTime) {
+ pid = mConfig.dropPid ? -1 : pid;
+ int workUid = ws == null ? -1 : ws.getAttributionUid();
+ Wakelock lock = new Wakelock(tag, uid, pid, flags, workUid);
+ WakelockEvent wle = new WakelockEvent(lock, acquired, nanoTime);
+
+ if (!acquired) {
+ // For release events, find the last acquire. If it exists and
+ // the duration is less than the cutoff, remove it and don't add
+ // the new event. If it is not found, assume that it was already
+ // written. mEvents is expected to be sorted, so we only need to
+ // look back until the cutoff time.
+ long cutoff = nanoTime - (mConfig.thresholdMs * NANOS_PER_MS);
+ for (int i = mEvents.size() - 1; i >= 0; i--) {
+ WakelockEvent curr = mEvents.get(i);
+ if (curr.timestampNs < cutoff) break;
+ if (pairsWith(curr, wle)) {
+ mEvents.remove(i);
+ return;
+ }
+ }
+ }
+
+ mEvents.add(wle);
+ scheduleWrite();
+ }
+
+ /** Writes pending events to the proto output stream. */
+ public void write(ProtoOutputStream stream, IncrementalState state, long nanoTime) {
+ ArrayList<WakelockEvent> pending = new ArrayList<WakelockEvent>();
+
+ // Remove events that are ready and add them to pending. Start events are
+ // ready only if they're older than the duration threshold. End events
+ // are always ready (they aren't added if matching a short wakelock).
+ long cutoff = nanoTime - (mConfig.thresholdMs * NANOS_PER_MS);
+ mEvents.removeIf((event) -> {
+ boolean ready = !event.acquired || event.timestampNs <= cutoff;
+ if (ready) pending.add(event);
+ return ready;
+ });
+
+ mHasPendingWrite = false;
+ if (!mEvents.isEmpty()) {
+ scheduleWrite();
+ }
+
+ writeToPacket(stream, state, pending);
+ }
+
+ private void scheduleWrite() {
+ if (!mHasPendingWrite) {
+ mHasPendingWrite = true;
+ mParent.mHandler.postDelayed(
+ () -> {
+ mParent.writeInstance(this);
+ },
+ mConfig.delayMs);
+ }
+ }
+
+ private static boolean pairsWith(WakelockEvent a, WakelockEvent b) {
+ return a.acquired != b.acquired && a.lock.equals(b.lock);
+ }
+
+ private static long encodeTs(WakelockEvent e, long referenceTs) {
+ return (e.timestampNs - referenceTs) << 1 | (e.acquired ? 1 : 0);
+ }
+
+ private static InstanceConfig parseConfig(ProtoInputStream stream) {
+ boolean dropPid = false;
+ int thresholdMs = 0;
+ int delayMs = 5000;
+
+ try {
+ while (stream.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ if (stream.getFieldNumber() == (int) APP_WAKELOCKS_CONFIG) {
+ final long token = stream.start(APP_WAKELOCKS_CONFIG);
+ while (stream.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (stream.getFieldNumber()) {
+ case (int) FILTER_DURATION_BELOW_MS:
+ thresholdMs = stream.readInt(FILTER_DURATION_BELOW_MS);
+ break;
+ case (int) WRITE_DELAY_MS:
+ delayMs = stream.readInt(WRITE_DELAY_MS);
+ break;
+ case (int) DROP_OWNER_PID:
+ dropPid = stream.readBoolean(DROP_OWNER_PID);
+ break;
+ }
+ }
+ stream.end(token);
+ break;
+ }
+ }
+ } catch (IOException e) {
+ throw new RuntimeException("Failed to read Wakelock DataSource config", e);
+ } catch (WireTypeMismatchException e) {
+ throw new RuntimeException("Failed to parse Wakelock DataSource config", e);
+ }
+
+ return new InstanceConfig(thresholdMs, delayMs, dropPid);
+ }
+
+ private static void writeToPacket(
+ ProtoOutputStream stream, IncrementalState state, ArrayList<WakelockEvent> events) {
+ if (events.isEmpty()) return;
+
+ // Events should be sorted, first event has lowest timestamp.
+ long packetTs = events.get(0).timestampNs;
+
+ stream.write(TIMESTAMP, packetTs);
+ stream.write(
+ SEQUENCE_FLAGS,
+ state.iids.isEmpty()
+ ? SEQ_INCREMENTAL_STATE_CLEARED
+ : SEQ_NEEDS_INCREMENTAL_STATE);
+
+ int[] iids = new int[events.size()];
+ long[] encodedTs = new long[events.size()];
+ long internToken = stream.start(INTERNED_DATA);
+ for (int i = 0; i < events.size(); i++) {
+ WakelockEvent event = events.get(i);
+ Wakelock lock = event.lock;
+
+ Integer iid = state.iids.get(lock);
+ if (iid == null) {
+ iid = state.iids.size() + 1;
+ state.iids.put(lock, iid);
+ long itemToken = stream.start(APP_WAKELOCK_INFO);
+ stream.write(IID, iid.intValue());
+ stream.write(TAG, lock.tag);
+ stream.write(FLAGS, lock.flags);
+ if (lock.ownerPid >= 0) stream.write(OWNER_PID, lock.ownerPid);
+ if (lock.ownerUid >= 0) stream.write(OWNER_UID, lock.ownerUid);
+ if (lock.workUid >= 0) stream.write(WORK_UID, lock.workUid);
+ stream.end(itemToken);
+ }
+
+ iids[i] = iid.intValue();
+ encodedTs[i] = encodeTs(event, packetTs);
+ }
+ stream.end(internToken);
+
+ long bundleToken = stream.start(APP_WAKELOCK_BUNDLE);
+ stream.writePackedUInt32(INTERN_ID, iids);
+ stream.writePackedUInt64(ENCODED_TS, encodedTs);
+ stream.end(bundleToken);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/power/feature/PowerManagerFlags.java b/services/core/java/com/android/server/power/feature/PowerManagerFlags.java
index 0208df0a..12b6c323 100644
--- a/services/core/java/com/android/server/power/feature/PowerManagerFlags.java
+++ b/services/core/java/com/android/server/power/feature/PowerManagerFlags.java
@@ -68,6 +68,13 @@
new FlagState(Flags.FLAG_DISABLE_FROZEN_PROCESS_WAKELOCKS,
Flags::disableFrozenProcessWakelocks);
+ private final FlagState mForceDisableWakelocks =
+ new FlagState(Flags.FLAG_FORCE_DISABLE_WAKELOCKS, Flags::forceDisableWakelocks);
+
+ private final FlagState mEnableAppWakelockDataSource =
+ new FlagState(Flags.FLAG_ENABLE_APP_WAKELOCK_DATA_SOURCE,
+ Flags::enableAppWakelockDataSource);
+
/** Returns whether early-screen-timeout-detector is enabled on not. */
public boolean isEarlyScreenTimeoutDetectorEnabled() {
return mEarlyScreenTimeoutDetectorFlagState.isEnabled();
@@ -122,6 +129,20 @@
}
/**
+ * @return Whether the feature to force disable wakelocks is enabled
+ */
+ public boolean isForceDisableWakelocksEnabled() {
+ return mForceDisableWakelocks.isEnabled();
+ }
+
+ /**
+ * @return Whether the new Perfetto data source for tracing app wakelocks is enabled
+ */
+ public boolean isAppWakelockDataSourceEnabled() {
+ return mEnableAppWakelockDataSource.isEnabled();
+ }
+
+ /**
* dumps all flagstates
* @param pw printWriter
*/
@@ -133,6 +154,8 @@
pw.println(" " + mMoveWscLoggingToNotifier);
pw.println(" " + mWakelockAttributionViaWorkchain);
pw.println(" " + mDisableFrozenProcessWakelocks);
+ pw.println(" " + mForceDisableWakelocks);
+ pw.println(" " + mEnableAppWakelockDataSource);
}
private static class FlagState {
diff --git a/services/core/java/com/android/server/power/feature/power_flags.aconfig b/services/core/java/com/android/server/power/feature/power_flags.aconfig
index ceec9b6..1af8956 100644
--- a/services/core/java/com/android/server/power/feature/power_flags.aconfig
+++ b/services/core/java/com/android/server/power/feature/power_flags.aconfig
@@ -70,3 +70,17 @@
description: "Feature flag to disable/enable wakelocks of a process when it is frozen/unfrozen"
bug: "291115867"
}
+
+flag {
+ name: "force_disable_wakelocks"
+ namespace: "power"
+ description: "Feature flag to force disable wakelocks"
+ bug: "370557028"
+}
+
+flag {
+ name: "enable_app_wakelock_data_source"
+ namespace: "power"
+ description: "Feature flag to enable a new Perfetto data source for tracing app wakelocks."
+ bug: "400774865"
+}
diff --git a/services/core/java/com/android/server/power/hint/HintManagerService.java b/services/core/java/com/android/server/power/hint/HintManagerService.java
index 32f7ecd..09cfeea 100644
--- a/services/core/java/com/android/server/power/hint/HintManagerService.java
+++ b/services/core/java/com/android/server/power/hint/HintManagerService.java
@@ -2703,12 +2703,13 @@
private void dump(PrintWriter pw, String prefix) {
synchronized (this) {
- pw.println(prefix + "SessionPID: " + mPid);
- pw.println(prefix + "SessionUID: " + mUid);
- pw.println(prefix + "SessionTIDs: " + Arrays.toString(mThreadIds));
- pw.println(prefix + "SessionTargetDurationNanos: " + mTargetDurationNanos);
- pw.println(prefix + "SessionAllowedByProcState: " + mUpdateAllowedByProcState);
- pw.println(prefix + "SessionForcePaused: " + mShouldForcePause);
+ pw.println(prefix + "PID: " + mPid);
+ pw.println(prefix + "UID: " + mUid);
+ pw.println(prefix + "TIDs: " + Arrays.toString(mThreadIds));
+ pw.println(prefix + "Tag: " + mTag);
+ pw.println(prefix + "TargetDurationNanos: " + mTargetDurationNanos);
+ pw.println(prefix + "AllowedByProcState: " + mUpdateAllowedByProcState);
+ pw.println(prefix + "ForcePaused: " + mShouldForcePause);
pw.println(prefix + "PowerEfficient: " + (mPowerEfficient ? "true" : "false"));
pw.println(prefix + "GraphicsPipeline: " + (mGraphicsPipeline ? "true" : "false"));
}
diff --git a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
index 0827369c..9315d0d 100644
--- a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
+++ b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
@@ -51,7 +51,6 @@
import android.os.BatteryStats;
import android.os.BatteryUsageStats;
import android.os.BatteryUsageStatsQuery;
-import android.os.Binder;
import android.os.BluetoothBatteryStats;
import android.os.Build;
import android.os.ConditionVariable;
@@ -93,7 +92,6 @@
import android.text.TextUtils;
import android.text.format.DateUtils;
import android.util.ArrayMap;
-import android.util.ArraySet;
import android.util.AtomicFile;
import android.util.IndentingPrintWriter;
import android.util.KeyValueListParser;
@@ -118,8 +116,6 @@
import com.android.internal.os.BackgroundThread;
import com.android.internal.os.BatteryStatsHistory;
import com.android.internal.os.BatteryStatsHistoryIterator;
-import com.android.internal.os.BinderCallsStats;
-import com.android.internal.os.BinderTransactionNameResolver;
import com.android.internal.os.Clock;
import com.android.internal.os.CpuScalingPolicies;
import com.android.internal.os.KernelCpuSpeedReader;
@@ -145,7 +141,6 @@
import com.android.modules.utils.TypedXmlSerializer;
import com.android.server.LocalServices;
import com.android.server.power.optimization.Flags;
-import com.android.server.power.stats.SystemServerCpuThreadReader.SystemServiceCpuThreadTimes;
import com.android.server.power.stats.format.MobileRadioPowerStatsLayout;
import libcore.util.EmptyArray;
@@ -190,7 +185,6 @@
private static final boolean DEBUG = false;
public static final boolean DEBUG_ENERGY = false;
private static final boolean DEBUG_ENERGY_CPU = DEBUG_ENERGY;
- private static final boolean DEBUG_BINDER_STATS = false;
private static final boolean DEBUG_MEMORY = false;
private static final String HISTORY_DIR = "battery-history";
@@ -198,8 +192,7 @@
// TODO: remove "tcp" from network methods, since we measure total stats.
// Current on-disk Parcel version. Must be updated when the format of the parcelable changes
- public static final int VERSION =
- !Flags.disableSystemServicePowerAttr() ? 214 : 215;
+ public static final int VERSION = 215;
// The maximum number of names wakelocks we will keep track of
// per uid; once the limit is reached, we batch the remaining wakelocks
@@ -295,12 +288,9 @@
protected KernelCpuUidClusterTimeReader mCpuUidClusterTimeReader;
@VisibleForTesting
protected KernelSingleUidTimeReader mKernelSingleUidTimeReader;
- @VisibleForTesting
- protected SystemServerCpuThreadReader mSystemServerCpuThreadReader;
private KernelMemoryBandwidthStats mKernelMemoryBandwidthStats;
private final LongSparseArray<SamplingTimer> mKernelMemoryStats = new LongSparseArray<>();
- private int[] mCpuPowerBracketMap;
private final CpuPowerStatsCollector mCpuPowerStatsCollector;
private final WakelockPowerStatsCollector mWakelockPowerStatsCollector;
private final ScreenPowerStatsCollector mScreenPowerStatsCollector;
@@ -1829,11 +1819,6 @@
private long[] mTmpCpuTimeInFreq;
- /**
- * Times spent by the system server threads handling incoming binder requests.
- */
- private LongSamplingCounterArray mBinderThreadCpuTimesUs;
-
private final PowerProfile mPowerProfile;
@VisibleForTesting
@@ -2152,9 +2137,6 @@
mCpuUidActiveTimeReader = new KernelCpuUidActiveTimeReader(true, mClock);
mCpuUidClusterTimeReader = new KernelCpuUidClusterTimeReader(true, mClock);
mKernelWakelockReader = new KernelWakelockReader();
- if (!Flags.disableSystemServicePowerAttr()) {
- mSystemServerCpuThreadReader = SystemServerCpuThreadReader.create();
- }
mKernelMemoryBandwidthStats = new KernelMemoryBandwidthStats();
mTmpRailStats = new RailStats();
}
@@ -7405,99 +7387,6 @@
}
}
- /**
- * Records timing data related to an incoming Binder call in order to attribute
- * the power consumption to the calling app.
- */
- public void noteBinderCallStats(int workSourceUid, long incrementalCallCount,
- Collection<BinderCallsStats.CallStat> callStats) {
- noteBinderCallStats(workSourceUid, incrementalCallCount, callStats,
- mClock.elapsedRealtime(), mClock.uptimeMillis());
- }
-
- public void noteBinderCallStats(int workSourceUid, long incrementalCallCount,
- Collection<BinderCallsStats.CallStat> callStats,
- long elapsedRealtimeMs, long uptimeMs) {
- synchronized (this) {
- getUidStatsLocked(workSourceUid, elapsedRealtimeMs, uptimeMs)
- .noteBinderCallStatsLocked(incrementalCallCount, callStats);
- }
- }
-
- /**
- * Takes note of native IDs of threads taking incoming binder calls. The CPU time
- * of these threads is attributed to the apps making those binder calls.
- */
- public void noteBinderThreadNativeIds(int[] binderThreadNativeTids) {
- mSystemServerCpuThreadReader.setBinderThreadNativeTids(binderThreadNativeTids);
- }
-
- /**
- * Estimates the proportion of system server CPU activity handling incoming binder calls
- * that can be attributed to each app
- */
- @VisibleForTesting
- public void updateSystemServiceCallStats() {
- // Start off by computing the average duration of recorded binder calls,
- // regardless of which binder or transaction. We will use this as a fallback
- // for calls that were not sampled at all.
- int totalRecordedCallCount = 0;
- long totalRecordedCallTimeMicros = 0;
- for (int i = 0; i < mUidStats.size(); i++) {
- Uid uid = mUidStats.valueAt(i);
- ArraySet<BinderCallStats> binderCallStats = uid.mBinderCallStats;
- for (int j = binderCallStats.size() - 1; j >= 0; j--) {
- BinderCallStats stats = binderCallStats.valueAt(j);
- totalRecordedCallCount += stats.recordedCallCount;
- totalRecordedCallTimeMicros += stats.recordedCpuTimeMicros;
- }
- }
-
- long totalSystemServiceTimeMicros = 0;
-
- // For every UID, use recorded durations of sampled binder calls to estimate
- // the total time the system server spent handling requests from this UID.
- for (int i = 0; i < mUidStats.size(); i++) {
- Uid uid = mUidStats.valueAt(i);
-
- long totalTimeForUidUs = 0;
- int totalCallCountForUid = 0;
- ArraySet<BinderCallStats> binderCallStats = uid.mBinderCallStats;
- for (int j = binderCallStats.size() - 1; j >= 0; j--) {
- BinderCallStats stats = binderCallStats.valueAt(j);
- totalCallCountForUid += stats.callCount;
- if (stats.recordedCallCount > 0) {
- totalTimeForUidUs +=
- stats.callCount * stats.recordedCpuTimeMicros / stats.recordedCallCount;
- } else if (totalRecordedCallCount > 0) {
- totalTimeForUidUs +=
- stats.callCount * totalRecordedCallTimeMicros / totalRecordedCallCount;
- }
- }
-
- if (totalCallCountForUid < uid.mBinderCallCount && totalRecordedCallCount > 0) {
- // Estimate remaining calls, which were not tracked because of binder call
- // stats sampling
- totalTimeForUidUs +=
- (uid.mBinderCallCount - totalCallCountForUid) * totalRecordedCallTimeMicros
- / totalRecordedCallCount;
- }
-
- uid.mSystemServiceTimeUs = totalTimeForUidUs;
- totalSystemServiceTimeMicros += totalTimeForUidUs;
- }
-
- for (int i = 0; i < mUidStats.size(); i++) {
- Uid uid = mUidStats.valueAt(i);
- if (totalSystemServiceTimeMicros > 0) {
- uid.mProportionalSystemServiceUsage =
- (double) uid.mSystemServiceTimeUs / totalSystemServiceTimeMicros;
- } else {
- uid.mProportionalSystemServiceUsage = 0;
- }
- }
- }
-
public String[] getWifiIfaces() {
synchronized (mWifiNetworkLock) {
return mWifiIfaces;
@@ -8122,61 +8011,6 @@
}
/**
- * Accumulates stats for a specific binder transaction.
- */
- @VisibleForTesting
- protected static class BinderCallStats {
- public Class<? extends Binder> binderClass;
- public int transactionCode;
- public String methodName;
-
- public long callCount;
- public long recordedCallCount;
- public long recordedCpuTimeMicros;
-
-
- @Override
- public int hashCode() {
- return binderClass.hashCode() * 31 + transactionCode;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (!(obj instanceof BinderCallStats)) {
- return false;
- }
- BinderCallStats bcsk = (BinderCallStats) obj;
- return binderClass.equals(bcsk.binderClass) && transactionCode == bcsk.transactionCode;
- }
-
- public String getClassName() {
- return binderClass.getName();
- }
-
- public String getMethodName() {
- return methodName;
- }
-
- @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
- public void ensureMethodName(BinderTransactionNameResolver resolver) {
- if (methodName == null) {
- methodName = resolver.getMethodName(binderClass, transactionCode);
- }
- }
-
- @Override
- public String toString() {
- return "BinderCallStats{"
- + binderClass
- + " transaction=" + transactionCode
- + " callCount=" + callCount
- + " recordedCallCount=" + recordedCallCount
- + " recorderCpuTimeMicros=" + recordedCpuTimeMicros
- + "}";
- }
- }
-
- /**
* The statistics associated with a particular uid.
*/
public static class Uid extends BatteryStats.Uid {
@@ -8350,16 +8184,6 @@
final SparseArray<Pid> mPids = new SparseArray<>();
/**
- * Grand total of system server binder calls made by this uid.
- */
- private long mBinderCallCount;
-
- /**
- * Detailed information about system server binder calls made by this uid.
- */
- private final ArraySet<BinderCallStats> mBinderCallStats = new ArraySet<>();
-
- /**
* EnergyConsumer consumption by this uid while on battery.
* Its '<b>custom</b> power buckets' correspond to the
* {@link android.hardware.power.stats.EnergyConsumer.ordinal}s of (custom) energy consumer
@@ -8539,20 +8363,6 @@
return mProcStateScreenOffTimeMs.getCountsLocked(timesInFreqMs, procState);
}
- public long getBinderCallCount() {
- return mBinderCallCount;
- }
-
- @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
- public ArraySet<BinderCallStats> getBinderCallStats() {
- return mBinderCallStats;
- }
-
- @Override
- public double getProportionalSystemServiceUsage() {
- return mProportionalSystemServiceUsage;
- }
-
/**
* Adds isolated UID to the list of children.
*/
@@ -9897,9 +9707,6 @@
}
mPackageStats.clear();
- mBinderCallCount = 0;
- mBinderCallStats.clear();
-
mProportionalSystemServiceUsage = 0;
mLastStepUserTimeMs = mLastStepSystemTimeMs = 0;
@@ -10065,40 +9872,6 @@
}
}
- // Reusable object used as a key to lookup values in mBinderCallStats
- private static BinderCallStats sTempBinderCallStats = new BinderCallStats();
-
- /**
- * Notes incoming binder call stats associated with this work source UID.
- */
- public void noteBinderCallStatsLocked(long incrementalCallCount,
- Collection<BinderCallsStats.CallStat> callStats) {
- if (DEBUG) {
- Slog.d(TAG, "noteBinderCalls() workSourceUid = [" + mUid + "], "
- + " incrementalCallCount: " + incrementalCallCount + " callStats = ["
- + new ArrayList<>(callStats) + "]");
- }
- mBinderCallCount += incrementalCallCount;
- for (BinderCallsStats.CallStat stat : callStats) {
- BinderCallStats bcs;
- sTempBinderCallStats.binderClass = stat.binderClass;
- sTempBinderCallStats.transactionCode = stat.transactionCode;
- int index = mBinderCallStats.indexOf(sTempBinderCallStats);
- if (index >= 0) {
- bcs = mBinderCallStats.valueAt(index);
- } else {
- bcs = new BinderCallStats();
- bcs.binderClass = stat.binderClass;
- bcs.transactionCode = stat.transactionCode;
- mBinderCallStats.add(bcs);
- }
-
- bcs.callCount += stat.incrementalCallCount;
- bcs.recordedCallCount = stat.recordedCallCount;
- bcs.recordedCpuTimeMicros = stat.cpuTimeMicros;
- }
- }
-
/**
* The statistics associated with a particular wake lock.
*/
@@ -11490,18 +11263,6 @@
mKernelCpuSpeedReaders[i] = new KernelCpuSpeedReader(cpus[0], freqs.length);
}
- // Initialize CPU power bracket map, which combines CPU states (cluster/freq pairs)
- // into a small number of brackets
- mCpuPowerBracketMap = new int[mCpuScalingPolicies.getScalingStepCount()];
- int index = 0;
- for (int policy : policies) {
- int steps = mCpuScalingPolicies.getFrequencies(policy).length;
- for (int step = 0; step < steps; step++) {
- mCpuPowerBracketMap[index++] =
- mPowerProfile.getCpuPowerBracketForScalingStep(policy, step);
- }
- }
-
if (mEstimatedBatteryCapacityMah == -1) {
// Initialize the estimated battery capacity to a known preset one.
mEstimatedBatteryCapacityMah = (int) mPowerProfile.getBatteryCapacity();
@@ -11510,18 +11271,6 @@
setDisplayCountLocked(mPowerProfile.getNumDisplays());
}
- /**
- * Starts tracking CPU time-in-state for threads of the system server process,
- * keeping a separate account of threads receiving incoming binder calls.
- */
- public void startTrackingSystemServerCpuTime() {
- mSystemServerCpuThreadReader.startTrackingThreadCpuTime();
- }
-
- public SystemServiceCpuThreadTimes getSystemServiceCpuThreadTimes() {
- return mSystemServerCpuThreadReader.readAbsolute();
- }
-
public void setCallback(BatteryCallback cb) {
mCallback = cb;
}
@@ -12160,10 +11909,6 @@
EnergyConsumerStats.resetIfNotNull(mGlobalEnergyConsumerStats);
- if (!Flags.disableSystemServicePowerAttr()) {
- resetIfNotNull(mBinderThreadCpuTimesUs, false, elapsedRealtimeUs);
- }
-
mNumAllUidCpuTimeReads = 0;
mNumUidsRemoved = 0;
@@ -14194,9 +13939,6 @@
mKernelCpuSpeedReaders[i].readDelta();
}
}
- if (!Flags.disableSystemServicePowerAttr()) {
- mSystemServerCpuThreadReader.readDelta();
- }
return;
}
@@ -14244,67 +13986,12 @@
mNumAllUidCpuTimeReads += 2;
}
- if (!Flags.disableSystemServicePowerAttr()) {
- updateSystemServerThreadStats();
- }
-
if (powerAccumulator != null) {
updateCpuEnergyConsumerStatsLocked(cpuClusterChargeUC, powerAccumulator);
}
}
/**
- * Estimates the proportion of the System Server CPU activity (per cluster per speed)
- * spent on handling incoming binder calls.
- */
- @VisibleForTesting
- public void updateSystemServerThreadStats() {
- // There are some simplifying assumptions made in this algorithm
- // 1) We assume that if a thread handles incoming binder calls, all of its activity
- // is spent doing that. Most incoming calls are handled by threads allocated
- // by the native layer in the binder thread pool, so this assumption is reasonable.
- // 2) We use the aggregate CPU time spent in different threads as a proxy for the CPU
- // cost. In reality, in multi-core CPUs, the CPU cost may not be linearly
- // affected by additional threads.
-
- SystemServerCpuThreadReader.SystemServiceCpuThreadTimes systemServiceCpuThreadTimes =
- mSystemServerCpuThreadReader.readDelta();
- if (systemServiceCpuThreadTimes == null) {
- return;
- }
-
- if (mBinderThreadCpuTimesUs == null) {
- mBinderThreadCpuTimesUs = new LongSamplingCounterArray(mOnBatteryTimeBase);
- }
- mBinderThreadCpuTimesUs.addCountLocked(systemServiceCpuThreadTimes.binderThreadCpuTimesUs);
-
- if (DEBUG_BINDER_STATS) {
- Slog.d(TAG, "System server threads per CPU cluster (incoming binder threads)");
- long binderThreadTimeMs = 0;
- final long[] binderThreadCpuTimesUs = mBinderThreadCpuTimesUs.getCountsLocked(
- BatteryStats.STATS_SINCE_CHARGED);
- int index = 0;
- int[] policies = mCpuScalingPolicies.getPolicies();
- for (int policy : policies) {
- StringBuilder sb = new StringBuilder();
- sb.append("policy").append(policy).append(": [");
- int numSpeeds = mCpuScalingPolicies.getFrequencies(policy).length;
- for (int speed = 0; speed < numSpeeds; speed++) {
- if (speed != 0) {
- sb.append(", ");
- }
- long binderCountMs = binderThreadCpuTimesUs[index] / 1000;
- sb.append(TextUtils.formatSimple("%10d", binderCountMs));
-
- binderThreadTimeMs += binderCountMs;
- index++;
- }
- Slog.d(TAG, sb.toString());
- }
- }
- }
-
- /**
* Mark the current partial timers as gone through a collection so that they will be
* considered in the next cpu times distribution to wakelock holders.
*/
@@ -15666,19 +15353,6 @@
}
}
-
- /**
- * Estimates the time spent by the system server handling incoming binder requests.
- */
- @Override
- public long[] getSystemServiceTimeAtCpuSpeeds() {
- if (mBinderThreadCpuTimesUs == null) {
- return null;
- }
-
- return mBinderThreadCpuTimesUs.getCountsLocked(BatteryStats.STATS_SINCE_CHARGED);
- }
-
/**
* Retrieve the statistics object for a particular uid, creating if needed.
*/
@@ -16311,28 +15985,6 @@
pw.print(" "); pw.print(u); pw.print(": "); pw.println(Arrays.toString(times));
}
}
-
- if (!Flags.disableSystemServicePowerAttr()) {
- updateSystemServiceCallStats();
- if (mBinderThreadCpuTimesUs != null) {
- pw.println("Per UID System server binder time in ms:");
- long[] systemServiceTimeAtCpuSpeeds = getSystemServiceTimeAtCpuSpeeds();
- for (int i = 0; i < size; i++) {
- int u = mUidStats.keyAt(i);
- Uid uid = mUidStats.get(u);
- double proportionalSystemServiceUsage = uid.getProportionalSystemServiceUsage();
- long timeUs = 0;
- for (int j = systemServiceTimeAtCpuSpeeds.length - 1; j >= 0; j--) {
- timeUs += systemServiceTimeAtCpuSpeeds[j] * proportionalSystemServiceUsage;
- }
-
- pw.print(" ");
- pw.print(u);
- pw.print(": ");
- pw.println(timeUs / 1000);
- }
- }
- }
}
/**
@@ -17063,11 +16715,6 @@
}
}
}
-
- if (!Flags.disableSystemServicePowerAttr()) {
- mBinderThreadCpuTimesUs =
- LongSamplingCounterArray.readSummaryFromParcelLocked(in, mOnBatteryTimeBase);
- }
}
/**
@@ -17610,10 +17257,6 @@
}
}
}
-
- if (!Flags.disableSystemServicePowerAttr()) {
- LongSamplingCounterArray.writeSummaryToParcelLocked(out, mBinderThreadCpuTimesUs);
- }
}
/**
@@ -17631,10 +17274,6 @@
// Pull the clock time. This may update the time and make a new history entry
// if we had originally pulled a time before the RTC was set.
getStartClockTime();
-
- if (!Flags.disableSystemServicePowerAttr()) {
- updateSystemServiceCallStats();
- }
}
@GuardedBy("this")
diff --git a/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java b/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java
index 03622ce..ebcb2e8 100644
--- a/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java
+++ b/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java
@@ -174,13 +174,6 @@
BatteryConsumer.POWER_COMPONENT_ANY)) {
mPowerCalculators.add(new CustomEnergyConsumerPowerCalculator(mPowerProfile));
}
- if (!com.android.server.power.optimization.Flags.disableSystemServicePowerAttr()) {
- // It is important that SystemServicePowerCalculator be applied last,
- // because it re-attributes some of the power estimated by the other
- // calculators.
- mPowerCalculators.add(
- new SystemServicePowerCalculator(mCpuScalingPolicies, mPowerProfile));
- }
}
}
return mPowerCalculators;
diff --git a/services/core/java/com/android/server/power/stats/SystemServerCpuThreadReader.java b/services/core/java/com/android/server/power/stats/SystemServerCpuThreadReader.java
deleted file mode 100644
index e036eab..0000000
--- a/services/core/java/com/android/server/power/stats/SystemServerCpuThreadReader.java
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.power.stats;
-
-import android.annotation.Nullable;
-import android.os.Process;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.os.KernelSingleProcessCpuThreadReader;
-
-import java.io.IOException;
-
-/**
- * Reads /proc/UID/task/TID/time_in_state files to obtain statistics on CPU usage
- * by various threads of the System Server.
- */
-public class SystemServerCpuThreadReader {
- private final KernelSingleProcessCpuThreadReader mKernelCpuThreadReader;
-
- private long[] mLastThreadCpuTimesUs;
- private long[] mLastBinderThreadCpuTimesUs;
-
- /**
- * Times (in microseconds) spent by the system server UID.
- */
- public static class SystemServiceCpuThreadTimes {
- // All threads
- public long[] threadCpuTimesUs;
- // Just the threads handling incoming binder calls
- public long[] binderThreadCpuTimesUs;
- }
-
- private final SystemServiceCpuThreadTimes mDeltaCpuThreadTimes =
- new SystemServiceCpuThreadTimes();
-
- /**
- * Creates a configured instance of SystemServerCpuThreadReader.
- */
- public static SystemServerCpuThreadReader create() {
- return new SystemServerCpuThreadReader(
- KernelSingleProcessCpuThreadReader.create(Process.myPid()));
- }
-
- @VisibleForTesting
- public SystemServerCpuThreadReader(int pid,
- KernelSingleProcessCpuThreadReader.CpuTimeInStateReader cpuTimeInStateReader)
- throws IOException {
- this(new KernelSingleProcessCpuThreadReader(pid, cpuTimeInStateReader));
- }
-
- @VisibleForTesting
- public SystemServerCpuThreadReader(KernelSingleProcessCpuThreadReader kernelCpuThreadReader) {
- mKernelCpuThreadReader = kernelCpuThreadReader;
- }
-
- /**
- * Start tracking CPU time-in-state for the process specified in the constructor.
- */
- public void startTrackingThreadCpuTime() {
- mKernelCpuThreadReader.startTrackingThreadCpuTimes();
- }
-
- public void setBinderThreadNativeTids(int[] nativeTids) {
- mKernelCpuThreadReader.setSelectedThreadIds(nativeTids);
- }
-
- /**
- * Returns delta of CPU times, per thread, since the previous call to this method.
- */
- @Nullable
- public SystemServiceCpuThreadTimes readDelta() {
- final int numCpuFrequencies = mKernelCpuThreadReader.getCpuFrequencyCount();
- if (mLastThreadCpuTimesUs == null) {
- mLastThreadCpuTimesUs = new long[numCpuFrequencies];
- mLastBinderThreadCpuTimesUs = new long[numCpuFrequencies];
-
- mDeltaCpuThreadTimes.threadCpuTimesUs = new long[numCpuFrequencies];
- mDeltaCpuThreadTimes.binderThreadCpuTimesUs = new long[numCpuFrequencies];
- }
-
- final KernelSingleProcessCpuThreadReader.ProcessCpuUsage processCpuUsage =
- mKernelCpuThreadReader.getProcessCpuUsage();
- if (processCpuUsage == null) {
- return null;
- }
-
- for (int i = numCpuFrequencies - 1; i >= 0; i--) {
- long threadCpuTimesUs = processCpuUsage.threadCpuTimesMillis[i] * 1000;
- long binderThreadCpuTimesUs = processCpuUsage.selectedThreadCpuTimesMillis[i] * 1000;
- mDeltaCpuThreadTimes.threadCpuTimesUs[i] =
- Math.max(0, threadCpuTimesUs - mLastThreadCpuTimesUs[i]);
- mDeltaCpuThreadTimes.binderThreadCpuTimesUs[i] =
- Math.max(0, binderThreadCpuTimesUs - mLastBinderThreadCpuTimesUs[i]);
- mLastThreadCpuTimesUs[i] = threadCpuTimesUs;
- mLastBinderThreadCpuTimesUs[i] = binderThreadCpuTimesUs;
- }
-
- return mDeltaCpuThreadTimes;
- }
-
- /** Returns CPU times, per thread group, since tracking started. */
- @Nullable
- public SystemServiceCpuThreadTimes readAbsolute() {
- final int numCpuFrequencies = mKernelCpuThreadReader.getCpuFrequencyCount();
- final KernelSingleProcessCpuThreadReader.ProcessCpuUsage processCpuUsage =
- mKernelCpuThreadReader.getProcessCpuUsage();
- if (processCpuUsage == null) {
- return null;
- }
- final SystemServiceCpuThreadTimes result = new SystemServiceCpuThreadTimes();
- result.threadCpuTimesUs = new long[numCpuFrequencies];
- result.binderThreadCpuTimesUs = new long[numCpuFrequencies];
- for (int i = 0; i < numCpuFrequencies; ++i) {
- result.threadCpuTimesUs[i] = processCpuUsage.threadCpuTimesMillis[i] * 1_000;
- result.binderThreadCpuTimesUs[i] =
- processCpuUsage.selectedThreadCpuTimesMillis[i] * 1_000;
- }
- return result;
- }
-}
diff --git a/services/core/java/com/android/server/power/stats/SystemServicePowerCalculator.java b/services/core/java/com/android/server/power/stats/SystemServicePowerCalculator.java
deleted file mode 100644
index 1d5d93e..0000000
--- a/services/core/java/com/android/server/power/stats/SystemServicePowerCalculator.java
+++ /dev/null
@@ -1,156 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.power.stats;
-
-import android.os.BatteryConsumer;
-import android.os.BatteryStats;
-import android.os.BatteryUsageStats;
-import android.os.BatteryUsageStatsQuery;
-import android.os.Process;
-import android.os.UidBatteryConsumer;
-import android.util.Log;
-import android.util.SparseArray;
-
-import com.android.internal.os.CpuScalingPolicies;
-import com.android.internal.os.PowerProfile;
-
-/**
- * Estimates the amount of power consumed by the System Server handling requests from
- * a given app.
- */
-public class SystemServicePowerCalculator extends PowerCalculator {
- private static final boolean DEBUG = false;
- private static final String TAG = "SystemServicePowerCalc";
-
- // Power estimators per CPU cluster, per CPU frequency. The array is flattened according
- // to this layout:
- // {cluster1-speed1, cluster1-speed2, ..., cluster2-speed1, cluster2-speed2, ...}
- private final UsageBasedPowerEstimator[] mPowerEstimators;
- private final CpuPowerCalculator mCpuPowerCalculator;
-
- public SystemServicePowerCalculator(CpuScalingPolicies cpuScalingPolicies,
- PowerProfile powerProfile) {
- mCpuPowerCalculator = new CpuPowerCalculator(cpuScalingPolicies, powerProfile);
- mPowerEstimators = new UsageBasedPowerEstimator[cpuScalingPolicies.getScalingStepCount()];
- int index = 0;
- int[] policies = cpuScalingPolicies.getPolicies();
- for (int policy : policies) {
- final int numSpeeds = cpuScalingPolicies.getFrequencies(policy).length;
- for (int speed = 0; speed < numSpeeds; speed++) {
- mPowerEstimators[index++] = new UsageBasedPowerEstimator(
- powerProfile.getAveragePowerForCpuScalingStep(policy, speed));
- }
- }
- }
-
- @Override
- public boolean isPowerComponentSupported(@BatteryConsumer.PowerComponent int powerComponent) {
- return powerComponent == BatteryConsumer.POWER_COMPONENT_SYSTEM_SERVICES;
- }
-
- @Override
- public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats,
- long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) {
- final BatteryStats.Uid systemUid = batteryStats.getUidStats().get(Process.SYSTEM_UID);
- if (systemUid == null) {
- return;
- }
-
- final long consumptionUC = systemUid.getCpuEnergyConsumptionUC();
- final int powerModel = getPowerModel(consumptionUC, query);
-
- double systemServicePowerMah;
- if (powerModel == BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION) {
- systemServicePowerMah = calculatePowerUsingEnergyConsumption(batteryStats,
- systemUid, consumptionUC);
- } else {
- systemServicePowerMah = calculatePowerUsingPowerProfile(batteryStats);
- }
-
- final SparseArray<UidBatteryConsumer.Builder> uidBatteryConsumerBuilders =
- builder.getUidBatteryConsumerBuilders();
- final UidBatteryConsumer.Builder systemServerConsumer = uidBatteryConsumerBuilders.get(
- Process.SYSTEM_UID);
-
- if (systemServerConsumer != null) {
- systemServicePowerMah = Math.min(systemServicePowerMah,
- systemServerConsumer.getTotalPower());
-
- // The system server power needs to be adjusted because part of it got
- // distributed to applications
- systemServerConsumer.setConsumedPower(
- BatteryConsumer.POWER_COMPONENT_REATTRIBUTED_TO_OTHER_CONSUMERS,
- -systemServicePowerMah, powerModel);
- }
-
- for (int i = uidBatteryConsumerBuilders.size() - 1; i >= 0; i--) {
- final UidBatteryConsumer.Builder app = uidBatteryConsumerBuilders.valueAt(i);
- if (app != systemServerConsumer) {
- final BatteryStats.Uid uid = app.getBatteryStatsUid();
- app.setConsumedPower(BatteryConsumer.POWER_COMPONENT_SYSTEM_SERVICES,
- systemServicePowerMah * uid.getProportionalSystemServiceUsage(),
- powerModel);
- }
- }
-
- builder.getAggregateBatteryConsumerBuilder(
- BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE)
- .setConsumedPower(BatteryConsumer.POWER_COMPONENT_SYSTEM_SERVICES,
- systemServicePowerMah);
- builder.getAggregateBatteryConsumerBuilder(
- BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS)
- .setConsumedPower(BatteryConsumer.POWER_COMPONENT_SYSTEM_SERVICES,
- systemServicePowerMah);
- }
-
- private double calculatePowerUsingEnergyConsumption(BatteryStats batteryStats,
- BatteryStats.Uid systemUid, long consumptionUC) {
- // Use the PowerProfile based model to estimate the ratio between the power consumed
- // while handling incoming binder calls and the entire System UID power consumption.
- // Apply that ratio to the _EnergyConsumer_ system UID power consumption to get a more
- // accurate estimate of the power consumed by incoming binder calls.
- final double systemServiceModeledPowerMah = calculatePowerUsingPowerProfile(batteryStats);
- final double systemUidModeledPowerMah = mCpuPowerCalculator.calculateUidModeledPowerMah(
- systemUid, BatteryStats.STATS_SINCE_CHARGED);
-
- if (systemUidModeledPowerMah > 0) {
- return uCtoMah(consumptionUC) * systemServiceModeledPowerMah / systemUidModeledPowerMah;
- } else {
- return 0;
- }
- }
-
- private double calculatePowerUsingPowerProfile(BatteryStats batteryStats) {
- final long[] systemServiceTimeAtCpuSpeeds = batteryStats.getSystemServiceTimeAtCpuSpeeds();
- if (systemServiceTimeAtCpuSpeeds == null) {
- return 0;
- }
-
- // TODO(179210707): additionally account for CPU active and per cluster battery use
-
- double powerMah = 0;
- final int size = Math.min(mPowerEstimators.length, systemServiceTimeAtCpuSpeeds.length);
- for (int i = 0; i < size; i++) {
- powerMah += mPowerEstimators[i].calculatePower(systemServiceTimeAtCpuSpeeds[i] / 1000);
- }
-
- if (DEBUG) {
- Log.d(TAG, "System service power:" + powerMah);
- }
- return powerMah;
- }
-}
diff --git a/services/core/java/com/android/server/power/stats/flags.aconfig b/services/core/java/com/android/server/power/stats/flags.aconfig
index 521ee58..5ab8fca5 100644
--- a/services/core/java/com/android/server/power/stats/flags.aconfig
+++ b/services/core/java/com/android/server/power/stats/flags.aconfig
@@ -17,14 +17,6 @@
}
flag {
- name: "disable_system_service_power_attr"
- namespace: "backstage_power"
- description: "Deprecation of system service power re-attribution"
- bug: "311793616"
- is_fixed_read_only: true
-}
-
-flag {
name: "streamlined_connectivity_battery_stats"
namespace: "backstage_power"
description: "Feature flag for streamlined connectivity battery stats"
diff --git a/services/core/java/com/android/server/powerstats/TimerTrigger.java b/services/core/java/com/android/server/powerstats/TimerTrigger.java
index 817a40d..d53b431 100644
--- a/services/core/java/com/android/server/powerstats/TimerTrigger.java
+++ b/services/core/java/com/android/server/powerstats/TimerTrigger.java
@@ -55,13 +55,9 @@
@Override
public void run() {
- if (Flags.alarmBasedPowerstatsLogging()) {
- final long nextAlarmMs = SystemClock.elapsedRealtime() + mPeriodMs;
- mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, nextAlarmMs,
- AlarmManager.WINDOW_EXACT, 0, mName, this, mHandler, null);
- } else {
- mHandler.postDelayed(this, mPeriodMs);
- }
+ final long nextAlarmMs = SystemClock.elapsedRealtime() + mPeriodMs;
+ mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, nextAlarmMs,
+ AlarmManager.WINDOW_EXACT, 0, mName, this, mHandler, null);
if (DEBUG) Slog.d(TAG, "Received delayed message (" + mName + "). Logging rail data");
logPowerStatsData(mMsgType);
}
diff --git a/services/core/java/com/android/server/powerstats/flags.aconfig b/services/core/java/com/android/server/powerstats/flags.aconfig
index 29ad7dc..2fc188f 100644
--- a/services/core/java/com/android/server/powerstats/flags.aconfig
+++ b/services/core/java/com/android/server/powerstats/flags.aconfig
@@ -3,16 +3,6 @@
container: "system"
flag {
- name: "alarm_based_powerstats_logging"
- namespace: "backstage_power"
- description: "Utilize new OomAdjuster implementation"
- bug: "294598168"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
name: "verify_non_null_arguments"
namespace: "backstage_power"
description: "Verify arguments passed are non-null"
diff --git a/services/core/java/com/android/server/security/advancedprotection/features/UsbDataAdvancedProtectionHook.java b/services/core/java/com/android/server/security/advancedprotection/features/UsbDataAdvancedProtectionHook.java
index 47cb75d..c59c19c 100644
--- a/services/core/java/com/android/server/security/advancedprotection/features/UsbDataAdvancedProtectionHook.java
+++ b/services/core/java/com/android/server/security/advancedprotection/features/UsbDataAdvancedProtectionHook.java
@@ -43,6 +43,7 @@
import android.os.Handler;
import android.os.Looper;
import android.os.RemoteException;
+import android.os.SystemProperties;
import android.os.UserHandle;
import android.security.Flags;
import android.util.Slog;
@@ -56,6 +57,7 @@
import android.security.advancedprotection.AdvancedProtectionFeature;
import com.android.internal.R;
+import com.android.internal.util.FrameworkStatsLog;
import java.util.Map;
import java.util.Objects;
@@ -70,9 +72,17 @@
private static final String APM_USB_FEATURE_NOTIF_CHANNEL = "APM_USB_SERVICE_NOTIF_CHANNEL";
private static final String CHANNEL_NAME = "BackgroundInstallUiNotificationChannel";
+ private static final String USB_DATA_PROTECTION_DISABLE_SYSTEM_PROPERTY =
+ "ro.usb.data_protection.apm.disabled";
+ private static final String USB_DATA_PROTECTION_REPLUG_REQUIRED_UPON_ENABLE_SYSTEM_PROPERTY =
+ "ro.usb.data_protection.apm.replug_required_upon_enable";
+ private static final String
+ USB_DATA_PROTECTION_DATA_REQUIRED_FOR_HIGH_POWER_CHARGE_SYSTEM_PROPERTY =
+ "ro.usb.data_protection.apm.data_required_for_high_power_charge";
private static final int APM_USB_FEATURE_CHANNEL_ID = 1;
- private static final int DELAY_DISABLE_MS = 1000;
+ private static final int DELAY_DISABLE_MS = 3000;
private static final int OS_USB_DISABLE_REASON_LOCKDOWN_MODE = 1;
+ private static final int USB_DATA_CHANGE_MAX_RETRY_ATTEMPTS = 3;
private final Context mContext;
private final Handler mDelayedDisableHandler = new Handler(Looper.getMainLooper());
@@ -85,20 +95,21 @@
private NotificationChannel mNotificationChannel;
private boolean mCanSetUsbDataSignal = false;
- private AdvancedProtectionFeature mFeature
- = new AdvancedProtectionFeature(FEATURE_ID_DISALLOW_USB);
+ private AdvancedProtectionFeature mFeature =
+ new AdvancedProtectionFeature(FEATURE_ID_DISALLOW_USB);
private boolean mBroadcastReceiverIsRegistered = false;
private boolean mInitialPlugInNotificationSent = false;
+ private boolean mIsAfterFirstUnlock = false;
public UsbDataAdvancedProtectionHook(Context context, boolean enabled) {
super(context, enabled);
mContext = context;
mUsbManager = mContext.getSystemService(UsbManager.class);
- mUsbManagerInternal = Objects.requireNonNull(
- LocalServices.getService(IUsbManagerInternal.class));
- onAdvancedProtectionChanged(enabled);
+ mUsbManagerInternal =
+ Objects.requireNonNull(LocalServices.getService(IUsbManagerInternal.class));
mCanSetUsbDataSignal = canSetUsbDataSignal();
+ onAdvancedProtectionChanged(enabled);
}
@Override
@@ -108,7 +119,14 @@
@Override
public boolean isAvailable() {
- return Flags.aapmFeatureUsbDataProtection() && mCanSetUsbDataSignal;
+ boolean usbDataProtectionDisabled =
+ SystemProperties.getBoolean(USB_DATA_PROTECTION_DISABLE_SYSTEM_PROPERTY, false);
+ if (usbDataProtectionDisabled) {
+ Slog.d(TAG, "USB data protection is disabled through system property");
+ }
+ return Flags.aapmFeatureUsbDataProtection()
+ && mCanSetUsbDataSignal
+ && !usbDataProtectionDisabled;
}
@Override
@@ -144,6 +162,7 @@
try {
if (ACTION_USER_PRESENT.equals(intent.getAction())
&& !mKeyguardManager.isKeyguardLocked()) {
+ mIsAfterFirstUnlock = true;
mDelayedDisableHandler.removeCallbacksAndMessages(null);
setUsbDataSignalIfPossible(true);
@@ -155,11 +174,10 @@
if (Build.IS_DEBUGGABLE) {
dumpUsbDevices();
}
- if(mKeyguardManager.isKeyguardLocked()) {
+ if (mKeyguardManager.isKeyguardLocked()) {
updateDelayedDisableTask(intent);
}
sendNotificationIfDeviceLocked(intent);
-
}
} catch (Exception e) {
Slog.e(TAG, "USB Data protection failed with: " + e.getMessage());
@@ -168,14 +186,16 @@
private void updateDelayedDisableTask(Intent intent) {
// For recovered intermittent/unreliable USB connections
- if(usbPortIsConnectedAndDataEnabled(intent)) {
+ if (usbPortIsConnectedAndDataEnabled(intent)) {
mDelayedDisableHandler.removeCallbacksAndMessages(null);
- } else if(!mDelayedDisableHandler.hasMessagesOrCallbacks()) {
- mDelayedDisableHandler.postDelayed(() -> {
- if (mKeyguardManager.isKeyguardLocked()) {
- setUsbDataSignalIfPossible(false);
- }
- }, DELAY_DISABLE_MS);
+ } else if (!mDelayedDisableHandler.hasMessagesOrCallbacks()) {
+ mDelayedDisableHandler.postDelayed(
+ () -> {
+ if (mKeyguardManager.isKeyguardLocked()) {
+ setUsbDataSignalIfPossible(false);
+ }
+ },
+ DELAY_DISABLE_MS);
}
}
@@ -197,7 +217,7 @@
Slog.d(TAG, "Device: " + device.getDeviceName());
}
UsbAccessory[] accessoryList = mUsbManager.getAccessoryList();
- if(accessoryList != null) {
+ if (accessoryList != null) {
for (UsbAccessory accessory : accessoryList) {
Slog.d(TAG, "Accessory: " + accessory.toString());
}
@@ -235,11 +255,31 @@
intent.getParcelableExtra(UsbManager.EXTRA_PORT_STATUS, UsbPortStatus.class);
if (mKeyguardManager.isKeyguardLocked()
&& usbPortIsConnectedWithDataDisabled(portStatus)) {
+
+ String notificationBody =
+ mContext.getString(
+ R.string.usb_apm_usb_plugged_in_when_locked_notification_text);
+ if (SystemProperties.getBoolean(
+ USB_DATA_PROTECTION_REPLUG_REQUIRED_UPON_ENABLE_SYSTEM_PROPERTY, false)) {
+ notificationBody =
+ mContext.getString(
+ R.string
+ .usb_apm_usb_plugged_in_when_locked_no_replug_notification_text);
+ }
+
+ if (SystemProperties.getBoolean(
+ USB_DATA_PROTECTION_DATA_REQUIRED_FOR_HIGH_POWER_CHARGE_SYSTEM_PROPERTY,
+ false)) {
+ notificationBody +=
+ " "
+ + mContext.getString(R.string
+ .usb_apm_usb_plugged_in_when_locked_low_power_charge_notification_text);
+ }
+
sendNotification(
mContext.getString(
R.string.usb_apm_usb_plugged_in_when_locked_notification_title),
- mContext.getString(
- R.string.usb_apm_usb_plugged_in_when_locked_notification_text));
+ notificationBody);
mInitialPlugInNotificationSent = true;
}
}
@@ -252,16 +292,35 @@
}
private void setUsbDataSignalIfPossible(boolean status) {
- if (!status && deviceHaveUsbDataConnection()) {
+ /*
+ * We check if there is already an existing USB connection and skip the USB
+ * disablement if there is one unless it is in BFU state.
+ */
+ if (!status && (deviceHaveUsbDataConnection() && mIsAfterFirstUnlock)) {
return;
}
- try {
- if (!mUsbManagerInternal.enableUsbDataSignal(status,
- OS_USB_DISABLE_REASON_LOCKDOWN_MODE)) {
- Slog.e(TAG, "USB Data protection toggle failed");
+
+ int usbChangeStateReattempts = 0;
+ while(usbChangeStateReattempts < USB_DATA_CHANGE_MAX_RETRY_ATTEMPTS) {
+ try {
+ if (mUsbManagerInternal.enableUsbDataSignal(
+ status, OS_USB_DISABLE_REASON_LOCKDOWN_MODE)) {
+ break;
+ } else {
+ Slog.e(TAG, "USB Data protection toggle attemptfailed");
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "RemoteException thrown when calling enableUsbDataSignal", e);
}
- } catch (RemoteException e) {
- Slog.e(TAG, "RemoteException thrown when calling enableUsbDataSignal", e);
+ usbChangeStateReattempts += 1;
+ }
+
+ // Log the error if the USB change state failed at least once.
+ if(usbChangeStateReattempts > 0) {
+ FrameworkStatsLog.write(
+ FrameworkStatsLog.ADVANCED_PROTECTION_USB_STATE_CHANGE_ERROR_REPORTED,
+ /* desired_signal_state */ status,
+ /* retries_occurred */ usbChangeStateReattempts);
}
}
diff --git a/services/core/java/com/android/server/stats/pull/AggregatedMobileDataStatsPuller.java b/services/core/java/com/android/server/stats/pull/AggregatedMobileDataStatsPuller.java
index 3831352..2b4eb49 100644
--- a/services/core/java/com/android/server/stats/pull/AggregatedMobileDataStatsPuller.java
+++ b/services/core/java/com/android/server/stats/pull/AggregatedMobileDataStatsPuller.java
@@ -16,14 +16,28 @@
package com.android.server.stats.pull;
+import static android.net.NetworkStats.METERED_YES;
+import static android.net.NetworkTemplate.MATCH_MOBILE;
+import static android.provider.Settings.Global.NETSTATS_UID_BUCKET_DURATION;
+
+import static com.android.server.stats.Flags.useNetworkStatsQuerySummary;
+import static com.android.server.stats.pull.netstats.NetworkStatsUtils.fromPublicNetworkStats;
+
+import static java.util.concurrent.TimeUnit.HOURS;
+import static java.util.concurrent.TimeUnit.MICROSECONDS;
+
import android.annotation.NonNull;
import android.app.ActivityManager;
import android.app.StatsManager;
import android.app.usage.NetworkStatsManager;
+import android.content.Context;
import android.net.NetworkStats;
+import android.net.NetworkTemplate;
import android.os.Handler;
import android.os.HandlerThread;
+import android.os.SystemClock;
import android.os.Trace;
+import android.provider.Settings;
import android.util.ArrayMap;
import android.util.Slog;
import android.util.SparseIntArray;
@@ -32,6 +46,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.selinux.RateLimiter;
+import com.android.server.stats.pull.netstats.NetworkStatsAccumulator;
import java.time.Duration;
import java.util.List;
@@ -45,6 +60,8 @@
private static final boolean DEBUG = false;
+ private static final long NETSTATS_UID_DEFAULT_BUCKET_DURATION_MS = HOURS.toMillis(2);
+
private static class UidProcState {
private final int mUid;
@@ -57,8 +74,12 @@
@Override
public boolean equals(Object o) {
- if (this == o) return true;
- if (!(o instanceof UidProcState key)) return false;
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof UidProcState key)) {
+ return false;
+ }
return mUid == key.mUid && mState == key.mState;
}
@@ -76,7 +97,6 @@
public int getState() {
return mState;
}
-
}
private static class MobileDataStats {
@@ -128,7 +148,6 @@
// No reason to keep more dimensions than 3000. The 3000 is the hard top for the statsd metrics
// dimensions guardrail. It also will keep the result binder transaction size capped to
- // approximately 220kB for 3000 atoms
private static final int UID_STATS_MAX_SIZE = 3000;
private final SparseIntArray mUidPreviousState;
@@ -141,17 +160,60 @@
private final RateLimiter mRateLimiter;
- AggregatedMobileDataStatsPuller(@NonNull NetworkStatsManager networkStatsManager) {
- if (DEBUG && Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) {
+ private final Context mContext;
+
+ private final NetworkStatsAccumulator mStatsAccumulator;
+
+ /**
+ * Polling NetworkStats is a heavy operation and it should be done sparingly. Atom pulls may
+ * happen in bursts, but these should be infrequent. The poll rate limit ensures that data is
+ * sufficiently fresh (i.e. not stale) while reducing system load during atom pull bursts.
+ */
+ private static final long NETSTATS_POLL_RATE_LIMIT_MS = 15000;
+ private long mLastNetworkStatsPollTime = -NETSTATS_POLL_RATE_LIMIT_MS;
+
+ AggregatedMobileDataStatsPuller(@NonNull Context context) {
+ final boolean traceEnabled = Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER);
+ if (traceEnabled) {
Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, TAG + "-Init");
}
- mRateLimiter = new RateLimiter(/* window= */ Duration.ofSeconds(1));
+ mContext = context;
+
+ if (useNetworkStatsQuerySummary()) {
+ // to be aligned with networkStatsManager.forceUpdate() frequency
+ mRateLimiter =
+ new RateLimiter(/* window= */ Duration.ofMillis(NETSTATS_POLL_RATE_LIMIT_MS));
+ } else {
+ mRateLimiter = new RateLimiter(/* window= */ Duration.ofSeconds(2));
+ }
mUidStats = new ArrayMap<>();
mUidPreviousState = new SparseIntArray();
- mNetworkStatsManager = networkStatsManager;
+ final long elapsedMillisSinceBoot = SystemClock.elapsedRealtime();
+ final long currentTimeMillis = MICROSECONDS.toMillis(SystemClock.currentTimeMicro());
+ final long bootTimeMillis = currentTimeMillis - elapsedMillisSinceBoot;
+ final long bucketDurationMillis =
+ Settings.Global.getLong(
+ mContext.getContentResolver(),
+ NETSTATS_UID_BUCKET_DURATION,
+ NETSTATS_UID_DEFAULT_BUCKET_DURATION_MS);
+
+ mNetworkStatsManager = mContext.getSystemService(NetworkStatsManager.class);
+
+ if (useNetworkStatsQuerySummary()) {
+ NetworkTemplate template =
+ new NetworkTemplate.Builder(MATCH_MOBILE).setMeteredness(METERED_YES).build();
+ mStatsAccumulator =
+ new NetworkStatsAccumulator(
+ template,
+ false,
+ bucketDurationMillis,
+ bootTimeMillis - bucketDurationMillis);
+ } else {
+ mStatsAccumulator = null;
+ }
HandlerThread mMobileDataStatsHandlerThread = new HandlerThread("MobileDataStatsHandler");
mMobileDataStatsHandlerThread.start();
@@ -163,7 +225,7 @@
updateNetworkStats(mNetworkStatsManager);
});
}
- if (DEBUG) {
+ if (traceEnabled) {
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
}
}
@@ -191,14 +253,14 @@
@GuardedBy("mLock")
private MobileDataStats getUidStatsForPreviousStateLocked(int uid) {
final int previousState = mUidPreviousState.get(uid, ActivityManager.PROCESS_STATE_UNKNOWN);
- if (DEBUG && previousState == ActivityManager.PROCESS_STATE_UNKNOWN) {
- Slog.d(TAG, "getUidStatsForPreviousStateLocked() no prev state info for uid "
- + uid + ". Tracking stats with ActivityManager.PROCESS_STATE_UNKNOWN");
- }
-
final UidProcState statsKey = new UidProcState(uid, previousState);
if (mUidStats.containsKey(statsKey)) {
return mUidStats.get(statsKey);
+ } else {
+ if (DEBUG && previousState == ActivityManager.PROCESS_STATE_UNKNOWN) {
+ Slog.d(TAG, "getUidStatsForPreviousStateLocked() no prev state info for uid "
+ + uid + ". Tracking stats with ActivityManager.PROCESS_STATE_UNKNOWN");
+ }
}
if (mUidStats.size() < UID_STATS_MAX_SIZE) {
MobileDataStats stats = new MobileDataStats();
@@ -215,11 +277,13 @@
// noteUidProcessStateImpl can be called back to back several times while
// the updateNetworkStats loops over several stats for multiple uids
// and during the first call in a batch of proc state change event it can
- // contain info for uid with unknown previous state yet which can happen due to a few
+ // contain info for uid with unknown previous state yet which can happen due to
+ // a few
// reasons:
// - app was just started
// - app was started before the ActivityManagerService
- // as result stats would be created with state == ActivityManager.PROCESS_STATE_UNKNOWN
+ // as result stats would be created with state ==
+ // ActivityManager.PROCESS_STATE_UNKNOWN
if (mNetworkStatsManager != null) {
updateNetworkStats(mNetworkStatsManager);
} else {
@@ -230,12 +294,44 @@
}
}
+ private NetworkStats getMobileUidStats(NetworkStatsManager networkStatsManager) {
+ if (useNetworkStatsQuerySummary()) {
+ // networkStatsManager.querySummary provides reduced data resolution
+ // and higher latency for fresh data compared to networkStatsManager.getMobileUidStats.
+ // On the other hand getMobileUidStats provides realtime data in the memory
+ // with higher performance cost.
+ // Assumption is the tradeoff is acceptable in regards to existing
+ // atom MOBILE_BYTES_TRANSFER use case
+ final long elapsedMillisSinceBoot = SystemClock.elapsedRealtime();
+ if (elapsedMillisSinceBoot - mLastNetworkStatsPollTime >= NETSTATS_POLL_RATE_LIMIT_MS) {
+ mLastNetworkStatsPollTime = elapsedMillisSinceBoot;
+ Slog.d(TAG, "getMobileUidStats() forceUpdate");
+ networkStatsManager.forceUpdate();
+ }
+
+ final long currentTimeMillis = MICROSECONDS.toMillis(SystemClock.currentTimeMicro());
+ return mStatsAccumulator.queryStats(
+ currentTimeMillis,
+ (aTemplate, aIncludeTags, aStartTime, aEndTime) -> {
+ final android.app.usage.NetworkStats queryNonTaggedStats =
+ networkStatsManager.querySummary(aTemplate, aStartTime, aEndTime);
+ final NetworkStats nonTaggedStats =
+ fromPublicNetworkStats(queryNonTaggedStats);
+ queryNonTaggedStats.close();
+ return nonTaggedStats;
+ });
+ } else {
+ return networkStatsManager.getMobileUidStats();
+ }
+ }
+
private void updateNetworkStats(NetworkStatsManager networkStatsManager) {
- if (DEBUG && Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) {
+ final boolean traceEnabled = Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER);
+ if (traceEnabled) {
Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, TAG + "-updateNetworkStats");
}
- final NetworkStats latestStats = networkStatsManager.getMobileUidStats();
+ final NetworkStats latestStats = getMobileUidStats(networkStatsManager);
if (isEmpty(latestStats)) {
if (DEBUG) {
Slog.w(TAG, "getMobileUidStats() failed");
@@ -243,6 +339,10 @@
}
return;
}
+ if (DEBUG) {
+ Slog.d(TAG, "latestStats: \n" + latestStats.toString());
+ }
+
NetworkStats delta = latestStats.subtract(mLastMobileUidStats);
mLastMobileUidStats = latestStats;
@@ -251,18 +351,22 @@
} else if (DEBUG) {
Slog.w(TAG, "updateNetworkStats() no delta");
}
- if (DEBUG) {
+ if (traceEnabled) {
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
}
}
private void updateNetworkStatsDelta(NetworkStats delta) {
- if (DEBUG && Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) {
+ final boolean traceEnabled = DEBUG && Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER);
+ if (traceEnabled) {
Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, TAG + "-updateNetworkStatsDelta");
}
synchronized (mLock) {
for (NetworkStats.Entry entry : delta) {
if (entry.getRxPackets() != 0 || entry.getTxPackets() != 0) {
+ if (DEBUG) {
+ Slog.d(TAG, entry.toString());
+ }
MobileDataStats stats = getUidStatsForPreviousStateLocked(entry.getUid());
if (stats != null) {
stats.addTxBytes(entry.getTxBytes());
@@ -273,16 +377,13 @@
}
}
}
- if (DEBUG) {
+ if (traceEnabled) {
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
}
}
@GuardedBy("mLock")
private int pullDataBytesTransferLocked(List<StatsEvent> pulledData) {
- if (DEBUG) {
- Slog.d(TAG, "pullDataBytesTransferLocked() start");
- }
for (Map.Entry<UidProcState, MobileDataStats> uidStats : mUidStats.entrySet()) {
if (!uidStats.getValue().isEmpty()) {
MobileDataStats stats = uidStats.getValue();
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index 889b494..6ca5c04 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -132,7 +132,6 @@
import android.os.BatteryManager;
import android.os.BatteryProperty;
import android.os.BatteryStats;
-import android.os.BatteryStatsInternal;
import android.os.BatteryStatsManager;
import android.os.BatteryUsageStats;
import android.os.Binder;
@@ -211,7 +210,6 @@
import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidClusterTimeReader;
import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidFreqTimeReader;
import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidUserSysTimeReader;
-import com.android.internal.os.KernelSingleProcessCpuThreadReader.ProcessCpuUsage;
import com.android.internal.os.LooperStats;
import com.android.internal.os.PowerProfile;
import com.android.internal.os.ProcessCpuTracker;
@@ -235,7 +233,6 @@
import com.android.server.pm.UserManagerInternal;
import com.android.server.power.stats.KernelWakelockReader;
import com.android.server.power.stats.KernelWakelockStats;
-import com.android.server.power.stats.SystemServerCpuThreadReader.SystemServiceCpuThreadTimes;
import com.android.server.stats.pull.IonMemoryUtil.IonAllocations;
import com.android.server.stats.pull.netstats.NetworkStatsAccumulator;
import com.android.server.stats.pull.netstats.NetworkStatsExt;
@@ -605,7 +602,7 @@
return pullCpuTimePerUidFreqLocked(atomTag, data);
}
case FrameworkStatsLog.CPU_CYCLES_PER_THREAD_GROUP_CLUSTER:
- return pullCpuCyclesPerThreadGroupCluster(atomTag, data);
+ return StatsManager.PULL_SKIP;
case FrameworkStatsLog.CPU_ACTIVE_TIME:
synchronized (mCpuActiveTimeLock) {
return pullCpuActiveTimeLocked(atomTag, data);
@@ -983,7 +980,6 @@
registerCpuTimePerUid();
registerCpuCyclesPerUidCluster();
registerCpuTimePerUidFreq();
- registerCpuCyclesPerThreadGroupCluster();
registerCpuActiveTime();
registerCpuClusterTime();
registerWifiActivityInfo();
@@ -1071,9 +1067,7 @@
+ ENABLE_MOBILE_DATA_STATS_AGGREGATED_PULLER);
}
if (ENABLE_MOBILE_DATA_STATS_AGGREGATED_PULLER) {
- mAggregatedMobileDataStatsPuller =
- new AggregatedMobileDataStatsPuller(
- mContext.getSystemService(NetworkStatsManager.class));
+ mAggregatedMobileDataStatsPuller = new AggregatedMobileDataStatsPuller(mContext);
}
}
@@ -2179,73 +2173,6 @@
return StatsManager.PULL_SUCCESS;
}
- private void registerCpuCyclesPerThreadGroupCluster() {
- if (KernelCpuBpfTracking.isSupported()
- && !com.android.server.power.optimization.Flags.disableSystemServicePowerAttr()) {
- int tagId = FrameworkStatsLog.CPU_CYCLES_PER_THREAD_GROUP_CLUSTER;
- PullAtomMetadata metadata = new PullAtomMetadata.Builder()
- .setAdditiveFields(new int[]{3, 4})
- .build();
- mStatsManager.setPullAtomCallback(
- tagId,
- metadata,
- DIRECT_EXECUTOR,
- mStatsCallbackImpl
- );
- }
- }
-
- int pullCpuCyclesPerThreadGroupCluster(int atomTag, List<StatsEvent> pulledData) {
- if (com.android.server.power.optimization.Flags.disableSystemServicePowerAttr()) {
- return StatsManager.PULL_SKIP;
- }
-
- SystemServiceCpuThreadTimes times = LocalServices.getService(BatteryStatsInternal.class)
- .getSystemServiceCpuThreadTimes();
- if (times == null) {
- return StatsManager.PULL_SKIP;
- }
-
- addCpuCyclesPerThreadGroupClusterAtoms(atomTag, pulledData,
- FrameworkStatsLog.CPU_CYCLES_PER_THREAD_GROUP_CLUSTER__THREAD_GROUP__SYSTEM_SERVER,
- times.threadCpuTimesUs);
- addCpuCyclesPerThreadGroupClusterAtoms(atomTag, pulledData,
- FrameworkStatsLog.CPU_CYCLES_PER_THREAD_GROUP_CLUSTER__THREAD_GROUP__SYSTEM_SERVER_BINDER,
- times.binderThreadCpuTimesUs);
-
- ProcessCpuUsage surfaceFlingerTimes = mSurfaceFlingerProcessCpuThreadReader.readAbsolute();
- if (surfaceFlingerTimes != null && surfaceFlingerTimes.threadCpuTimesMillis != null) {
- long[] surfaceFlingerTimesUs =
- new long[surfaceFlingerTimes.threadCpuTimesMillis.length];
- for (int i = 0; i < surfaceFlingerTimesUs.length; ++i) {
- surfaceFlingerTimesUs[i] = surfaceFlingerTimes.threadCpuTimesMillis[i] * 1_000;
- }
- addCpuCyclesPerThreadGroupClusterAtoms(atomTag, pulledData,
- FrameworkStatsLog.CPU_CYCLES_PER_THREAD_GROUP_CLUSTER__THREAD_GROUP__SURFACE_FLINGER,
- surfaceFlingerTimesUs);
- }
-
- return StatsManager.PULL_SUCCESS;
- }
-
- private static void addCpuCyclesPerThreadGroupClusterAtoms(
- int atomTag, List<StatsEvent> pulledData, int threadGroup, long[] cpuTimesUs) {
- int[] freqsClusters = KernelCpuBpfTracking.getFreqsClusters();
- int clusters = KernelCpuBpfTracking.getClusters();
- long[] freqs = KernelCpuBpfTracking.getFreqs();
- long[] aggregatedCycles = new long[clusters];
- long[] aggregatedTimesUs = new long[clusters];
- for (int i = 0; i < cpuTimesUs.length; ++i) {
- aggregatedCycles[freqsClusters[i]] += freqs[i] * cpuTimesUs[i] / 1_000;
- aggregatedTimesUs[freqsClusters[i]] += cpuTimesUs[i];
- }
- for (int cluster = 0; cluster < clusters; ++cluster) {
- pulledData.add(FrameworkStatsLog.buildStatsEvent(
- atomTag, threadGroup, cluster, aggregatedCycles[cluster] / 1_000_000L,
- aggregatedTimesUs[cluster] / 1_000));
- }
- }
-
private void registerCpuActiveTime() {
// the throttling is 3sec, handled in
// frameworks/base/core/java/com/android/internal/os/KernelCpuProcReader
diff --git a/services/core/java/com/android/server/stats/stats_flags.aconfig b/services/core/java/com/android/server/stats/stats_flags.aconfig
index f5f3174..2c15d9e0 100644
--- a/services/core/java/com/android/server/stats/stats_flags.aconfig
+++ b/services/core/java/com/android/server/stats/stats_flags.aconfig
@@ -45,4 +45,12 @@
description: "Adds PressureStallInformation atom logging"
bug: "365731097"
is_fixed_read_only: true
-}
\ No newline at end of file
+}
+
+flag {
+ name: "use_network_stats_query_summary"
+ namespace: "statsd"
+ description: "Use NetworkStats querySummary API for AggregatedMobileDataStatsPuller"
+ bug: "398327159"
+ is_fixed_read_only: true
+}
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
index 6405353..e3a4d2b 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
@@ -317,16 +317,13 @@
// detected time if, for example, the age of all suggestions are considered.
NetworkTimeSuggestion lastNetworkSuggestion = mLastNetworkSuggestion.get();
if (lastNetworkSuggestion == null || !lastNetworkSuggestion.equals(suggestion)) {
- if (com.android.internal.os.Flags.applicationSharedMemoryEnabled()
- && android.os.Flags.networkTimeUsesSharedMemory()) {
- UnixEpochTime networkUnixEpochTime = suggestion.getUnixEpochTime();
- long lastNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis =
- networkUnixEpochTime.getUnixEpochTimeMillis()
- - networkUnixEpochTime.getElapsedRealtimeMillis();
- ApplicationSharedMemory.getInstance()
- .setLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis(
- lastNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis);
- }
+ UnixEpochTime networkUnixEpochTime = suggestion.getUnixEpochTime();
+ long lastNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis =
+ networkUnixEpochTime.getUnixEpochTimeMillis()
+ - networkUnixEpochTime.getElapsedRealtimeMillis();
+ ApplicationSharedMemory.getInstance()
+ .setLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis(
+ lastNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis);
mLastNetworkSuggestion.set(suggestion);
notifyNetworkTimeUpdateListenersAsynchronously();
@@ -360,11 +357,8 @@
@Override
public synchronized void clearLatestNetworkSuggestion() {
- if (com.android.internal.os.Flags.applicationSharedMemoryEnabled()
- && android.os.Flags.networkTimeUsesSharedMemory()) {
- ApplicationSharedMemory.getInstance()
- .clearLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis();
- }
+ ApplicationSharedMemory.getInstance()
+ .clearLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis();
mLastNetworkSuggestion.set(null);
notifyNetworkTimeUpdateListenersAsynchronously();
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index 3c3bfeb..5c8c24c 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -44,6 +44,7 @@
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.content.pm.UserInfo;
+import android.content.res.Resources;
import android.graphics.Rect;
import android.hardware.hdmi.HdmiClient;
import android.hardware.hdmi.HdmiControlManager;
@@ -145,6 +146,8 @@
private static final long SET_TV_AS_ACTIVE_SOURCE_IF_NO_REQUEST_DELAY_IN_MILLIS
= 10 * 1000; // 10 seconds
+ private static String sConfiguredTvProfileName;
+
// There are two different formats of DVB frontend devices. One is /dev/dvb%d.frontend%d,
// another one is /dev/dvb/adapter%d/frontend%d. Followings are the patterns for selecting the
// DVB frontend devices from the list of files in the /dev and /dev/dvb/adapter%d directory.
@@ -219,6 +222,7 @@
}
initExternalInputLoggingConfigs();
+ initConfigTvProfileName();
}
@Override
@@ -302,6 +306,15 @@
mExternalInputLoggingDeviceBrandNames.addAll(Arrays.asList(deviceBrandNames));
}
+ private void initConfigTvProfileName() {
+ try {
+ sConfiguredTvProfileName = mContext.getResources().getString(
+ R.string.config_configured_tv_profile_name);
+ } catch (Resources.NotFoundException e) {
+ sConfiguredTvProfileName = "";
+ }
+ }
+
private class MyPackageMonitor extends PackageMonitor {
MyPackageMonitor(boolean supportsPackageRestartQuery) {
super(supportsPackageRestartQuery);
@@ -381,6 +394,7 @@
intentFilter.addAction(Intent.ACTION_USER_REMOVED);
intentFilter.addAction(Intent.ACTION_USER_STARTED);
intentFilter.addAction(Intent.ACTION_USER_STOPPED);
+ intentFilter.addAction(Intent.ACTION_USER_INFO_CHANGED);
mContext.registerReceiverAsUser(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -391,10 +405,28 @@
removeUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
} else if (Intent.ACTION_USER_STARTED.equals(action)) {
int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
- startUser(userId);
+ UserInfo userInfo = mUserManager.getUserInfo(userId);
+ // Start user directly for devices without the configuration and when the
+ // profile has been configured previously, i.e. has onboarded successfully
+ if (sConfiguredTvProfileName.isEmpty() || (userInfo.name.equals(
+ sConfiguredTvProfileName) && userInfo.isProfile())) {
+ startUser(userId);
+ }
+ // For non-configured new user, do not call startUser until
+ // ACTION_USER_INFO_CHANGED is received
} else if (Intent.ACTION_USER_STOPPED.equals(action)) {
int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
stopUser(userId);
+ } else if (Intent.ACTION_USER_INFO_CHANGED.equals(action)) {
+ // Act upon this intent only if config is defined
+ if (!sConfiguredTvProfileName.isEmpty()) {
+ int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
+ UserInfo userInfo = mUserManager.getUserInfo(userId);
+ if (userInfo.name.equals(sConfiguredTvProfileName) && userInfo.isProfile()
+ && mUserManager.isUserRunning(userId)) {
+ startUser(userId);
+ }
+ }
}
}
}, UserHandle.ALL, intentFilter, null, null);
@@ -577,9 +609,8 @@
}
UserInfo userInfo = mUserManager.getUserInfo(userId);
UserInfo parentInfo = mUserManager.getProfileParent(userId);
- if (userInfo.isProfile()
- && parentInfo != null
- && parentInfo.id == mCurrentUserId) {
+ // User is guaranteed to be a profile here as we have checked before calling startUser
+ if (parentInfo != null && parentInfo.id == mCurrentUserId) {
int prevUserId = mCurrentUserId;
mCurrentUserId = userId;
// only the children of the current user can be started in background
diff --git a/services/core/java/com/android/server/utils/AnrTimer.java b/services/core/java/com/android/server/utils/AnrTimer.java
index e990dbd..cfc12ac 100644
--- a/services/core/java/com/android/server/utils/AnrTimer.java
+++ b/services/core/java/com/android/server/utils/AnrTimer.java
@@ -36,6 +36,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.Keep;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.TimeoutRecord;
import com.android.internal.util.RingBuffer;
import java.lang.ref.WeakReference;
@@ -122,19 +123,11 @@
public abstract int getUid(V obj);
/**
- * Return true if the feature is enabled. By default, the value is take from the Flags class
- * but it can be changed for local testing.
- */
- private static boolean anrTimerServiceEnabled() {
- return Flags.anrTimerService();
- }
-
- /**
* Return true if tracing is feature-enabled. This has no effect unless tracing is configured.
* Note that this does not represent any per-process overrides via an Injector.
*/
public static boolean traceFeatureEnabled() {
- return anrTimerServiceEnabled() && Flags.anrTimerTrace();
+ return Flags.anrTimerTrace();
}
/**
@@ -142,7 +135,7 @@
*/
static class Injector {
boolean serviceEnabled() {
- return AnrTimer.anrTimerServiceEnabled();
+ return true;
}
boolean traceEnabled() {
@@ -692,21 +685,18 @@
/**
* Accept the expired timer associated with arg. This indicates that the caller considers the
* timer expiration to be a true ANR. (See {@link #discard} for an alternate response.) The
- * function returns a {@link TimerLock} if an expired timer was found and null otherwise.
- * After this call, the timer does not exist. It is an error to accept a running timer,
- * however, the running timer will be canceled.
+ * function stores a {@link TimerLock} in the {@link TimeoutRecord} argument. The TimerLock
+ * records information about the expired timer for retrieval during ANR report generation.
+ * After this call, the timer does not exist.
*
- * If a non-null TimerLock is returned, the TimerLock must be closed before the target process
- * is dumped (for an ANR report) or continued.
- *
- * Note: the return value is always null if the feature is not enabled.
+ * It is a protocol error to accept a running timer, however, the running timer will be
+ * canceled.
*
* @param arg The key by which the timer is known. This is never examined or modified.
- * @return A TimerLock if an expired timer was accepted.
+ * @param timeoutRecord The TimeoutRecord that will hold information about the expired timer.
*/
- @Nullable
- public TimerLock accept(@NonNull V arg) {
- return mFeature.accept(arg);
+ public void accept(@NonNull V arg, @NonNull TimeoutRecord timeoutRecord) {
+ timeoutRecord.setExpiredTimer(mFeature.accept(arg));
}
/**
diff --git a/services/core/java/com/android/server/utils/flags.aconfig b/services/core/java/com/android/server/utils/flags.aconfig
index 0e9c732..961c53a 100644
--- a/services/core/java/com/android/server/utils/flags.aconfig
+++ b/services/core/java/com/android/server/utils/flags.aconfig
@@ -2,14 +2,6 @@
container: "system"
flag {
- name: "anr_timer_service"
- namespace: "system_performance"
- is_fixed_read_only: true
- description: "Feature flag for the ANR timer service"
- bug: "282428924"
-}
-
-flag {
name: "anr_timer_trace"
namespace: "system_performance"
is_fixed_read_only: true
diff --git a/services/core/java/com/android/server/vibrator/VibrationScaler.java b/services/core/java/com/android/server/vibrator/VibrationScaler.java
index b3862cc..2e1c527 100644
--- a/services/core/java/com/android/server/vibrator/VibrationScaler.java
+++ b/services/core/java/com/android/server/vibrator/VibrationScaler.java
@@ -17,7 +17,6 @@
package com.android.server.vibrator;
import android.annotation.NonNull;
-import android.hardware.vibrator.V1_0.EffectStrength;
import android.os.ExternalVibrationScale;
import android.os.VibrationAttributes;
import android.os.VibrationEffect;
@@ -236,12 +235,12 @@
/** Mapping of Vibrator.VIBRATION_INTENSITY_* values to {@link EffectStrength}. */
private static int intensityToEffectStrength(int intensity) {
return switch (intensity) {
- case Vibrator.VIBRATION_INTENSITY_LOW -> EffectStrength.LIGHT;
- case Vibrator.VIBRATION_INTENSITY_MEDIUM -> EffectStrength.MEDIUM;
- case Vibrator.VIBRATION_INTENSITY_HIGH -> EffectStrength.STRONG;
+ case Vibrator.VIBRATION_INTENSITY_LOW -> VibrationEffect.EFFECT_STRENGTH_LIGHT;
+ case Vibrator.VIBRATION_INTENSITY_MEDIUM -> VibrationEffect.EFFECT_STRENGTH_MEDIUM;
+ case Vibrator.VIBRATION_INTENSITY_HIGH -> VibrationEffect.EFFECT_STRENGTH_STRONG;
default -> {
Slog.w(TAG, "Got unexpected vibration intensity: " + intensity);
- yield EffectStrength.STRONG;
+ yield VibrationEffect.EFFECT_STRENGTH_STRONG;
}
};
}
diff --git a/services/core/java/com/android/server/wm/AbsAppSnapshotController.java b/services/core/java/com/android/server/wm/AbsAppSnapshotController.java
index ee173a2..0779332 100644
--- a/services/core/java/com/android/server/wm/AbsAppSnapshotController.java
+++ b/services/core/java/com/android/server/wm/AbsAppSnapshotController.java
@@ -165,14 +165,8 @@
switch (getSnapshotMode(source)) {
case SNAPSHOT_MODE_APP_THEME:
Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "drawAppThemeSnapshot");
- if (Flags.excludeDrawingAppThemeSnapshotFromLock()) {
- if (allowAppTheme) {
- supplier.setSupplier(drawAppThemeSnapshot(source));
- }
- } else {
- final Supplier<TaskSnapshot> original = drawAppThemeSnapshot(source);
- final TaskSnapshot snapshot = original != null ? original.get() : null;
- supplier.setSnapshot(snapshot);
+ if (allowAppTheme) {
+ supplier.setSupplier(drawAppThemeSnapshot(source));
}
Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
break;
@@ -325,7 +319,7 @@
@VisibleForTesting
@Nullable
Rect prepareTaskSnapshot(TYPE source, TaskSnapshot.Builder builder) {
- final Pair<ActivityRecord, WindowState> result = checkIfReadyToSnapshot(source);
+ final Pair<ActivityRecord, WindowState> result = checkIfReadyToScreenshot(source);
if (result == null) {
return null;
}
@@ -385,14 +379,14 @@
}
/**
- * Check if the state of the Task is appropriate to capture a snapshot, such like the task
- * snapshot or the associated IME surface snapshot.
+ * Check if the state of the Task is appropriate to capture a screenshot, such like the task
+ * snapshot or the associated IME screenshot.
*
- * @param source the target object to capture the snapshot
+ * @param source the target object to capture the screenshot
* @return Pair of (the top activity of the task, the main window of the task) if passed the
- * state checking. Returns {@code null} if the task state isn't ready to snapshot.
+ * state checking. Returns {@code null} if the task state isn't ready to screenshot.
*/
- Pair<ActivityRecord, WindowState> checkIfReadyToSnapshot(TYPE source) {
+ Pair<ActivityRecord, WindowState> checkIfReadyToScreenshot(TYPE source) {
if (!mService.mPolicy.isScreenOn()) {
if (DEBUG_SCREENSHOT) {
Slog.i(TAG_WM, "Attempted to take screenshot while display was off.");
@@ -525,10 +519,6 @@
mCache.onAppDied(activity);
}
- boolean isAnimatingByRecents(@NonNull Task task) {
- return task.isAnimatingByRecents();
- }
-
void dump(PrintWriter pw, String prefix) {
pw.println(prefix + "mHighResSnapshotScale=" + mHighResSnapshotScale);
pw.println(prefix + "mSnapshotEnabled=" + mSnapshotEnabled);
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index f243d4f..8fac2a4 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -76,6 +76,7 @@
import android.view.Display;
import android.view.MagnificationSpec;
import android.view.Surface;
+import android.view.View;
import android.view.ViewConfiguration;
import android.view.WindowManager;
import android.view.WindowManager.TransitionFlags;
@@ -1365,7 +1366,8 @@
return mCallbacksDispatcher != null;
}
- public void onRectangleOnScreenRequested(int displayId, Rect rectangle) {
+ public void onRectangleOnScreenRequested(int displayId, Rect rectangle,
+ @View.RectangleOnScreenRequestSource int source) {
if (isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
logTrace(
TAG + ".onRectangleOnScreenRequested",
@@ -1373,7 +1375,7 @@
"rectangle={" + rectangle + "}");
}
if (mCallbacksDispatcher != null) {
- mCallbacksDispatcher.onRectangleOnScreenRequested(displayId, rectangle);
+ mCallbacksDispatcher.onRectangleOnScreenRequested(displayId, rectangle, source);
}
}
@@ -1399,7 +1401,8 @@
mHandler = new Handler(looper);
}
- void onRectangleOnScreenRequested(int displayId, Rect rectangle) {
+ void onRectangleOnScreenRequested(int displayId, Rect rectangle,
+ @View.RectangleOnScreenRequestSource int source) {
if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
mAccessibilityTracing.logTrace(LOG_TAG + ".onRectangleOnScreenRequested",
FLAGS_MAGNIFICATION_CALLBACK, "rectangle={" + rectangle + "}");
@@ -1409,7 +1412,7 @@
}
final Message m = PooledLambda.obtainMessage(
mCallbacks::onRectangleOnScreenRequested, displayId, rectangle.left,
- rectangle.top, rectangle.right, rectangle.bottom);
+ rectangle.top, rectangle.right, rectangle.bottom, source);
mHandler.sendMessage(m);
}
}
diff --git a/services/core/java/com/android/server/wm/ActionChain.java b/services/core/java/com/android/server/wm/ActionChain.java
index d63044a..f60b6f3 100644
--- a/services/core/java/com/android/server/wm/ActionChain.java
+++ b/services/core/java/com/android/server/wm/ActionChain.java
@@ -23,6 +23,8 @@
import com.android.window.flags.Flags;
+import java.util.ArrayList;
+
/**
* Represents a chain of WM actions where each action is "caused by" the prior action (except the
* first one of course). A whole chain is associated with one Transition (in fact, the purpose
@@ -102,7 +104,7 @@
/** The transition that this chain's changes belong to. */
@Nullable
- Transition mTransition;
+ private Transition mTransition;
/** The previous action in the chain. */
@Nullable
@@ -124,37 +126,68 @@
}
}
- private Transition getTransition() {
+ void attachTransition(Transition transit) {
+ if (mTransition != null) {
+ throw new IllegalStateException("can't attach transition to chain that is already"
+ + " attached to a transition");
+ }
+ if (mPrevious != null) {
+ throw new IllegalStateException("Can only attach transition to the head of a chain");
+ }
+ mTransition = transit;
+ if (mTransition != null) {
+ mTransition.recordChain(this);
+ }
+ }
+
+ @Nullable
+ Transition getTransition() {
if (!Flags.transitTrackerPlumbing()) {
- return mTmpAtm.getTransitionController().getCollectingTransition();
+ return isFinishing() ? mTransition
+ : mTmpAtm.getTransitionController().getCollectingTransition();
}
return mTransition;
}
+ void detachTransition() {
+ mTransition = null;
+ }
+
boolean isFinishing() {
return mType == TYPE_FINISH;
}
+ boolean isCollecting() {
+ final Transition transition = getTransition();
+ return transition != null && transition.isCollecting();
+ }
+
+ /** Returns {@code true} if the display contains a collecting transition. */
+ boolean isCollectingOnDisplay(@NonNull DisplayContent dc) {
+ return isCollecting() && getTransition().isOnDisplay(dc);
+ }
+
/**
* Some common checks to determine (and report) whether this chain has a collecting transition.
+ * Returns the collecting transition or {@code null} if there is an issue.
*/
- private boolean expectCollecting() {
+ private Transition expectCollecting() {
final Transition transition = getTransition();
if (transition == null) {
Slog.e(TAG, "Can't collect into a chain with no transition");
- return false;
+ return null;
}
if (isFinishing()) {
Slog.e(TAG, "Trying to collect into a finished transition");
- return false;
+ return null;
}
if (transition.mController.getCollectingTransition() != mTransition) {
Slog.e(TAG, "Mismatch between current collecting ("
+ transition.mController.getCollectingTransition() + ") and chain ("
+ transition + ")");
- return false;
+ return null;
}
- return true;
+ return transition;
}
/**
@@ -163,8 +196,32 @@
*/
void collect(@NonNull WindowContainer wc) {
if (!wc.mTransitionController.isShellTransitionsEnabled()) return;
- if (!expectCollecting()) return;
- getTransition().collect(wc);
+ final Transition transition = expectCollecting();
+ if (transition == null) return;
+ transition.collect(wc);
+ }
+
+ /**
+ * Collects a window container which will be removed or invisible.
+ */
+ void collectClose(@NonNull WindowContainer<?> wc) {
+ if (!wc.mTransitionController.isShellTransitionsEnabled()) return;
+ final Transition transition = expectCollecting();
+ if (wc.isVisibleRequested()) {
+ transition.collectExistenceChange(wc);
+ } else {
+ // Removing a non-visible window doesn't require a transition, but if there is one
+ // collecting, this should be a member just in case.
+ collect(wc);
+ }
+ }
+
+ private static class AsyncStart {
+ final int mStackPos;
+ long mThreadId;
+ AsyncStart(int stackPos) {
+ mStackPos = stackPos;
+ }
}
/**
@@ -173,16 +230,50 @@
static class Tracker {
private final ActivityTaskManagerService mAtm;
+ /**
+ * Track the current stack of nested chain entries within a synchronous operation. Chains
+ * can nest when some entry-points are, themselves, used within the logic of another
+ * entry-point.
+ */
+ private final ArrayList<ActionChain> mStack = new ArrayList<>();
+
+ /** thread-id of the current action. Used to detect mismatched start/end situations. */
+ private long mCurrentThread;
+
+ /** Stack of suspended actions for dealing with async-start "gaps". */
+ private final ArrayList<AsyncStart> mAsyncStarts = new ArrayList<>();
+
Tracker(ActivityTaskManagerService atm) {
mAtm = atm;
}
private ActionChain makeChain(String source, @LinkType int type, Transition transit) {
- final ActionChain out = new ActionChain(source, type, transit);
- if (!Flags.transitTrackerPlumbing()) {
- out.mTmpAtm = mAtm;
+ int base = getThreadBase();
+ if (base < mStack.size()) {
+ // verify thread-id matches. This isn't a perfect check, but it should be
+ // reasonably effective at detecting imbalance.
+ long expectedThread = mAsyncStarts.isEmpty() ? mCurrentThread
+ : mAsyncStarts.getLast().mThreadId;
+ if (Thread.currentThread().getId() != expectedThread) {
+ // This means something went wrong. Reset the stack.
+ String msg = "Likely improperly balanced ActionChain: ["
+ + mStack.get(base).mSource;
+ for (int i = (base + 1); i < mStack.size(); ++i) {
+ msg += ", " + mStack.get(i).mSource;
+ }
+ Slog.wtfStack(TAG, msg + "]");
+ mStack.subList(base, mStack.size()).clear();
+ }
+ } else if (!mAsyncStarts.isEmpty()) {
+ mAsyncStarts.getLast().mThreadId = Thread.currentThread().getId();
+ } else {
+ mCurrentThread = Thread.currentThread().getId();
}
- return out;
+ mStack.add(new ActionChain(source, type, transit));
+ if (!Flags.transitTrackerPlumbing()) {
+ mStack.getLast().mTmpAtm = mAtm;
+ }
+ return mStack.getLast();
}
private ActionChain makeChain(String source, @LinkType int type) {
@@ -191,6 +282,83 @@
}
/**
+ * async start is the one "gap" where normally-contained actions can "interrupt"
+ * an ongoing one, so detect/handle those specially.
+ */
+ private int getThreadBase() {
+ if (mAsyncStarts.isEmpty()) return 0;
+ return mAsyncStarts.getLast().mStackPos;
+ }
+
+ /**
+ * There are some complicated call-paths through WM which are unnecessarily messy to plumb
+ * through or which travel out of the WMS/ATMS domain (eg. into policy). For these cases,
+ * we assume that as long as we still have a synchronous call stack, the same initial
+ * action should apply. This means we can use a stack of "nesting" chains to associate
+ * deep call-paths with their shallower counterparts.
+ *
+ * Starting a chain will push onto the stack, calling {@link #endPartial} will pop off the
+ * stack, and calling `end` here will *clear* the stack.
+ *
+ * Unlike {@link #endPartial}, this `end` call is for closing a top-level session. It will
+ * error if its associated start/end are, themselves, nested. This is used as a safety
+ * measure to catch cases where a start is missing a corresponding end.
+ *
+ * @see #endPartial
+ */
+ void end() {
+ int base = getThreadBase();
+ if (mStack.size() > (base + 1)) {
+ String msg = "Improperly balanced ActionChain: [" + mStack.get(base).mSource;
+ for (int i = (base + 1); i < mStack.size(); ++i) {
+ msg += ", " + mStack.get(i).mSource;
+ }
+ Slog.wtfStack(TAG, msg + "]");
+ }
+ mStack.subList(base, mStack.size()).clear();
+ }
+
+ /**
+ * Like {@link #end} except it just simply pops without checking if it is a root-level
+ * session. This should only be used when there's a chance that the associated start/end
+ * will, itself, be nested.
+ *
+ * @see #end
+ */
+ void endPartial() {
+ if (mStack.isEmpty()) {
+ Slog.wtfStack(TAG, "Trying to double-close action-chain");
+ return;
+ }
+ mStack.removeLast();
+ }
+
+ /**
+ * Temporary query. Eventually anything that needs to check this should have its own chain
+ * link.
+ */
+ boolean isInChain() {
+ return !mStack.isEmpty();
+ }
+
+ /**
+ * Special handling during "gaps" in atomicity while using the async-start hack. The
+ * "end" tracking needs to account for this and we also want to track/report how often
+ * this happens.
+ */
+ void pushAsyncStart() {
+ if (mStack.isEmpty()) {
+ Slog.wtfStack(TAG, "AsyncStart outside of chain!?");
+ return;
+ }
+ mAsyncStarts.add(new AsyncStart(mStack.size()));
+ }
+
+ void popAsyncStart() {
+ mAsyncStarts.removeLast();
+ }
+
+ /**
* Starts tracking a normal action.
* @see #TYPE_NORMAL
*/
@@ -206,6 +374,15 @@
}
/**
+ * Create a chain-link for a decision-point between making a new transition or using the
+ * global collecting one.
+ */
+ @NonNull
+ ActionChain startTransit(String source) {
+ return makeChain(source, TYPE_DEFAULT);
+ }
+
+ /**
* Starts tracking an action that finishes a transition.
* @see #TYPE_NORMAL
*/
diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java
index fd4e38e..06d5a47 100644
--- a/services/core/java/com/android/server/wm/ActivityClientController.java
+++ b/services/core/java/com/android/server/wm/ActivityClientController.java
@@ -858,13 +858,17 @@
if (r == null) {
return false;
}
+ final ActionChain chain = mService.mChainTracker.startTransit("fromTransluce");
// Create a transition if the activity is playing in case the below activity didn't
// commit invisible. That's because if any activity below this one has changed its
// visibility while playing transition, there won't able to commit visibility until
// the running transition finish.
final Transition transition = r.mTransitionController.isShellTransitionsEnabled()
- && !r.mTransitionController.isCollecting()
+ && !chain.isCollecting()
? r.mTransitionController.createTransition(TRANSIT_TO_BACK) : null;
+ if (transition != null) {
+ chain.attachTransition(transition);
+ }
final boolean changed = r.setOccludesParent(true);
if (transition != null) {
if (changed) {
@@ -880,6 +884,7 @@
transition.abort();
}
}
+ mService.mChainTracker.end();
return changed;
}
} finally {
@@ -904,10 +909,14 @@
if (under != null) {
under.returningOptions = safeOptions != null ? safeOptions.getOptions(r) : null;
}
+ final ActionChain chain = mService.mChainTracker.startTransit("toTransluce");
// Create a transition to make sure the activity change is collected.
final Transition transition = r.mTransitionController.isShellTransitionsEnabled()
- && !r.mTransitionController.isCollecting()
+ && !chain.isCollecting()
? r.mTransitionController.createTransition(TRANSIT_TO_FRONT) : null;
+ if (transition != null) {
+ chain.attachTransition(transition);
+ }
final boolean changed = r.setOccludesParent(false);
if (transition != null) {
if (changed) {
@@ -927,6 +936,7 @@
transition.abort();
}
}
+ mService.mChainTracker.end();
return changed;
}
} finally {
@@ -1270,12 +1280,14 @@
transition.abort();
return;
}
+ final ActionChain chain = mService.mChainTracker.start("reqMWFS", transition);
final Task requestingTask = r.getTask();
- transition.collect(requestingTask);
+ chain.collect(requestingTask);
executeMultiWindowFullscreenRequest(fullscreenRequest, requestingTask);
r.mTransitionController.requestStartTransition(transition, requestingTask,
null /* remoteTransition */, null /* displayChange */);
transition.setReady(requestingTask, true);
+ mService.mChainTracker.end();
}
private static void reportMultiwindowFullscreenRequestValidatingResult(IRemoteCallback callback,
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 034a3a0..ce24c17 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -145,7 +145,6 @@
import static com.android.server.wm.ActivityRecordProto.ALL_DRAWN;
import static com.android.server.wm.ActivityRecordProto.APP_STOPPED;
import static com.android.server.wm.ActivityRecordProto.CLIENT_VISIBLE;
-import static com.android.server.wm.ActivityRecordProto.DEFER_HIDING_CLIENT;
import static com.android.server.wm.ActivityRecordProto.ENABLE_RECENTS_SCREENSHOT;
import static com.android.server.wm.ActivityRecordProto.FILLS_PARENT;
import static com.android.server.wm.ActivityRecordProto.FRONT_OF_TASK;
@@ -227,7 +226,6 @@
import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
import static com.android.server.wm.WindowContainerChildProto.ACTIVITY;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_CONFIGURATION;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT_REPEATS;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW_VERBOSE;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
@@ -307,7 +305,6 @@
import android.service.contentcapture.ActivityEvent;
import android.service.dreams.DreamActivity;
import android.service.voice.IVoiceInteractionSession;
-import android.util.ArraySet;
import android.util.EventLog;
import android.util.Log;
import android.util.MergedConfiguration;
@@ -363,8 +360,6 @@
import dalvik.annotation.optimization.NeverCompile;
-import com.google.android.collect.Sets;
-
import org.xmlpull.v1.XmlPullParserException;
import java.io.File;
@@ -528,11 +523,6 @@
// True if the visible state of this token was forced to true due to a transferred starting
// window.
private boolean mVisibleSetFromTransferredStartingWindow;
- // TODO: figure out how to consolidate with the same variable in ActivityRecord.
- private boolean mDeferHidingClient; // If true we told WM to defer reporting to the client
- // process that it is hidden.
- private boolean mLastDeferHidingClient; // If true we will defer setting mClientVisible to false
- // and reporting to the client that it is hidden.
boolean nowVisible; // is this activity's window visible?
boolean mClientVisibilityDeferred;// was the visibility change message to client deferred?
boolean idle; // has the activity gone idle?
@@ -1131,7 +1121,6 @@
pw.println(ActivityInfo.screenOrientationToString(super.getOverrideOrientation()));
pw.println(prefix + "mVisibleRequested=" + mVisibleRequested
+ " mVisible=" + mVisible + " mClientVisible=" + isClientVisible()
- + ((mDeferHidingClient) ? " mDeferHidingClient=" + mDeferHidingClient : "")
+ " reportedDrawn=" + mReportedDrawn + " reportedVisible=" + reportedVisible);
if (paused) {
pw.print(prefix); pw.print("paused="); pw.println(paused);
@@ -1172,9 +1161,6 @@
else TimeUtils.formatDuration(lastVisibleTime, now, pw);
pw.println();
}
- if (mDeferHidingClient) {
- pw.println(prefix + "mDeferHidingClient=" + mDeferHidingClient);
- }
if (mServiceConnectionsHolder != null) {
pw.print(prefix); pw.print("connections="); pw.println(mServiceConnectionsHolder);
}
@@ -1716,11 +1702,6 @@
return mAppCompatController.getLetterboxOverrides().hasWallpaperBackgroundForLetterbox();
}
- void updateLetterboxSurfaceIfNeeded(WindowState winHint, Transaction t) {
- mAppCompatController.getLetterboxPolicy()
- .updateLetterboxSurfaceIfNeeded(winHint, t, getPendingTransaction());
- }
-
void updateLetterboxSurfaceIfNeeded(WindowState winHint) {
mAppCompatController.getLetterboxPolicy().updateLetterboxSurfaceIfNeeded(winHint);
}
@@ -3574,6 +3555,7 @@
// root task is not visible if it only contains finishing activities.
&& mRootWindowContainer.isTopDisplayFocusedRootTask(rootTask);
+ final ActionChain chain = mAtmService.mChainTracker.startTransit("AR.finish");
mAtmService.deferWindowLayout();
try {
mTaskSupervisor.mNoHistoryActivities.remove(this);
@@ -3598,12 +3580,14 @@
final boolean endTask = task.getTopNonFinishingActivity() == null
&& !task.isClearingToReuseTask();
final WindowContainer<?> trigger = endTask ? task : this;
- final Transition newTransition =
- mTransitionController.requestCloseTransitionIfNeeded(trigger);
- final Transition transition = newTransition != null
- ? newTransition : mTransitionController.getCollectingTransition();
- if (transition != null) {
- transition.collectClose(trigger);
+ Transition newTransition = null;
+ if (!chain.isCollecting()) {
+ chain.attachTransition(
+ mTransitionController.requestCloseTransitionIfNeeded(trigger));
+ newTransition = chain.getTransition();
+ }
+ if (chain.isCollecting()) {
+ chain.getTransition().collectClose(trigger);
}
// We are finishing the top focused activity and its task has nothing to be focused so
// the next focusable task should be focused.
@@ -3628,24 +3612,11 @@
Slog.v(TAG_TRANSITION, "Prepare close transition: finishing " + this);
}
- // When finishing the activity preemptively take the snapshot before the app window
- // is marked as hidden and any configuration changes take place
- // Note that RecentsAnimation will handle task snapshot while switching apps with
- // the best capture timing (e.g. IME window capture),
- // No need additional task capture while task is controlled by RecentsAnimation.
- if (!mTransitionController.isShellTransitionsEnabled()
- && !task.isAnimatingByRecents()) {
- final ArraySet<Task> tasks = Sets.newArraySet(task);
- mAtmService.mWindowManager.mTaskSnapshotController.snapshotTasks(tasks);
- mAtmService.mWindowManager.mTaskSnapshotController
- .addSkipClosingAppSnapshotTasks(tasks);
- }
-
// Tell window manager to prepare for this one to be removed.
setVisibility(false);
// Propagate the last IME visibility in the same task, so the IME can show
// automatically if the next activity has a focused editable view.
- if (mLastImeShown && mTransitionController.isShellTransitionsEnabled()) {
+ if (mLastImeShown) {
final ActivityRecord nextRunning = task.topRunningActivity();
if (nextRunning != null) {
nextRunning.mLastImeShown = true;
@@ -3706,6 +3677,7 @@
return FINISH_RESULT_REQUESTED;
} finally {
mAtmService.continueWindowLayout();
+ mAtmService.mChainTracker.endPartial();
}
}
@@ -3813,7 +3785,7 @@
}
if (isCurrentVisible) {
- if (isNextNotYetVisible || delayRemoval || (next != null && isInTransition())) {
+ if (isNextNotYetVisible || delayRemoval || (next != null && inTransition())) {
// Add this activity to the list of stopping activities. It will be processed and
// destroyed when the next activity reports idle.
addToStopping(false /* scheduleIdle */, false /* idleDelayed */,
@@ -4291,12 +4263,11 @@
// closing the task.
final WindowContainer trigger = remove && task != null && task.getChildCount() == 1
? task : this;
- final Transition tr = mTransitionController.requestCloseTransitionIfNeeded(trigger);
- if (tr != null) {
- tr.collectClose(trigger);
- } else if (mTransitionController.isCollecting()) {
- mTransitionController.getCollectingTransition().collectClose(trigger);
+ final ActionChain chain = mAtmService.mChainTracker.startTransit("appDied");
+ if (!chain.isCollecting()) {
+ chain.attachTransition(mTransitionController.requestCloseTransitionIfNeeded(trigger));
}
+ chain.collectClose(trigger);
cleanUp(true /* cleanServices */, true /* setState */);
if (remove) {
if (mStartingData != null && mVisible && task != null) {
@@ -4311,6 +4282,7 @@
}
removeFromHistory("appDied");
}
+ mAtmService.mChainTracker.end();
}
@Override
@@ -4467,16 +4439,6 @@
updateLetterboxSurfaceIfNeeded(child);
}
- void setAppLayoutChanges(int changes, String reason) {
- if (!mChildren.isEmpty()) {
- final DisplayContent dc = getDisplayContent();
- dc.pendingLayoutChanges |= changes;
- if (DEBUG_LAYOUT_REPEATS) {
- mWmService.mWindowPlacerLocked.debugLayoutRepeats(reason, dc.pendingLayoutChanges);
- }
- }
- }
-
/**
* Returns {@code true} if the requested orientation of this activity is the same as the
* resolved orientation of the from activity.
@@ -5273,30 +5235,6 @@
task.lastDescription = description;
}
- void setDeferHidingClient() {
- if (Flags.removeDeferHidingClient()) {
- return;
- }
- mDeferHidingClient = true;
- }
-
- void clearDeferHidingClient() {
- if (Flags.removeDeferHidingClient()) {
- return;
- }
- if (!mDeferHidingClient) return;
- mDeferHidingClient = false;
- if (!mVisibleRequested) {
- // Hiding the client is no longer deferred and the app isn't visible still, go ahead and
- // update the visibility.
- setVisibility(false);
- }
- }
-
- boolean getDeferHidingClient() {
- return mDeferHidingClient;
- }
-
boolean canAffectSystemUiFlags() {
final TaskFragment taskFragment = getTaskFragment();
return taskFragment != null && taskFragment.canAffectSystemUiFlags()
@@ -5367,22 +5305,17 @@
Slog.w(TAG_WM, "Attempted to set visibility of non-existing app token: " + token);
return;
}
- if (visible == mVisibleRequested && visible == mVisible && visible == isClientVisible()
- && mTransitionController.isShellTransitionsEnabled()) {
- // For shell transition, it is no-op if there is no state change.
+ if (visible == mVisibleRequested && visible == mVisible && visible == isClientVisible()) {
return;
}
- if (visible) {
- mDeferHidingClient = false;
- }
- setVisibility(visible, mDeferHidingClient);
+ setVisibilityInner(visible);
mAtmService.addWindowLayoutReasons(
ActivityTaskManagerService.LAYOUT_REASON_VISIBILITY_CHANGED);
mTaskSupervisor.getActivityMetricsLogger().notifyVisibilityChanged(this);
mTaskSupervisor.mAppVisibilitiesChangedSinceLastPause = true;
}
- private void setVisibility(boolean visible, boolean deferHidingClient) {
+ private void setVisibilityInner(boolean visible) {
// Don't set visibility to false if we were already not visible. This prevents WM from
// adding the app to the closing app list which doesn't make sense for something that is
// already not visible. However, set visibility to true even if we are already visible.
@@ -5391,13 +5324,6 @@
// TODO: Probably a good idea to separate the concept of opening/closing apps from the
// concept of setting visibility...
if (!visible && !mVisibleRequested) {
-
- if (!deferHidingClient && mLastDeferHidingClient) {
- // We previously deferred telling the client to hide itself when visibility was
- // initially set to false. Now we would like it to hide, so go ahead and set it.
- mLastDeferHidingClient = deferHidingClient;
- setClientVisible(false);
- }
return;
}
@@ -5435,7 +5361,6 @@
mAtmService.mBackNavigationController.onAppVisibilityChanged(this, visible);
setVisibleRequested(visible);
- mLastDeferHidingClient = deferHidingClient;
if (!visible) {
// Because starting window was transferred, this activity may be a trampoline which has
@@ -5574,6 +5499,7 @@
mWmService.mActivityManagerAppTransitionNotifier.onAppTransitionFinishedLocked(
token);
}
+ mWmService.mAnimator.addSurfaceVisibilityUpdateIncludingAnimatableParents(this);
}
}
@@ -5990,15 +5916,6 @@
try {
final boolean canEnterPictureInPicture = checkEnterPictureInPictureState(
"makeInvisible", true /* beforeStopping */);
- // Defer telling the client it is hidden if it can enter Pip and isn't current paused,
- // stopped or stopping. This gives it a chance to enter Pip in onPause().
- final boolean deferHidingClient = canEnterPictureInPicture
- && !isState(STARTED, STOPPING, STOPPED, PAUSED);
- if (deferHidingClient) {
- setDeferHidingClient();
- } else {
- clearDeferHidingClient();
- }
setVisibility(false);
switch (mState) {
@@ -6643,16 +6560,6 @@
return mReportedDrawn;
}
- @Override
- void setClientVisible(boolean clientVisible) {
- if (!Flags.removeDeferHidingClient()) {
- // TODO(shell-transitions): Remove mDeferHidingClient once everything is
- // shell-transitions. pip activities should just remain in clientVisible.
- if (!clientVisible && mDeferHidingClient) return;
- }
- super.setClientVisible(clientVisible);
- }
-
/**
* Updated this app token tracking states for interesting and drawn windows based on the window.
*
@@ -7221,6 +7128,15 @@
@Override
void prepareSurfaces() {
+ if (mWmService.mFlags.mEnsureSurfaceVisibility) {
+ // Input sink surface is not a part of animation, so apply in a steady state
+ // (non-sync) with pending transaction.
+ if (mVisible && mSyncState == SYNC_STATE_NONE) {
+ mActivityRecordInputSink.applyChangesToSurfaceIfChanged(getPendingTransaction());
+ }
+ super.prepareSurfaces();
+ return;
+ }
final boolean isDecorSurfaceBoosted =
getTask() != null && getTask().isDecorSurfaceBoosted();
final boolean show = (isVisible()
@@ -7246,6 +7162,15 @@
super.prepareSurfaces();
}
+ @Override
+ void updateSurfaceVisibility(Transaction t) {
+ final boolean visible = mVisible
+ // Ensure that the activity content is hidden when the decor surface is boosted to
+ // prevent UI redressing attack.
+ && (task == null || !task.isDecorSurfaceBoosted());
+ t.setVisibility(mSurfaceControl, visible);
+ }
+
/**
* @return Whether our {@link #getSurfaceControl} is currently showing.
*/
@@ -7583,6 +7508,9 @@
@Override
void resolveOverrideConfiguration(Configuration newParentConfiguration) {
+ final AppCompatSizeCompatModePolicy scmPolicy =
+ mAppCompatController.getSizeCompatModePolicy();
+ scmPolicy.clearSizeCompatModeIfNeededOnResolveOverrideConfiguration();
final Configuration requestedOverrideConfig = getRequestedOverrideConfiguration();
if (requestedOverrideConfig.assetsSeq != ASSETS_SEQ_UNDEFINED
&& newParentConfiguration.assetsSeq > requestedOverrideConfig.assetsSeq) {
@@ -7640,8 +7568,6 @@
// Don't apply aspect ratio if app is overridden to fullscreen by device user/manufacturer.
aspectRatioPolicy.resolveAspectRatioRestrictionIfNeeded(newParentConfiguration);
final AppCompatDisplayInsets appCompatDisplayInsets = getAppCompatDisplayInsets();
- final AppCompatSizeCompatModePolicy scmPolicy =
- mAppCompatController.getSizeCompatModePolicy();
if (appCompatDisplayInsets != null) {
scmPolicy.resolveSizeCompatModeConfiguration(newParentConfiguration,
appCompatDisplayInsets, mTmpBounds);
@@ -7951,10 +7877,6 @@
}
}
- boolean isInTransition() {
- return inTransitionSelfOrParent();
- }
-
/**
* In some cases, applying insets to bounds changes the orientation. For example, if a
* close-to-square display rotates to portrait to respect a portrait orientation activity, after
@@ -8205,22 +8127,6 @@
requestsTmpConfig, gender, mAtmService.mGrammaticalManagerInternal, getUid());
}
- @VisibleForTesting
- @Override
- Rect getAnimationBounds(int appRootTaskClipMode) {
- // Use TaskFragment-bounds if available so that activity-level letterbox (maxAspectRatio) is
- // included in the animation.
- final TaskFragment taskFragment = getTaskFragment();
- return taskFragment != null ? taskFragment.getBounds() : getBounds();
- }
-
- @Override
- void getAnimationPosition(Point outPosition) {
- // Always animate from zero because if the activity doesn't fill the task, the letterbox
- // will fill the remaining area that should be included in the animation.
- outPosition.set(0, 0);
- }
-
@Override
public void onConfigurationChanged(Configuration newParentConfig) {
// We want to collect the ActivityRecord if the windowing mode is changed, so that it will
@@ -8864,13 +8770,16 @@
transition.abort();
return;
}
+ final ActionChain chain = mAtmService.mChainTracker.start(
+ "restartProc", transition);
// Request invisible so there will be a change after the activity is restarted
// to be visible.
setVisibleRequested(false);
- transition.collect(this);
+ chain.collect(this);
mTransitionController.requestStartTransition(transition, task,
null /* remoteTransition */, null /* displayChange */);
scheduleStopForRestartProcess();
+ mAtmService.mChainTracker.end();
});
} else {
scheduleStopForRestartProcess();
@@ -9225,7 +9134,6 @@
proto.write(VISIBLE, mVisible);
proto.write(VISIBLE_REQUESTED, mVisibleRequested);
proto.write(CLIENT_VISIBLE, isClientVisible());
- proto.write(DEFER_HIDING_CLIENT, mDeferHidingClient);
proto.write(REPORTED_DRAWN, mReportedDrawn);
proto.write(REPORTED_VISIBLE, reportedVisible);
proto.write(NUM_INTERESTING_WINDOWS, mNumInterestingWindows);
@@ -9495,8 +9403,14 @@
}
boolean canCaptureSnapshot() {
- if (!isSurfaceShowing() || findMainWindow() == null) {
- return false;
+ if (mWmService.mFlags.mEnsureSurfaceVisibility) {
+ if (!mVisible) {
+ return false;
+ }
+ } else {
+ if (!isSurfaceShowing() || findMainWindow() == null) {
+ return false;
+ }
}
return forAllWindows(
// Ensure at least one window for the top app is visible before attempting to
diff --git a/services/core/java/com/android/server/wm/ActivityRecordInputSink.java b/services/core/java/com/android/server/wm/ActivityRecordInputSink.java
index ea6506a..a300a24 100644
--- a/services/core/java/com/android/server/wm/ActivityRecordInputSink.java
+++ b/services/core/java/com/android/server/wm/ActivityRecordInputSink.java
@@ -116,7 +116,7 @@
final boolean allowPassthrough = activityBelowInTask != null && (
activityBelowInTask.mAllowedTouchUid == mActivityRecord.getUid()
|| activityBelowInTask.isUid(mActivityRecord.getUid()));
- if (allowPassthrough || !mIsCompatEnabled || mActivityRecord.isInTransition()
+ if (allowPassthrough || !mIsCompatEnabled || mActivityRecord.inTransition()
|| !mActivityRecord.mActivityRecordInputSinkEnabled) {
// Set to non-touchable, so the touch events can pass through.
mInputWindowHandleWrapper.setInputConfigMasked(InputConfig.NOT_TOUCHABLE,
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index f2ef60d..d565225 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1508,21 +1508,26 @@
mService.resumeAppSwitches();
}
- // Only do the create here since startActivityInner can abort. If it doesn't abort,
- // the requestStart will be sent in handleStartRequest.
- final Transition newTransition = r.mTransitionController.isShellTransitionsEnabled()
- ? r.mTransitionController.createAndStartCollecting(TRANSIT_OPEN) : null;
+ final ActionChain chain = mService.mChainTracker.startTransit("startAct");
// Because startActivity must run immediately, it can get combined with another
// transition meaning it is no-longer independent. This is NOT desirable, but is the
// only option for the time being.
- final boolean isIndependent = newTransition != null;
- final Transition transition = isIndependent ? newTransition
- : mService.getTransitionController().getCollectingTransition();
+ boolean isIndependent = false;
+ if (!chain.isCollecting()) {
+ // Only do the create here since startActivityInner can abort. If it doesn't abort,
+ // the requestStart will be sent in handleStartRequest.
+ chain.attachTransition(r.mTransitionController.createAndStartCollecting(TRANSIT_OPEN));
+ isIndependent = chain.getTransition() != null;
+ }
mLastStartActivityResult = startActivityUnchecked(r, sourceRecord, voiceSession,
request.voiceInteractor, startFlags, checkedOptions,
- inTask, inTaskFragment, balVerdict, intentGrants, realCallingUid, transition,
- isIndependent);
+ inTask, inTaskFragment, balVerdict, intentGrants, realCallingUid,
+ chain.getTransition(), isIndependent);
+
+ // Because the pending-intent usage in the waitAsyncStart hack "exits" ATMS into
+ // AMS and re-enters, this can be nested.
+ mService.mChainTracker.endPartial();
if (request.outActivity != null) {
request.outActivity[0] = mLastStartActivityRecord;
@@ -3067,11 +3072,9 @@
}
}
- if (com.android.window.flags.Flags.fixLayoutExistingTask()) {
- // Layout the task to ensure the Task is in correct bounds.
- mSupervisor.getLaunchParamsController().layoutTask(intentTask,
- mStartActivity.info.windowLayout, mStartActivity, mSourceRecord, mOptions);
- }
+ // Layout the task to ensure the Task is in correct bounds.
+ mSupervisor.getLaunchParamsController().layoutTask(intentTask,
+ mStartActivity.info.windowLayout, mStartActivity, mSourceRecord, mOptions);
// If the target task is not in the front, then we need to bring it to the front.
final boolean differentTopTask;
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index 7123a7c..21b730e 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -803,10 +803,4 @@
/** Returns whether assist data is allowed. */
public abstract boolean isAssistDataAllowed();
-
- /**
- * Delegate back gesture request from shell.
- * Returns true if the back gesture request was successful, false otherwise.
- */
- public abstract boolean requestBackGesture();
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index bd2b88a..15aa40a 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -1902,18 +1902,6 @@
}
}
- @Override
- public void registerBackGestureDelegate(RemoteCallback requestObserver) {
- mAmInternal.enforceCallingPermission(START_TASKS_FROM_RECENTS,
- "registerBackGestureDelegate()");
- final long origId = Binder.clearCallingIdentity();
- try {
- mBackNavigationController.registerBackGestureDelegate(requestObserver);
- } finally {
- Binder.restoreCallingIdentity(origId);
- }
- }
-
/**
* Public API to check if the client is allowed to start an activity on specified display.
*
@@ -2112,10 +2100,12 @@
setLastResumedActivityUncheckLocked(r, "setFocusedTask-alreadyTop");
return;
}
- final Transition transition = (getTransitionController().isCollecting()
- || !getTransitionController().isShellTransitionsEnabled()) ? null
- : getTransitionController().createTransition(TRANSIT_TO_FRONT);
+ final ActionChain chain = mChainTracker.startTransit("setFocusedTask");
+ final Transition transition = (!chain.isCollecting()
+ && getTransitionController().isShellTransitionsEnabled())
+ ? getTransitionController().createTransition(TRANSIT_TO_FRONT) : null;
if (transition != null) {
+ chain.attachTransition(transition);
// Set ready before doing anything. If order does change, then that will set it unready
// so that we wait for the new lifecycles to complete.
transition.setReady(task, true /* ready */);
@@ -2137,6 +2127,7 @@
true /* updateInputWindows */);
}
}
+ mChainTracker.end();
if (transition != null && !movedToTop) {
// No order changes and focus-changes, alone, aren't captured in transitions.
transition.abort();
@@ -2993,11 +2984,13 @@
transition.abort();
return;
}
+ final ActionChain chain = mChainTracker.start("resizeTask", transition);
getTransitionController().requestStartTransition(transition, task,
null /* remoteTransition */, null /* displayChange */);
- transition.collect(task);
+ chain.collect(task);
task.resize(bounds, resizeMode, preserveWindow);
transition.setReady(task, true);
+ mChainTracker.end();
});
}
} finally {
@@ -3859,8 +3852,13 @@
}
getTransitionController().startCollectOrQueue(enterPipTransition, (deferred) -> {
+ mChainTracker.start("enterPip2", enterPipTransition);
+ // Collecting PiP activity explicitly to avoid stopping PiP activity while Shell
+ // handles the request; see task supervisor's processStoppingAndFinishingActivities.
+ enterPipTransition.collect(r);
getTransitionController().requestStartTransition(enterPipTransition,
r.getTask(), null /* remoteTransition */, null /* displayChange */);
+ mChainTracker.end();
});
return true;
}
@@ -3900,6 +3898,7 @@
r.mAutoEnteringPip = isAutoEnter;
if (transition != null) {
+ mChainTracker.start("enterPip1", transition);
mRootWindowContainer.moveActivityToPinnedRootTaskAndRequestStart(r,
"enterPictureInPictureMode");
} else if (getTransitionController().isCollecting()
@@ -3921,6 +3920,9 @@
"auto-pip");
}
r.mAutoEnteringPip = false;
+ if (transition != null) {
+ mChainTracker.end();
+ }
}
};
@@ -7594,11 +7596,6 @@
public boolean isAssistDataAllowed() {
return ActivityTaskManagerService.this.isAssistDataAllowed();
}
-
- @Override
- public boolean requestBackGesture() {
- return mBackNavigationController.requestBackGesture();
- }
}
/** Cache the return value for {@link #isPip2ExperimentEnabled()} */
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index 05b109c..8a61cfc 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -1567,6 +1567,7 @@
return;
}
+ final ActionChain chain = mService.mChainTracker.startTransit("findTaskToFront");
try {
// We allow enter PiP for previous front task if not requested otherwise via options.
boolean shouldCauseEnterPip = options == null
@@ -1577,12 +1578,12 @@
mService.deferWindowLayout();
boolean newTransition = false;
- Transition transition = task.mTransitionController.getCollectingTransition();
- if (transition == null && task.mTransitionController.isShellTransitionsEnabled()) {
- transition = task.mTransitionController.createTransition(TRANSIT_TO_FRONT);
+ if (!chain.isCollecting() && task.mTransitionController.isShellTransitionsEnabled()) {
+ chain.attachTransition(
+ task.mTransitionController.createTransition(TRANSIT_TO_FRONT));
newTransition = true;
}
- task.mTransitionController.collect(task);
+ chain.collect(task);
reason = reason + " findTaskToMoveToFront";
boolean reparented = false;
if (task.isResizeable() && canUseActivityOptionsLaunchBounds(options)) {
@@ -1626,13 +1627,14 @@
r.showStartingWindow(true /* taskSwitch */);
}
if (newTransition) {
- task.mTransitionController.requestStartTransition(transition, task,
+ task.mTransitionController.requestStartTransition(chain.getTransition(), task,
options != null ? options.getRemoteTransition() : null,
null /* displayChange */);
}
} finally {
mUserLeaving = false;
mService.continueWindowLayout();
+ mService.mChainTracker.endPartial();
}
}
@@ -1669,13 +1671,10 @@
}
private void removePinnedRootTaskInSurfaceTransaction(Task rootTask) {
- final Transition transition = rootTask.mTransitionController.requestTransitionIfNeeded(
- TRANSIT_TO_BACK, 0 /* flags */, rootTask, rootTask.mDisplayContent);
- if (transition == null) {
- rootTask.mTransitionController.collect(rootTask);
- } else {
- transition.collect(rootTask);
- }
+ final ActionChain chain = mService.mChainTracker.startTransit("remPinTask");
+ rootTask.mTransitionController.requestTransitionIfNeeded(
+ TRANSIT_TO_BACK, 0 /* flags */, rootTask, rootTask.mDisplayContent, chain);
+ chain.collect(rootTask);
/**
* Workaround: Force-stop all the activities in the root pinned task before we reparent them
@@ -1699,6 +1698,7 @@
if (rootTask.getParent() == null) {
// The activities in the task may already be finishing. Then the task could be removed
// when performing the idle check.
+ mService.mChainTracker.endPartial();
return;
}
@@ -1721,6 +1721,7 @@
mRootWindowContainer.resumeFocusedTasksTopActivities();
} finally {
mService.continueWindowLayout();
+ mService.mChainTracker.endPartial();
}
}
@@ -1769,9 +1770,14 @@
// Prevent recursion.
return;
}
- Transition transit = task.mTransitionController.requestCloseTransitionIfNeeded(task);
- if (transit != null) {
- transit.collectClose(task);
+ final ActionChain chain = mService.mChainTracker.startTransit("removeTask");
+ final boolean wasCollecting = chain.isCollecting();
+ if (!wasCollecting) {
+ chain.attachTransition(task.mTransitionController.requestCloseTransitionIfNeeded(task));
+ }
+ chain.collectClose(task);
+ final Transition transition = chain.getTransition();
+ if (!wasCollecting && transition != null) {
if (!task.mTransitionController.useFullReadyTracking()) {
// If a transition was created here, it means this is an isolated removeTask. It's
// possible for there to be no consequent operations (eg. this is a multiwindow task
@@ -1779,14 +1785,7 @@
// tracker so that it doesn't get stuck. However, since the old ready tracker
// doesn't support multiple conditions, we have to touch it here at the beginning
// before anything that may need it to wait (setReady(false)).
- transit.setReady(task, true);
- }
- } else {
- // If we failed to create a transition, there might be already a currently collecting
- // transition. Let's use it if possible.
- transit = task.mTransitionController.getCollectingTransition();
- if (transit != null) {
- transit.collectClose(task);
+ transition.setReady(task, true);
}
}
@@ -1825,6 +1824,7 @@
task, callingUid, callingPid, callerActivityClassName);
} finally {
task.mInRemoveTask = false;
+ mService.mChainTracker.endPartial();
}
}
static CharSequence getApplicationLabel(PackageManager pm, String packageName) {
@@ -2171,7 +2171,7 @@
final ActivityRecord s = mStoppingActivities.get(i);
// Activity in a force hidden task should not be counted as animating, i.e., we want to
// send onStop before any configuration change when removing pip transition is ongoing.
- final boolean animating = s.isInTransition()
+ final boolean animating = s.inTransition()
&& s.getTask() != null && !s.getTask().isForceHidden();
ProtoLog.v(WM_DEBUG_STATES, "Stopping %s: nowVisible=%b animating=%b "
+ "finishing=%s", s, s.nowVisible, animating, s.finishing);
@@ -2811,6 +2811,7 @@
final ActivityOptions activityOptions = options != null
? options.getOptions(this)
: null;
+ boolean inChain;
synchronized (mService.mGlobalLock) {
final boolean isCallerRecents = mRecentTasks.isCallerRecents(callingUid);
boolean moveHomeTaskForward = isCallerRecents;
@@ -2926,6 +2927,10 @@
intent = task.intent;
intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY);
userId = task.mUserId;
+ inChain = mService.mChainTracker.isInChain();
+ if (inChain) {
+ mService.mChainTracker.pushAsyncStart();
+ }
}
// ActivityStarter will acquire the lock where the places need, so execute the request
// outside of the lock.
@@ -2948,6 +2953,9 @@
task.removeIfPossible("start-from-recents");
}
mService.continueWindowLayout();
+ if (inChain) {
+ mService.mChainTracker.popAsyncStart();
+ }
}
}
}
diff --git a/services/core/java/com/android/server/wm/AppCompatDeviceStateQuery.java b/services/core/java/com/android/server/wm/AppCompatDeviceStateQuery.java
index 3abea24..343599f 100644
--- a/services/core/java/com/android/server/wm/AppCompatDeviceStateQuery.java
+++ b/services/core/java/com/android/server/wm/AppCompatDeviceStateQuery.java
@@ -46,7 +46,7 @@
final DisplayContent dc = mActivityRecord.mDisplayContent;
return dc != null && task != null && !dc.inTransition()
&& dc.getDisplayRotation().isDeviceInPosture(
- DeviceStateController.DeviceState.HALF_FOLDED, isTabletop)
+ DeviceStateController.DeviceStateEnum.HALF_FOLDED, isTabletop)
&& task.getWindowingMode() == WINDOWING_MODE_FULLSCREEN;
}
diff --git a/services/core/java/com/android/server/wm/AppCompatLetterboxPolicy.java b/services/core/java/com/android/server/wm/AppCompatLetterboxPolicy.java
index 27511b2..5eebb7d 100644
--- a/services/core/java/com/android/server/wm/AppCompatLetterboxPolicy.java
+++ b/services/core/java/com/android/server/wm/AppCompatLetterboxPolicy.java
@@ -178,19 +178,6 @@
return mLetterboxPolicyState.isFullyTransparentBarAllowed(rect);
}
- /**
- * Updates the letterbox surfaces in case this is needed.
- *
- * @param winHint The WindowState for the letterboxed Activity.
- * @param t The current Transaction.
- * @param inputT The pending transaction used for the input surface.
- */
- void updateLetterboxSurfaceIfNeeded(@NonNull WindowState winHint,
- @NonNull SurfaceControl.Transaction t,
- @NonNull SurfaceControl.Transaction inputT) {
- mLetterboxPolicyState.updateLetterboxSurfaceIfNeeded(winHint, t, inputT);
- }
-
void updateLetterboxSurfaceIfNeeded(@NonNull WindowState winHint) {
mLetterboxPolicyState.updateLetterboxSurfaceIfNeeded(winHint,
mActivityRecord.getSyncTransaction(), mActivityRecord.getPendingTransaction());
@@ -480,6 +467,15 @@
private final Point mLetterboxPosition = new Point();
private boolean mRunning;
+ // The model needs to store the bounds for the multiple surfaces which will be
+ // created in Shell anyway.
+ private final Rect mLeftBounds = new Rect();
+ private final Rect mTopBounds = new Rect();
+ private final Rect mRightBounds = new Rect();
+ private final Rect mBottomBounds = new Rect();
+ private final Rect[] mSurfacesBounds =
+ new Rect[]{mLeftBounds, mTopBounds, mRightBounds, mBottomBounds};
+
@Override
public void layoutLetterboxIfNeeded(@NonNull WindowState w) {
mRunning = true;
@@ -488,6 +484,7 @@
calculateLetterboxInnerBounds(mActivityRecord, w, mInnerBounds);
mActivityRecord.mAppCompatController.getReachabilityPolicy()
.setLetterboxInnerBoundsSupplier(() -> mInnerBounds);
+ updateSurfacesBounds();
}
@Override
@@ -528,10 +525,10 @@
public Rect getLetterboxInsets() {
if (isRunning()) {
return new Rect(
- Math.max(0, mInnerBounds.left - mOuterBounds.left),
- Math.max(0, mOuterBounds.top - mInnerBounds.top),
- Math.max(0, mOuterBounds.right - mInnerBounds.right),
- Math.max(0, mInnerBounds.bottom - mOuterBounds.bottom)
+ Math.max(0, mLeftBounds.width()),
+ Math.max(0, mTopBounds.height()),
+ Math.max(0, mRightBounds.width()),
+ Math.max(0, mBottomBounds.height())
);
}
return new Rect();
@@ -570,15 +567,25 @@
start(winHint);
}
+ /**
+ * @return {@code true} if bar shown within a given rectangle is allowed to be fully
+ * transparent when the current activity is displayed.
+ */
@Override
public boolean isFullyTransparentBarAllowed(@NonNull Rect rect) {
- // TODO(b/374921442) Handle Transparent Activities Letterboxing in Shell.
- // At the moment Shell handles letterbox with a single surface. This would make
- // notIntersectsOrFullyContains() to return false in the existing Letterbox
- // implementation.
- // Note: Previous implementation is
- // !isRunning() || mLetterbox.notIntersectsOrFullyContains(rect);
- return !isRunning();
+ return !isRunning() || AppCompatLetterboxUtils.fullyContainsOrNotIntersects(rect,
+ mSurfacesBounds);
+ }
+
+ private void updateSurfacesBounds() {
+ mTopBounds.set(mOuterBounds.left, mOuterBounds.top, mOuterBounds.right,
+ mInnerBounds.top);
+ mLeftBounds.set(mOuterBounds.left, mOuterBounds.top, mInnerBounds.left,
+ mOuterBounds.bottom);
+ mRightBounds.set(mInnerBounds.right, mOuterBounds.top, mOuterBounds.right,
+ mOuterBounds.bottom);
+ mBottomBounds.set(mOuterBounds.left, mInnerBounds.bottom, mOuterBounds.right,
+ mOuterBounds.bottom);
}
}
}
diff --git a/services/core/java/com/android/server/wm/AppCompatLetterboxUtils.java b/services/core/java/com/android/server/wm/AppCompatLetterboxUtils.java
index e5b61db..158244c 100644
--- a/services/core/java/com/android/server/wm/AppCompatLetterboxUtils.java
+++ b/services/core/java/com/android/server/wm/AppCompatLetterboxUtils.java
@@ -99,4 +99,31 @@
outInnerBounds.set(
transparentPolicy.isRunning() ? activity.getBounds() : window.getFrame());
}
+
+
+ /**
+ * Returns {@code true} if the letterbox does not overlap with the bar, or the letterbox can
+ * fully cover the window frame.
+ *
+ * @param rect The area of the window frame.
+ * @param boundsToCheck A Collection of bounds to check.
+ */
+ static boolean fullyContainsOrNotIntersects(@NonNull Rect rect, @NonNull Rect[] boundsToCheck) {
+ // TODO(b/409293223): Make this algorithm simpler and more efficient.
+ int emptyCount = 0;
+ int noOverlappingCount = 0;
+ for (Rect bounds : boundsToCheck) {
+ if (bounds.isEmpty()) {
+ // empty letterbox
+ emptyCount++;
+ } else if (!Rect.intersects(bounds, rect)) {
+ // no overlapping
+ noOverlappingCount++;
+ } else if (bounds.contains(rect)) {
+ // overlapping and covered
+ return true;
+ }
+ }
+ return (emptyCount + noOverlappingCount) == boundsToCheck.length;
+ }
}
diff --git a/services/core/java/com/android/server/wm/AppCompatReachabilityPolicy.java b/services/core/java/com/android/server/wm/AppCompatReachabilityPolicy.java
index b4778667..0c10b02 100644
--- a/services/core/java/com/android/server/wm/AppCompatReachabilityPolicy.java
+++ b/services/core/java/com/android/server/wm/AppCompatReachabilityPolicy.java
@@ -27,14 +27,16 @@
import static com.android.server.wm.AppCompatConfiguration.LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_CENTER;
import static com.android.server.wm.AppCompatConfiguration.LETTERBOX_VERTICAL_REACHABILITY_POSITION_CENTER;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.Rect;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.window.flags.Flags;
import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.function.Supplier;
/**
@@ -50,6 +52,26 @@
@VisibleForTesting
Supplier<Rect> mLetterboxInnerBoundsSupplier;
+ /** @hide */
+ @IntDef(prefix = { "REACHABILITY_SOURCE_" }, value = {
+ REACHABILITY_SOURCE_CORE,
+ REACHABILITY_SOURCE_SHELL,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ReachabilitySource {}
+
+ /**
+ * This is the value used when the double tap is coming from the Letterbox surfaces in Core.
+ * @hide
+ */
+ public static final int REACHABILITY_SOURCE_CORE = 0;
+
+ /**
+ * This is the value used when the double tap is coming from the Letterbox surfaces in Shell.
+ * @hide
+ */
+ public static final int REACHABILITY_SOURCE_SHELL = 1;
+
AppCompatReachabilityPolicy(@NonNull ActivityRecord activityRecord,
@NonNull AppCompatConfiguration appCompatConfiguration) {
mActivityRecord = activityRecord;
@@ -66,14 +88,25 @@
}
/**
- * Handles double tap events for reachability.
+ * Handles double tap events for reachability from Core.
* <p/>
* @param x Double tap x coordinate.
* @param y Double tap y coordinate.
*/
void handleDoubleTap(int x, int y) {
- handleHorizontalDoubleTap(x);
- handleVerticalDoubleTap(y);
+ handleDoubleTap(x, y, REACHABILITY_SOURCE_CORE);
+ }
+
+ /**
+ * Handles double tap events for reachability.
+ * <p/>
+ * @param x Double tap x coordinate.
+ * @param y Double tap y coordinate.
+ * @param source The Source of the double-tap event.
+ */
+ void handleDoubleTap(int x, int y, @ReachabilitySource int source) {
+ handleHorizontalDoubleTap(x, source);
+ handleVerticalDoubleTap(y, source);
}
void dump(@NonNull PrintWriter pw, @NonNull String prefix) {
@@ -95,14 +128,14 @@
mActivityRecord.getParent().getConfiguration()));
}
- private void handleHorizontalDoubleTap(int x) {
+ private void handleHorizontalDoubleTap(int x, @ReachabilitySource int source) {
final AppCompatReachabilityOverrides reachabilityOverrides =
mActivityRecord.mAppCompatController.getReachabilityOverrides();
- // We don't return early when the Shell letterbox implementation is enabled because
- // double tap is always sent via transitions.
- final boolean isInTransition = !Flags.appCompatRefactoring()
- && mActivityRecord.isInTransition();
- if (!reachabilityOverrides.isHorizontalReachabilityEnabled() || isInTransition) {
+ // The check on the transition state only makes sense if the event is coming from core.
+ // In case the event is coming from Shell, the transition should not be considered.
+ final boolean skipWhenOnTransition = source == REACHABILITY_SOURCE_CORE
+ && mActivityRecord.inTransition();
+ if (!reachabilityOverrides.isHorizontalReachabilityEnabled() || skipWhenOnTransition) {
return;
}
final Rect letterboxInnerFrame = getLetterboxInnerFrame();
@@ -144,14 +177,14 @@
mActivityRecord.recomputeConfiguration();
}
- private void handleVerticalDoubleTap(int y) {
+ private void handleVerticalDoubleTap(int y, @ReachabilitySource int source) {
final AppCompatReachabilityOverrides reachabilityOverrides =
mActivityRecord.mAppCompatController.getReachabilityOverrides();
- // We don't return early when the Shell letterbox implementation is enabled because
- // double tap is always sent via transitions.
- final boolean isInTransition = !Flags.appCompatRefactoring()
- && mActivityRecord.isInTransition();
- if (!reachabilityOverrides.isVerticalReachabilityEnabled() || isInTransition) {
+ // The check on the transition state only makes sense if the event is coming from core.
+ // In case the event is coming from Shell, the transition should not be considered.
+ final boolean skipWhenOnTransition = source == REACHABILITY_SOURCE_CORE
+ && mActivityRecord.inTransition();
+ if (!reachabilityOverrides.isVerticalReachabilityEnabled() || skipWhenOnTransition) {
return;
}
final Rect letterboxInnerFrame = getLetterboxInnerFrame();
diff --git a/services/core/java/com/android/server/wm/AppCompatSizeCompatModePolicy.java b/services/core/java/com/android/server/wm/AppCompatSizeCompatModePolicy.java
index 0f1e36d7..363718f 100644
--- a/services/core/java/com/android/server/wm/AppCompatSizeCompatModePolicy.java
+++ b/services/core/java/com/android/server/wm/AppCompatSizeCompatModePolicy.java
@@ -171,8 +171,7 @@
.clearInheritedAppCompatDisplayInsets();
}
- void clearSizeCompatMode() {
- clearSizeCompatModeAttributes();
+ private Configuration clearOverrideConfiguration() {
// Clear config override in #updateAppCompatDisplayInsets().
final int activityType = mActivityRecord.getActivityType();
final Configuration overrideConfig = mActivityRecord.getRequestedOverrideConfiguration();
@@ -180,9 +179,23 @@
// Keep the activity type which was set when attaching to a task to prevent leaving it
// undefined.
overrideConfig.windowConfiguration.setActivityType(activityType);
+ return overrideConfig;
+ }
+
+ void clearSizeCompatMode() {
+ clearSizeCompatModeAttributes();
+ final Configuration overrideConfig = clearOverrideConfiguration();
mActivityRecord.onRequestedOverrideConfigurationChanged(overrideConfig);
}
+ void clearSizeCompatModeIfNeededOnResolveOverrideConfiguration() {
+ if (mAppCompatDisplayInsets == null || !mActivityRecord.isUniversalResizeable()) {
+ return;
+ }
+ clearSizeCompatModeAttributes();
+ clearOverrideConfiguration();
+ }
+
void dump(@NonNull PrintWriter pw, @NonNull String prefix) {
if (mSizeCompatScale != 1f || hasSizeCompatBounds()) {
pw.println(prefix + "mSizeCompatScale=" + mSizeCompatScale + " mSizeCompatBounds="
diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java
index 819e4ab..381d2fc 100644
--- a/services/core/java/com/android/server/wm/BackNavigationController.java
+++ b/services/core/java/com/android/server/wm/BackNavigationController.java
@@ -17,6 +17,8 @@
package com.android.server.wm;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+import static android.view.Display.INVALID_DISPLAY;
+import static android.view.Display.TYPE_INTERNAL;
import static android.view.RemoteAnimationTarget.MODE_CLOSING;
import static android.view.RemoteAnimationTarget.MODE_OPENING;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_APP_PROGRESS_GENERATION_ALLOWED;
@@ -24,6 +26,7 @@
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_OLD_NONE;
import static android.view.WindowManager.TRANSIT_PREPARE_BACK_NAVIGATION;
+import static android.window.DesktopExperienceFlags.ENABLE_INDEPENDENT_BACK_IN_PROJECTED;
import static android.window.SystemOverrideOnBackInvokedCallback.OVERRIDE_FINISH_AND_REMOVE_TASK;
import static android.window.SystemOverrideOnBackInvokedCallback.OVERRIDE_UNDEFINED;
@@ -35,6 +38,7 @@
import static com.android.server.wm.BackNavigationProto.SHOW_WALLPAPER;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_PREDICT_BACK;
import static com.android.server.wm.WindowContainer.SYNC_STATE_NONE;
+import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
import android.annotation.BinderThread;
import android.annotation.NonNull;
@@ -53,6 +57,7 @@
import android.util.Pair;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
+import android.view.Display;
import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
import android.view.WindowInsets;
@@ -133,12 +138,14 @@
}
}
- boolean requestBackGesture() {
+ boolean requestBackGesture(int displayId) {
synchronized (mWindowManagerService.mGlobalLock) {
if (mGestureRequest == null) {
return false;
}
- mGestureRequest.sendResult(null);
+ final Bundle result = new Bundle();
+ result.putInt(BackNavigationInfo.KEY_DISPLAY_ID, displayId);
+ mGestureRequest.sendResult(result);
return true;
}
}
@@ -183,7 +190,30 @@
return null;
}
- window = wmService.getFocusedWindowLocked();
+ // In projected mode, main device remains unchanged when connected to external display.
+ // System back should act independently per display instead of the top focused display.
+ final boolean inProjectedMode = isInProjectedMode(adapter.mOriginDisplayId);
+ if (inProjectedMode) {
+ // Updates current focus to null if not top focused display.
+ wmService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL,
+ false /* updateInputWindows */);
+ final DisplayContent dc = wmService.mRoot.getDisplayContent(
+ adapter.mOriginDisplayId);
+ window = dc.findFocusedWindow();
+ if (window == null) {
+ ProtoLog.w(WM_DEBUG_BACK_PREVIEW,
+ "No focused window on display %d, default to top current task's window",
+ adapter.mOriginDisplayId);
+ currentTask = dc.getFocusedRootTask();
+ if (currentTask != null) {
+ final ActivityRecord activity = currentTask.getTopVisibleActivity();
+ window = activity != null
+ ? activity.findMainWindow(false /*includeStartingApp*/) : null;
+ }
+ }
+ } else {
+ window = wmService.getFocusedWindowLocked();
+ }
if (window == null) {
// We don't have any focused window, fallback ont the top currentTask of the focused
@@ -428,12 +458,6 @@
ProtoLog.w(WM_DEBUG_BACK_PREVIEW,
"Pending back animation due to another animation is running");
mPendingAnimationBuilder = builder;
- // Current transition is still running, we have to defer the hiding to the
- // client process to prevent the unexpected relayout when handling the back
- // animation.
- for (int i = prevActivities.size() - 1; i >= 0; --i) {
- prevActivities.get(i).setDeferHidingClient();
- }
} else {
scheduleAnimation(builder);
}
@@ -724,6 +748,39 @@
return mAnimationHandler.isStartingSurfaceDrawn(openActivity);
}
+ /**
+ * Whether there is a connected display that supports a desktop windowing session with
+ * the main internal display unchanged.
+ */
+ @VisibleForTesting
+ boolean isInProjectedMode(int originDisplayId) {
+ if (!ENABLE_INDEPENDENT_BACK_IN_PROJECTED.isTrue() || originDisplayId == INVALID_DISPLAY) {
+ return false;
+ }
+
+ boolean internalDisplaySupportsDesktop = false;
+ boolean externalDisplaySupportsDesktop = false;
+
+ final Display[] displays = mWindowManagerService.mDisplayManager.getDisplays();
+ for (final Display display : displays) {
+ final int displayId = display.getDisplayId();
+ final DisplayContent dc = mWindowManagerService.mRoot.getDisplayContent(displayId);
+ // Display where back navigation started is null, use default system back behaviour.
+ if (dc == null && originDisplayId == displayId) {
+ return false;
+ }
+ final TaskDisplayArea tda = dc != null ? dc.getDefaultTaskDisplayArea() : null;
+ if (tda != null) {
+ if (display.getType() == TYPE_INTERNAL) {
+ internalDisplaySupportsDesktop |= tda.inFreeformWindowingMode();
+ } else {
+ externalDisplaySupportsDesktop |= tda.inFreeformWindowingMode();
+ }
+ }
+ }
+ return !internalDisplaySupportsDesktop && externalDisplaySupportsDesktop;
+ }
+
@VisibleForTesting
class NavigationMonitor {
// The window which triggering the back navigation.
@@ -1895,7 +1952,8 @@
}
}
// Force update mLastSurfaceShowing for opening activity and its task.
- if (mWindowManagerService.mRoot.mTransitionController.isShellTransitionsEnabled()) {
+ if (mWindowManagerService.mRoot.mTransitionController.isShellTransitionsEnabled()
+ && !mWindowManagerService.mFlags.mEnsureSurfaceVisibility) {
for (int i = visibleOpenActivities.length - 1; i >= 0; --i) {
WindowContainer.enforceSurfaceVisible(visibleOpenActivities[i]);
}
diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
index f8a50b3..23e4e38 100644
--- a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
+++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
@@ -44,14 +44,13 @@
import static com.android.server.wm.ActivityTaskManagerService.APP_SWITCH_FG_ONLY;
import static com.android.server.wm.ActivityTaskSupervisor.getApplicationLabel;
import static com.android.server.wm.PendingRemoteAnimationRegistry.TIMEOUT_MS;
+import static com.android.window.flags.Flags.balAdditionalLogging;
import static com.android.window.flags.Flags.balAdditionalStartModes;
import static com.android.window.flags.Flags.balDontBringExistingBackgroundTaskStackToFg;
-import static com.android.window.flags.Flags.balImprovedMetrics;
import static com.android.window.flags.Flags.balRequireOptInByPendingIntentCreator;
import static com.android.window.flags.Flags.balShowToastsBlocked;
import static com.android.window.flags.Flags.balStrictModeGracePeriod;
import static com.android.window.flags.Flags.balStrictModeRo;
-import static com.android.window.flags.Flags.balAdditionalLogging;
import static java.lang.annotation.RetentionPolicy.SOURCE;
import static java.util.Objects.requireNonNull;
@@ -1942,39 +1941,18 @@
logIfOnlyAllowedBy(finalVerdict, state, BAL_ALLOW_NON_APP_VISIBLE_WINDOW);
logIfOnlyAllowedBy(finalVerdict, state, BAL_ALLOW_TOKEN);
- if (balImprovedMetrics()) {
- if (shouldLogStats(finalVerdict, state)) {
- String activityName;
- if (shouldLogIntentActivity(finalVerdict, state)) {
- Intent intent = state.mIntent;
- activityName = intent == null ? "noIntent" // should never happen
- : requireNonNull(intent.getComponent()).flattenToShortString();
- } else {
- activityName = "";
- }
- writeBalAllowedLog(activityName, finalVerdict.getCode(), state);
+ if (shouldLogStats(finalVerdict, state)) {
+ String activityName;
+ if (shouldLogIntentActivity(finalVerdict, state)) {
+ Intent intent = state.mIntent;
+ activityName = intent == null ? "noIntent" // should never happen
+ : requireNonNull(intent.getComponent()).flattenToShortString();
} else {
- writeBalAllowedLogMinimal(state);
+ activityName = "";
}
+ writeBalAllowedLog(activityName, finalVerdict.getCode(), state);
} else {
- @BalCode int code = finalVerdict.getCode();
- int callingUid = state.mCallingUid;
- int realCallingUid = state.mRealCallingUid;
- Intent intent = state.mIntent;
-
- if (code == BAL_ALLOW_PENDING_INTENT
- && (callingUid < Process.FIRST_APPLICATION_UID
- || realCallingUid < Process.FIRST_APPLICATION_UID)) {
- String activityName = intent != null
- ? requireNonNull(intent.getComponent()).flattenToShortString() : "";
- writeBalAllowedLog(activityName, BAL_ALLOW_PENDING_INTENT,
- state);
- }
- if (code == BAL_ALLOW_PERMISSION || code == BAL_ALLOW_FOREGROUND
- || code == BAL_ALLOW_SAW_PERMISSION) {
- // We don't need to know which activity in this case.
- writeBalAllowedLog("", code, state);
- }
+ writeBalAllowedLogMinimal(state);
}
return finalVerdict;
}
diff --git a/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java b/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java
index 2605310..130c6069 100644
--- a/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java
+++ b/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java
@@ -27,8 +27,6 @@
import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_GRACE_PERIOD;
import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_PERMISSION;
import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_TOKEN;
-import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_VISIBLE_WINDOW;
-import static com.android.window.flags.Flags.balImprovedMetrics;
import static java.util.Objects.requireNonNull;
@@ -133,9 +131,6 @@
BalVerdict tokenVerdict = isBackgroundStartAllowedByToken(uid,
packageName, checkConfiguration.isCheckingForFgsStart);
if (tokenVerdict.allows()) {
- if (!balImprovedMetrics()) {
- return new BalVerdict(BAL_ALLOW_PERMISSION, tokenVerdict.toString());
- }
return tokenVerdict;
}
}
@@ -143,9 +138,7 @@
// But still respect the appSwitchState.
if (checkConfiguration.checkVisibility && appSwitchState != APP_SWITCH_DISALLOW
&& isBoundByForegroundUid()) {
- return new BalVerdict(balImprovedMetrics() ? BAL_ALLOW_BOUND_BY_FOREGROUND
- : BAL_ALLOW_VISIBLE_WINDOW, /*background*/
- "process bound by foreground uid");
+ return new BalVerdict(BAL_ALLOW_BOUND_BY_FOREGROUND, "process bound by foreground uid");
}
// Allow if the caller has an activity in any foreground task.
if (checkConfiguration.checkOtherExemptions && hasActivityInVisibleTask
diff --git a/services/core/java/com/android/server/wm/BlurController.java b/services/core/java/com/android/server/wm/BlurController.java
index 41d9dbf..3ef31ac 100644
--- a/services/core/java/com/android/server/wm/BlurController.java
+++ b/services/core/java/com/android/server/wm/BlurController.java
@@ -16,7 +16,7 @@
package com.android.server.wm;
-import static android.os.PowerManager.THERMAL_STATUS_CRITICAL;
+import static android.os.PowerManager.THERMAL_STATUS_SEVERE;
import static android.view.CrossWindowBlurListeners.CROSS_WINDOW_BLUR_SUPPORTED;
import android.content.BroadcastReceiver;
@@ -46,7 +46,7 @@
private final Object mLock = new Object();
private volatile boolean mBlurEnabled;
private boolean mInPowerSaveMode;
- private boolean mCriticalThermalStatus;
+ private boolean mDisabledByThermal;
private boolean mBlurDisabledSetting;
private boolean mTunnelModeEnabled = false;
@@ -92,10 +92,10 @@
mBlurDisabledSetting = getBlurDisabledSetting();
powerManager.addThermalStatusListener((status) -> {
- mCriticalThermalStatus = status >= THERMAL_STATUS_CRITICAL;
+ mDisabledByThermal = status >= THERMAL_STATUS_SEVERE;
updateBlurEnabled();
});
- mCriticalThermalStatus = powerManager.getCurrentThermalStatus() >= THERMAL_STATUS_CRITICAL;
+ mDisabledByThermal = powerManager.getCurrentThermalStatus() >= THERMAL_STATUS_SEVERE;
TunnelModeEnabledListener.register(mTunnelModeListener);
@@ -120,7 +120,7 @@
private void updateBlurEnabled() {
synchronized (mLock) {
final boolean newEnabled = CROSS_WINDOW_BLUR_SUPPORTED && !mBlurDisabledSetting
- && !mInPowerSaveMode && !mTunnelModeEnabled && !mCriticalThermalStatus;
+ && !mInPowerSaveMode && !mTunnelModeEnabled && !mDisabledByThermal;
if (mBlurEnabled == newEnabled) {
return;
}
diff --git a/services/core/java/com/android/server/wm/ContentRecorder.java b/services/core/java/com/android/server/wm/ContentRecorder.java
index 1c25692..973493e 100644
--- a/services/core/java/com/android/server/wm/ContentRecorder.java
+++ b/services/core/java/com/android/server/wm/ContentRecorder.java
@@ -19,6 +19,7 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.content.Context.MEDIA_PROJECTION_SERVICE;
import static android.content.res.Configuration.ORIENTATION_UNDEFINED;
+import static android.view.ContentRecordingSession.RECORD_CONTENT_BELOW_OVERLAY;
import static android.view.ContentRecordingSession.RECORD_CONTENT_DISPLAY;
import static android.view.ContentRecordingSession.RECORD_CONTENT_TASK;
import static android.view.ViewProtoEnums.DISPLAY_STATE_OFF;
@@ -38,6 +39,7 @@
import android.os.ServiceManager;
import android.view.ContentRecordingSession;
import android.view.ContentRecordingSession.RecordContent;
+import android.view.WindowManager;
import android.window.DesktopExperienceFlags;
import android.view.Display;
import android.view.DisplayInfo;
@@ -47,6 +49,9 @@
import com.android.internal.protolog.ProtoLog;
import com.android.server.display.feature.DisplayManagerFlags;
+import java.util.LinkedList;
+import java.util.Queue;
+
/**
* Manages content recording for a particular {@link DisplayContent}.
*/
@@ -374,6 +379,7 @@
return;
}
+ final SurfaceControl stopAt = findOwnerTopOverlayWindow(mRecordedWindowContainer);
final SurfaceControl sourceSurface = mRecordedWindowContainer.getSurfaceControl();
if (sourceSurface == null || !sourceSurface.isValid()) {
ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
@@ -410,7 +416,12 @@
mDisplayContent.getDisplayId(), mDisplayContent.getDisplayInfo().state);
// Create a mirrored hierarchy for the SurfaceControl of the DisplayArea to capture.
- mRecordedSurface = SurfaceControl.mirrorSurface(sourceSurface);
+ if (com.android.media.projection.flags.Flags.recordingOverlay()
+ && com.android.graphics.surfaceflinger.flags.Flags.stopLayer()) {
+ mRecordedSurface = SurfaceControl.mirrorSurface(sourceSurface, stopAt);
+ } else {
+ mRecordedSurface = SurfaceControl.mirrorSurface(sourceSurface);
+ }
SurfaceControl.Transaction transaction =
mDisplayContent.mWmService.mTransactionFactory.get()
// Set the mMirroredSurface's parent to the root SurfaceControl for this
@@ -453,6 +464,39 @@
// when the VirtualDisplay is destroyed - which will clean up this DisplayContent.
}
+ @Nullable
+ private SurfaceControl findOwnerTopOverlayWindow(WindowContainer recordedWindowContainer) {
+ if (!com.android.media.projection.flags.Flags.recordingOverlay()) {
+ return null;
+ }
+ if (!com.android.graphics.surfaceflinger.flags.Flags.stopLayer()) {
+ return null;
+ }
+ if (mContentRecordingSession.getContentToRecord() != RECORD_CONTENT_BELOW_OVERLAY) {
+ return null;
+ }
+
+ // If recording below overlay, find the overlay window with the same owner uid as the
+ // recording session to stop mirroring at that surface.
+ // Perform a breadth-first search to find the overlay window.
+ Queue<WindowContainer<WindowContainer>> toSearch = new LinkedList<>();
+ toSearch.add(recordedWindowContainer);
+ while (!toSearch.isEmpty()) {
+ WindowContainer<WindowContainer> wc = toSearch.poll();
+ if (wc != null && wc.getWindow() != null
+ && wc.getWindowType() == WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
+ && wc.getWindow().mOwnerUid
+ == mContentRecordingSession.getRecordingOwnerUid()) {
+ return wc.getSurfaceControl();
+ }
+
+ for (WindowContainer child : wc.mChildren) {
+ toSearch.add(child);
+ }
+ }
+ return null;
+ }
+
/**
* Retrieves the {@link WindowContainer} for the level of the hierarchy to start recording,
* indicated by the {@link #mContentRecordingSession}. Performs any error handling and state
@@ -468,6 +512,7 @@
final IBinder tokenToRecord = mContentRecordingSession.getTokenToRecord();
switch (contentToRecord) {
case RECORD_CONTENT_DISPLAY:
+ case RECORD_CONTENT_BELOW_OVERLAY:
// Given the id of the display to record, retrieve the associated DisplayContent.
final DisplayContent dc =
mDisplayContent.mWmService.mRoot.getDisplayContent(
diff --git a/services/core/java/com/android/server/wm/DeferredDisplayUpdater.java b/services/core/java/com/android/server/wm/DeferredDisplayUpdater.java
index b802754..9b0b497 100644
--- a/services/core/java/com/android/server/wm/DeferredDisplayUpdater.java
+++ b/services/core/java/com/android/server/wm/DeferredDisplayUpdater.java
@@ -101,6 +101,8 @@
/** Whether {@link #mScreenUnblocker} should wait for transition to be ready. */
private boolean mShouldWaitForTransitionWhenScreenOn;
+ private boolean mInPhysicalDisplayChangeTransition;
+
/** The message to notify PhoneWindowManager#finishWindowsDrawn. */
@Nullable
private Message mScreenUnblocker;
@@ -196,6 +198,10 @@
mDisplayContent.mAtmService.startPowerMode(POWER_MODE_REASON_CHANGE_DISPLAY);
+ if (physicalDisplayUpdated) {
+ mInPhysicalDisplayChangeTransition = true;
+ }
+
mDisplayContent.mTransitionController.startCollectOrQueue(transition, deferred -> {
final Rect startBounds = new Rect(0, 0, mDisplayContent.mInitialDisplayWidth,
mDisplayContent.mInitialDisplayHeight);
@@ -211,6 +217,8 @@
}
}
+ final ActionChain chain = mDisplayContent.mAtmService.mChainTracker.start(
+ "dispChg", transition);
mDisplayContent.mAtmService.deferWindowLayout();
try {
onStartCollect.run();
@@ -227,6 +235,7 @@
// Run surface placement after requestStartTransition, so shell side can receive
// the transition request before handling task info changes.
mDisplayContent.mAtmService.continueWindowLayout();
+ mDisplayContent.mAtmService.mChainTracker.end();
}
});
}
@@ -318,11 +327,15 @@
* properties.
*/
void onDisplayContentDisplayPropertiesPostChanged() {
+ if (mInPhysicalDisplayChangeTransition) {
+ return;
+ }
// Unblock immediately in case there is no transition. This is unlikely to happen.
- if (mScreenUnblocker != null && !mDisplayContent.mTransitionController.inTransition()) {
+ if (mScreenUnblocker != null) {
mScreenUnblocker.sendToTarget();
mScreenUnblocker = null;
}
+ mShouldWaitForTransitionWhenScreenOn = false;
}
/**
@@ -356,6 +369,7 @@
*/
private void continueScreenUnblocking() {
synchronized (mDisplayContent.mWmService.mGlobalLock) {
+ mInPhysicalDisplayChangeTransition = false;
mShouldWaitForTransitionWhenScreenOn = false;
mDisplayContent.mWmService.mH.removeCallbacks(mScreenUnblockTimeoutRunnable);
if (mScreenUnblocker == null) {
diff --git a/services/core/java/com/android/server/wm/DesktopModeLaunchParamsModifier.java b/services/core/java/com/android/server/wm/DesktopModeLaunchParamsModifier.java
index 61e8e09..285aecab 100644
--- a/services/core/java/com/android/server/wm/DesktopModeLaunchParamsModifier.java
+++ b/services/core/java/com/android/server/wm/DesktopModeLaunchParamsModifier.java
@@ -94,8 +94,7 @@
return RESULT_SKIP;
}
- if (com.android.window.flags.Flags.fixLayoutExistingTask()
- && task.getCreatedByOrganizerTask() != null) {
+ if (task.getCreatedByOrganizerTask() != null) {
appendLog("has created-by-organizer-task, skipping");
return RESULT_SKIP;
}
diff --git a/services/core/java/com/android/server/wm/DeviceStateAutoRotateSettingController.java b/services/core/java/com/android/server/wm/DeviceStateAutoRotateSettingController.java
index f42d60d..f4589d0 100644
--- a/services/core/java/com/android/server/wm/DeviceStateAutoRotateSettingController.java
+++ b/services/core/java/com/android/server/wm/DeviceStateAutoRotateSettingController.java
@@ -16,8 +16,10 @@
package com.android.server.wm;
+import android.annotation.NonNull;
import android.content.Context;
import android.database.ContentObserver;
+import android.hardware.devicestate.DeviceState;
import android.os.Handler;
import android.provider.Settings;
@@ -54,7 +56,7 @@
}
/** Notify controller device state has changed */
- public void onDeviceStateChange(DeviceStateController.DeviceState deviceState) {
+ public void onDeviceStateChange(@NonNull DeviceState deviceState) {
if (Flags.enableDeviceStateAutoRotateSettingLogging()) {
mDeviceStateAutoRotateSettingIssueLogger.onDeviceStateChange();
}
diff --git a/services/core/java/com/android/server/wm/DeviceStateController.java b/services/core/java/com/android/server/wm/DeviceStateController.java
index 475a504..68a4535 100644
--- a/services/core/java/com/android/server/wm/DeviceStateController.java
+++ b/services/core/java/com/android/server/wm/DeviceStateController.java
@@ -26,6 +26,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
+import android.hardware.devicestate.DeviceState;
import android.hardware.devicestate.DeviceStateManager;
import android.hardware.devicestate.feature.flags.FeatureFlags;
import android.hardware.devicestate.feature.flags.FeatureFlagsImpl;
@@ -39,10 +40,10 @@
import java.util.ArrayList;
import java.util.Collections;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executor;
-import java.util.function.Consumer;
/**
* Class that listens for a callback from display manager and responds to device state
@@ -64,18 +65,19 @@
private final List<Integer> mConcurrentDisplayDeviceStates;
@NonNull
private final List<Integer> mReverseRotationAroundZAxisStates;
+ private final Map<Integer, DeviceState> mIdentifierToDeviceState = new HashMap<>();
@GuardedBy("mWmLock")
@NonNull
@VisibleForTesting
- final Map<Consumer<DeviceState>, Executor> mDeviceStateCallbacks = new ArrayMap<>();
+ final Map<DeviceStateListener, Executor> mDeviceStateCallbacks = new ArrayMap<>();
private final boolean mMatchBuiltInDisplayOrientationToDefaultDisplay;
@NonNull
- private DeviceState mCurrentDeviceState = DeviceState.UNKNOWN;
+ private DeviceStateEnum mCurrentDeviceStateEnum = DeviceStateEnum.UNKNOWN;
private int mCurrentState;
- public enum DeviceState {
+ public enum DeviceStateEnum {
UNKNOWN,
OPEN,
FOLDED,
@@ -102,6 +104,7 @@
for (int i = 0; i < deviceStates.size(); i++) {
final android.hardware.devicestate.DeviceState state = deviceStates.get(i);
+ mIdentifierToDeviceState.put(state.getIdentifier(), state);
if (state.hasProperty(
PROPERTY_FEATURE_REAR_DISPLAY)) {
mRearDisplayDeviceStates.add(state.getIdentifier());
@@ -146,14 +149,14 @@
* post the work onto their own worker thread to avoid holding the WindowManagerGlobalLock for
* an extended period of time.
*/
- void registerDeviceStateCallback(@NonNull Consumer<DeviceState> callback,
+ void registerDeviceStateCallback(@NonNull DeviceStateListener callback,
@NonNull @CallbackExecutor Executor executor) {
synchronized (mWmLock) {
mDeviceStateCallbacks.put(callback, executor);
}
}
- void unregisterDeviceStateCallback(@NonNull Consumer<DeviceState> callback) {
+ void unregisterDeviceStateCallback(@NonNull DeviceStateListener callback) {
synchronized (mWmLock) {
mDeviceStateCallbacks.remove(callback);
}
@@ -193,43 +196,45 @@
*/
public void onDeviceStateReceivedByDisplayManager(int state) {
mCurrentState = state;
- final DeviceState deviceState;
+ final DeviceStateEnum deviceStateEnum;
if (ArrayUtils.contains(mHalfFoldedDeviceStates, state)) {
- deviceState = DeviceState.HALF_FOLDED;
+ deviceStateEnum = DeviceStateEnum.HALF_FOLDED;
} else if (ArrayUtils.contains(mFoldedDeviceStates, state)) {
- deviceState = DeviceState.FOLDED;
+ deviceStateEnum = DeviceStateEnum.FOLDED;
} else if (ArrayUtils.contains(mRearDisplayDeviceStates, state)) {
- deviceState = DeviceState.REAR;
+ deviceStateEnum = DeviceStateEnum.REAR;
} else if (ArrayUtils.contains(mOpenDeviceStates, state)) {
- deviceState = DeviceState.OPEN;
+ deviceStateEnum = DeviceStateEnum.OPEN;
} else if (ArrayUtils.contains(mConcurrentDisplayDeviceStates, state)) {
- deviceState = DeviceState.CONCURRENT;
+ deviceStateEnum = DeviceStateEnum.CONCURRENT;
} else {
- deviceState = DeviceState.UNKNOWN;
+ deviceStateEnum = DeviceStateEnum.UNKNOWN;
}
- if (mCurrentDeviceState == null || !mCurrentDeviceState.equals(deviceState)) {
- mCurrentDeviceState = deviceState;
+ if (mCurrentDeviceStateEnum == null || !mCurrentDeviceStateEnum.equals(deviceStateEnum)) {
+ mCurrentDeviceStateEnum = deviceStateEnum;
// Make a copy here because it's possible that the consumer tries to remove a callback
// while we're still iterating through the list, which would end up in a
// ConcurrentModificationException. Note that cannot use a List<Map.Entry> because the
// entries are tied to the backing map. So, if a client removes a callback while
// we are notifying clients, we will get a NPE.
- final List<Pair<Consumer<DeviceState>, Executor>> entries = copyDeviceStateCallbacks();
+ final List<Pair<DeviceStateListener, Executor>> entries =
+ copyDeviceStateCallbacks();
for (int i = 0; i < entries.size(); i++) {
- final Pair<Consumer<DeviceState>, Executor> entry = entries.get(i);
- entry.second.execute(() -> entry.first.accept(deviceState));
+ final Pair<DeviceStateListener, Executor> entry = entries.get(i);
+ entry.second.execute(() -> entry.first.onDeviceStateChanged(deviceStateEnum,
+ mIdentifierToDeviceState.get(state)));
}
}
}
@VisibleForTesting
@NonNull
- List<Pair<Consumer<DeviceState>, Executor>> copyDeviceStateCallbacks() {
- final List<Pair<Consumer<DeviceState>, Executor>> entries = new ArrayList<>();
+ List<Pair<DeviceStateListener, Executor>> copyDeviceStateCallbacks() {
+ final List<Pair<DeviceStateListener, Executor>> entries = new ArrayList<>();
synchronized (mWmLock) {
mDeviceStateCallbacks.forEach((deviceStateConsumer, executor) -> {
@@ -250,4 +255,10 @@
}
return valueList;
}
+
+ public interface DeviceStateListener {
+ // TODO(b/409761673): Remove DeviceStateEnum from the callback
+ void onDeviceStateChanged(DeviceStateEnum deviceStateEnum,
+ @NonNull DeviceState deviceState);
+ }
}
diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java
index d7d5b44..444885a 100644
--- a/services/core/java/com/android/server/wm/DisplayArea.java
+++ b/services/core/java/com/android/server/wm/DisplayArea.java
@@ -830,13 +830,6 @@
}
}
- // If SystemUI is dragging for recents, we want to reset the dim state so any dim layer
- // on the display level fades out.
- if (!mTransitionController.isShellTransitionsEnabled()
- && forAllTasks(task -> !task.canAffectSystemUiFlags())) {
- mDimmer.resetDimStates();
- }
-
if (mDimmer.hasDimState()) {
if (mDimmer.updateDims(getSyncTransaction())) {
scheduleAnimation();
diff --git a/services/core/java/com/android/server/wm/DisplayAreaPolicy.java b/services/core/java/com/android/server/wm/DisplayAreaPolicy.java
index 618fb90..1e69186 100644
--- a/services/core/java/com/android/server/wm/DisplayAreaPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayAreaPolicy.java
@@ -164,7 +164,6 @@
.build())
.addFeature(new Feature.Builder(wmService.mPolicy, "AppZoomOut",
FEATURE_APP_ZOOM_OUT)
- .all()
.upTo(TYPE_VOLUME_OVERLAY)
.except(TYPE_NAVIGATION_BAR, TYPE_NAVIGATION_BAR_PANEL,
TYPE_STATUS_BAR, TYPE_NOTIFICATION_SHADE,
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 4e033a6..803ea18 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -177,6 +177,7 @@
import android.graphics.Region;
import android.graphics.Region.Op;
import android.hardware.HardwareBuffer;
+import android.hardware.devicestate.DeviceState;
import android.hardware.display.DisplayManagerInternal;
import android.hardware.display.VirtualDisplayConfig;
import android.metrics.LogMaker;
@@ -615,7 +616,7 @@
@VisibleForTesting
final DeviceStateController mDeviceStateController;
- final Consumer<DeviceStateController.DeviceState> mDeviceStateConsumer;
+ final DeviceStateController.DeviceStateListener mDeviceStateListener;
final RemoteDisplayChangeController mRemoteDisplayChangeController;
/** Windows added since {@link #mCurrentFocus} was set to null. Used for ANR blaming. */
@@ -629,11 +630,6 @@
*/
int mLayoutSeq = 0;
- /** The number of deferrals of updating the IME layering target. */
- private int mUpdateImeLayeringTargetDeferCount;
- /** Whether the IME layering target was requested to be updated while being deferred. */
- private boolean mUpdateImeLayeringTargetRequestedWhileDeferred;
-
private MagnificationSpec mMagnificationSpec;
private InputMonitor mInputMonitor;
@@ -663,7 +659,7 @@
* {@link android.view.inputmethod.InputConnection}.
*
* @see #updateImeInputAndControlTarget
- * @see WindowManagerInternal#updateInputMethodTargetWindow
+ * @see WindowManagerInternal#updateImeTargetWindow
* @see #findFocusedWindow()
*/
@Nullable
@@ -1204,11 +1200,11 @@
mDisplayRotation = new DisplayRotation(mWmService, this, mDisplayInfo.address,
mDeviceStateController, root.getDisplayRotationCoordinator());
- mDeviceStateConsumer =
- (@NonNull DeviceStateController.DeviceState newFoldState) -> {
- mDisplayRotation.foldStateChanged(newFoldState);
- };
- mDeviceStateController.registerDeviceStateCallback(mDeviceStateConsumer,
+ mDeviceStateListener =
+ (@NonNull DeviceStateController.DeviceStateEnum deviceStateEnum,
+ @NonNull DeviceState deviceState) -> mDisplayRotation.foldStateChanged(
+ deviceStateEnum, deviceState);
+ mDeviceStateController.registerDeviceStateCallback(mDeviceStateListener,
new HandlerExecutor(mWmService.mH));
mCloseToSquareMaxAspectRatio = mWmService.mContext.getResources().getFloat(
@@ -1594,20 +1590,22 @@
if (mLastHasContent && mTransitionController.isShellTransitionsEnabled()) {
final Rect startBounds = currentDisplayConfig.windowConfiguration.getBounds();
final Rect endBounds = mTmpConfiguration.windowConfiguration.getBounds();
- if (!mTransitionController.isCollecting()) {
+ final ActionChain chain = mAtmService.mChainTracker.startTransit("recfgDisp");
+ if (!chain.isCollecting()) {
final TransitionRequestInfo.DisplayChange change =
new TransitionRequestInfo.DisplayChange(mDisplayId);
change.setStartAbsBounds(startBounds);
change.setEndAbsBounds(endBounds);
- requestChangeTransition(changes, change);
+ requestChangeTransition(changes, change, chain);
} else {
- final Transition transition = mTransitionController.getCollectingTransition();
+ final Transition transition = chain.getTransition();
transition.setKnownConfigChanges(this, changes);
// A collecting transition is existed. The sync method must be set before
// collecting this display, so WindowState#prepareSync can use the sync method.
mTransitionController.setDisplaySyncMethod(startBounds, endBounds, this);
collectDisplayChange(transition);
}
+ mAtmService.mChainTracker.endPartial();
}
sendNewConfiguration();
}
@@ -1778,9 +1776,6 @@
* different fixed orientations will still keep their original appearances.
*/
void applyFixedRotationForNonTopVisibleActivityIfNeeded() {
- if (!mWmService.mFlags.mRespectNonTopVisibleFixedOrientation) {
- return;
- }
final ActivityRecord orientationSrcApp = getLastOrientationSourceApp();
if (orientationSrcApp == null || orientationSrcApp.fillsParent()) {
return;
@@ -1804,9 +1799,6 @@
* then the bottom one will apply the fixed rotation transform for its orientation.
*/
void applyFixedRotationForNonTopVisibleActivityIfNeeded(@NonNull ActivityRecord ar) {
- if (!mWmService.mFlags.mRespectNonTopVisibleFixedOrientation) {
- return;
- }
final ActivityRecord orientationSrcApp = getLastOrientationSourceApp();
if (orientationSrcApp != null) {
applyFixedRotationForNonTopVisibleActivityIfNeeded(ar,
@@ -1913,8 +1905,7 @@
return false;
}
if (r.hasFixedRotationTransform()) {
- if (mWmService.mFlags.mRespectNonTopVisibleFixedOrientation
- && mFixedRotationLaunchingApp == null) {
+ if (mFixedRotationLaunchingApp == null) {
// It could be finishing the previous top translucent activity, and the next fixed
// orientation activity becomes the current top.
setFixedRotationLaunchingAppUnchecked(r,
@@ -1923,16 +1914,9 @@
// It has been set and not yet finished.
return true;
}
- if (mWmService.mFlags.mRespectNonTopVisibleFixedOrientation) {
- if (r.isReportedDrawn()) {
- // It is late for a drawn app. Either this is already a stable state or it needs
- // a rotation animation to handle the change.
- return false;
- }
- } else if (!r.occludesParent() || r.isReportedDrawn()) {
- // While entering or leaving a translucent or floating activity (e.g. dialog style),
- // there is a visible activity in the background. Then it still needs rotation animation
- // to cover the activity configuration change.
+ if (r.isReportedDrawn()) {
+ // It is late for a drawn app. Either this is already a stable state or it needs
+ // a rotation animation to handle the change.
return false;
}
if (checkOpening) {
@@ -2033,7 +2017,7 @@
if (prevRotatedLaunchingApp != null
&& prevRotatedLaunchingApp.getWindowConfiguration().getRotation() == rotation
// It is animating so we can expect there will have a transition callback.
- && (prevRotatedLaunchingApp.isInTransition())) {
+ && prevRotatedLaunchingApp.inTransition()) {
// It may be the case that multiple activities launch consecutively. Because their
// rotation are the same, the transformed state can be shared to avoid duplicating
// the heavy operations. This also benefits that the states of multiple activities
@@ -3323,6 +3307,16 @@
return true;
}
+ void updateShouldShowSystemDecorations() {
+ final boolean shouldShow = mDisplay.canHostTasks();
+ if (allowContentModeSwitch() &&
+ (shouldShow != mWmService.mDisplayWindowSettings
+ .shouldShowSystemDecorsLocked(this))) {
+ mWmService.mDisplayWindowSettings
+ .setShouldShowSystemDecorsInternalLocked(this, shouldShow);
+ }
+ }
+
DisplayCutout loadDisplayCutout(int displayWidth, int displayHeight) {
if (mDisplayPolicy == null || mInitialDisplayCutout == null) {
return null;
@@ -3425,7 +3419,7 @@
}
mUnknownAppVisibilityController.clear();
mTransitionController.unregisterLegacyListener(mFixedRotationTransitionListener);
- mDeviceStateController.unregisterDeviceStateCallback(mDeviceStateConsumer);
+ mDeviceStateController.unregisterDeviceStateCallback(mDeviceStateListener);
super.removeImmediately();
if (DEBUG_DISPLAY) Slog.v(TAG_WM, "Removing display=" + this);
mPointerEventDispatcher.dispose();
@@ -3549,10 +3543,12 @@
* Requests to start a transition for a display change. {@code changes} must be non-zero.
*/
void requestChangeTransition(@ActivityInfo.Config int changes,
- @Nullable TransitionRequestInfo.DisplayChange displayChange) {
+ @Nullable TransitionRequestInfo.DisplayChange displayChange,
+ @NonNull ActionChain chain) {
final TransitionController controller = mTransitionController;
final Transition t = controller.requestStartDisplayTransition(TRANSIT_CHANGE, 0 /* flags */,
this, null /* remoteTransition */, displayChange);
+ chain.attachTransition(t);
t.collect(this);
mAtmService.startPowerMode(POWER_MODE_REASON_CHANGE_DISPLAY);
if (mAsyncRotationController != null) {
@@ -4168,14 +4164,6 @@
}
final WindowState curTarget = mImeLayeringTarget;
- if (!canUpdateImeLayeringTarget()) {
- if (DEBUG_INPUT_METHOD) {
- Slog.w(TAG_WM, "Defer updating IME layering target");
- }
- mUpdateImeLayeringTargetRequestedWhileDeferred = true;
- return curTarget;
- }
-
// TODO(multidisplay): Needs some serious rethought when the target and IME are not on the
// same display. Or even when the current IME/target are not on the same screen as the next
// IME/target. For now only look for input windows on the main screen.
@@ -4214,13 +4202,6 @@
return target;
}
- /** Computes and updates the IME layering target in the candidate app window token if needed. */
- void computeImeLayeringTargetIfNeeded(@NonNull ActivityRecord candidate) {
- if (mImeLayeringTarget != null && mImeLayeringTarget.mActivityRecord == candidate) {
- computeImeLayeringTarget(true /* update */);
- }
- }
-
private boolean isImeControlledByApp() {
return mImeInputTarget != null && mImeInputTarget.shouldControlIme();
}
@@ -4327,8 +4308,11 @@
return DISPLAY_IME_POLICY_FALLBACK_DISPLAY;
}
final int imePolicy = mWmService.mDisplayWindowSettings.getImePolicyLocked(this);
+ // Show IME locally if display is eligible for desktop mode and the flag is enabled.
if (imePolicy == DISPLAY_IME_POLICY_FALLBACK_DISPLAY
- && isPublicSecondaryDisplayWithDesktopModeForceEnabled()) {
+ && (isPublicSecondaryDisplayWithDesktopModeForceEnabled()
+ || (DesktopExperienceFlags.ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT.isTrue()
+ && (isSystemDecorationsSupported() && allowContentModeSwitch())))) {
// If the display has not explicitly requested for the IME to be hidden then it shall
// show the IME locally.
return DISPLAY_IME_POLICY_LOCAL;
@@ -4383,7 +4367,7 @@
final boolean nonAppImeLayeringTargetAnimatingExit = mImeLayeringTarget.mAnimatingExit
&& mImeLayeringTarget.mAttrs.type != TYPE_BASE_APPLICATION
&& mImeLayeringTarget.isSelfAnimating(0, ANIMATION_TYPE_WINDOW_ANIMATION);
- if (mImeLayeringTarget.inTransitionSelfOrParent()
+ if (mImeLayeringTarget.inTransition()
|| nonAppImeLayeringTargetAnimatingExit) {
showImeScreenshot();
}
@@ -4489,80 +4473,105 @@
}
// ========== Begin of ImeScreenshot stuff ==========
- /** The screenshot IME surface to place on the task while transitioning to the next task. */
+ /** The IME screenshot surface to place on the task while transitioning to the next task. */
+ @VisibleForTesting
+ @Nullable
ImeScreenshot mImeScreenshot;
static final class ImeScreenshot {
- private WindowState mImeTarget;
- private SurfaceControl.Builder mSurfaceBuilder;
- private SurfaceControl mImeSurface;
- private Point mImeSurfacePosition;
- ImeScreenshot(SurfaceControl.Builder surfaceBuilder, @NonNull WindowState imeTarget) {
- mSurfaceBuilder = surfaceBuilder;
+ /** The name of the IME screenshot surface. */
+ private static final String SURFACE_NAME = "IME-screenshot-surface";
+
+ @NonNull
+ private final WindowState mImeTarget;
+
+ /** Builder for the surface. */
+ @NonNull
+ private final SurfaceControl.Builder mSurfaceBuilder;
+
+ /**
+ * The surface of the IME screenshot. This is only created while the screenshot is
+ * requested.
+ */
+ @Nullable
+ private SurfaceControl mSurface;
+ /**
+ * Position of the {@link #mSurface} relative to its parent, or {@code null} if no surface
+ * exists.
+ */
+ @Nullable
+ private Point mSurfacePosition;
+
+ ImeScreenshot(@NonNull WindowState imeTarget,
+ @NonNull SurfaceControl.Builder surfaceBuilder) {
mImeTarget = imeTarget;
+ mSurfaceBuilder = surfaceBuilder;
}
+ @NonNull
WindowState getImeTarget() {
return mImeTarget;
}
+ @Nullable
@VisibleForTesting
- SurfaceControl getImeScreenshotSurface() {
- return mImeSurface;
+ SurfaceControl getSurface() {
+ return mSurface;
}
- private SurfaceControl createImeSurface(ScreenCapture.ScreenshotHardwareBuffer b,
- Transaction t) {
+ @NonNull
+ private SurfaceControl createSurface(@NonNull ScreenCapture.ScreenshotHardwareBuffer b,
+ @NonNull Transaction t) {
final HardwareBuffer buffer = b.getHardwareBuffer();
- ProtoLog.i(WM_DEBUG_IME, "create IME snapshot for %s, buff width=%s, height=%s",
+ ProtoLog.i(WM_DEBUG_IME, "create IME screenshot for %s, buff width=%s, height=%s",
mImeTarget, buffer.getWidth(), buffer.getHeight());
final WindowState imeWindow = mImeTarget.getDisplayContent().mInputMethodWindow;
final ActivityRecord activity = mImeTarget.mActivityRecord;
- final SurfaceControl imeParent = mImeTarget.mAttrs.type == TYPE_BASE_APPLICATION
+ final SurfaceControl parent = mImeTarget.mAttrs.type == TYPE_BASE_APPLICATION
? activity.getSurfaceControl()
: mImeTarget.getSurfaceControl();
- final SurfaceControl imeSurface = mSurfaceBuilder
- .setName("IME-snapshot-surface")
+ final SurfaceControl surface = mSurfaceBuilder
+ .setName(SURFACE_NAME)
.setBLASTLayer()
.setFormat(buffer.getFormat())
- // Attaching IME snapshot to the associated IME layering target on the
+ // Attaching IME screenshot to the associated IME layering target on the
// activity when:
// - The target is activity main window: attaching on top of the activity.
// - The target is non-activity main window (e.g. activity overlay or
// dialog-themed activity): attaching on top of the target since the layer has
// already above the activity.
- .setParent(imeParent)
+ .setParent(parent)
.setCallsite("DisplayContent.attachAndShowImeScreenshotOnTarget")
.build();
- // Make IME snapshot as trusted overlay
- InputMonitor.setTrustedOverlayInputInfo(imeSurface, t, imeWindow.getDisplayId(),
- "IME-snapshot-surface");
- t.setBuffer(imeSurface, buffer);
- t.setColorSpace(activity.mSurfaceControl, ColorSpace.get(ColorSpace.Named.SRGB));
- t.setLayer(imeSurface, 1);
+ // Make IME screenshot as trusted overlay
+ InputMonitor.setTrustedOverlayInputInfo(surface, t, imeWindow.getDisplayId(),
+ SURFACE_NAME);
+ t.setBuffer(surface, buffer);
+ t.setColorSpace(activity.getSurfaceControl(), ColorSpace.get(ColorSpace.Named.SRGB));
+ t.setLayer(surface, 1);
- final Point surfacePosition = new Point(imeWindow.getFrame().left,
+ final var surfacePosition = new Point(imeWindow.getFrame().left,
imeWindow.getFrame().top);
- if (imeParent == activity.getSurfaceControl()) {
- t.setPosition(imeSurface, surfacePosition.x, surfacePosition.y);
- } else {
+ if (parent != activity.getSurfaceControl()) {
surfacePosition.offset(-mImeTarget.getFrame().left, -mImeTarget.getFrame().top);
surfacePosition.offset(mImeTarget.mAttrs.surfaceInsets.left,
mImeTarget.mAttrs.surfaceInsets.top);
- t.setPosition(imeSurface, surfacePosition.x, surfacePosition.y);
}
- mImeSurfacePosition = surfacePosition;
- ProtoLog.i(WM_DEBUG_IME, "Set IME snapshot position: (%d, %d)", surfacePosition.x,
- surfacePosition.y);
- return imeSurface;
+ t.setPosition(surface, surfacePosition.x, surfacePosition.y);
+ mSurfacePosition = surfacePosition;
+ ProtoLog.i(WM_DEBUG_IME, "Set IME screenshot surface position: (%d, %d)",
+ surfacePosition.x, surfacePosition.y);
+ return surface;
}
- private void removeImeSurface(Transaction t) {
- if (mImeSurface != null) {
- ProtoLog.i(WM_DEBUG_IME, "remove IME snapshot, caller=%s", Debug.getCallers(6));
- t.remove(mImeSurface);
- mImeSurface = null;
+ void removeSurface(@NonNull Transaction t) {
+ if (mSurface != null) {
+ ProtoLog.i(WM_DEBUG_IME, "remove IME screenshot surface, caller=%s",
+ Debug.getCallers(6));
+ t.remove(mSurface);
+ mSurface = null;
+ mSurfacePosition = null;
}
if (DEBUG_IME_VISIBILITY) {
EventLog.writeEvent(IMF_REMOVE_IME_SCREENSHOT, mImeTarget.toString());
@@ -4570,100 +4579,90 @@
}
/**
- * Attaches the snapshot of IME (a snapshot will be taken if there wasn't one) to the IME
- * target task and shows it. If the given {@param anyTargetTask} is true, the snapshot won't
- * be skipped by the activity type of IME target task.
+ * Attaches the screenshot of IME (a screenshot will be taken if there wasn't one) to the
+ * IME target task and shows it. If the given {@param anyTargetTask} is true, the screenshot
+ * won't be skipped by the activity type of IME target task.
*/
- void attachAndShow(Transaction t, boolean anyTargetTask) {
+ void attachAndShow(@NonNull Transaction t, boolean anyTargetTask) {
final DisplayContent dc = mImeTarget.getDisplayContent();
// Prepare IME screenshot for the target if it allows to attach into.
final Task task = mImeTarget.getTask();
// Re-new the IME screenshot when it does not exist or the size changed.
- final boolean renewImeSurface = mImeSurface == null
- || mImeSurface.getWidth() != dc.mInputMethodWindow.getFrame().width()
- || mImeSurface.getHeight() != dc.mInputMethodWindow.getFrame().height();
+ final boolean renewSurface = mSurface == null
+ || mSurface.getWidth() != dc.mInputMethodWindow.getFrame().width()
+ || mSurface.getHeight() != dc.mInputMethodWindow.getFrame().height();
// The exclusion of home/recents is an optimization for regular task switch because
// home/recents won't appear in recents task.
if (task != null && (anyTargetTask || !task.isActivityTypeHomeOrRecents())) {
- ScreenCapture.ScreenshotHardwareBuffer imeBuffer = renewImeSurface
- ? dc.mWmService.mTaskSnapshotController.snapshotImeFromAttachedTask(task)
+ final ScreenCapture.ScreenshotHardwareBuffer buffer = renewSurface
+ ? dc.mWmService.mTaskSnapshotController.screenshotImeFromAttachedTask(task)
: null;
- if (imeBuffer != null) {
- // Remove the last IME surface when the surface needs to renew.
- removeImeSurface(t);
- mImeSurface = createImeSurface(imeBuffer, t);
+ if (buffer != null) {
+ // Remove the last surface when the surface needs to renew.
+ removeSurface(t);
+ mSurface = createSurface(buffer, t);
}
}
- final boolean isValidSnapshot = mImeSurface != null && mImeSurface.isValid();
+ final boolean isValid = mSurface != null && mSurface.isValid();
// Showing the IME screenshot if the target has already in app transition stage.
// Note that if the current IME insets is not showing, no need to show IME screenshot
// to reflect the true IME insets visibility and the app task layout as possible.
- if (isValidSnapshot
- && dc.getInsetsStateController().getImeSourceProvider().isImeShowing()) {
- ProtoLog.i(WM_DEBUG_IME, "show IME snapshot, ime target=%s, callers=%s",
+ if (isValid && dc.getInsetsStateController().getImeSourceProvider().isImeShowing()) {
+ ProtoLog.i(WM_DEBUG_IME, "show IME screenshot, ime target=%s, callers=%s",
mImeTarget, Debug.getCallers(6));
- t.show(mImeSurface);
+ t.show(mSurface);
if (DEBUG_IME_VISIBILITY) {
EventLog.writeEvent(IMF_SHOW_IME_SCREENSHOT, mImeTarget.toString(),
- dc.mInputMethodWindow.mTransitFlags, mImeSurfacePosition.toString());
+ dc.mInputMethodWindow.mTransitFlags, mSurfacePosition.toString());
}
- } else if (!isValidSnapshot) {
- removeImeSurface(t);
+ } else if (!isValid) {
+ removeSurface(t);
}
}
- void detach(Transaction t) {
- removeImeSurface(t);
- }
-
@Override
public String toString() {
- StringBuilder sb = new StringBuilder(64);
- sb.append("ImeScreenshot{");
- sb.append(Integer.toHexString(System.identityHashCode(this)));
- sb.append(" imeTarget=" + mImeTarget);
- sb.append(" surface=" + mImeSurface);
- sb.append('}');
- return sb.toString();
+ return "ImeScreenshot{" + Integer.toHexString(System.identityHashCode(this))
+ + " imeTarget: " + mImeTarget
+ + " surface: " + mSurface
+ + " surfacePosition: " + mSurfacePosition
+ + '}';
}
}
private void attachImeScreenshotOnTargetIfNeeded() {
- // No need to attach screenshot if the IME target not exists or screen is off.
+ // No need to attach screenshot if the IME layering target does not exist or screen is off.
if (!shouldImeAttachedToApp() || !mWmService.mPolicy.isScreenOn()) {
return;
}
// Prepare IME screenshot for the target if it allows to attach into.
if (mInputMethodWindow != null && mInputMethodWindow.isVisible()) {
- attachImeScreenshotOnTarget(mImeLayeringTarget);
+ attachImeScreenshotOnTarget(mImeLayeringTarget, false /* hideImeWindow */);
}
}
- private void attachImeScreenshotOnTarget(WindowState imeTarget) {
- attachImeScreenshotOnTarget(imeTarget, false);
- }
-
- private void attachImeScreenshotOnTarget(WindowState imeTarget, boolean hideImeWindow) {
+ private void attachImeScreenshotOnTarget(@NonNull WindowState imeTarget,
+ boolean hideImeWindow) {
final SurfaceControl.Transaction t = getPendingTransaction();
- // Remove the obsoleted IME snapshot first in case the new snapshot happens to
+ // Remove the old IME screenshot first in case the new screenshot happens to
// override the current one before the transition finish and the surface never be
// removed on the task.
- removeImeSurfaceImmediately();
- mImeScreenshot = new ImeScreenshot(
- mWmService.mSurfaceControlFactory.get(), imeTarget);
- // If the caller requests to hide IME, then allow to show IME snapshot for any target task.
- // So IME won't look like suddenly disappeared. It usually happens when turning off screen.
+ removeImeScreenshotImmediately();
+ mImeScreenshot = new ImeScreenshot(imeTarget, mWmService.mSurfaceControlFactory.get());
+ // If the caller requests to hide IME, then allow to show IME screenshot for any target
+ // task. So IME won't look like it suddenly disappeared. It usually happens when turning
+ // the screen off.
mImeScreenshot.attachAndShow(t, hideImeWindow /* anyTargetTask */);
if (mInputMethodWindow != null && hideImeWindow) {
- // Hide the IME window when deciding to show IME snapshot on demand.
+ // Hide the IME window when deciding to show IME screenshot on demand.
// InsetsController will make IME visible again before animating it.
mInputMethodWindow.hide(false, false);
}
}
/**
- * Shows the IME screenshot and attach to the IME layering target window.
+ * Shows the IME screenshot and attaches it to the IME layering target window.
*
* Used when the IME target window with IME visible is transitioning to the next target.
* e.g. App transitioning or swiping this the task of the IME target window to recents app.
@@ -4676,15 +4675,16 @@
* Shows the IME screenshot and attach it to the given IME target window.
*/
@VisibleForTesting
- void showImeScreenshot(WindowState imeTarget) {
+ void showImeScreenshot(@NonNull WindowState imeTarget) {
attachImeScreenshotOnTarget(imeTarget, true /* hideImeWindow */);
}
/**
- * Removes the IME screenshot when the caller is a part of the attached target window.
+ * Removes the IME screenshot if the given target matches, or contains the IME screenshot
+ * target.
*/
- void removeImeSurfaceByTarget(WindowContainer win) {
- if (mImeScreenshot == null || win == null) {
+ void removeImeScreenshotByTarget(@NonNull WindowContainer win) {
+ if (mImeScreenshot == null) {
return;
}
// The starting window shouldn't be the input target to attach the IME screenshot during
@@ -4698,14 +4698,14 @@
final boolean winIsOrContainsScreenshotTarget = (win == screenshotTarget
|| win.getWindow(w -> w == screenshotTarget) != null);
if (winIsOrContainsScreenshotTarget) {
- removeImeSurfaceImmediately();
+ removeImeScreenshotImmediately();
}
}
/** Removes the IME screenshot immediately. */
- void removeImeSurfaceImmediately() {
+ void removeImeScreenshotImmediately() {
if (mImeScreenshot != null) {
- mImeScreenshot.detach(getSyncTransaction());
+ mImeScreenshot.removeSurface(getSyncTransaction());
mImeScreenshot = null;
}
}
@@ -5192,9 +5192,21 @@
}
/**
- * Creates a LayerCaptureArgs object to represent the entire DisplayContent
+ * Creates a {@link LayerCaptureArgs} object.
+ *
+ * If {@code useWindowingLayerAsScreenshotRoot} is false, the returned
+ * {@code LayerCaptureArgs} will represent the entire DisplayContent.
+ *
+ * If {@code useWindowingLayerAsScreenshotRoot} is true, the
+ * {@code LayerCaptureArgs} will represent the surface area of the windowing layer.
+ * @param predicate An optional filter function to determine which windows are captured. If
+ * null, all windows are included.
+ * @param useWindowingLayerAsScreenshotRoot Whether to use the windowing layer's
+ * surface area as the screenshot root.
+ * @return A {@code LayerCaptureArgs} object configured according to the parameters.
*/
- LayerCaptureArgs getLayerCaptureArgs(@Nullable ToBooleanFunction<WindowState> predicate) {
+ LayerCaptureArgs getLayerCaptureArgs(@Nullable ToBooleanFunction<WindowState> predicate,
+ boolean useWindowingLayerAsScreenshotRoot) {
if (!mWmService.mPolicy.isScreenOn()) {
if (DEBUG_SCREENSHOT) {
Slog.i(TAG_WM, "Attempted to take screenshot while display was off.");
@@ -5204,8 +5216,9 @@
getBounds(mTmpRect);
mTmpRect.offsetTo(0, 0);
- LayerCaptureArgs.Builder builder = new LayerCaptureArgs.Builder(getSurfaceControl())
- .setSourceCrop(mTmpRect);
+ SurfaceControl sc =
+ useWindowingLayerAsScreenshotRoot ? getWindowingLayer() : getSurfaceControl();
+ LayerCaptureArgs.Builder builder = new LayerCaptureArgs.Builder(sc).setSourceCrop(mTmpRect);
if (predicate != null) {
ArrayList<SurfaceControl> excludeLayers = new ArrayList<>();
@@ -5614,40 +5627,6 @@
}
}
- /**
- * Defers updating the IME layering target, tracking the number of deferrals in a counter. When
- * all of these are cleared through {@link #continueUpdateImeLayeringTarget} the update will
- * take place.
- */
- void deferUpdateImeLayeringTarget() {
- if (mUpdateImeLayeringTargetDeferCount == 0) {
- mUpdateImeLayeringTargetRequestedWhileDeferred = false;
- }
- mUpdateImeLayeringTargetDeferCount++;
- }
-
- /**
- * Attempts to continue updating the IME layering target by clearing one deferred update set
- * by {@link #deferUpdateImeLayeringTarget}. If no deferred updates remain, and the update
- * was requested while deferred, then this will trigger the update.
- */
- void continueUpdateImeLayeringTarget() {
- if (mUpdateImeLayeringTargetDeferCount == 0) {
- return;
- }
-
- mUpdateImeLayeringTargetDeferCount--;
- if (mUpdateImeLayeringTargetDeferCount == 0
- && mUpdateImeLayeringTargetRequestedWhileDeferred) {
- computeImeLayeringTarget(true /* update */);
- }
- }
-
- /** Checks whether the IME layering target can be updated, or is currently being deferred. */
- private boolean canUpdateImeLayeringTarget() {
- return mUpdateImeLayeringTargetDeferCount == 0;
- }
-
InputMonitor getInputMonitor() {
return mInputMonitor;
}
@@ -5680,8 +5659,9 @@
* WindowContainer)
*/
void requestTransitionAndLegacyPrepare(@WindowManager.TransitionType int transit,
- @WindowManager.TransitionFlags int flags, @Nullable WindowContainer trigger) {
- mTransitionController.requestTransitionIfNeeded(transit, flags, trigger, this);
+ @WindowManager.TransitionFlags int flags, @Nullable WindowContainer trigger,
+ @NonNull ActionChain chain) {
+ mTransitionController.requestTransitionIfNeeded(transit, flags, trigger, this, chain);
}
void executeAppTransition() {
@@ -5735,8 +5715,7 @@
}
/**
- * This is the development option to force enable desktop mode on all secondary public displays
- * that are not owned by a virtual device.
+ * This is the development option to force enable desktop mode on all secondary public displays.
* When this is enabled, it also force enable system decorations on those displays.
*
* If we need a per-display config to enable desktop mode for production, that config should
@@ -5746,9 +5725,16 @@
if (!mWmService.mForceDesktopModeOnExternalDisplays || isDefaultDisplay || isPrivate()) {
return false;
}
- // Desktop mode is not supported on virtual devices.
- int deviceId = mRootWindowContainer.mTaskSupervisor.getDeviceIdForDisplayId(mDisplayId);
- return deviceId == Context.DEVICE_ID_DEFAULT;
+ if (mDwpcHelper != null && !mDwpcHelper.isWindowingModeSupported(WINDOWING_MODE_FREEFORM)) {
+ return false;
+ }
+ // Virtual displays need to explicitly opt in via the system decorations.
+ if (mDisplay.getType() == Display.TYPE_VIRTUAL
+ && !mWmService.mDisplayWindowSettings.shouldShowSystemDecorsLocked(this)
+ && (mDisplay.getFlags() & FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS) == 0) {
+ return false;
+ }
+ return true;
}
/**
@@ -6323,15 +6309,16 @@
if (changes != 0) {
Slog.i(TAG, "Override config changes=" + Integer.toHexString(changes) + " "
+ mTempConfig + " for displayId=" + mDisplayId);
+ final ActionChain chain = mAtmService.mChainTracker.startTransit("dispOverCfg");
if (isReady() && mTransitionController.isShellTransitionsEnabled() && mLastHasContent) {
- final Transition transition = mTransitionController.getCollectingTransition();
- if (transition != null) {
- collectDisplayChange(transition);
+ if (chain.isCollecting()) {
+ collectDisplayChange(chain.getTransition());
} else {
- requestChangeTransition(changes, null /* displayChange */);
+ requestChangeTransition(changes, null /* displayChange */, chain);
}
}
onRequestedOverrideConfigurationChanged(mTempConfig);
+ mAtmService.mChainTracker.endPartial();
final boolean isDensityChange = (changes & ActivityInfo.CONFIG_DENSITY) != 0;
if (isDensityChange && mDisplayId == DEFAULT_DISPLAY) {
@@ -6530,6 +6517,17 @@
&& (mAtmService.mRunningVoice == null);
}
+ /** Returns {@code} if the screen is not in a fully interactive state. */
+ boolean isScreenSleeping() {
+ for (int i = mAllSleepTokens.size() - 1; i >= 0; i--) {
+ if (mAllSleepTokens.get(i).isScreenOff()) {
+ return true;
+ }
+ }
+ // If AOD is active, there may be only keyguard sleep token but awake state is false.
+ // Then still treat the case as sleeping.
+ return !mAllSleepTokens.isEmpty() && !mDisplayPolicy.isAwake();
+ }
void ensureActivitiesVisible(ActivityRecord starting, boolean notifyClients) {
if (mInEnsureActivitiesVisible) {
@@ -6698,7 +6696,7 @@
final boolean rotationChanged = super.setIgnoreOrientationRequest(ignoreOrientationRequest);
mWmService.mDisplayWindowSettings.setIgnoreOrientationRequest(
this, mSetIgnoreOrientationRequest);
- if (ignoreOrientationRequest && mWmService.mFlags.mRespectNonTopVisibleFixedOrientation) {
+ if (ignoreOrientationRequest) {
forAllActivities(r -> {
r.finishFixedRotationTransform();
});
@@ -6934,9 +6932,7 @@
// In most cases this is a no-op if the activity doesn't have fixed rotation.
// Otherwise it could be from finishing recents animation while the display has
// different orientation.
- if (!mWmService.mFlags.mRespectNonTopVisibleFixedOrientation) {
- r.finishFixedRotationTransform();
- } else if (!r.isVisible()) {
+ if (!r.isVisible()) {
r.finishFixedRotationTransform();
}
return;
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index cb8bbc0..5188c71 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -55,6 +55,7 @@
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
+import android.hardware.devicestate.DeviceState;
import android.hardware.power.Boost;
import android.os.Handler;
import android.os.SystemClock;
@@ -429,9 +430,7 @@
final boolean isTv = mContext.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_LEANBACK);
mDefaultFixedToUserRotation =
- (isCar || isTv || mService.mIsPc
- || mDisplayContent.isPublicSecondaryDisplayWithDesktopModeForceEnabled()
- || !mDisplayContent.shouldRotateWithContent())
+ (isCar || isTv || mService.mIsPc || !mDisplayContent.shouldRotateWithContent())
// For debug purposes the next line turns this feature off with:
// $ adb shell setprop config.override_forced_orient true
// $ adb shell wm size reset
@@ -597,23 +596,23 @@
mDisplayContent.mWaitingForConfig = true;
if (mDisplayContent.mTransitionController.isShellTransitionsEnabled()) {
- final boolean wasCollecting = mDisplayContent.mTransitionController.isCollecting();
- if (!wasCollecting) {
+ final ActionChain chain = mService.mAtmService.mChainTracker.startTransit("updateRot");
+ if (!chain.isCollecting()) {
if (mDisplayContent.getLastHasContent()) {
final TransitionRequestInfo.DisplayChange change =
new TransitionRequestInfo.DisplayChange(mDisplayContent.getDisplayId(),
oldRotation, mRotation);
mDisplayContent.requestChangeTransition(
- ActivityInfo.CONFIG_WINDOW_CONFIGURATION, change);
+ ActivityInfo.CONFIG_WINDOW_CONFIGURATION, change, chain);
}
} else {
- mDisplayContent.collectDisplayChange(
- mDisplayContent.mTransitionController.getCollectingTransition());
+ mDisplayContent.collectDisplayChange(chain.getTransition());
// Use remote-rotation infra since the transition has already been requested
// TODO(shell-transitions): Remove this once lifecycle management can cover all
// rotation cases.
startRemoteRotation(oldRotation, mRotation);
}
+ mService.mAtmService.mChainTracker.endPartial();
return true;
}
@@ -1542,7 +1541,7 @@
pw.println(prefix + " mLastHingeAngleEventTime="
+ mFoldController.mLastHingeAngleEventTime);
pw.println(prefix + " mDeviceState="
- + mFoldController.mDeviceState);
+ + mFoldController.mDeviceStateEnum);
}
if (!mRotationHistory.mRecords.isEmpty()) {
@@ -1575,7 +1574,7 @@
proto.end(token);
}
- boolean isDeviceInPosture(DeviceStateController.DeviceState state, boolean isTabletop) {
+ boolean isDeviceInPosture(DeviceStateController.DeviceStateEnum state, boolean isTabletop) {
if (mFoldController == null) return false;
return mFoldController.isDeviceInPosture(state, isTabletop);
}
@@ -1589,10 +1588,12 @@
* that in case of physical display change the {@link DisplayRotation#physicalDisplayChanged}
* method will be invoked *after* this one.
*/
- void foldStateChanged(DeviceStateController.DeviceState deviceState) {
+ // TODO(b/409761673) Migrate to only using android.hardware.devicestate.DeviceState
+ void foldStateChanged(DeviceStateController.DeviceStateEnum deviceStateEnum,
+ DeviceState deviceState) {
if (mFoldController != null) {
synchronized (mLock) {
- mFoldController.foldStateChanged(deviceState);
+ mFoldController.foldStateChanged(deviceStateEnum);
if (mDeviceStateAutoRotateSettingController != null) {
mDeviceStateAutoRotateSettingController.onDeviceStateChange(deviceState);
}
@@ -1651,8 +1652,11 @@
private final boolean mPauseAutorotationDuringUnfolding;
@Surface.Rotation
private int mHalfFoldSavedRotation = -1; // No saved rotation
- private DeviceStateController.DeviceState mDeviceState =
- DeviceStateController.DeviceState.UNKNOWN;
+
+ // TODO(b/409761673) Migrate DeviceStateController.DeviceStateEnum to
+ // android.hardware.devicestate.DeviceState
+ private DeviceStateController.DeviceStateEnum mDeviceStateEnum =
+ DeviceStateController.DeviceStateEnum.UNKNOWN;
private long mLastHingeAngleEventTime = 0;
private long mLastDisplaySwitchTime = 0;
private boolean mShouldIgnoreSensorRotation;
@@ -1706,8 +1710,9 @@
mActivityBoundsUpdateCallback = new Runnable() {
public void run() {
- if (mDeviceState == DeviceStateController.DeviceState.OPEN
- || mDeviceState == DeviceStateController.DeviceState.HALF_FOLDED) {
+ if (mDeviceStateEnum == DeviceStateController.DeviceStateEnum.OPEN
+ || mDeviceStateEnum
+ == DeviceStateController.DeviceStateEnum.HALF_FOLDED) {
synchronized (mLock) {
final Task topFullscreenTask =
mDisplayContent.getTask(
@@ -1767,35 +1772,35 @@
}
}
- boolean isDeviceInPosture(DeviceStateController.DeviceState state, boolean isTabletop) {
- if (state != mDeviceState) {
+ boolean isDeviceInPosture(DeviceStateController.DeviceStateEnum state, boolean isTabletop) {
+ if (state != mDeviceStateEnum) {
return false;
}
- if (mDeviceState == DeviceStateController.DeviceState.HALF_FOLDED) {
+ if (mDeviceStateEnum == DeviceStateController.DeviceStateEnum.HALF_FOLDED) {
return isTabletop == mTabletopRotations.contains(mRotation);
}
return true;
}
- DeviceStateController.DeviceState getFoldState() {
- return mDeviceState;
+ DeviceStateController.DeviceStateEnum getFoldState() {
+ return mDeviceStateEnum;
}
boolean isSeparatingHinge() {
- return mDeviceState == DeviceStateController.DeviceState.HALF_FOLDED
- || (mDeviceState == DeviceStateController.DeviceState.OPEN
+ return mDeviceStateEnum == DeviceStateController.DeviceStateEnum.HALF_FOLDED
+ || (mDeviceStateEnum == DeviceStateController.DeviceStateEnum.OPEN
&& mIsDisplayAlwaysSeparatingHinge);
}
boolean overrideFrozenRotation() {
return mAllowHalfFoldAutoRotationOverride
- && mDeviceState == DeviceStateController.DeviceState.HALF_FOLDED;
+ && mDeviceStateEnum == DeviceStateController.DeviceStateEnum.HALF_FOLDED;
}
boolean shouldRevertOverriddenRotation() {
// When transitioning to open.
return mAllowHalfFoldAutoRotationOverride
- && mDeviceState == DeviceStateController.DeviceState.OPEN
+ && mDeviceStateEnum == DeviceStateController.DeviceStateEnum.OPEN
&& !mShouldIgnoreSensorRotation // Ignore if the hinge angle still moving
&& mInHalfFoldTransition
&& mDisplayContent.getRotationReversionController().isOverrideActive(
@@ -1813,32 +1818,32 @@
return savedRotation;
}
- void foldStateChanged(DeviceStateController.DeviceState newState) {
+ void foldStateChanged(DeviceStateController.DeviceStateEnum newState) {
ProtoLog.v(WM_DEBUG_ORIENTATION,
"foldStateChanged: displayId %d, halfFoldStateChanged %s, "
+ "saved rotation: %d, mUserRotation: %d, mLastSensorRotation: %d, "
+ "mLastOrientation: %d, mRotation: %d",
mDisplayContent.getDisplayId(), newState.name(), mHalfFoldSavedRotation,
mUserRotation, mLastSensorRotation, mLastOrientation, mRotation);
- if (mDeviceState == DeviceStateController.DeviceState.UNKNOWN) {
- mDeviceState = newState;
+ if (mDeviceStateEnum == DeviceStateController.DeviceStateEnum.UNKNOWN) {
+ mDeviceStateEnum = newState;
return;
}
- if (newState == DeviceStateController.DeviceState.HALF_FOLDED
- && mDeviceState != DeviceStateController.DeviceState.HALF_FOLDED) {
+ if (newState == DeviceStateController.DeviceStateEnum.HALF_FOLDED
+ && mDeviceStateEnum != DeviceStateController.DeviceStateEnum.HALF_FOLDED) {
// The device has transitioned to HALF_FOLDED state: save the current rotation and
// update the device rotation.
mDisplayContent.getRotationReversionController().beforeOverrideApplied(
REVERSION_TYPE_HALF_FOLD);
mHalfFoldSavedRotation = mRotation;
- mDeviceState = newState;
+ mDeviceStateEnum = newState;
// Now mFoldState is set to HALF_FOLDED, the overrideFrozenRotation function will
// return true, so rotation is unlocked.
mService.updateRotation(false /* alwaysSendConfiguration */,
false /* forceRelayout */);
} else {
mInHalfFoldTransition = true;
- mDeviceState = newState;
+ mDeviceStateEnum = newState;
// Tell the device to update its orientation.
mService.updateRotation(false /* alwaysSendConfiguration */,
false /* forceRelayout */);
@@ -1887,8 +1892,8 @@
mLastDisplaySwitchTime = uptimeMillis();
final boolean isUnfolding =
- mDeviceState == DeviceStateController.DeviceState.OPEN
- || mDeviceState == DeviceStateController.DeviceState.HALF_FOLDED;
+ mDeviceStateEnum == DeviceStateController.DeviceStateEnum.OPEN
+ || mDeviceStateEnum == DeviceStateController.DeviceStateEnum.HALF_FOLDED;
if (isUnfolding) {
// Temporary disable rotation sensor updates when unfolding
@@ -2069,7 +2074,7 @@
final long mTimestamp = System.currentTimeMillis();
final int mHalfFoldSavedRotation;
final boolean mInHalfFoldTransition;
- final DeviceStateController.DeviceState mDeviceState;
+ final DeviceStateController.DeviceStateEnum mDeviceStateEnum;
@Nullable final boolean[] mRotationReversionSlots;
@Nullable final String mDisplayRotationCompatPolicySummary;
@@ -2102,11 +2107,11 @@
if (dr.mFoldController != null) {
mHalfFoldSavedRotation = dr.mFoldController.mHalfFoldSavedRotation;
mInHalfFoldTransition = dr.mFoldController.mInHalfFoldTransition;
- mDeviceState = dr.mFoldController.mDeviceState;
+ mDeviceStateEnum = dr.mFoldController.mDeviceStateEnum;
} else {
mHalfFoldSavedRotation = NO_FOLD_CONTROLLER;
mInHalfFoldTransition = false;
- mDeviceState = DeviceStateController.DeviceState.UNKNOWN;
+ mDeviceStateEnum = DeviceStateController.DeviceStateEnum.UNKNOWN;
}
mDisplayRotationCompatPolicySummary = dc.mAppCompatCameraPolicy
.getSummaryForDisplayRotationHistoryRecord();
@@ -2132,7 +2137,7 @@
pw.println(prefix + " halfFoldSavedRotation="
+ mHalfFoldSavedRotation
+ " mInHalfFoldTransition=" + mInHalfFoldTransition
- + " mFoldState=" + mDeviceState);
+ + " mFoldState=" + mDeviceStateEnum);
}
if (mDisplayRotationCompatPolicySummary != null) {
pw.println(prefix + mDisplayRotationCompatPolicySummary);
diff --git a/services/core/java/com/android/server/wm/EmbeddedWindowController.java b/services/core/java/com/android/server/wm/EmbeddedWindowController.java
index 1bf65d1..7104c7e 100644
--- a/services/core/java/com/android/server/wm/EmbeddedWindowController.java
+++ b/services/core/java/com/android/server/wm/EmbeddedWindowController.java
@@ -240,10 +240,9 @@
static class EmbeddedWindow implements InputTarget {
final IBinder mClient;
- @Nullable WindowState mHostWindowState;
+ @Nullable final WindowState mHostWindowState;
@Nullable final ActivityRecord mHostActivityRecord;
- String mName;
- final String mInputHandleName;
+ final String mName;
final int mOwnerUid;
final int mOwnerPid;
final WindowManagerService mWmService;
@@ -281,12 +280,13 @@
* @param displayId used for focus requests
*/
EmbeddedWindow(Session session, WindowManagerService service, IBinder clientToken,
- @Nullable WindowState hostWindowState, int ownerUid, int ownerPid,
- int windowType, int displayId, InputTransferToken inputTransferToken,
- String inputHandleName, boolean isFocusable) {
+ WindowState hostWindowState, int ownerUid, int ownerPid, int windowType,
+ int displayId, InputTransferToken inputTransferToken, String inputHandleName,
+ boolean isFocusable) {
mSession = session;
mWmService = service;
mClient = clientToken;
+ mHostWindowState = hostWindowState;
mHostActivityRecord = (mHostWindowState != null) ? mHostWindowState.mActivityRecord
: null;
mOwnerUid = ownerUid;
@@ -294,9 +294,11 @@
mWindowType = windowType;
mDisplayId = displayId;
mInputTransferToken = inputTransferToken;
+ final String hostWindowName =
+ (mHostWindowState != null) ? "-" + mHostWindowState.getWindowTag().toString()
+ : "";
mIsFocusable = isFocusable;
- mInputHandleName = inputHandleName;
- updateHost(hostWindowState);
+ mName = "Embedded{" + inputHandleName + hostWindowName + "}";
}
@Override
@@ -485,19 +487,5 @@
proto.end(token2);
proto.end(token);
}
-
- public void updateHost(WindowState hostWindowState) {
- if (mHostWindowState == hostWindowState && mName != null) {
- return;
- }
-
- ProtoLog.d(WM_DEBUG_EMBEDDED_WINDOWS, "[%s] Updated host window from %s to %s",
- this, mHostWindowState, hostWindowState);
- mHostWindowState = hostWindowState;
- final String hostWindowName =
- (mHostWindowState != null) ? "-" + mHostWindowState.getWindowTag().toString()
- : "";
- mName = "Embedded{" + mInputHandleName + hostWindowName + "}";
- }
}
}
diff --git a/services/core/java/com/android/server/wm/EventLogTags.logtags b/services/core/java/com/android/server/wm/EventLogTags.logtags
index 5767db1..74a3bf6 100644
--- a/services/core/java/com/android/server/wm/EventLogTags.logtags
+++ b/services/core/java/com/android/server/wm/EventLogTags.logtags
@@ -77,9 +77,9 @@
# IME surface parent is updated.
32003 imf_update_ime_parent (surface name|3)
-# IME snapshot is shown.
+# IME screenshot is shown.
32004 imf_show_ime_screenshot (target window|3),(transition|1),(surface position|3)
-# IME snapshot is hidden.
+# IME screenshot is hidden.
32005 imf_remove_ime_screenshot (target window|3)
# Request surface flinger to show / hide the wallpaper surface.
diff --git a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
index f2e6490..2bca83f 100644
--- a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
@@ -855,7 +855,7 @@
private static boolean isWindowClosing(@NonNull WindowState win) {
final var ar = win.mActivityRecord;
return win.mAnimatingExit || (ar != null
- && ((ar.isInTransition() && !ar.isVisibleRequested()) || ar.willCloseOrEnterPip()));
+ && ((ar.inTransition() && !ar.isVisibleRequested()) || ar.willCloseOrEnterPip()));
}
private boolean isTargetChangedWithinActivity(@NonNull InsetsControlTarget target) {
diff --git a/services/core/java/com/android/server/wm/ImeTargetVisibilityPolicy.java b/services/core/java/com/android/server/wm/ImeTargetVisibilityPolicy.java
index 4c8b091..319dc67 100644
--- a/services/core/java/com/android/server/wm/ImeTargetVisibilityPolicy.java
+++ b/services/core/java/com/android/server/wm/ImeTargetVisibilityPolicy.java
@@ -31,19 +31,19 @@
public abstract class ImeTargetVisibilityPolicy {
/**
- * Shows the IME screenshot and attach it to the given IME target window.
+ * Shows the IME screenshot and attaches it to the given IME target window.
*
- * @param imeTarget The target window to show the IME screenshot.
- * @param displayId A unique id to identify the display.
- * @return {@code true} if success, {@code false} otherwise.
+ * @param imeTarget the token of the IME target window.
+ * @param displayId the ID of the display to show the screenshot on.
+ * @return {@code true} if successful, {@code false} otherwise.
*/
public abstract boolean showImeScreenshot(@NonNull IBinder imeTarget, int displayId);
/**
- * Removes the IME screenshot on the given display.
+ * Removes the IME screenshot from the given display.
*
* @param displayId The target display of showing IME screenshot.
- * @return {@code true} if success, {@code false} otherwise.
+ * @return {@code true} if successful, {@code false} otherwise.
*/
public abstract boolean removeImeScreenshot(int displayId);
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index 16e88a3..e4e2790 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -438,12 +438,12 @@
SoftInputShowHideReason.HIDE_RECENTS_ANIMATION,
mDisplayContent.getDisplayId());
}
- // Ensure removing the IME snapshot when the app no longer to show on the
- // task snapshot (also taking the new task snaphot to update the overview).
+ // Ensure removing the IME screenshot when the app no longer to show on the
+ // task snapshot (also taking the new task snapshot to update the overview).
final ActivityRecord app = mDisplayContent.getImeInputTarget() != null
? mDisplayContent.getImeInputTarget().getActivityRecord() : null;
if (app != null) {
- mDisplayContent.removeImeSurfaceImmediately();
+ mDisplayContent.removeImeScreenshotImmediately();
if (app.getTask() != null) {
mDisplayContent.mAtmService.takeTaskSnapshot(app.getTask().mTaskId,
true /* updateCache */);
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index 3b715d6..f742b53 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -16,6 +16,8 @@
package com.android.server.wm;
+import static android.view.InsetsSource.FLAG_INVALID;
+
import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_WINDOW_INSETS;
import static com.android.server.wm.InsetsSourceProviderProto.CAPTURED_LEASH;
import static com.android.server.wm.InsetsSourceProviderProto.CLIENT_VISIBLE;
@@ -147,6 +149,7 @@
mStateController.notifyControlChanged(mControlTarget, this);
}
};
+ setFlags(FLAG_INVALID, FLAG_INVALID);
}
private boolean updateInsetsHint() {
@@ -218,12 +221,15 @@
mWindowContainer = windowContainer;
// TODO: remove the frame provider for non-WindowState container.
mFrameProvider = frameProvider;
+ if (frameProvider == null) {
+ // This clears mFlagsFromFrameProvider.
+ mSource.setFlags(mFlagsFromServer);
+ }
mOverrideFrames.clear();
mOverrideFrameProviders = overrideFrameProviders;
if (windowContainer == null) {
setServerVisible(false);
mSource.setVisibleFrame(null);
- mSource.setFlags(0, 0xffffffff);
mSourceFrame.setEmpty();
} else {
mWindowContainer.getInsetsSourceProviders().put(mSource.getId(), this);
@@ -561,7 +567,7 @@
// If the IME is attached to an app window, only consider it initially visible
// if the parent is visible and wasn't part of a transition.
initiallyVisible =
- imeParentWindow != null && !imeParentWindow.inTransitionSelfOrParent()
+ imeParentWindow != null && !imeParentWindow.inTransition()
&& imeParentWindow.isVisible()
&& imeParentWindow.isVisibleRequested();
} else {
@@ -659,9 +665,12 @@
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PROTECTED)
void setServerVisible(boolean serverVisible) {
- mServerVisible = serverVisible;
+ if (mServerVisible != serverVisible) {
+ mServerVisible = serverVisible;
+ setFlags(serverVisible ? 0 : FLAG_INVALID, FLAG_INVALID);
+ updateVisibility();
+ }
updateSourceFrameForServerVisibility();
- updateVisibility();
}
protected void updateVisibility() {
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index c28d66e..051beaf 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -179,7 +179,11 @@
for (int i = mProviders.size() - 1; i >= 0; i--) {
mProviders.valueAt(i).onPostLayout();
}
- if (!mLastState.equals(mState)) {
+ if (!mLastState.equals(
+ mState,
+ false /* excludesCaptionBar */,
+ false /* excludesInvisibleIme */,
+ true /* excludesInvalidSource */)) {
mLastState.set(mState, true /* copySources */);
notifyInsetsChanged();
}
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index ff1e400..b05bc1f 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -39,7 +39,6 @@
import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_TO_SHADE;
import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_WITH_WALLPAPER;
-import static com.android.window.flags.Flags.reduceKeyguardTransitions;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -206,10 +205,10 @@
return;
}
+ final ActionChain chain = mService.mChainTracker.startTransit("setKGShown");
+
if (ENABLE_NEW_KEYGUARD_SHELL_TRANSITIONS) {
- final TransitionController transitionController =
- mWindowManager.mAtmService.getTransitionController();
- final Transition transition = transitionController.getCollectingTransition();
+ final Transition transition = chain.getTransition();
if (transition != null && displayId == DEFAULT_DISPLAY) {
if (!keyguardShowing && state.mKeyguardShowing) {
transition.addFlag(TRANSIT_FLAG_KEYGUARD_GOING_AWAY);
@@ -257,11 +256,11 @@
if (!ENABLE_NEW_KEYGUARD_SHELL_TRANSITIONS) {
if (keyguardChanged) {
dc.requestTransitionAndLegacyPrepare(TRANSIT_TO_FRONT,
- TRANSIT_FLAG_KEYGUARD_APPEARING, /* trigger= */ null);
+ TRANSIT_FLAG_KEYGUARD_APPEARING, /* trigger= */ null, chain);
}
if (mWindowManager.mFlags.mAodTransition && aodChanged && aodShowing) {
dc.requestTransitionAndLegacyPrepare(TRANSIT_TO_FRONT,
- TRANSIT_FLAG_AOD_APPEARING, /* trigger= */ null);
+ TRANSIT_FLAG_AOD_APPEARING, /* trigger= */ null, chain);
}
}
dc.mWallpaperController.adjustWallpaperWindows();
@@ -281,6 +280,7 @@
// Ensure the new state takes effect.
mWindowManager.mWindowPlacerLocked.performSurfacePlacement();
}
+ mService.mChainTracker.endPartial();
}
private void setWakeTransitionReady() {
@@ -305,6 +305,7 @@
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "keyguardGoingAway");
mService.deferWindowLayout();
state.mKeyguardGoingAway = true;
+ final ActionChain chain = mService.mChainTracker.startTransit("kgGoAway");
try {
state.writeEventLog("keyguardGoingAway");
final int transitFlags = convertTransitFlags(flags);
@@ -313,7 +314,7 @@
// TRANSIT_FLAG_KEYGUARD_GOING_AWAY to indicate that it should animate keyguard going
// away.
dc.mAtmService.getTransitionController().requestTransitionIfNeeded(
- TRANSIT_TO_BACK, transitFlags, null /* trigger */, dc);
+ TRANSIT_TO_BACK, transitFlags, null /* trigger */, dc, chain);
updateKeyguardSleepToken();
// Some stack visibility might change (e.g. docked stack)
@@ -325,6 +326,7 @@
scheduleGoingAwayTimeout(displayId);
} finally {
mService.continueWindowLayout();
+ mService.mChainTracker.endPartial();
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
}
@@ -457,8 +459,7 @@
final DisplayContent dc = mRootWindowContainer.getDisplayContent(displayId);
final boolean locked = isKeyguardLocked(displayId);
- final boolean executeTransition = !tc.isShellTransitionsEnabled()
- || (locked && !tc.isCollecting() && !reduceKeyguardTransitions());
+ final boolean executeTransition = !tc.isShellTransitionsEnabled();
final int transitType, transitFlags, notFlags;
if (state.mOccluded) {
@@ -472,15 +473,16 @@
}
mWindowManager.mPolicy.onKeyguardOccludedChangedLw(state.mOccluded);
+ final ActionChain chain = mService.mChainTracker.startTransit("kgOccludeChg");
mService.deferWindowLayout();
try {
if (locked) {
if (tc.isShellTransitionsEnabled()) {
final Task trigger = (state.mOccluded && topActivity != null)
? topActivity.getRootTask() : null;
- tc.requestTransitionIfNeeded(transitType, transitFlags, trigger, dc);
- final Transition transition = tc.getCollectingTransition();
- if ((transition.getFlags() & notFlags) != 0 && reduceKeyguardTransitions()) {
+ tc.requestTransitionIfNeeded(transitType, transitFlags, trigger, dc, chain);
+ final Transition transition = chain.getTransition();
+ if ((transition.getFlags() & notFlags) != 0) {
transition.removeFlag(notFlags);
} else {
transition.addFlag(transitFlags);
@@ -502,6 +504,7 @@
}
} finally {
mService.continueWindowLayout();
+ mService.mChainTracker.endPartial();
}
}
@@ -509,17 +512,19 @@
* Called when keyguard going away state changed.
*/
private void handleDismissInsecureKeyguard(DisplayContent dc) {
+ final ActionChain chain = mService.mChainTracker.startTransit("kgDisInsec");
mService.deferWindowLayout();
try {
// We are deprecating TRANSIT_KEYGUARD_GOING_AWAY for Shell transition and use
// TRANSIT_FLAG_KEYGUARD_GOING_AWAY to indicate that it should animate keyguard going
// away.
dc.mAtmService.getTransitionController().requestTransitionIfNeeded(
- TRANSIT_OPEN, TRANSIT_FLAG_KEYGUARD_GOING_AWAY, null /* trigger */, dc);
+ TRANSIT_OPEN, TRANSIT_FLAG_KEYGUARD_GOING_AWAY, null /* trigger */, dc, chain);
updateKeyguardSleepToken();
mWindowManager.executeAppTransition();
} finally {
mService.continueWindowLayout();
+ mService.mChainTracker.endPartial();
}
}
@@ -779,7 +784,7 @@
controller.handleDismissInsecureKeyguard(display);
controller.scheduleGoingAwayTimeout(mDisplayId);
}
- if (occludedChanged && (reduceKeyguardTransitions() || !startedGoingAway)) {
+ if (occludedChanged) {
controller.handleOccludedChanged(mDisplayId, mTopOccludesActivity);
}
diff --git a/services/core/java/com/android/server/wm/LaunchParamsUtil.java b/services/core/java/com/android/server/wm/LaunchParamsUtil.java
index 9416daf..c65aa2e 100644
--- a/services/core/java/com/android/server/wm/LaunchParamsUtil.java
+++ b/services/core/java/com/android/server/wm/LaunchParamsUtil.java
@@ -18,17 +18,24 @@
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
import static android.util.DisplayMetrics.DENSITY_DEFAULT;
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Display.INVALID_DISPLAY;
+import static android.window.DisplayAreaOrganizer.FEATURE_UNDEFINED;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.ActivityOptions;
import android.content.pm.ActivityInfo;
import android.graphics.Rect;
import android.util.Size;
import android.view.Gravity;
import android.view.View;
+import android.window.WindowContainerToken;
+
+import java.util.function.Consumer;
/**
* The static class that defines some utility constants and functions that are shared among launch
@@ -272,4 +279,182 @@
final int yOffset = (int) (fractionOfVerticalOffset * (stableBounds.height() - height));
inOutBounds.offset(xOffset, yOffset);
}
+
+ @NonNull
+ static TaskDisplayArea getPreferredLaunchTaskDisplayArea(
+ @NonNull ActivityTaskSupervisor supervisor, @Nullable Task task,
+ @Nullable ActivityOptions options, @Nullable ActivityRecord source,
+ @Nullable LaunchParamsController.LaunchParams currentParams,
+ @Nullable ActivityRecord activityRecord, @Nullable ActivityStarter.Request request,
+ @NonNull Consumer<String> logger) {
+ TaskDisplayArea taskDisplayArea = null;
+
+ final WindowContainerToken optionLaunchTaskDisplayAreaToken = options != null
+ ? options.getLaunchTaskDisplayArea() : null;
+ if (optionLaunchTaskDisplayAreaToken != null) {
+ taskDisplayArea = (TaskDisplayArea) WindowContainer.fromBinder(
+ optionLaunchTaskDisplayAreaToken.asBinder());
+ logger.accept("display-area-token-from-option=" + taskDisplayArea);
+ }
+
+ if (taskDisplayArea == null && options != null) {
+ final int launchTaskDisplayAreaFeatureId = options.getLaunchTaskDisplayAreaFeatureId();
+ if (launchTaskDisplayAreaFeatureId != FEATURE_UNDEFINED) {
+ final int launchDisplayId = options.getLaunchDisplayId() == INVALID_DISPLAY
+ ? DEFAULT_DISPLAY : options.getLaunchDisplayId();
+ final DisplayContent dc = supervisor.mRootWindowContainer
+ .getDisplayContent(launchDisplayId);
+ if (dc != null) {
+ taskDisplayArea = dc.getItemFromTaskDisplayAreas(tda ->
+ tda.mFeatureId == launchTaskDisplayAreaFeatureId ? tda : null);
+ logger.accept("display-area-feature-from-option=" + taskDisplayArea);
+ }
+ }
+ }
+
+ // If task display area is not specified in options - try display id
+ if (taskDisplayArea == null) {
+ final int optionLaunchId =
+ options != null ? options.getLaunchDisplayId() : INVALID_DISPLAY;
+ if (optionLaunchId != INVALID_DISPLAY) {
+ final DisplayContent dc = supervisor.mRootWindowContainer
+ .getDisplayContent(optionLaunchId);
+ if (dc != null) {
+ taskDisplayArea = dc.getDefaultTaskDisplayArea();
+ logger.accept("display-from-option=" + optionLaunchId);
+ }
+ }
+ }
+
+ // If the source activity is a no-display activity, pass on the launch display area token
+ // from source activity as currently preferred.
+ if (taskDisplayArea == null && source != null && source.isNoDisplay()) {
+ taskDisplayArea = source.mHandoverTaskDisplayArea;
+ if (taskDisplayArea != null) {
+ logger.accept("display-area-from-no-display-source=" + taskDisplayArea);
+ } else {
+ // Try handover display id
+ final int displayId = source.mHandoverLaunchDisplayId;
+ final DisplayContent dc =
+ supervisor.mRootWindowContainer.getDisplayContent(displayId);
+ if (dc != null) {
+ taskDisplayArea = dc.getDefaultTaskDisplayArea();
+ logger.accept("display-from-no-display-source=" + displayId);
+ }
+ }
+ }
+
+ if (taskDisplayArea == null && source != null) {
+ final TaskDisplayArea sourceDisplayArea = source.getDisplayArea();
+ logger.accept("display-area-from-source=" + sourceDisplayArea);
+ taskDisplayArea = sourceDisplayArea;
+ }
+
+ final Task rootTask = (taskDisplayArea == null && task != null)
+ ? task.getRootTask() : null;
+ if (rootTask != null) {
+ logger.accept("display-from-task=" + rootTask.getDisplayId());
+ taskDisplayArea = rootTask.getDisplayArea();
+ }
+
+ if (taskDisplayArea == null && options != null) {
+ final int callerDisplayId = options.getCallerDisplayId();
+ final DisplayContent dc =
+ supervisor.mRootWindowContainer.getDisplayContent(callerDisplayId);
+ if (dc != null) {
+ taskDisplayArea = dc.getDefaultTaskDisplayArea();
+ logger.accept("display-from-caller=" + callerDisplayId);
+ }
+ }
+
+ if (taskDisplayArea == null && currentParams != null) {
+ taskDisplayArea = currentParams.mPreferredTaskDisplayArea;
+ logger.accept("display-area-from-current-params=" + taskDisplayArea);
+ }
+
+ // Re-route to default display if the device didn't declare support for multi-display
+ if (taskDisplayArea != null && !supervisor.mService.mSupportsMultiDisplay
+ && taskDisplayArea.getDisplayId() != DEFAULT_DISPLAY) {
+ taskDisplayArea = supervisor.mRootWindowContainer.getDefaultTaskDisplayArea();
+ logger.accept("display-area-from-no-multidisplay=" + taskDisplayArea);
+ }
+
+ // Re-route to default display if the home activity doesn't support multi-display
+ if (taskDisplayArea != null && activityRecord != null && activityRecord.isActivityTypeHome()
+ && !supervisor.mRootWindowContainer.canStartHomeOnDisplayArea(activityRecord.info,
+ taskDisplayArea, false /* allowInstrumenting */)) {
+ taskDisplayArea = supervisor.mRootWindowContainer.getDefaultTaskDisplayArea();
+ logger.accept("display-area-from-home=" + taskDisplayArea);
+ }
+
+ return (taskDisplayArea != null)
+ ? taskDisplayArea
+ : getFallbackDisplayAreaForActivity(activityRecord, request, supervisor, logger);
+ }
+
+ /**
+ * Calculates the default {@link TaskDisplayArea} for a task. We attempt to put the activity
+ * within the same display area if possible. The strategy is to find the display in the
+ * following order:
+ *
+ * <ol>
+ * <li>The display area of the top activity from the launching process will be used</li>
+ * <li>The display area of the top activity from the real launching process will be used
+ * </li>
+ * <li>Default display area from the associated root window container.</li>
+ * </ol>
+ * @param activityRecord the activity being started
+ * @param request optional {@link ActivityStarter.Request} made to start the activity record
+ * @return {@link TaskDisplayArea} to house the task
+ */
+ @NonNull
+ private static TaskDisplayArea getFallbackDisplayAreaForActivity(
+ @Nullable ActivityRecord activityRecord, @Nullable ActivityStarter.Request request,
+ @NonNull ActivityTaskSupervisor supervisor, @NonNull Consumer<String> logger) {
+ if (activityRecord != null) {
+ final WindowProcessController controllerFromLaunchingRecord =
+ supervisor.mService.getProcessController(
+ activityRecord.launchedFromPid, activityRecord.launchedFromUid);
+ if (controllerFromLaunchingRecord != null) {
+ final TaskDisplayArea taskDisplayAreaForLaunchingRecord =
+ controllerFromLaunchingRecord.getTopActivityDisplayArea();
+ if (taskDisplayAreaForLaunchingRecord != null) {
+ logger.accept("display-area-for-launching-record="
+ + taskDisplayAreaForLaunchingRecord);
+ return taskDisplayAreaForLaunchingRecord;
+ }
+ }
+
+ final WindowProcessController controllerFromProcess =
+ supervisor.mService.getProcessController(
+ activityRecord.getProcessName(), activityRecord.getUid());
+ if (controllerFromProcess != null) {
+ final TaskDisplayArea displayAreaForRecord =
+ controllerFromProcess.getTopActivityDisplayArea();
+ if (displayAreaForRecord != null) {
+ logger.accept("display-area-for-record=" + displayAreaForRecord);
+ return displayAreaForRecord;
+ }
+ }
+ }
+
+ if (request != null) {
+ final WindowProcessController controllerFromRequest =
+ supervisor.mService.getProcessController(
+ request.realCallingPid, request.realCallingUid);
+ if (controllerFromRequest != null) {
+ final TaskDisplayArea displayAreaFromSourceProcess =
+ controllerFromRequest.getTopActivityDisplayArea();
+ if (displayAreaFromSourceProcess != null) {
+ logger.accept("display-area-source-process=" + displayAreaFromSourceProcess);
+ return displayAreaFromSourceProcess;
+ }
+ }
+ }
+
+ final TaskDisplayArea defaultTaskDisplayArea =
+ supervisor.mRootWindowContainer.getDefaultTaskDisplayArea();
+ logger.accept("display-area-from-default-fallback=" + defaultTaskDisplayArea);
+ return defaultTaskDisplayArea;
+ }
}
diff --git a/services/core/java/com/android/server/wm/Letterbox.java b/services/core/java/com/android/server/wm/Letterbox.java
index 3a4d2ca..f9b76df 100644
--- a/services/core/java/com/android/server/wm/Letterbox.java
+++ b/services/core/java/com/android/server/wm/Letterbox.java
@@ -68,6 +68,7 @@
// for overlaping an app window and letterbox surfaces.
private final LetterboxSurface mFullWindowSurface = new LetterboxSurface("fullWindow");
private final LetterboxSurface[] mSurfaces = { mLeft, mTop, mRight, mBottom };
+ private final Rect[] mTmpSurfacesRect = new Rect[4];
@NonNull
private final AppCompatReachabilityPolicy mAppCompatReachabilityPolicy;
@@ -138,23 +139,11 @@
*
* @param rect The area of the window frame.
*/
- boolean notIntersectsOrFullyContains(Rect rect) {
- int emptyCount = 0;
- int noOverlappingCount = 0;
- for (LetterboxSurface surface : mSurfaces) {
- final Rect surfaceRect = surface.mLayoutFrameGlobal;
- if (surfaceRect.isEmpty()) {
- // empty letterbox
- emptyCount++;
- } else if (!Rect.intersects(surfaceRect, rect)) {
- // no overlapping
- noOverlappingCount++;
- } else if (surfaceRect.contains(rect)) {
- // overlapping and covered
- return true;
- }
+ boolean notIntersectsOrFullyContains(@NonNull Rect rect) {
+ for (int i = 0; i < mTmpSurfacesRect.length; i++) {
+ mTmpSurfacesRect[i] = mSurfaces[i].mLayoutFrameGlobal;
}
- return (emptyCount + noOverlappingCount) == mSurfaces.length;
+ return AppCompatLetterboxUtils.fullyContainsOrNotIntersects(rect, mTmpSurfacesRect);
}
/**
diff --git a/services/core/java/com/android/server/wm/OWNERS b/services/core/java/com/android/server/wm/OWNERS
index 0989fc0..fed7333 100644
--- a/services/core/java/com/android/server/wm/OWNERS
+++ b/services/core/java/com/android/server/wm/OWNERS
@@ -19,8 +19,8 @@
jiamingliu@google.com
pdwilliams@google.com
charlesccchen@google.com
-marziana@google.com
mcarli@google.com
+pbdr@google.com
# Files related to background activity launches
per-file Background*Start* = set noparent
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 142bf25..17a4943 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -184,7 +184,6 @@
private static final long SLEEP_TRANSITION_WAIT_MILLIS = 1000L;
- private Object mLastWindowFreezeSource = null;
// Per-display WindowManager overrides that are passed on.
private final SparseArray<DisplayBrightnessOverrideRequest> mDisplayBrightnessOverrides =
new SparseArray<>();
@@ -2023,12 +2022,17 @@
}
/** Wrapper/Helper for tests */
- void moveActivityToPinnedRootTask(@NonNull ActivityRecord r, String reason) {
- Transition newTransit = (r.mTransitionController.isCollecting()
+ void moveActivityToPinnedRootTaskForTest(@NonNull ActivityRecord r, String reason) {
+ final ActionChain chain = mService.mChainTracker.startTransit("toPinnedTest");
+ Transition newTransit = (chain.isCollecting()
|| !r.mTransitionController.isShellTransitionsEnabled())
? null : r.mTransitionController.createTransition(TRANSIT_PIP);
+ if (newTransit != null) {
+ chain.attachTransition(newTransit);
+ }
moveActivityToPinnedRootTaskInner(r, null /* launchIntoPipHostActivity */, reason,
null /* bounds */, newTransit != null);
+ mService.mChainTracker.endPartial();
}
void moveActivityToPinnedRootTask(@NonNull ActivityRecord r,
@@ -2069,13 +2073,17 @@
try {
// This will change the root pinned task's windowing mode to its original mode, ensuring
// we only have one root task that is in pinned mode.
- final Task rootPinnedTask = taskDisplayArea.getRootPinnedTask();
- if (rootPinnedTask != null) {
- transitionController.collect(rootPinnedTask);
- // The new ActivityRecord should replace the existing PiP, so it's more desirable
- // that the old PiP disappears instead of turning to full-screen at the same time,
- // as the Task#dismissPip is trying to do.
+ // The new ActivityRecord should replace the existing PiP, so it's more desirable
+ // that the old PiP disappears instead of turning to full-screen at the same time,
+ // as the Task#dismissPip is trying to do.
+ if (ActivityTaskManagerService.isPip2ExperimentEnabled()) {
removeRootTasksInWindowingModes(WINDOWING_MODE_PINNED);
+ } else {
+ final Task rootPinnedTask = taskDisplayArea.getRootPinnedTask();
+ if (rootPinnedTask != null) {
+ transitionController.collect(rootPinnedTask);
+ removeRootTasksInWindowingModes(WINDOWING_MODE_PINNED);
+ }
}
transitionController.collect(task);
@@ -2520,8 +2528,10 @@
if (deferred && !display.shouldSleep()) {
transition.abort();
} else {
+ mService.mChainTracker.start("enterPip1", transition);
display.mTransitionController.requestStartTransition(transition,
null /* trigger */, null /* remote */, null /* display */);
+ mService.mChainTracker.end();
// Force playing immediately so that unrelated ops can't be collected.
transition.playNow();
}
@@ -2555,8 +2565,8 @@
if (display.mTransitionController.isShellTransitionsEnabled()
&& !scheduledSleepTransition
- // Only care if there are actual sleep tokens.
- && displayShouldSleep && !display.mAllSleepTokens.isEmpty()) {
+ // Only care if there are actual sleep states.
+ && displayShouldSleep && display.isScreenSleeping()) {
scheduledSleepTransition = true;
if (!mHandler.hasMessages(MSG_SEND_SLEEP_TRANSITION)) {
@@ -2570,9 +2580,10 @@
continue;
}
+ final ActionChain chain = mService.mChainTracker.startTransit("sleepTokens");
// Prepare transition before resume top activity, so it can be collected.
if (!displayShouldSleep && display.mTransitionController.isShellTransitionsEnabled()
- && !display.mTransitionController.isCollecting()) {
+ && !chain.isCollecting()) {
// Use NONE if keyguard is not showing.
int transit = TRANSIT_NONE;
Task startTask = null;
@@ -2585,8 +2596,9 @@
if (wasSleeping) {
transit = TRANSIT_WAKE;
}
- display.mTransitionController.requestStartTransition(
- display.mTransitionController.createTransition(transit, flags),
+ chain.attachTransition(
+ display.mTransitionController.createTransition(transit, flags));
+ display.mTransitionController.requestStartTransition(chain.getTransition(),
startTask, null /* remoteTransition */, null /* displayChange */);
}
// Set the sleeping state of the root tasks on the display.
@@ -2614,6 +2626,7 @@
rootTask.ensureActivitiesVisible(null /* starting */);
}
});
+ mService.mChainTracker.endPartial();
}
if (!scheduledSleepTransition) {
@@ -2747,11 +2760,7 @@
}
if (ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT.isTrue()) {
- if (display.allowContentModeSwitch()) {
- mWindowManager.mDisplayWindowSettings
- .setShouldShowSystemDecorsInternalLocked(display,
- display.mDisplay.canHostTasks());
- }
+ display.updateShouldShowSystemDecorations();
final boolean inTopology = mWindowManager.mDisplayWindowSettings
.shouldShowSystemDecorsLocked(display);
@@ -2971,9 +2980,6 @@
/** This method is called for visible freeform task from top to bottom. */
private void computeNonOccludedFreeformAreaRatio(@NonNull Task task) {
- if (!com.android.window.flags.Flags.processPriorityPolicyForMultiWindowMode()) {
- return;
- }
if (mTmpOccludingRegion == null) {
mTmpOccludingRegion = new Region();
mTmpTaskRegion = new Region();
@@ -3881,6 +3887,10 @@
mHashKey = makeSleepTokenKey(mTag, mDisplayId);
}
+ boolean isScreenOff() {
+ return DISPLAY_OFF_SLEEP_TOKEN_TAG.equals(mTag);
+ }
+
@Override
public String toString() {
return "{\"" + mTag + "\", display " + mDisplayId
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index c5b47f9..1727d64 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -657,11 +657,12 @@
}
@Override
- public void onRectangleOnScreenRequested(IBinder token, Rect rectangle) {
+ public void onRectangleOnScreenRequested(IBinder token, Rect rectangle,
+ @View.RectangleOnScreenRequestSource int source) {
synchronized (mService.mGlobalLock) {
final long identity = Binder.clearCallingIdentity();
try {
- mService.onRectangleOnScreenRequested(token, rectangle);
+ mService.onRectangleOnScreenRequested(token, rectangle, source);
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -916,16 +917,10 @@
int privateFlags, int inputFeatures, int type, IBinder windowToken,
InputTransferToken inputTransferToken, String inputHandleName,
InputChannel outInputChannel) {
- if (!Flags.updateHostInputTransferToken()) {
- // This is not a valid security check, callers can pass in a bogus token. If the
- // token is not known to wm, then input APIs is request focus or transferTouchGesture
- // will fail. Removing this check allows SCVH to be created before associating with a
- // host window.
- if (hostInputTransferToken == null && !mCanAddInternalSystemWindow) {
- // Callers without INTERNAL_SYSTEM_WINDOW permission cannot grant input channel to
- // embedded windows without providing a host window input token
- throw new SecurityException("Requires INTERNAL_SYSTEM_WINDOW permission");
- }
+ if (hostInputTransferToken == null && !mCanAddInternalSystemWindow) {
+ // Callers without INTERNAL_SYSTEM_WINDOW permission cannot grant input channel to
+ // embedded windows without providing a host window input token
+ throw new SecurityException("Requires INTERNAL_SYSTEM_WINDOW permission");
}
final long identity = Binder.clearCallingIdentity();
@@ -940,14 +935,12 @@
}
@Override
- public void updateInputChannel(IBinder channelToken,
- @Nullable InputTransferToken hostInputTransferToken,
- int displayId, SurfaceControl surface,
+ public void updateInputChannel(IBinder channelToken, int displayId, SurfaceControl surface,
int flags, int privateFlags, int inputFeatures, Region region) {
final long identity = Binder.clearCallingIdentity();
try {
- mService.updateInputChannel(channelToken, hostInputTransferToken, displayId, surface,
- flags, mCanAddInternalSystemWindow ? privateFlags : 0, inputFeatures, region);
+ mService.updateInputChannel(channelToken, displayId, surface, flags,
+ mCanAddInternalSystemWindow ? privateFlags : 0, inputFeatures, region);
} finally {
Binder.restoreCallingIdentity(identity);
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index c052246..8a7694c 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -1216,9 +1216,6 @@
mAtmService.notifyTaskPersisterLocked(this, false /* flush */);
- if (!com.android.window.flags.Flags.processPriorityPolicyForMultiWindowMode()) {
- return;
- }
mRootWindowContainer.invalidateTaskLayersAndUpdateOomAdjIfNeeded();
}
@@ -3034,16 +3031,6 @@
return super.makeAnimationLeash().setMetadata(METADATA_TASK_ID, mTaskId);
}
- boolean shouldAnimate() {
- /**
- * Animations are handled by the TaskOrganizer implementation.
- */
- if (isOrganized()) {
- return false;
- }
- return true;
- }
-
@Override
void setInitialSurfaceControlProperties(SurfaceControl.Builder b) {
b.setEffectLayer().setMetadata(METADATA_TASK_ID, mTaskId);
@@ -3107,15 +3094,6 @@
return getActivity((r) -> !r.mIsExiting && r.isClientVisible() && r.isVisibleRequested());
}
- /**
- * Return the top visible activity. The activity has a window on which contents are drawn.
- * However it's possible that the activity has already been requested to be invisible, but the
- * visibility is not yet committed.
- */
- ActivityRecord getTopRealVisibleActivity() {
- return getActivity((r) -> !r.mIsExiting && r.isClientVisible() && r.isVisible());
- }
-
ActivityRecord getTopWaitSplashScreenActivity() {
return getActivity((r) -> {
return r.mHandleExitSplashScreen
@@ -3255,10 +3233,6 @@
return isRootTask() && callback.test(this) ? this : null;
}
- void dontAnimateDimExit() {
- mDimmer.dontAnimateExit();
- }
-
String getName() {
return "Task=" + mTaskId;
}
@@ -3320,6 +3294,10 @@
scheduleAnimation();
}
+ if (mWmService.mFlags.mEnsureSurfaceVisibility) {
+ return;
+ }
+
// Let organizer manage task visibility for shell transition. So don't change it's
// visibility during collecting.
if (mTransitionController.isCollecting() && mCreatedByOrganizer) {
@@ -3343,6 +3321,11 @@
mLastSurfaceShowing = show;
}
+ @Override
+ void updateSurfaceVisibility(SurfaceControl.Transaction t) {
+ t.setVisibility(mSurfaceControl, isVisible());
+ }
+
/**
* Fills in a {@link TaskInfo} with information from this task. Note that the base intent in the
* task info will not include any extras or clip data.
@@ -3525,12 +3508,6 @@
info.topActivityRequestOpenInBrowserEducationTimestamp = 0;
}
- @Nullable PictureInPictureParams getPictureInPictureParams() {
- final Task topTask = getTopMostTask();
- if (topTask == null) return null;
- return getPictureInPictureParams(topTask.getTopMostActivity());
- }
-
private static @Nullable PictureInPictureParams getPictureInPictureParams(ActivityRecord top) {
return (top == null || top.pictureInPictureArgs.empty())
? null : new PictureInPictureParams(top.pictureInPictureArgs);
@@ -3719,8 +3696,9 @@
}
mDecorSurfaceContainer.commitBoostedState();
- // assignChildLayers() calls scheduleAnimation(), which calls prepareSurfaces()
- // to ensure child surface visibility.
+ forAllActivities(mWmService.mAnimator::addSurfaceVisibilityUpdate,
+ true /* traverseTopToBottom */);
+ // This calls scheduleAnimation(), then WindowAnimator will update surface visibility.
assignChildLayers();
}
@@ -4489,7 +4467,8 @@
}
void onPictureInPictureParamsChanged() {
- if (inPinnedWindowingMode() || DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PIP.isTrue()) {
+ if (inPinnedWindowingMode()
+ || DesktopExperienceFlags.ENABLE_DESKTOP_WINDOWING_PIP.isTrue()) {
dispatchTaskInfoChangedIfNeeded(true /* force */);
}
}
@@ -4812,7 +4791,14 @@
// rotation change) after leaving this scope, the visibility operation will be
// put in sync transaction, then it is not synced with reparent.
if (lastParentBeforePip.mSyncState == SYNC_STATE_NONE) {
- lastParentBeforePip.prepareSurfaces();
+ if (mWmService.mFlags.mEnsureSurfaceVisibility) {
+ if (lastParentBeforePip.isVisible()) {
+ lastParentBeforePip.getPendingTransaction().show(
+ lastParentBeforePip.mSurfaceControl);
+ }
+ } else {
+ lastParentBeforePip.prepareSurfaces();
+ }
// If the moveToFront is a part of finishing transition, then make sure
// the z-order of tasks are up-to-date.
if (topActivity.mTransitionController.inFinishingTransition(topActivity)) {
@@ -4874,6 +4860,7 @@
}
final Transition transition = new Transition(TRANSIT_TO_BACK, 0 /* flags */,
mTransitionController, mWmService.mSyncEngine);
+ mAtmService.mChainTracker.start("abortPip", transition);
mTransitionController.moveToCollecting(transition);
mTransitionController.requestStartTransition(transition, this, null /* remoteTransition */,
null /* displayChange */);
@@ -4892,6 +4879,7 @@
top.setWindowingMode(WINDOWING_MODE_UNDEFINED);
top.mWaitForEnteringPinnedMode = false;
}
+ mAtmService.mChainTracker.end();
return true;
}
@@ -5034,9 +5022,10 @@
/** Whether this Task is multi window (exclude PiP) and not filling parent. */
boolean isNonFullscreenMultiWindow() {
- final int windowingMode = getWindowingMode();
- return windowingMode != WINDOWING_MODE_FULLSCREEN && windowingMode != WINDOWING_MODE_PINNED
- && !fillsParent();
+ if (getWindowingMode() == WINDOWING_MODE_PINNED) {
+ return false;
+ }
+ return !fillsParentBounds();
}
/**
@@ -5551,8 +5540,9 @@
Slog.w(TAG, " Force finishing activity "
+ r.intent.getComponent().flattenToShortString());
Task finishedTask = r.getTask();
+ final ActionChain chain = mAtmService.mChainTracker.startTransit("finishTopCrash");
mDisplayContent.requestTransitionAndLegacyPrepare(TRANSIT_CLOSE, TRANSIT_FLAG_APP_CRASHED,
- finishedTask);
+ finishedTask, chain);
r.finishIfPossible(reason, false /* oomAdj */);
// Also terminate any activities below it that aren't yet stopped, to avoid a situation
@@ -5568,6 +5558,7 @@
}
}
}
+ mAtmService.mChainTracker.endPartial();
return finishedTask;
}
@@ -5795,49 +5786,37 @@
tr.forAllActivities(a -> { a.appTimeTracker = timeTracker; });
}
- try {
- // Defer updating the IME layering target since the it will try to get computed before
- // updating all closing and opening apps, which can cause it to get calculated
- // incorrectly.
- mDisplayContent.deferUpdateImeLayeringTarget();
-
- // Don't refocus if invisible to current user
- final ActivityRecord top = tr.getTopNonFinishingActivity();
- if (top == null || !top.showToCurrentUser()) {
- positionChildAtTop(tr);
- if (top != null) {
- mTaskSupervisor.mRecentTasks.add(top.getTask());
- }
- ActivityOptions.abort(options);
- return;
+ // Don't refocus if invisible to current user
+ final ActivityRecord top = tr.getTopNonFinishingActivity();
+ if (top == null || !top.showToCurrentUser()) {
+ positionChildAtTop(tr);
+ if (top != null) {
+ mTaskSupervisor.mRecentTasks.add(top.getTask());
}
+ ActivityOptions.abort(options);
+ return;
+ }
- // Set focus to the top running activity of this task and move all its parents to top.
- top.moveFocusableActivityToTop(reason);
+ // Set focus to the top running activity of this task and move all its parents to top.
+ top.moveFocusableActivityToTop(reason);
- if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION, "Prepare to front transition: task=" + tr);
- if (noAnimation) {
- mTaskSupervisor.mNoAnimActivities.add(top);
- mTransitionController.collect(top);
- mTransitionController.setNoAnimation(top);
- ActivityOptions.abort(options);
- } else {
- updateTransitLocked(TRANSIT_TO_FRONT, options);
- }
+ if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION, "Prepare to front transition: task=" + tr);
+ if (noAnimation) {
+ mTaskSupervisor.mNoAnimActivities.add(top);
+ mTransitionController.collect(top);
+ mTransitionController.setNoAnimation(top);
+ ActivityOptions.abort(options);
+ } else {
+ updateTransitLocked(TRANSIT_TO_FRONT, options);
+ }
- // If a new task is moved to the front, then mark the existing top activity as
- // supporting
+ // If a new task is moved to the front, then mark the existing top activity as supporting
+ // picture-in-picture while paused only if the task would not be considered an overlay on
+ // top of the current activity (eg. not fullscreen, or the assistant)
+ enableEnterPipOnTaskSwitch(pipCandidate, tr, null /* toFrontActivity */, options);
- // picture-in-picture while paused only if the task would not be considered an oerlay
- // on top
- // of the current activity (eg. not fullscreen, or the assistant)
- enableEnterPipOnTaskSwitch(pipCandidate, tr, null /* toFrontActivity */, options);
-
- if (!deferResume) {
- mRootWindowContainer.resumeFocusedTasksTopActivities();
- }
- } finally {
- mDisplayContent.continueUpdateImeLayeringTarget();
+ if (!deferResume) {
+ mRootWindowContainer.resumeFocusedTasksTopActivities();
}
}
@@ -5905,10 +5884,12 @@
// TODO(b/277838915): Consider to make it concurrent to eliminate the special case.
final Transition collecting = mTransitionController.getCollectingTransition();
if (collecting != null && collecting.mType == TRANSIT_OPEN) {
+ final ActionChain chain = mAtmService.mChainTracker.startDefault("taskToBack");
// It can be a CLOSING participate of an OPEN transition. This avoids the deferred
// transition from moving task to back after the task was moved to front.
- collecting.collect(tr);
+ chain.collect(tr);
moveTaskToBackInner(tr, collecting);
+ mAtmService.mChainTracker.endPartial();
return true;
}
final Transition transition = new Transition(TRANSIT_TO_BACK, 0 /* flags */,
@@ -5924,10 +5905,13 @@
transition.abort();
return;
}
+ final ActionChain chain = mAtmService.mChainTracker.start(
+ "taskToBack", transition);
mTransitionController.requestStartTransition(transition, tr,
null /* remoteTransition */, null /* displayChange */);
- mTransitionController.collect(tr);
+ chain.collect(tr);
moveTaskToBackInner(tr, transition);
+ mAtmService.mChainTracker.endPartial();
});
} else {
moveTaskToBackInner(tr, null /* transition */);
@@ -6102,21 +6086,8 @@
.build();
}
- if (com.android.window.flags.Flags.fixLayoutExistingTask()) {
- mTaskSupervisor.getLaunchParamsController()
- .layoutTask(task, info.windowLayout, activity, source, options);
- } else {
- int displayId = getDisplayId();
- if (displayId == INVALID_DISPLAY) displayId = DEFAULT_DISPLAY;
- final boolean isLockscreenShown =
- mAtmService.mKeyguardController.isKeyguardOrAodShowing(displayId);
- if (!mTaskSupervisor.getLaunchParamsController()
- .layoutTask(task, info.windowLayout, activity, source, options)
- && !getRequestedOverrideBounds().isEmpty()
- && task.isResizeable() && !isLockscreenShown) {
- task.setBounds(getRequestedOverrideBounds());
- }
- }
+ mTaskSupervisor.getLaunchParamsController().layoutTask(task, info.windowLayout, activity,
+ source, options);
return task;
}
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index f8ea205..8a435c5 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -38,7 +38,6 @@
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ROOT_TASK;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-import android.annotation.ColorInt;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityOptions;
@@ -46,7 +45,6 @@
import android.content.pm.ActivityInfo;
import android.content.pm.ActivityInfo.ScreenOrientation;
import android.content.res.Configuration;
-import android.graphics.Color;
import android.os.UserHandle;
import android.util.Slog;
import android.view.SurfaceControl;
@@ -78,22 +76,6 @@
DisplayContent mDisplayContent;
- /**
- * Keeps track of the last set color layer so that it can be reset during surface migrations.
- */
- private @ColorInt int mBackgroundColor = 0;
-
- /**
- * 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;
-
// Cached reference to some special tasks we tend to get a lot so we don't need to loop
// through the list to find them.
private Task mRootHomeTask;
@@ -291,14 +273,6 @@
}
@Override
- void setInitialSurfaceControlProperties(SurfaceControl.Builder b) {
- // We want an effect layer instead of the default container layer so that we can set a
- // background color on it for task animations.
- b.setEffectLayer();
- super.setInitialSurfaceControlProperties(b);
- }
-
- @Override
void addChild(WindowContainer child, int position) {
if (child.asTaskDisplayArea() != null) {
if (DEBUG_ROOT_TASK) {
@@ -745,51 +719,10 @@
return startLayer;
}
- void setBackgroundColor(@ColorInt int colorInt) {
- setBackgroundColor(colorInt, false /* restore */);
- }
-
- void setBackgroundColor(@ColorInt int colorInt, boolean restore) {
- mBackgroundColor = colorInt;
- Color color = Color.valueOf(colorInt);
-
- // We don't want to increment the mColorLayerCounter if we are restoring the background
- // color after a surface migration because in that case the mColorLayerCounter already
- // accounts for setting that background color.
- if (!restore) {
- mColorLayerCounter++;
- }
-
- // Only apply the background color if the TDA is actually attached and has a valid surface
- // to set the background color on. We still want to keep track of the background color state
- // even if we are not showing it for when/if the TDA is reattached and gets a valid surface
- if (mSurfaceControl != null) {
- getPendingTransaction()
- .setColor(mSurfaceControl,
- new float[]{color.red(), color.green(), color.blue()});
- scheduleAnimation();
- }
- }
-
- void clearBackgroundColor() {
- mColorLayerCounter--;
-
- // Only clear the color layer if we have received the same amounts of clear as set
- // requests and TDA has a non null surface control (i.e. is attached)
- if (mColorLayerCounter == 0 && mSurfaceControl != null) {
- getPendingTransaction().unsetColor(mSurfaceControl);
- scheduleAnimation();
- }
- }
-
@Override
void migrateToNewSurfaceControl(SurfaceControl.Transaction t) {
super.migrateToNewSurfaceControl(t);
- if (mColorLayerCounter > 0) {
- setBackgroundColor(mBackgroundColor, true /* restore */);
- }
-
reassignLayer(t);
scheduleAnimation();
}
@@ -1624,65 +1557,9 @@
}
/**
- * Adjusts the {@param rootTask} behind the last visible rootTask in the display if necessary.
- * Generally used in conjunction with {@link #moveRootTaskBehindRootTask}.
- */
- // TODO(b/151575894): Remove special root task movement methods.
- void moveRootTaskBehindBottomMostVisibleRootTask(Task rootTask) {
- if (rootTask.shouldBeVisible(null)) {
- // Skip if the root task is already visible
- return;
- }
-
- // Move the root task to the bottom to not affect the following visibility checks
- rootTask.getParent().positionChildAt(POSITION_BOTTOM, rootTask,
- false /* includingParents */);
-
- // Find the next position where the root task should be placed
- final boolean isRootTask = rootTask.isRootTask();
- final int numRootTasks =
- isRootTask ? mChildren.size() : rootTask.getParent().getChildCount();
- for (int rootTaskNdx = 0; rootTaskNdx < numRootTasks; rootTaskNdx++) {
- Task s;
- if (isRootTask) {
- final WindowContainer child = mChildren.get(rootTaskNdx);
- if (child.asTaskDisplayArea() != null) {
- s = child.asTaskDisplayArea().getBottomMostVisibleRootTask(rootTask);
- } else {
- s = child.asTask();
- }
- } else {
- s = rootTask.getParent().getChildAt(rootTaskNdx).asTask();
- }
- if (s == rootTask || s == null) {
- continue;
- }
- final int winMode = s.getWindowingMode();
- final boolean isValidWindowingMode = winMode == WINDOWING_MODE_FULLSCREEN;
- if (s.shouldBeVisible(null) && isValidWindowingMode) {
- // Move the provided root task to behind this root task
- final int position = Math.max(0, rootTaskNdx - 1);
- rootTask.getParent().positionChildAt(position, rootTask,
- false /*includingParents */);
- break;
- }
- }
- }
-
- @Nullable
- private Task getBottomMostVisibleRootTask(Task excludeRootTask) {
- return getRootTask(task -> {
- final int winMode = task.getWindowingMode();
- final boolean isValidWindowingMode = winMode == WINDOWING_MODE_FULLSCREEN;
- return task.shouldBeVisible(null) && isValidWindowingMode;
- }, false /* traverseTopToBottom */);
- }
-
- /**
* Moves the {@param rootTask} behind the given {@param behindRootTask} if possible. If
* {@param behindRootTask} is not currently in the display, then then the root task is moved
- * to the back. Generally used in conjunction with
- * {@link #moveRootTaskBehindBottomMostVisibleRootTask}.
+ * to the back.
*/
void moveRootTaskBehindRootTask(Task rootTask, Task behindRootTask) {
if (behindRootTask == null || behindRootTask == rootTask) {
@@ -1744,9 +1621,7 @@
@Override
boolean canCreateRemoteAnimationTarget() {
- // In the legacy transition system, promoting animation target from TaskFragment to
- // TaskDisplayArea prevents running finish animation. See b/194649929.
- return WindowManagerService.sEnableShellTransitions;
+ 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 a3ecb7e..0a85c4a 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -95,6 +95,7 @@
import android.util.proto.ProtoOutputStream;
import android.view.DisplayInfo;
import android.view.SurfaceControl;
+import android.window.DesktopExperienceFlags;
import android.window.ITaskFragmentOrganizer;
import android.window.TaskFragmentAnimationParams;
import android.window.TaskFragmentInfo;
@@ -420,6 +421,9 @@
private final EnsureActivitiesVisibleHelper mEnsureActivitiesVisibleHelper =
new EnsureActivitiesVisibleHelper(this);
+ private final boolean mEnableSeeThroughTaskFragments =
+ DesktopExperienceFlags.ENABLE_SEE_THROUGH_TASK_FRAGMENTS.isTrue();
+
/** Creates an embedded task fragment. */
TaskFragment(ActivityTaskManagerService atmService, IBinder fragmentToken,
boolean createdByOrganizer) {
@@ -897,16 +901,6 @@
}
/**
- * Checks if all activities in the task fragment are embedded as fully trusted.
- * @see #isFullyTrustedEmbedding(ActivityRecord, int)
- * @param uid uid of the TaskFragment organizer.
- */
- boolean isFullyTrustedEmbedding(int uid) {
- // Traverse all activities to see if any of them are not fully trusted embedding.
- return !forAllActivities(r -> !isFullyTrustedEmbedding(r, uid));
- }
-
- /**
* Checks if all activities in the task fragment are allowed to be embedded in trusted mode.
* @see #isAllowedToEmbedActivityInTrustedMode(ActivityRecord)
*/
@@ -1332,9 +1326,15 @@
continue;
}
- final int otherWindowingMode = other.getWindowingMode();
- if (otherWindowingMode == WINDOWING_MODE_FULLSCREEN
- || (otherWindowingMode != WINDOWING_MODE_PINNED && other.matchParentBounds())) {
+ // Must fill the parent to affect visibility.
+ boolean affectsSiblingVisibility = other.fillsParentBounds();
+ if (mEnableSeeThroughTaskFragments) {
+ // It also must have filling content itself, to prevent empty or only partially
+ // occluding containers from affecting visibility.
+ affectsSiblingVisibility &= other.hasFillingContent();
+ }
+ if (affectsSiblingVisibility) {
+ // This task fragment is fully covered by |other|.
if (isTranslucent(other, starting)) {
// Can be visible behind a translucent TaskFragment.
gotTranslucentFullscreen = true;
@@ -2037,8 +2037,6 @@
// We don't need to schedule another stop, we only need to let it happen.
prev.setState(STOPPING, "completePausedLocked");
} else if (!prev.isVisibleRequested() || shouldSleepOrShutDownActivities()) {
- // Clear out any deferred client hide we might currently have.
- prev.clearDeferHidingClient();
// If we were visible then resumeTopActivities will release resources before
// stopping.
prev.addToStopping(true /* scheduleIdle */, false /* idleDelayed */,
@@ -2053,7 +2051,12 @@
if (resumeNext) {
final Task topRootTask = mRootWindowContainer.getTopDisplayFocusedRootTask();
if (topRootTask != null && !topRootTask.shouldSleepOrShutDownActivities()) {
- mRootWindowContainer.resumeFocusedTasksTopActivities(topRootTask, prev);
+ final boolean resumed =
+ mRootWindowContainer.resumeFocusedTasksTopActivities(topRootTask, prev);
+ if (!resumed && mWmService.mSyncEngine.hasActiveSync()) {
+ // TODO(b/294925498): Remove this once we have accurate ready tracking.
+ mWmService.requestTraversal();
+ }
} else {
// checkReadyForSleep();
final ActivityRecord top =
diff --git a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
index ae329d7..d5d0fa2 100644
--- a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
@@ -258,14 +258,17 @@
taskFragment.onTaskFragmentOrganizerRemoved();
}
+ final ActionChain chain = wasVisible
+ ? mAtmService.mChainTracker.startTransit("TF.dispose") : null;
final TransitionController transitionController = mAtmService.getTransitionController();
- if (wasVisible && transitionController.isShellTransitionsEnabled()
- && !transitionController.isCollecting()) {
+ if (chain != null && transitionController.isShellTransitionsEnabled()
+ && !chain.isCollecting()) {
final Task task = mOrganizedTaskFragments.get(0).getTask();
final boolean containsNonEmbeddedActivity =
task != null && task.getActivity(a -> !a.isEmbedded()) != null;
- transitionController.requestStartTransition(
- transitionController.createTransition(WindowManager.TRANSIT_CLOSE),
+ chain.attachTransition(
+ transitionController.createTransition(WindowManager.TRANSIT_CLOSE));
+ transitionController.requestStartTransition(chain.getTransition(),
// The task will be removed if all its activities are embedded, then the
// task is the trigger.
containsNonEmbeddedActivity ? null : task,
@@ -286,6 +289,9 @@
}
} finally {
mAtmService.continueWindowLayout();
+ if (chain != null) {
+ mAtmService.mChainTracker.endPartial();
+ }
}
for (int i = mDeferredTransitions.size() - 1; i >= 0; i--) {
diff --git a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
index 6c7d979..b60bbd8 100644
--- a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
+++ b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
@@ -33,13 +33,12 @@
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT;
import static android.util.DisplayMetrics.DENSITY_DEFAULT;
-import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.Display.INVALID_DISPLAY;
import static android.window.DisplayAreaOrganizer.FEATURE_UNDEFINED;
import static com.android.server.wm.ActivityStarter.Request;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.wm.LaunchParamsUtil.getPreferredLaunchTaskDisplayArea;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -51,7 +50,6 @@
import android.util.Size;
import android.util.Slog;
import android.view.Gravity;
-import android.window.WindowContainerToken;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.wm.LaunchParamsController.LaunchParams;
@@ -132,8 +130,8 @@
}
// STEP 1: Determine the suggested display area to launch the activity/task.
- final TaskDisplayArea suggestedDisplayArea = getPreferredLaunchTaskDisplayArea(task,
- options, source, currentParams, activity, request);
+ final TaskDisplayArea suggestedDisplayArea = getPreferredLaunchTaskDisplayArea(mSupervisor,
+ task, options, source, currentParams, activity, request, this::appendLog);
outParams.mPreferredTaskDisplayArea = suggestedDisplayArea;
final DisplayContent display = suggestedDisplayArea.mDisplayContent;
if (DEBUG) {
@@ -406,183 +404,6 @@
&& launchMode != task.getRequestedOverrideWindowingMode();
}
- private TaskDisplayArea getPreferredLaunchTaskDisplayArea(@Nullable Task task,
- @Nullable ActivityOptions options, @Nullable ActivityRecord source,
- @Nullable LaunchParams currentParams, @Nullable ActivityRecord activityRecord,
- @Nullable Request request) {
- TaskDisplayArea taskDisplayArea = null;
-
- final WindowContainerToken optionLaunchTaskDisplayAreaToken = options != null
- ? options.getLaunchTaskDisplayArea() : null;
- if (optionLaunchTaskDisplayAreaToken != null) {
- taskDisplayArea = (TaskDisplayArea) WindowContainer.fromBinder(
- optionLaunchTaskDisplayAreaToken.asBinder());
- if (DEBUG) appendLog("display-area-token-from-option=" + taskDisplayArea);
- }
-
- if (taskDisplayArea == null && options != null) {
- final int launchTaskDisplayAreaFeatureId = options.getLaunchTaskDisplayAreaFeatureId();
- if (launchTaskDisplayAreaFeatureId != FEATURE_UNDEFINED) {
- final int launchDisplayId = options.getLaunchDisplayId() == INVALID_DISPLAY
- ? DEFAULT_DISPLAY : options.getLaunchDisplayId();
- final DisplayContent dc = mSupervisor.mRootWindowContainer
- .getDisplayContent(launchDisplayId);
- if (dc != null) {
- taskDisplayArea = dc.getItemFromTaskDisplayAreas(tda ->
- tda.mFeatureId == launchTaskDisplayAreaFeatureId ? tda : null);
- if (DEBUG) appendLog("display-area-feature-from-option=" + taskDisplayArea);
- }
- }
- }
-
- // If task display area is not specified in options - try display id
- if (taskDisplayArea == null) {
- final int optionLaunchId =
- options != null ? options.getLaunchDisplayId() : INVALID_DISPLAY;
- if (optionLaunchId != INVALID_DISPLAY) {
- final DisplayContent dc = mSupervisor.mRootWindowContainer
- .getDisplayContent(optionLaunchId);
- if (dc != null) {
- taskDisplayArea = dc.getDefaultTaskDisplayArea();
- if (DEBUG) appendLog("display-from-option=" + optionLaunchId);
- }
- }
- }
-
- // If the source activity is a no-display activity, pass on the launch display area token
- // from source activity as currently preferred.
- if (taskDisplayArea == null && source != null && source.isNoDisplay()) {
- taskDisplayArea = source.mHandoverTaskDisplayArea;
- if (taskDisplayArea != null) {
- if (DEBUG) appendLog("display-area-from-no-display-source=" + taskDisplayArea);
- } else {
- // Try handover display id
- final int displayId = source.mHandoverLaunchDisplayId;
- final DisplayContent dc =
- mSupervisor.mRootWindowContainer.getDisplayContent(displayId);
- if (dc != null) {
- taskDisplayArea = dc.getDefaultTaskDisplayArea();
- if (DEBUG) appendLog("display-from-no-display-source=" + displayId);
- }
- }
- }
-
- if (taskDisplayArea == null && source != null) {
- final TaskDisplayArea sourceDisplayArea = source.getDisplayArea();
- if (DEBUG) appendLog("display-area-from-source=" + sourceDisplayArea);
- taskDisplayArea = sourceDisplayArea;
- }
-
- Task rootTask = (taskDisplayArea == null && task != null)
- ? task.getRootTask() : null;
- if (rootTask != null) {
- if (DEBUG) appendLog("display-from-task=" + rootTask.getDisplayId());
- taskDisplayArea = rootTask.getDisplayArea();
- }
-
- if (taskDisplayArea == null && options != null) {
- final int callerDisplayId = options.getCallerDisplayId();
- final DisplayContent dc =
- mSupervisor.mRootWindowContainer.getDisplayContent(callerDisplayId);
- if (dc != null) {
- taskDisplayArea = dc.getDefaultTaskDisplayArea();
- if (DEBUG) appendLog("display-from-caller=" + callerDisplayId);
- }
- }
-
- if (taskDisplayArea == null && currentParams != null) {
- taskDisplayArea = currentParams.mPreferredTaskDisplayArea;
- if (DEBUG) appendLog("display-area-from-current-params=" + taskDisplayArea);
- }
-
- // Re-route to default display if the device didn't declare support for multi-display
- if (taskDisplayArea != null && !mSupervisor.mService.mSupportsMultiDisplay
- && taskDisplayArea.getDisplayId() != DEFAULT_DISPLAY) {
- taskDisplayArea = mSupervisor.mRootWindowContainer.getDefaultTaskDisplayArea();
- if (DEBUG) appendLog("display-area-from-no-multidisplay=" + taskDisplayArea);
- }
-
- // Re-route to default display if the home activity doesn't support multi-display
- if (taskDisplayArea != null && activityRecord != null && activityRecord.isActivityTypeHome()
- && !mSupervisor.mRootWindowContainer.canStartHomeOnDisplayArea(activityRecord.info,
- taskDisplayArea, false /* allowInstrumenting */)) {
- taskDisplayArea = mSupervisor.mRootWindowContainer.getDefaultTaskDisplayArea();
- if (DEBUG) appendLog("display-area-from-home=" + taskDisplayArea);
- }
-
- return (taskDisplayArea != null)
- ? taskDisplayArea
- : getFallbackDisplayAreaForActivity(activityRecord, request);
- }
-
- /**
- * Calculates the default {@link TaskDisplayArea} for a task. We attempt to put the activity
- * within the same display area if possible. The strategy is to find the display in the
- * following order:
- *
- * <ol>
- * <li>The display area of the top activity from the launching process will be used</li>
- * <li>The display area of the top activity from the real launching process will be used
- * </li>
- * <li>Default display area from the associated root window container.</li>
- * </ol>
- * @param activityRecord the activity being started
- * @param request optional {@link Request} made to start the activity record
- * @return {@link TaskDisplayArea} to house the task
- */
- private TaskDisplayArea getFallbackDisplayAreaForActivity(
- @Nullable ActivityRecord activityRecord, @Nullable Request request) {
- if (activityRecord != null) {
- WindowProcessController controllerFromLaunchingRecord =
- mSupervisor.mService.getProcessController(
- activityRecord.launchedFromPid, activityRecord.launchedFromUid);
- if (controllerFromLaunchingRecord != null) {
- final TaskDisplayArea taskDisplayAreaForLaunchingRecord =
- controllerFromLaunchingRecord.getTopActivityDisplayArea();
- if (taskDisplayAreaForLaunchingRecord != null) {
- if (DEBUG) {
- appendLog("display-area-for-launching-record="
- + taskDisplayAreaForLaunchingRecord);
- }
- return taskDisplayAreaForLaunchingRecord;
- }
- }
-
- WindowProcessController controllerFromProcess =
- mSupervisor.mService.getProcessController(
- activityRecord.getProcessName(), activityRecord.getUid());
- if (controllerFromProcess != null) {
- final TaskDisplayArea displayAreaForRecord =
- controllerFromProcess.getTopActivityDisplayArea();
- if (displayAreaForRecord != null) {
- if (DEBUG) appendLog("display-area-for-record=" + displayAreaForRecord);
- return displayAreaForRecord;
- }
- }
- }
-
- if (request != null) {
- WindowProcessController controllerFromRequest =
- mSupervisor.mService.getProcessController(
- request.realCallingPid, request.realCallingUid);
- if (controllerFromRequest != null) {
- final TaskDisplayArea displayAreaFromSourceProcess =
- controllerFromRequest.getTopActivityDisplayArea();
- if (displayAreaFromSourceProcess != null) {
- if (DEBUG) {
- appendLog("display-area-source-process=" + displayAreaFromSourceProcess);
- }
- return displayAreaFromSourceProcess;
- }
- }
- }
-
- final TaskDisplayArea defaultTaskDisplayArea =
- mSupervisor.mRootWindowContainer.getDefaultTaskDisplayArea();
- if (DEBUG) appendLog("display-area-from-default-fallback=" + defaultTaskDisplayArea);
- return defaultTaskDisplayArea;
- }
-
private boolean canInheritWindowingModeFromSource(@NonNull DisplayContent display,
TaskDisplayArea suggestedDisplayArea, @Nullable ActivityRecord source) {
if (source == null) {
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index 7f62076..2082f0c 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -616,6 +616,9 @@
return null;
}
t.setPosition(leash, window.mSurfacePosition.x, window.mSurfacePosition.y);
+ if (com.android.window.flags.Flags.splashScreenViewSyncTransaction()) {
+ t.apply();
+ }
return leash;
}
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index 8a93772..c3bc339 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -27,19 +27,15 @@
import android.os.Environment;
import android.os.Handler;
import android.util.ArraySet;
-import android.util.IntArray;
import android.util.Slog;
import android.view.Display;
import android.window.ScreenCapture;
import android.window.TaskSnapshot;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.server.policy.WindowManagerPolicy.ScreenOffListener;
import com.android.server.wm.BaseAppSnapshotPersister.PersistInfoProvider;
-import com.android.window.flags.Flags;
import java.util.ArrayList;
-import java.util.Set;
/**
* When an app token becomes invisible, we take a snapshot (bitmap) of the corresponding task and
@@ -58,7 +54,6 @@
static final String SNAPSHOTS_DIRNAME = "snapshots";
private final TaskSnapshotPersister mPersister;
- private final IntArray mSkipClosingAppSnapshotTasks = new IntArray();
private final ArraySet<Task> mTmpTasks = new ArraySet<>();
private final Handler mHandler = new Handler();
@@ -113,21 +108,6 @@
enableLowResSnapshots, lowResScaleFactor, use16BitFormat);
}
- /**
- * Adds the given {@param tasks} to the list of tasks which should not have their snapshots
- * taken upon the next processing of the set of closing apps. The caller is responsible for
- * calling {@link #snapshotTasks} to ensure that the task has an up-to-date snapshot.
- */
- @VisibleForTesting
- void addSkipClosingAppSnapshotTasks(Set<Task> tasks) {
- if (shouldDisableSnapshots()) {
- return;
- }
- for (Task task : tasks) {
- mSkipClosingAppSnapshotTasks.add(task.mTaskId);
- }
- }
-
void snapshotTasks(ArraySet<Task> tasks) {
for (int i = tasks.size() - 1; i >= 0; i--) {
recordSnapshot(tasks.valueAt(i));
@@ -159,7 +139,7 @@
return;
}
final int mode = getSnapshotMode(task);
- if (Flags.excludeDrawingAppThemeSnapshotFromLock() && mode == SNAPSHOT_MODE_APP_THEME) {
+ if (mode == SNAPSHOT_MODE_APP_THEME) {
mService.mH.post(supplier::handleSnapshot);
} else {
supplier.handleSnapshot();
@@ -245,46 +225,45 @@
}
@Nullable
- private ScreenCapture.ScreenshotHardwareBuffer createImeSnapshot(@NonNull Task task,
- int pixelFormat) {
+ private ScreenCapture.ScreenshotHardwareBuffer createImeScreenshot(@NonNull Task task,
+ @PixelFormat.Format int pixelFormat) {
if (task.getSurfaceControl() == null) {
if (DEBUG_SCREENSHOT) {
- Slog.w(TAG_WM, "Failed to take screenshot. No surface control for " + task);
+ Slog.w(TAG_WM, "Failed to create IME screenshot. No surface control for " + task);
}
return null;
}
final WindowState imeWindow = task.getDisplayContent().mInputMethodWindow;
- ScreenCapture.ScreenshotHardwareBuffer imeBuffer = null;
if (imeWindow != null && imeWindow.isVisible()) {
final Rect bounds = imeWindow.getParentFrame();
bounds.offsetTo(0, 0);
- ScreenCapture.LayerCaptureArgs captureArgs = new ScreenCapture.LayerCaptureArgs.Builder(
+ final var captureArgs = new ScreenCapture.LayerCaptureArgs.Builder(
imeWindow.getSurfaceControl())
.setSourceCrop(bounds)
.setFrameScale(1.0f)
.setPixelFormat(pixelFormat)
.setCaptureSecureLayers(true)
.build();
- imeBuffer = ScreenCapture.captureLayers(captureArgs);
+ return ScreenCapture.captureLayers(captureArgs);
}
- return imeBuffer;
+ return null;
}
/**
- * Create the snapshot of the IME surface on the task which used for placing on the closing
- * task to keep IME visibility while app transitioning.
+ * Captures the screenshot of the IME surface on the task. This will be placed on the closing
+ * task snapshot, to maintain the IME visibility while transitioning to a different task.
*/
@Nullable
- ScreenCapture.ScreenshotHardwareBuffer snapshotImeFromAttachedTask(@NonNull Task task) {
- // Check if the IME targets task ready to take the corresponding IME snapshot, if not,
- // means the task is not yet visible for some reasons and no need to snapshot IME surface.
- if (checkIfReadyToSnapshot(task) == null) {
+ ScreenCapture.ScreenshotHardwareBuffer screenshotImeFromAttachedTask(@NonNull Task task) {
+ // Check if the IME target task is ready to capture the IME screenshot. If not, this means
+ // the task is not yet visible for some reason, so it doesn't need the screenshot.
+ if (checkIfReadyToScreenshot(task) == null) {
return null;
}
final int pixelFormat = mPersistInfoProvider.use16BitFormat()
? PixelFormat.RGB_565
: PixelFormat.RGBA_8888;
- return createImeSnapshot(task, pixelFormat);
+ return createImeScreenshot(task, pixelFormat);
}
@Override
@@ -302,20 +281,6 @@
return topActivity.getLetterboxInsets();
}
- void getClosingTasksInner(Task task, ArraySet<Task> outClosingTasks) {
- // Since RecentsAnimation will handle task snapshot while switching apps with the
- // best capture timing (e.g. IME window capture),
- // No need additional task capture while task is controlled by RecentsAnimation.
- if (isAnimatingByRecents(task)) {
- mSkipClosingAppSnapshotTasks.add(task.mTaskId);
- }
- // If the task of the app is not visible anymore, it means no other app in that task
- // is opening. Thus, the task is closing.
- if (!task.isVisible() && mSkipClosingAppSnapshotTasks.indexOf(task.mTaskId) < 0) {
- outClosingTasks.add(task);
- }
- }
-
void removeAndDeleteSnapshot(int taskId, int userId) {
mCache.onIdRemoved(taskId);
mPersister.removeSnapshot(taskId, userId);
@@ -336,9 +301,6 @@
* Record task snapshots before shutdown.
*/
void prepareShutdown() {
- if (!Flags.recordTaskSnapshotsBeforeShutdown()) {
- return;
- }
final ArrayList<SnapshotSupplier> supplierArrayList = new ArrayList<>();
synchronized (mService.mGlobalLock) {
// Make write items run in a batch.
@@ -367,9 +329,6 @@
}
void waitFlush(long timeout) {
- if (!Flags.recordTaskSnapshotsBeforeShutdown()) {
- return;
- }
mPersister.mSnapshotPersistQueue.waitFlush(timeout);
}
@@ -415,7 +374,7 @@
// Since RecentsAnimation will handle task snapshot while switching apps with the best
// capture timing (e.g. IME window capture), No need additional task capture while task
// is controlled by RecentsAnimation.
- if (task.isVisible() && !isAnimatingByRecents(task)) {
+ if (task.isVisible() && !task.isAnimatingByRecents()) {
mTmpTasks.add(task);
}
}, true /* traverseTopToBottom */);
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotPersister.java b/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
index 538fd8d..5622001 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
@@ -26,7 +26,6 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.pm.UserManagerInternal;
-import com.android.window.flags.Flags;
import java.io.File;
import java.util.Arrays;
@@ -51,7 +50,7 @@
PersistInfoProvider persistInfoProvider,
boolean disableSnapshots) {
super(persistQueue, persistInfoProvider);
- mDisableSnapshots = Flags.checkDisabledSnapshotsInTaskPersister() && disableSnapshots;
+ mDisableSnapshots = disableSnapshots;
}
/**
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 67b3efe..2f88456 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -45,6 +45,7 @@
import static android.view.WindowManager.TransitionFlags;
import static android.view.WindowManager.TransitionType;
import static android.view.WindowManager.transitTypeToString;
+import static android.window.DesktopExperienceFlags.ENABLE_DISPLAY_FOCUS_IN_SHELL_TRANSITIONS;
import static android.window.TaskFragmentAnimationParams.DEFAULT_ANIMATION_BACKGROUND_COLOR;
import static android.window.TransitionInfo.AnimationOptions;
import static android.window.TransitionInfo.FLAGS_IS_OCCLUDED_NO_ANIMATION;
@@ -76,7 +77,6 @@
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_PREDICT_BACK;
import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
import static com.android.server.wm.WindowState.BLAST_TIMEOUT_DURATION;
-import static com.android.window.flags.Flags.enableDisplayFocusInShellTransitions;
import android.annotation.ColorInt;
import android.annotation.IntDef;
@@ -1285,7 +1285,7 @@
}
if (!chain.isFinishing()) {
throw new IllegalStateException("Can't finish on a non-finishing transition "
- + chain.mTransition);
+ + chain.getTransition());
}
mLogger.mFinishTimeNs = SystemClock.elapsedRealtimeNanos();
mController.mLoggerHandler.post(mLogger::logOnFinish);
@@ -1593,7 +1593,7 @@
transientTDA.pauseBackTasks(null /* resuming */);
}
}
- dc.removeImeSurfaceImmediately();
+ dc.removeImeScreenshotImmediately();
dc.handleCompleteDeferredRemoval();
}
validateKeyguardOcclusion();
@@ -1834,6 +1834,7 @@
}
mConfigAtEndActivities = null;
}
+ ensureParticipantSurfaceVisibility();
primaryDisplay.getPendingTransaction().merge(transaction);
primaryDisplay.scheduleAnimation();
mSyncId = -1;
@@ -2041,6 +2042,13 @@
}
}
+ void ensureParticipantSurfaceVisibility() {
+ for (int i = mParticipants.size() - 1; i >= 0; i--) {
+ mController.mAtm.mWindowManager.mAnimator.addSurfaceVisibilityUpdate(
+ mParticipants.valueAt(i));
+ }
+ }
+
@VisibleForTesting
void overrideAnimationOptionsToInfoIfNecessary(@NonNull TransitionInfo info) {
if (mOverrideOptions == null) {
@@ -2169,7 +2177,7 @@
return true;
}
}
- if (enableDisplayFocusInShellTransitions() && mOnTopDisplayStart
+ if (ENABLE_DISPLAY_FOCUS_IN_SHELL_TRANSITIONS.isTrue() && mOnTopDisplayStart
!= mController.mAtm.mRootWindowContainer.getTopFocusedDisplayContent()) {
return true;
}
@@ -2204,7 +2212,7 @@
includesOrderChange = true;
break;
}
- includesOrderChange |= enableDisplayFocusInShellTransitions()
+ includesOrderChange |= ENABLE_DISPLAY_FOCUS_IN_SHELL_TRANSITIONS.isTrue()
&& mOnTopDisplayStart != mOnTopDisplayAtReady;
if (!includesOrderChange && !reportCurrent) {
// This transition doesn't include an order change, so if it isn't required to report
@@ -2237,7 +2245,7 @@
onTopTasksEnd = reportedOnTop != null ? reportedOnTop : new ArrayList<>();
onTopTasksEnd.clear();
- if (enableDisplayFocusInShellTransitions()
+ if (ENABLE_DISPLAY_FOCUS_IN_SHELL_TRANSITIONS.isTrue()
&& mOnTopDisplayStart != onTopDisplayEnd
&& displayId == onTopDisplayEnd.mDisplayId) {
addToTopChange(onTopDisplayEnd);
@@ -2276,6 +2284,7 @@
mFinishTransaction.apply();
}
mController.finishTransition(mController.mAtm.mChainTracker.startFinish("clean-up", this));
+ mController.mAtm.mChainTracker.endPartial();
}
private void cleanUpInternal() {
@@ -2526,7 +2535,7 @@
}
/** Returns the parent that the remote animator can animate or control. */
- private static WindowContainer<?> getAnimatableParent(WindowContainer<?> wc) {
+ static WindowContainer<?> getAnimatableParent(WindowContainer<?> wc) {
WindowContainer<?> parent = wc.getParent();
while (parent != null
&& (!parent.canCreateRemoteAnimationTarget() && !parent.isOrganized())) {
@@ -3986,7 +3995,7 @@
if (mReadyGroups.containsKey(wc)) {
return;
}
- mReadyGroups.put(wc, false);
+ mReadyGroups.put(wc, mReadyOverride);
}
/**
@@ -4007,11 +4016,14 @@
}
}
- /** Marks this as ready regardless of individual groups. */
+ /** Marks everything as ready by default. */
void setAllReady() {
ProtoLog.v(WmProtoLogGroups.WM_DEBUG_WINDOW_TRANSITIONS, " Setting allReady override");
mUsed = true;
mReadyOverride = true;
+ for (int i = 0; i < mReadyGroups.size(); ++i) {
+ mReadyGroups.setValueAt(i, true);
+ }
}
/** @return true if all tracked subtrees are ready. */
@@ -4024,9 +4036,7 @@
if (!mUsed) return false;
// If we are deferring readiness, we never report ready. This is usually temporary.
if (mDeferReadyDepth > 0) return false;
- // Next check all the ready groups to see if they are ready. We can short-cut this if
- // ready-override is set (which is treated as "everything is marked ready").
- if (mReadyOverride) return true;
+ // Next check all the ready groups to see if they are ready.
for (int i = mReadyGroups.size() - 1; i >= 0; --i) {
final WindowContainer wc = mReadyGroups.keyAt(i);
if (!wc.isAttached() || !wc.isVisibleRequested()) continue;
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index 02b53b0..bc79273 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -762,20 +762,21 @@
@Nullable
Transition requestTransitionIfNeeded(@WindowManager.TransitionType int type,
@WindowManager.TransitionFlags int flags, @Nullable WindowContainer trigger,
- @NonNull WindowContainer readyGroupRef) {
+ @NonNull WindowContainer readyGroupRef, @NonNull ActionChain chain) {
if (mTransitionPlayers.isEmpty()) {
return null;
}
Transition newTransition = null;
- if (isCollecting()) {
+ if (chain.isCollecting()) {
// Make the collecting transition wait until this request is ready.
- mCollectingTransition.setReady(readyGroupRef, false);
+ chain.getTransition().setReady(readyGroupRef, false);
if ((flags & KEYGUARD_VISIBILITY_TRANSIT_FLAGS) != 0) {
// Add keyguard flags to affect keyguard visibility
- mCollectingTransition.addFlag(flags & KEYGUARD_VISIBILITY_TRANSIT_FLAGS);
+ chain.getTransition().addFlag(flags & KEYGUARD_VISIBILITY_TRANSIT_FLAGS);
}
} else {
- newTransition = requestStartTransition(createTransition(type, flags),
+ chain.attachTransition(createTransition(type, flags));
+ newTransition = requestStartTransition(chain.getTransition(),
trigger != null ? trigger.asTask() : null, null /* remote */, null /* disp */);
}
return newTransition;
@@ -1008,9 +1009,9 @@
void finishTransition(@NonNull ActionChain chain) {
if (!chain.isFinishing()) {
throw new IllegalStateException("Can't finish on a non-finishing transition "
- + chain.mTransition);
+ + chain.getTransition());
}
- final Transition record = chain.mTransition;
+ final Transition record = chain.getTransition();
// It is usually a no-op but make sure that the metric consumer is removed.
mTransitionMetricsReporter.reportAnimationStart(record.getToken(), 0 /* startTime */);
// It is a no-op if the transition did not change the display.
@@ -1043,6 +1044,15 @@
validateStates();
mAtm.mWindowManager.onAnimationFinished();
}
+
+ // Make sure the surface visibility respects the hierarchy state (updateAnimatingState
+ // should have scheduled a frame to update).
+ record.ensureParticipantSurfaceVisibility();
+ // The targets added by Transition#tryPromote also need to update.
+ for (int i = record.mTargets.size() - 1; i >= 0; i--) {
+ mAtm.mWindowManager.mAnimator.addSurfaceVisibilityUpdate(
+ record.mTargets.get(i).mContainer);
+ }
}
/** Called by {@link Transition#finishTransition} if it committed invisible to any activities */
@@ -1098,6 +1108,9 @@
final boolean isPlaying = !mPlayingTransitions.isEmpty();
Slog.e(TAG, "Set visible without transition " + wc + " playing=" + isPlaying
+ " caller=" + caller);
+ if (mAtm.mWindowManager.mFlags.mEnsureSurfaceVisibility) {
+ return;
+ }
if (!isPlaying) {
WindowContainer.enforceSurfaceVisible(wc);
return;
diff --git a/services/core/java/com/android/server/wm/TransparentPolicy.java b/services/core/java/com/android/server/wm/TransparentPolicy.java
index 88ea073..e12486c 100644
--- a/services/core/java/com/android/server/wm/TransparentPolicy.java
+++ b/services/core/java/com/android/server/wm/TransparentPolicy.java
@@ -338,9 +338,6 @@
if (task == null || task.getWindowingMode() == WINDOWING_MODE_FREEFORM) {
return false;
}
- if (!mActivityRecord.mWmService.mFlags.mRespectNonTopVisibleFixedOrientation) {
- return true;
- }
// Do not enable the policy if the activity can affect display orientation.
final int orientation = mActivityRecord.getOverrideOrientation();
return orientation == SCREEN_ORIENTATION_UNSPECIFIED
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index e4228f9..5256d68 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -276,15 +276,6 @@
return false;
}
- void hideDeferredWallpapersIfNeededLegacy() {
- for (int i = mWallpaperTokens.size() - 1; i >= 0; i--) {
- final WallpaperWindowToken token = mWallpaperTokens.get(i);
- if (!token.isVisibleRequested()) {
- token.commitVisibility(false);
- }
- }
- }
-
void hideWallpapers(final WindowState winGoingAway) {
if (mWallpaperTarget != null
&& (mWallpaperTarget != winGoingAway || mPrevWallpaperTarget != null)) {
diff --git a/services/core/java/com/android/server/wm/WallpaperWindowToken.java b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
index b083dd2..5596363 100644
--- a/services/core/java/com/android/server/wm/WallpaperWindowToken.java
+++ b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
@@ -30,6 +30,7 @@
import android.os.IBinder;
import android.os.RemoteException;
import android.util.SparseArray;
+import android.view.SurfaceControl;
import com.android.internal.protolog.ProtoLog;
@@ -81,9 +82,17 @@
}
@Override
+ void updateSurfaceVisibility(SurfaceControl.Transaction t) {
+ t.setVisibility(mSurfaceControl, isVisible());
+ }
+
+ @Override
public void prepareSurfaces() {
super.prepareSurfaces();
+ if (mWmService.mFlags.mEnsureSurfaceVisibility) {
+ return;
+ }
// Similar to Task.prepareSurfaces, outside of transitions we need to apply visibility
// changes directly. In transitions the transition player will take care of applying the
// visibility change.
@@ -189,6 +198,9 @@
wallpaper.requestUpdateWallpaperIfNeeded();
}
}
+ if (visible != wasClientVisible) {
+ mWmService.mAnimator.addSurfaceVisibilityUpdate(this);
+ }
}
/**
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index e50545d..4f3b1ab 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -72,6 +72,8 @@
private boolean mAnimationFrameCallbackScheduled;
boolean mNotifyWhenNoAnimation = false;
+ private final ArrayList<WindowContainer<?>> mPendingVisibilityUpdates = new ArrayList<>();
+
/**
* A list of runnable that need to be run after {@link WindowContainer#prepareSurfaces} is
* executed and the corresponding transaction is closed and applied.
@@ -154,6 +156,15 @@
dc.prepareSurfaces();
}
+ if (!mPendingVisibilityUpdates.isEmpty()) {
+ Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "updateSurfaceVisibility");
+ for (int i = mPendingVisibilityUpdates.size() - 1; i >= 0; i--) {
+ updateSurfaceVisibility(mPendingVisibilityUpdates.get(i));
+ }
+ mPendingVisibilityUpdates.clear();
+ Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
+ }
+
for (int i = 0; i < numDisplays; i++) {
final DisplayContent dc = root.getChildAt(i);
if (accessibilityController.hasCallbacks()) {
@@ -279,6 +290,44 @@
}
/**
+ * Updates surface visibility of the window container according to its hierarchy visibility
+ * if it is not in an active transition.
+ */
+ private void updateSurfaceVisibility(WindowContainer<?> wc) {
+ if (wc.mSurfaceControl == null) {
+ return;
+ }
+ final TransitionController controller = mService.mRoot.mTransitionController;
+ if (controller.isCollecting(wc) || controller.isPlayingTarget(wc)) {
+ // Let the transition handle surface visibility.
+ return;
+ }
+ wc.updateSurfaceVisibility(mTransaction);
+ }
+
+ /**
+ * The surface visibility of the window container will be evaluated on next frame. Assume the
+ * caller has invoked {@link #scheduleAnimation}.
+ */
+ void addSurfaceVisibilityUpdate(WindowContainer<?> wc) {
+ if (!mService.mFlags.mEnsureSurfaceVisibility) {
+ return;
+ }
+ if (!mPendingVisibilityUpdates.contains(wc)) {
+ mPendingVisibilityUpdates.add(wc);
+ }
+ }
+
+ /** Same as {@link #addSurfaceVisibilityUpdate} but including animatable parents. */
+ void addSurfaceVisibilityUpdateIncludingAnimatableParents(WindowContainer<?> wc) {
+ addSurfaceVisibilityUpdate(wc);
+ for (WindowContainer<?> p = Transition.getAnimatableParent(wc);
+ p != null; p = Transition.getAnimatableParent(p)) {
+ addSurfaceVisibilityUpdate(p);
+ }
+ }
+
+ /**
* Adds a runnable to be executed after {@link WindowContainer#prepareSurfaces} is called and
* the corresponding transaction is closed, applied, and committed.
*/
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 247a51d..027ee22 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
@@ -39,7 +40,6 @@
import static com.android.server.wm.IdentifierProto.TITLE;
import static com.android.server.wm.IdentifierProto.USER_ID;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_ALL;
-import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
@@ -1257,32 +1257,13 @@
}
/**
- * @return {@code true} if in this subtree of the hierarchy we have an
- * {@code ActivityRecord#isAnimating(TRANSITION)}, {@code false} otherwise.
- */
- boolean isAppTransitioning() {
- return getActivity(app -> app.isAnimating(PARENTS | TRANSITION)) != null;
- }
-
- /**
- * Returns {@code true} if self or the parent container of the window is in transition, e.g.
- * the app or recents transition. This method is only used when legacy and shell transition
- * have the same condition to check the animation state.
- */
- boolean inTransitionSelfOrParent() {
- if (!mTransitionController.isShellTransitionsEnabled()) {
- return isAnimating(PARENTS | TRANSITION, ANIMATION_TYPE_APP_TRANSITION);
- }
- return inTransition();
- }
-
- /**
* @return Whether our own container running an animation at the moment.
*/
final boolean isAnimating() {
return isAnimating(0 /* self only */);
}
+ /** Returns {@code true} if itself or its parent container of the window is in transition. */
boolean inTransition() {
return mTransitionController.inTransition(this);
}
@@ -1312,6 +1293,17 @@
}
}
+ /** Updates this surface visibility. This can only be called from {@link WindowAnimator}. */
+ void updateSurfaceVisibility(Transaction t) {
+ // Only ensure visible case by default. Because the surfaces of ActivityRecord, Task and
+ // WallpaperWindowToken can be hidden according to their visibility. While other container
+ // types such as DisplayArea may not usually be involved in a transition, so skip invisible
+ // case to avoid having no chance of being shown again.
+ if (mVisibleRequested) {
+ t.show(mSurfaceControl);
+ }
+ }
+
/**
* Returns true if the container or one of its children is considered visible from the
* WindowManager perspective which usually means valid surface and some other internal state
@@ -1724,6 +1716,54 @@
return false;
}
+ /**
+ * Returns true if this container fills its parent by policy or bounds.
+ *
+ * Note: this does not necessarily mean the container "occludes" its siblings or affects their
+ * lifecycle. A container may fill its parent but have no content in it, so it would be
+ * equivalent to not existing.
+ *
+ * TODO(b/409417223): Consolidate with {@link #matchParentBounds}.
+ */
+ boolean fillsParentBounds() {
+ final int windowingMode = getWindowingMode();
+ return windowingMode == WINDOWING_MODE_FULLSCREEN
+ || (windowingMode != WINDOWING_MODE_PINNED && matchParentBounds());
+ }
+
+ /**
+ * Returns true if this container or its children have content that fills it.
+ *
+ * Note: a container that fills its parent may not occlude its siblings, such as when it is
+ * translucent.
+ */
+ boolean hasFillingContent() {
+ final int childCount = getChildCount();
+ if (childCount == 0) {
+ return false;
+ }
+ for (int i = 0; i < childCount; i++) {
+ final WindowContainer<?> child = getChildAt(i);
+ if (child.fillsParentBounds() && child.hasFillingContent()) {
+ // At least one child fills this container and has content filling itself.
+ return true;
+ }
+ if (child.asTaskFragment() != null
+ && child.asTaskFragment().hasAdjacentTaskFragment()) {
+ // There's at least one child adjacent task fragment. Consider the parent filling
+ // as long as all of the adjacent task fragments have filling content. Whether
+ // or not they fill the parent in union is not important.
+ final boolean allFillingContent = child.hasFillingContent()
+ && !child.asTaskFragment().forOtherAdjacentTaskFragments(
+ tf -> !tf.hasFillingContent());
+ if (allFillingContent) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
/** Computes LONG, SIZE and COMPAT parts of {@link Configuration#screenLayout}. */
static int computeScreenLayout(int sourceScreenLayout, int screenWidthDp,
int screenHeightDp) {
@@ -2706,8 +2746,6 @@
void assignChildLayers(Transaction t) {
int layer = 0;
- // We use two passes as a way to promote children which
- // need Z-boosting to the end of the list.
for (int j = 0; j < mChildren.size(); ++j) {
final WindowContainer wc = mChildren.get(j);
wc.assignChildLayers(t);
@@ -2828,6 +2866,7 @@
}
}
+ /** Updates common surface attributes. This can only be called from {@link WindowAnimator}. */
void prepareSurfaces() {
for (int i = 0; i < mChildren.size(); i++) {
mChildren.get(i).prepareSurfaces();
@@ -2979,17 +3018,6 @@
return getParentSurfaceControl();
}
- // TODO: Remove this and use #getBounds() instead once we set an app transition animation
- // on TaskStack.
- Rect getAnimationBounds(int appRootTaskClipMode) {
- return getBounds();
- }
-
- /** Gets the position relative to parent for animation. */
- void getAnimationPosition(Point outPosition) {
- getRelativePosition(outPosition);
- }
-
final SurfaceAnimationRunner getSurfaceAnimationRunner() {
return mWmService.mSurfaceAnimationRunner;
}
@@ -3164,6 +3192,9 @@
}
static void enforceSurfaceVisible(@NonNull WindowContainer<?> wc) {
+ if (wc.mWmService.mFlags.mEnsureSurfaceVisibility) {
+ return;
+ }
if (wc.mSurfaceControl == null) {
return;
}
@@ -3785,11 +3816,6 @@
return true;
}
- private interface IAnimationStarter {
- void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden,
- @AnimationType int type, @Nullable AnimationAdapter snapshotAnim);
- }
-
void addTrustedOverlay(SurfaceControlViewHost.SurfacePackage overlay,
@Nullable WindowState initialWindowState) {
if (mOverlayHost == null) {
diff --git a/services/core/java/com/android/server/wm/WindowManagerFlags.java b/services/core/java/com/android/server/wm/WindowManagerFlags.java
index 006362c..0a6a11d 100644
--- a/services/core/java/com/android/server/wm/WindowManagerFlags.java
+++ b/services/core/java/com/android/server/wm/WindowManagerFlags.java
@@ -50,12 +50,13 @@
final boolean mInsetsDecoupledConfiguration = Flags.insetsDecoupledConfiguration();
- final boolean mRespectNonTopVisibleFixedOrientation =
- Flags.respectNonTopVisibleFixedOrientation();
-
final boolean mAodTransition = Flags.aodTransition();
final boolean mDispatchFirstKeyguardLockedState = Flags.dispatchFirstKeyguardLockedState();
+ // TODO(b/383241933): Remove isSurfaceShowing(), mLastSurfaceShowing, enforceSurfaceVisible
+ // with this flag.
+ final boolean mEnsureSurfaceVisibility = Flags.respectHierarchySurfaceVisibility();
+
/* End Available Flags */
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 8ed9327..6fa48e8 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -147,7 +147,7 @@
* @param bottom The rectangle bottom.
*/
void onRectangleOnScreenRequested(int displayId, int left, int top, int right,
- int bottom);
+ int bottom, int source);
}
}
@@ -202,7 +202,7 @@
/**
* Called when the region where magnification operates changes. Note that this isn't the
- * entire screen. For example, IMEs are not magnified.
+ * entire screen. For example, IMEs are not always magnified.
*
* @param magnificationRegion the current magnification region
*/
@@ -689,15 +689,15 @@
public abstract void setDismissImeOnBackKeyPressed(boolean dismissImeOnBackKeyPressed);
/**
- * Notifies WindowManagerService that the current IME window status is being changed.
+ * Notifies WindowManagerService that the current IME target window has changed. This will get
+ * set as the new {@link DisplayContent#mImeInputTarget}.
*
* <p>Only {@link com.android.server.inputmethod.InputMethodManagerService} is the expected and
* tested caller of this method.</p>
*
- * @param imeTargetWindowToken token to identify the target window that the IME is associated
- * with
+ * @param windowToken the token to identify the IME target window.
*/
- public abstract void updateInputMethodTargetWindow(@NonNull IBinder imeTargetWindowToken);
+ public abstract void updateImeTargetWindow(@NonNull IBinder windowToken);
/**
* Returns true when the hardware keyboard is available.
@@ -778,7 +778,7 @@
/**
* Checks whether the specified IME client has IME focus or not.
*
- * @param windowToken The window token of the input method client
+ * @param windowToken The token of the IME client window
* @param uid UID of the process to be queried
* @param pid PID of the process to be queried
* @param displayId Display ID reported from the client. Note that this method also verifies
@@ -786,8 +786,9 @@
* @return {@code true} if the IME client specified with {@code uid}, {@code pid}, and
* {@code displayId} has IME focus
*/
- public abstract @ImeClientFocusResult int hasInputMethodClientFocus(IBinder windowToken,
- int uid, int pid, int displayId);
+ @ImeClientFocusResult
+ public abstract int hasInputMethodClientFocus(IBinder windowToken, int uid, int pid,
+ int displayId);
@Retention(SOURCE)
@IntDef({
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 8af7cd2..aca142f 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -96,6 +96,7 @@
import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_MISSING_WINDOW;
import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_NOT_VISIBLE_ON_SCREEN;
import static android.view.flags.Flags.sensitiveContentAppProtection;
+import static android.window.DesktopExperienceFlags.ENABLE_DISPLAY_FOCUS_IN_SHELL_TRANSITIONS;
import static android.window.WindowProviderService.isWindowProviderService;
import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_ADD_REMOVE;
@@ -151,8 +152,8 @@
import static com.android.server.wm.WindowManagerServiceDumpProto.POLICY;
import static com.android.server.wm.WindowManagerServiceDumpProto.ROOT_WINDOW_CONTAINER;
import static com.android.server.wm.WindowManagerServiceDumpProto.WINDOW_FRAMES_VALID;
+import static com.android.systemui.shared.Flags.enableLppAssistInvocationEffect;
import static com.android.window.flags.Flags.enableDeviceStateAutoRotateSettingRefactor;
-import static com.android.window.flags.Flags.enableDisplayFocusInShellTransitions;
import static com.android.window.flags.Flags.enablePresentationForConnectedDisplays;
import static com.android.window.flags.Flags.multiCrop;
import static com.android.window.flags.Flags.setScPropertiesInClient;
@@ -308,6 +309,7 @@
import android.window.AddToSurfaceSyncGroupResult;
import android.window.ClientWindowFrames;
import android.window.ConfigurationChangeSetting;
+import android.window.DesktopExperienceFlags;
import android.window.DesktopModeFlags;
import android.window.IGlobalDragListener;
import android.window.IScreenRecordingCallback;
@@ -444,11 +446,6 @@
static final int LOGTAG_INPUT_FOCUS = 62001;
/**
- * Use WMShell for app transition.
- */
- public static final boolean sEnableShellTransitions = getShellTransitEnabled();
-
- /**
* Allows a fullscreen windowing mode activity to launch in its desired orientation directly
* when the display has different orientation.
*/
@@ -701,6 +698,9 @@
boolean mBootAnimationStopped = false;
long mBootWaitForWindowsStartTime = -1;
+ // Cache whether to Magnify the Navigation Bar and IME.
+ private boolean mMagnifyNavAndIme = false;
+
/** Dump of the windows and app tokens at the time of the last ANR. Cleared after
* LAST_ANR_LIFETIME_DURATION_MSECS */
String mLastANRState;
@@ -819,6 +819,8 @@
Settings.Secure.getUriFor(Settings.Secure.IMMERSIVE_MODE_CONFIRMATIONS);
private final Uri mDisableSecureWindowsUri =
Settings.Secure.getUriFor(Settings.Secure.DISABLE_SECURE_WINDOWS);
+ private final Uri mMagnifyNavAndImeEnabledUri = Settings.Secure.getUriFor(
+ Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MAGNIFY_NAV_AND_IME);
private final Uri mPolicyControlUri =
Settings.Global.getUriFor(Settings.Global.POLICY_CONTROL);
private final Uri mForceDesktopModeOnExternalDisplaysUri = Settings.Global.getUriFor(
@@ -851,6 +853,10 @@
UserHandle.USER_ALL);
resolver.registerContentObserver(mDisableSecureWindowsUri, false, this,
UserHandle.USER_ALL);
+ if (com.android.server.accessibility.Flags.enableMagnificationMagnifyNavBarAndIme()) {
+ resolver.registerContentObserver(mMagnifyNavAndImeEnabledUri, false, this,
+ UserHandle.USER_ALL);
+ }
resolver.registerContentObserver(mPolicyControlUri, false, this, UserHandle.USER_ALL);
resolver.registerContentObserver(mForceDesktopModeOnExternalDisplaysUri, false, this,
UserHandle.USER_ALL);
@@ -907,6 +913,10 @@
return;
}
+ if (mMagnifyNavAndImeEnabledUri.equals(uri)) {
+ updateMagnifyNavAndIme();
+ }
+
if (mDevelopmentOverrideDesktopExperienceUri.equals(uri)) {
updateDevelopmentOverrideDesktopExperience();
return;
@@ -931,6 +941,7 @@
void loadSettings() {
updateMaximumObscuringOpacityForTouch();
updateDisableSecureWindows();
+ updateMagnifyNavAndIme();
}
void updateMaximumObscuringOpacityForTouch() {
@@ -1030,6 +1041,25 @@
mRoot.refreshSecureSurfaceState();
}
}
+
+ void updateMagnifyNavAndIme() {
+ if (!com.android.server.accessibility.Flags.enableMagnificationMagnifyNavBarAndIme()) {
+ mMagnifyNavAndIme = false;
+ return;
+ }
+
+ boolean enabledMagnifyNavAndIme = Settings.Secure.getIntForUser(
+ mContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MAGNIFY_NAV_AND_IME,
+ 0, mCurrentUserId) == 1;
+ if (mMagnifyNavAndIme == enabledMagnifyNavAndIme) {
+ return;
+ }
+
+ synchronized (mGlobalLock) {
+ mMagnifyNavAndIme = enabledMagnifyNavAndIme;
+ }
+ }
}
PowerManager mPowerManager;
@@ -1461,11 +1491,14 @@
}, mTransactionFactory);
mSystemPerformanceHinter.mTraceTag = TRACE_TAG_WINDOW_MANAGER;
- if (Flags.condenseConfigurationChangeForSimpleMode()) {
- LocalServices.addService(
- ConfigurationChangeSetting.ConfigurationChangeSettingInternal.class,
- new ConfigurationChangeSettingInternalImpl());
- }
+ LocalServices.addService(
+ ConfigurationChangeSetting.ConfigurationChangeSettingInternal.class,
+ new ConfigurationChangeSettingInternalImpl());
+ }
+
+ @VisibleForTesting
+ boolean isMagnifyNavAndImeEnabled() {
+ return mMagnifyNavAndIme;
}
DisplayAreaPolicy.Provider getDisplayAreaPolicyProvider() {
@@ -1846,26 +1879,27 @@
// Only a presentation window needs a transition because its visibility affets the
// lifecycle of apps below (b/390481865).
if (enablePresentationForConnectedDisplays() && win.isPresentation()) {
- final boolean wasTransitionOnDisplay =
- win.mTransitionController.isCollectingTransitionOnDisplay(displayContent);
+ final ActionChain chain = mAtmService.mChainTracker.startTransit("addPresoWin");
+ final boolean wasTransitionOnDisplay = chain.isCollectingOnDisplay(displayContent);
Transition newlyCreatedTransition = null;
- if (!win.mTransitionController.isCollecting()) {
- newlyCreatedTransition =
- win.mTransitionController.createAndStartCollecting(TRANSIT_OPEN);
+ if (!chain.isCollecting()) {
+ chain.attachTransition(
+ win.mTransitionController.createAndStartCollecting(TRANSIT_OPEN));
+ newlyCreatedTransition = chain.getTransition();
}
- win.mTransitionController.collect(win.mToken);
+ chain.collect(win.mToken);
res |= addWindowInner(win, displayPolicy, activity, displayContent, outInsetsState,
outAttachedFrame, outActiveControls, client, outSizeCompatScale, attrs,
callingUid);
// A presentation hides all activities behind on the same display.
win.mDisplayContent.ensureActivitiesVisible(/*starting=*/ null,
/*notifyClients=*/ true);
- if (!wasTransitionOnDisplay && win.mTransitionController
- .isCollectingTransitionOnDisplay(displayContent)) {
+ if (!wasTransitionOnDisplay && chain.isCollectingOnDisplay(displayContent)) {
// Set the display ready only when the display gets added to the collecting
// transition in this operation.
win.mTransitionController.setReady(win.mToken);
}
+ mAtmService.mChainTracker.end();
if (newlyCreatedTransition != null) {
win.mTransitionController.requestStartTransition(newlyCreatedTransition, null,
null /* remoteTransition */, null /* displayChange */);
@@ -2297,7 +2331,11 @@
}
}
- public void onRectangleOnScreenRequested(IBinder token, Rect rectangle) {
+ /**
+ * Called when a rectangle of a view is requested to be visible on the screen.
+ */
+ public void onRectangleOnScreenRequested(IBinder token, Rect rectangle,
+ @View.RectangleOnScreenRequestSource int source) {
final AccessibilityController.AccessibilityControllerInternalImpl a11yControllerInternal =
AccessibilityController.getAccessibilityControllerInternal(this);
synchronized (mGlobalLock) {
@@ -2305,7 +2343,7 @@
WindowState window = mWindowMap.get(token);
if (window != null) {
a11yControllerInternal.onRectangleOnScreenRequested(
- window.getDisplayId(), rectangle);
+ window.getDisplayId(), rectangle, source);
}
}
}
@@ -3360,16 +3398,20 @@
@Override
public void moveDisplayToTopIfAllowed(int displayId) {
- moveDisplayToTopInternal(displayId);
- syncInputTransactions(true /* waitForAnimations */);
+ final boolean moved = moveDisplayToTopInternal(displayId);
+ if (moved) {
+ syncInputTransactions(true /* waitForAnimations */);
+ }
}
/**
* Moves the given display to the top. If it cannot be moved to the top this method does
* nothing (e.g. if the display has the flag FLAG_STEAL_TOP_FOCUS_DISABLED set).
* @param displayId The display to move to the top.
+ *
+ * @return whether the move actually occurred.
*/
- void moveDisplayToTopInternal(int displayId) {
+ boolean moveDisplayToTopInternal(int displayId) {
synchronized (mGlobalLock) {
final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
if (displayContent != null && mRoot.getTopChild() != displayContent) {
@@ -3379,15 +3421,16 @@
"Not moving display (displayId=%d) to top. Top focused displayId=%d. "
+ "Reason: FLAG_STEAL_TOP_FOCUS_DISABLED",
displayId, mRoot.getTopFocusedDisplayContent().getDisplayId());
- return;
+ return false;
}
+ final ActionChain chain = mAtmService.mChainTracker.startTransit("dispToTop");
Transition transition = null;
boolean transitionNewlyCreated = false;
- if (enableDisplayFocusInShellTransitions()) {
+ if (ENABLE_DISPLAY_FOCUS_IN_SHELL_TRANSITIONS.isTrue()) {
transition = mAtmService.getTransitionController().requestTransitionIfNeeded(
TRANSIT_TO_FRONT, 0 /* flags */, null /* trigger */,
- displayContent);
+ displayContent, chain);
if (transition != null) {
transitionNewlyCreated = true;
} else {
@@ -3401,10 +3444,12 @@
// Nothing prevented us from moving the display to the top. Let's do it!
displayContent.getParent().positionChildAt(WindowContainer.POSITION_TOP,
displayContent, true /* includingParents */);
+ mAtmService.mChainTracker.end();
if (transitionNewlyCreated) {
transition.setReady(displayContent, true /* ready */);
}
}
+ return true;
}
}
@@ -3792,8 +3837,10 @@
public void setCurrentUser(@UserIdInt int newUserId) {
synchronized (mGlobalLock) {
final TransitionController controller = mAtmService.getTransitionController();
- if (!controller.isCollecting() && controller.isShellTransitionsEnabled()) {
- controller.requestStartTransition(controller.createTransition(TRANSIT_OPEN),
+ final ActionChain chain = mAtmService.mChainTracker.startTransit("setUser");
+ if (!chain.isCollecting() && controller.isShellTransitionsEnabled()) {
+ chain.attachTransition(controller.createTransition(TRANSIT_OPEN));
+ controller.requestStartTransition(chain.getTransition(),
null /* trigger */, null /* remote */, null /* disp */);
}
mCurrentUserId = newUserId;
@@ -3809,14 +3856,26 @@
// Notify whether the root docked task exists for the current user
final DisplayContent displayContent = getDefaultDisplayContentLocked();
- // If the display is already prepared, update the density.
- // Otherwise, we'll update it when it's prepared.
if (mDisplayReady) {
+ // If the display is already prepared, update the density.
+ // Otherwise, we'll update it when it's prepared.
final int forcedDensity = getForcedDisplayDensityForUserLocked(newUserId);
final int targetDensity = forcedDensity != 0
? forcedDensity : displayContent.getInitialDisplayDensity();
displayContent.setForcedDensity(targetDensity, UserHandle.USER_CURRENT);
+
+ // Because DisplayWindowSettingsProvider.mOverrideSettings has been reset for the
+ // new user, we need to update DisplayWindowSettings.mShouldShowSystemDecors to
+ // ensure it reflects the latest value.
+ if (DesktopExperienceFlags.ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT.isTrue()) {
+ final int displayCount = mRoot.mChildren.size();
+ for (int i = 0; i < displayCount; ++i) {
+ final DisplayContent dc = mRoot.mChildren.get(i);
+ dc.updateShouldShowSystemDecorations();
+ }
+ }
}
+ mAtmService.mChainTracker.end();
}
}
@@ -4257,7 +4316,8 @@
}
captureArgs = null;
} else {
- captureArgs = displayContent.getLayerCaptureArgs(predicate);
+ captureArgs = displayContent.getLayerCaptureArgs(predicate,
+ /*useWindowingLayerAsScreenshotRoot*/ enableLppAssistInvocationEffect());
}
}
@@ -4281,12 +4341,16 @@
}
/**
- * Takes a snapshot of the screen. In landscape mode this grabs the whole screen.
- * In portrait mode, it grabs the upper region of the screen based on the vertical dimension
- * of the target image.
+ * Requests a screenshot to be taken for Assist purposes.
+ *
+ * This method initiates the process of capturing the current screen content and delivering it
+ * to the provided {@link IAssistDataReceiver}.
+ *
+ * @param receiver The {@link IAssistDataReceiver} that will receive the screenshot bitmap. Must
+ * not be null.
*/
@Override
- public boolean requestAssistScreenshot(final IAssistDataReceiver receiver) {
+ public void requestAssistScreenshot(final IAssistDataReceiver receiver) {
final ScreenshotHardwareBuffer shb = takeAssistScreenshot(/* predicate= */ null);
final Bitmap bm = shb != null ? shb.asBitmap() : null;
FgThread.getHandler().post(() -> {
@@ -4295,8 +4359,6 @@
} catch (RemoteException e) {
}
});
-
- return true;
}
/**
@@ -6335,12 +6397,6 @@
public void setConfigurationChangeSettingsForUser(
@NonNull List<ConfigurationChangeSetting> settings, int userId) {
setConfigurationChangeSettingsForUser_enforcePermission();
- if (!Flags.condenseConfigurationChangeForSimpleMode()) {
- throw new IllegalStateException(
- "setConfigurationChangeSettingsForUser shouldn't be called when "
- + "condenseConfigurationChangeForSimpleMode is disabled, "
- + "please enable the flag.");
- }
final int callingUserId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
Binder.getCallingUid(), userId, false, true,
@@ -8113,15 +8169,13 @@
}
@Override
- public void updateInputMethodTargetWindow(@NonNull IBinder imeTargetWindowToken) {
+ public void updateImeTargetWindow(@NonNull IBinder windowToken) {
// TODO (b/34628091): Use this method to address the window animation issue.
if (DEBUG_INPUT_METHOD) {
- Slog.w(TAG_WM, "updateInputMethodTargetWindow:"
- + " imeTargetWindowToken=" + imeTargetWindowToken);
+ Slog.w(TAG_WM, "updateImeTargetWindow windowToken: " + windowToken);
}
synchronized (mGlobalLock) {
- final InputTarget imeInputTarget =
- getInputTargetFromWindowTokenLocked(imeTargetWindowToken);
+ final InputTarget imeInputTarget = getInputTargetFromWindowTokenLocked(windowToken);
if (imeInputTarget != null) {
imeInputTarget.getDisplayContent()
.updateImeInputAndControlTarget(imeInputTarget);
@@ -8250,8 +8304,8 @@
}
@Override
- public @ImeClientFocusResult int hasInputMethodClientFocus(IBinder windowToken,
- int uid, int pid, int displayId) {
+ @ImeClientFocusResult
+ public int hasInputMethodClientFocus(IBinder windowToken, int uid, int pid, int displayId) {
if (displayId == Display.INVALID_DISPLAY) {
return ImeClientFocusResult.INVALID_DISPLAY_ID;
}
@@ -8875,7 +8929,7 @@
}
final DisplayContent dc = mRoot.getDisplayContent(displayId);
if (dc == null) {
- Slog.w(TAG, "Invalid displayId:" + displayId + ", fail to show ime screenshot");
+ Slog.w(TAG, "Invalid displayId:" + displayId + ", fail to show IME screenshot");
return false;
}
@@ -8889,12 +8943,12 @@
final DisplayContent dc = mRoot.getDisplayContent(displayId);
if (dc == null) {
Slog.w(TAG, "Invalid displayId:" + displayId
- + ", fail to remove ime screenshot");
+ + ", fail to remove IME screenshot");
return false;
}
- dc.removeImeSurfaceImmediately();
+ dc.removeImeScreenshotImmediately();
+ return true;
}
- return true;
}
}
@@ -9454,12 +9508,10 @@
/**
* Updates the flags on an existing surface's input channel. This assumes the surface provided
- * is the one associated with the provided input-channel. If this isn't the case, behavior is
- * undefined.
+ * is the one associated with the provided input-channel. If this isn't the case, behavior
+ * is undefined.
*/
- void updateInputChannel(IBinder channelToken,
- @Nullable InputTransferToken hostInputTransferToken, int displayId,
- SurfaceControl surface,
+ void updateInputChannel(IBinder channelToken, int displayId, SurfaceControl surface,
int flags, int privateFlags, int inputFeatures, Region region) {
final InputApplicationHandle applicationHandle;
final String name;
@@ -9473,11 +9525,6 @@
name = win.toString();
applicationHandle = win.getApplicationHandle();
win.setIsFocusable((flags & FLAG_NOT_FOCUSABLE) == 0);
- if (Flags.updateHostInputTransferToken()) {
- WindowState hostWindowState = hostInputTransferToken != null
- ? mInputToWindowMap.get(hostInputTransferToken.getToken()) : null;
- win.updateHost(hostWindowState);
- }
}
updateInputChannel(channelToken, win.mOwnerUid, win.mOwnerPid, displayId, surface, name,
@@ -10027,9 +10074,15 @@
if (imeTargetWindowTask == null) {
return false;
}
- if (imeTargetWindow.mActivityRecord != null
- && imeTargetWindow.mActivityRecord.mLastImeShown) {
- return true;
+ if (android.view.inputmethod.Flags.disableImeRestoreOnActivityCreate()) {
+ if (imeTargetWindow.isRequestedVisible(WindowInsets.Type.ime())) {
+ return true;
+ }
+ } else {
+ if (imeTargetWindow.mActivityRecord != null
+ && imeTargetWindow.mActivityRecord.mLastImeShown) {
+ return true;
+ }
}
final TaskSnapshot snapshot = mTaskSnapshotController.getSnapshot(
imeTargetWindowTask.mTaskId, false /* isLowResolution */);
@@ -10456,10 +10509,6 @@
}
}
- private static boolean getShellTransitEnabled() {
- return true;
- }
-
/**
* Dump ViewRootImpl for visible non-activity windows.
*/
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 16eb827..56dcef1 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -91,6 +91,7 @@
import static com.android.server.wm.ActivityTaskManagerService.enforceTaskPermission;
import static com.android.server.wm.ActivityTaskManagerService.isPip2ExperimentEnabled;
import static com.android.server.wm.ActivityTaskSupervisor.REMOVE_FROM_RECENTS;
+import static com.android.server.wm.AppCompatReachabilityPolicy.REACHABILITY_SOURCE_SHELL;
import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_PINNED_TASK;
import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_TASK_ORG;
import static com.android.server.wm.TaskFragment.EMBEDDED_DIM_AREA_PARENT_TASK;
@@ -243,6 +244,7 @@
synchronized (mGlobalLock) {
final ActionChain chain = mService.mChainTracker.startLegacy("applyTransactLegacy");
applyTransaction(t, -1 /*syncId*/, chain, caller);
+ mService.mChainTracker.end();
}
} finally {
Binder.restoreCallingIdentity(ident);
@@ -263,6 +265,7 @@
if (callback == null) {
final ActionChain chain = mService.mChainTracker.startLegacy("applySyncLegacy");
applyTransaction(t, -1 /* syncId*/, chain, caller);
+ mService.mChainTracker.end();
return -1;
}
@@ -284,6 +287,7 @@
mTransitionController.startLegacySyncOrQueue(syncGroup, (deferred) -> {
applyTransaction(t, syncId, mService.mChainTracker.startLegacy(
"applySyncLegacy"), caller, deferred);
+ mService.mChainTracker.end();
setSyncReady(syncId);
});
} else {
@@ -291,6 +295,7 @@
mService.mWindowManager.mSyncEngine.startSyncSet(syncGroup);
applyTransaction(t, syncId, mService.mChainTracker.startLegacy(
"applySyncLegacy"), caller);
+ mService.mChainTracker.end();
setSyncReady(syncId);
} else {
// Because the BLAST engine only supports one sync at a time, queue the
@@ -300,6 +305,7 @@
() -> {
applyTransaction(t, syncId, mService.mChainTracker.startLegacy(
"applySyncLegacy"), caller);
+ mService.mChainTracker.end();
setSyncReady(syncId);
});
}
@@ -338,6 +344,7 @@
}
applyTransaction(t, -1 /* syncId */,
mService.mChainTracker.startLegacy("wrongLegacyTransit"), caller);
+ mService.mChainTracker.end();
return null;
}
final WindowContainerTransaction wct =
@@ -367,6 +374,7 @@
if (needsSetReady) {
setAllReadyIfNeeded(nextTransition, wct);
}
+ mService.mChainTracker.end();
});
return nextTransition.getToken();
}
@@ -377,8 +385,9 @@
+ " means Shell took too long to respond to a request. WM State may be"
+ " incorrect now, please file a bug");
final ActionChain chain = mService.mChainTracker.startFailsafe("startTransit");
- chain.mTransition = null;
+ chain.detachTransition();
applyTransaction(wct, -1 /*syncId*/, chain, caller);
+ mService.mChainTracker.end();
return transition.getToken();
}
// Currently, application of wct can span multiple looper loops (ie.
@@ -401,6 +410,7 @@
if (wctApplied != null) {
wctApplied.meet();
}
+ mService.mChainTracker.end();
}
});
} else {
@@ -411,6 +421,7 @@
if (wctApplied != null) {
wctApplied.meet();
}
+ mService.mChainTracker.end();
}
// Since the transition is already provided, it means WMCore is determining the
// "readiness lifecycle" outside the provided transaction, so don't set ready here.
@@ -500,6 +511,7 @@
}
mTransitionController.finishTransition(chain);
mTransitionController.mFinishingTransition = null;
+ mService.mChainTracker.end();
}
} finally {
Binder.restoreCallingIdentity(ident);
@@ -537,6 +549,7 @@
// No need to worry about transition when Shell transition is not enabled.
applyTransaction(wct, -1 /* syncId */,
mService.mChainTracker.startLegacy("legacyTFTransact"), caller);
+ mService.mChainTracker.end();
return;
}
@@ -545,8 +558,8 @@
// Although there is an active sync, we want to apply the transaction now.
// TODO(b/232042367) Redesign the organizer update on activity callback so that we
// we will know about the transition explicitly.
- final ActionChain chain = mService.mChainTracker.startDefault("tfTransact");
- if (chain.mTransition == null) {
+ final ActionChain chain = mService.mChainTracker.startTransit("tfTransact");
+ if (chain.getTransition() == null) {
// This should rarely happen, and we should try to avoid using
// {@link #applySyncTransaction} with Shell transition.
// We still want to apply and merge the transaction to the active sync
@@ -557,6 +570,7 @@
+ " applySyncTransaction().");
}
applyTransaction(wct, -1 /* syncId */, chain, caller);
+ mService.mChainTracker.end();
return;
}
@@ -569,6 +583,7 @@
}
final ActionChain chain = mService.mChainTracker.start("tfTransact", transition);
final int effects = applyTransaction(wct, -1 /* syncId */, chain, caller, deferred);
+ mService.mChainTracker.end();
if (effects == TRANSACT_EFFECTS_NONE && transition.mParticipants.isEmpty()
// Always send the remote transition even if it is no-op because the remote
// handler may still want to handle it.
@@ -618,21 +633,23 @@
boolean deferResume = true;
mService.mTaskSupervisor.setDeferRootVisibilityUpdate(true /* deferUpdate */);
boolean deferTransitionReady = false;
- if (chain.mTransition != null && !t.isEmpty() && !chain.isFinishing()) {
- if (chain.mTransition.isCollecting()) {
+ Transition transition = chain.getTransition();
+ if (transition != null && !t.isEmpty() && !chain.isFinishing()) {
+ if (transition.isCollecting()) {
deferTransitionReady = true;
- chain.mTransition.deferTransitionReady();
+ transition.deferTransitionReady();
} else {
Slog.w(TAG, "Transition is not collecting when applyTransaction."
- + " transition=" + chain.mTransition + " state="
- + chain.mTransition.getState());
- chain.mTransition = null;
+ + " transition=" + transition + " state="
+ + transition.getState());
+ chain.detachTransition();
+ transition = null;
}
}
try {
final ArraySet<WindowContainer<?>> haveConfigChanges = new ArraySet<>();
- if (chain.mTransition != null) {
- chain.mTransition.applyDisplayChangeIfNeeded(haveConfigChanges);
+ if (transition != null) {
+ transition.applyDisplayChangeIfNeeded(haveConfigChanges);
if (!haveConfigChanges.isEmpty()) {
effects |= TRANSACT_EFFECTS_CLIENT_CONFIG;
}
@@ -640,7 +657,7 @@
final List<WindowContainerTransaction.HierarchyOp> hops = t.getHierarchyOps();
final int hopSize = hops.size();
Iterator<Map.Entry<IBinder, WindowContainerTransaction.Change>> entries;
- if (chain.mTransition != null) {
+ if (transition != null) {
// Mark any config-at-end containers before applying config changes so that
// the config changes don't dispatch to client.
entries = t.getChanges().entrySet().iterator();
@@ -650,7 +667,7 @@
if (!entry.getValue().getConfigAtTransitionEnd()) continue;
final WindowContainer wc = WindowContainer.fromBinder(entry.getKey());
if (wc == null || !wc.isAttached()) continue;
- chain.mTransition.setConfigAtEnd(wc);
+ transition.setConfigAtEnd(wc);
}
}
entries = t.getChanges().entrySet().iterator();
@@ -685,8 +702,8 @@
}
} else {
// Disable entering pip (eg. when recents pretends to finish itself)
- if (chain.mTransition != null) {
- chain.mTransition.setCanPipOnFinish(false /* canPipOnFinish */);
+ if (transition != null) {
+ transition.setCanPipOnFinish(false /* canPipOnFinish */);
}
}
}
@@ -764,11 +781,11 @@
}
} finally {
if (deferTransitionReady) {
- if (chain.mTransition.isCollecting()) {
- chain.mTransition.continueTransitionReady();
+ if (transition.isCollecting()) {
+ transition.continueTransitionReady();
} else {
- Slog.wtf(TAG, "Too late, transition : " + chain.mTransition.getSyncId()
- + " state: " + chain.mTransition.getState() + " is not collecting");
+ Slog.wtf(TAG, "Too late, transition : " + transition.getSyncId()
+ + " state: " + transition.getState() + " is not collecting");
}
}
mService.mTaskSupervisor.setDeferRootVisibilityUpdate(false /* deferUpdate */);
@@ -1175,7 +1192,7 @@
break;
}
case HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT: {
- effects |= reparentChildrenTasksHierarchyOp(hop, chain.mTransition, syncId,
+ effects |= reparentChildrenTasksHierarchyOp(hop, chain.getTransition(), syncId,
isInLockTaskMode);
break;
}
@@ -1219,19 +1236,17 @@
break;
}
final Task currentTask = wc.asTask();
- if (chain.mTransition != null) {
- chain.mTransition.collect(wc);
- }
+ chain.collect(wc);
if (currentTask != null) {
final ActivityRecord top = currentTask.topRunningActivity();
if (top != null) {
final ActivityRecord topOpaqueActivity = top.mAppCompatController
.getTransparentPolicy().getFirstOpaqueActivity().orElse(top);
- if (chain.mTransition != null) {
- chain.mTransition.collect(top);
+ if (chain.isCollecting()) {
+ chain.collect(top);
// We also add the topOpaqueActivity if top is transparent.
if (top != topOpaqueActivity) {
- chain.mTransition.collect(topOpaqueActivity);
+ chain.collect(topOpaqueActivity);
}
}
final Bundle bundle = hop.getAppCompatOptions();
@@ -1239,7 +1254,8 @@
final int doubleTapX = bundle.getInt(REACHABILITY_EVENT_X);
final int doubleTapY = bundle.getInt(REACHABILITY_EVENT_Y);
topOpaqueActivity.mAppCompatController.getReachabilityPolicy()
- .handleDoubleTap(doubleTapX, doubleTapY);
+ .handleDoubleTap(doubleTapX, doubleTapY,
+ REACHABILITY_SOURCE_SHELL);
}
}
}
@@ -1266,13 +1282,13 @@
if (syncId >= 0) {
addToSyncSet(syncId, wc);
}
- if (chain.mTransition != null) {
- chain.mTransition.collect(wc);
+ if (chain.isCollecting()) {
+ chain.collect(wc);
if (hop.isReparent()) {
if (wc.getParent() != null) {
// Collect the current parent. It's visibility may change as
// a result of this reparenting.
- chain.mTransition.collect(wc.getParent());
+ chain.collect(wc.getParent());
}
if (hop.getNewParent() != null) {
final WindowContainer parentWc =
@@ -1281,7 +1297,7 @@
Slog.e(TAG, "Can't resolve parent window from token");
break;
}
- chain.mTransition.collect(parentWc);
+ chain.collect(parentWc);
}
}
}
@@ -1462,8 +1478,7 @@
// tasks does not actually change for the transient-hide tasks, but we do want
// the restoration of these transient-hide tasks to top to be a part of this
// finish transition
- final Transition collectingTransition =
- mTransitionController.getCollectingTransition();
+ final Transition collectingTransition = chain.getTransition();
if (collectingTransition != null) {
collectingTransition.updateChangesForRestoreTransientHideTasks(
transientLaunchTransition);
@@ -1575,8 +1590,9 @@
"Attempt to operate on unknown or detached container: " + container);
break;
}
- if (chain.mTransition != null) {
- chain.mTransition.collect(container);
+ final Transition transition = chain.getTransition();
+ if (transition != null) {
+ transition.collect(container);
}
container.setSafeRegionBounds(hop.getSafeRegionBounds());
effects |= TRANSACT_EFFECTS_CLIENT_CONFIG;
@@ -1627,7 +1643,7 @@
break;
}
createTaskFragment(taskFragmentCreationParams, errorCallbackToken, caller,
- chain.mTransition);
+ chain.getTransition());
break;
}
case OP_TYPE_DELETE_TASK_FRAGMENT: {
@@ -1644,7 +1660,7 @@
break;
}
}
- effects |= deleteTaskFragment(taskFragment, chain.mTransition);
+ effects |= deleteTaskFragment(taskFragment, chain.getTransition());
break;
}
case OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT: {
@@ -1695,7 +1711,7 @@
opType, exception);
break;
}
- if (chain.mTransition != null) {
+ if (chain.isCollecting()) {
chain.collect(activity);
if (activity.getParent() != null) {
// Collect the current parent. Its visibility may change as a result of
@@ -1864,9 +1880,9 @@
// If any TaskFragment in the Task is collected by the transition, we make the decor
// surface visible in sync with the TaskFragment transition. Otherwise, we make the
// decor surface visible immediately.
- final TaskFragment syncTaskFragment = chain.mTransition != null
- ? task.getTaskFragment(chain.mTransition.mParticipants::contains)
- : null;
+ final Transition transition = chain.getTransition();
+ final TaskFragment syncTaskFragment = transition != null
+ ? task.getTaskFragment(transition.mParticipants::contains) : null;
if (syncTaskFragment != null) {
task.moveOrCreateDecorSurfaceFor(taskFragment, false /* visible */);
@@ -1917,7 +1933,7 @@
// The decor surface boost/unboost must be applied after the transition is
// completed. Otherwise, the decor surface could be moved before Shell completes
// the transition, causing flicker.
- runAfterTransition(chain.mTransition, task::commitDecorSurfaceBoostedState);
+ runAfterTransition(chain.getTransition(), task::commitDecorSurfaceBoostedState);
}
break;
}
@@ -2051,11 +2067,16 @@
mGlobalLock.notifyAll();
}
});
- while (starterResult[0] == null) {
- try {
- mGlobalLock.wait();
- } catch (InterruptedException ignored) {
+ try {
+ mService.mChainTracker.pushAsyncStart();
+ while (starterResult[0] == null) {
+ try {
+ mGlobalLock.wait();
+ } catch (InterruptedException ignored) {
+ }
}
+ } finally {
+ mService.mChainTracker.popAsyncStart();
}
return starterResult[0];
}
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index d356128..5f1d1eb 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -480,6 +480,7 @@
boolean handleAppCrash() {
boolean hasVisibleActivity = false;
ArrayList<ActivityRecord> activities = new ArrayList<>(mActivities);
+ final ActionChain chain = mAtm.mChainTracker.startTransit("appCrash");
for (int i = activities.size() - 1; i >= 0; --i) {
final ActivityRecord r = activities.get(i);
Slog.w(TAG, " Force finishing activity "
@@ -488,11 +489,14 @@
if (r.isVisibleRequested()) {
hasVisibleActivity = true;
Task finishingTask = r.getTask();
- r.mDisplayContent.requestTransitionAndLegacyPrepare(TRANSIT_CLOSE,
- TRANSIT_FLAG_APP_CRASHED, finishingTask);
+ if (!chain.isCollecting()) {
+ r.mDisplayContent.requestTransitionAndLegacyPrepare(TRANSIT_CLOSE,
+ TRANSIT_FLAG_APP_CRASHED, finishingTask, chain);
+ }
}
r.destroyIfPossible("handleAppCrashed");
}
+ mAtm.mChainTracker.end();
return hasVisibleActivity;
}
@@ -1285,8 +1289,6 @@
stateFlags |= ACTIVITY_STATE_FLAG_HAS_RESUMED;
final int windowingMode = r.getWindowingMode();
if (windowingMode == WINDOWING_MODE_MULTI_WINDOW
- && com.android.window.flags.Flags
- .processPriorityPolicyForMultiWindowMode()
&& task.hasAdjacentTask()) {
stateFlags |= ACTIVITY_STATE_FLAG_RESUMED_SPLIT_SCREEN;
} else if (windowingMode == WINDOWING_MODE_FREEFORM) {
@@ -1340,7 +1342,6 @@
}
if (hasResumedFreeform
- && com.android.window.flags.Flags.processPriorityPolicyForMultiWindowMode()
// Exclude task layer 1 because it is already the top most.
&& minTaskLayer > 1) {
if (minTaskLayer <= 1 + MAX_NUM_PERCEPTIBLE_FREEFORM
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 2903347..8880813 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1847,13 +1847,6 @@
boolean isReadyForDisplay() {
final boolean parentAndClientVisible = !isParentWindowHidden()
&& mViewVisibility == View.VISIBLE;
- // TODO(b/338426357): Remove this once the last target using legacy transitions is moved to
- // shell transitions
- if (!mTransitionController.isShellTransitionsEnabled()) {
- return mHasSurface && isVisibleByPolicy() && !mDestroying
- && ((parentAndClientVisible && mToken.isVisible())
- || isAnimating(TRANSITION | PARENTS));
- }
return mHasSurface && isVisibleByPolicy() && !mDestroying && mToken.isVisible()
&& (parentAndClientVisible || isAnimating(TRANSITION | PARENTS));
}
@@ -2211,8 +2204,8 @@
mAttrs.type, false /* visible */, true /* removed */, dc.getDisplayId());
}
if (isImeLayeringTarget()) {
- // Remove the attached IME screenshot surface.
- dc.removeImeSurfaceByTarget(this);
+ // Remove the attached IME screenshot.
+ dc.removeImeScreenshotByTarget(this);
// Set mImeLayeringTarget as null when the removed window is the IME layering target,
// in case computeImeLayeringTarget may use the outdated target.
dc.setImeLayeringTarget(null /* target */);
@@ -2367,14 +2360,16 @@
// Only a presentation window needs a transition because its visibility affets the
// lifecycle of apps below (b/390481865).
if (enablePresentationForConnectedDisplays() && isPresentation()) {
- final boolean wasTransitionOnDisplay =
- mTransitionController.isCollectingTransitionOnDisplay(displayContent);
+ final ActionChain chain =
+ mWmService.mAtmService.mChainTracker.startTransit("removeWin");
+ final boolean wasTransitionOnDisplay = chain.isCollectingOnDisplay(displayContent);
Transition newlyCreatedTransition = null;
- if (!mTransitionController.isCollecting()) {
- newlyCreatedTransition =
- mTransitionController.createAndStartCollecting(TRANSIT_CLOSE);
+ if (!chain.isCollecting()) {
+ chain.attachTransition(
+ mTransitionController.createAndStartCollecting(TRANSIT_CLOSE));
+ newlyCreatedTransition = chain.getTransition();
}
- mTransitionController.collect(mToken);
+ chain.collect(mToken);
mAnimatingExit = true;
mRemoveOnExit = true;
mToken.setVisibleRequested(false);
@@ -2387,6 +2382,7 @@
// transition in this operation.
mTransitionController.setReady(mToken);
}
+ mWmService.mAtmService.mChainTracker.endPartial();
if (newlyCreatedTransition != null) {
mTransitionController.requestStartTransition(newlyCreatedTransition, null,
null /* remoteTransition */, null /* displayChange */);
@@ -2776,7 +2772,7 @@
final boolean wasShowWhenLocked = (sa.flags & FLAG_SHOW_WHEN_LOCKED) != 0;
final boolean removeShowWhenLocked = (mAttrs.flags & FLAG_SHOW_WHEN_LOCKED) == 0;
sa.flags = (sa.flags & ~mask) | (mAttrs.flags & mask);
- if (Flags.keepAppWindowHideWhileLocked() && wasShowWhenLocked && removeShowWhenLocked) {
+ if (wasShowWhenLocked && removeShowWhenLocked) {
// Trigger unoccluding animation if needed.
mActivityRecord.checkKeyguardFlagsChanged();
mActivityRecord.deferStartingWindowRemovalForKeyguardUnoccluding();
@@ -3240,7 +3236,7 @@
} else {
logExclusionRestrictions(EXCLUSION_LEFT);
logExclusionRestrictions(EXCLUSION_RIGHT);
- getDisplayContent().removeImeSurfaceByTarget(this);
+ getDisplayContent().removeImeScreenshotByTarget(this);
}
// Exclude toast because legacy apps may show toast window by themselves, so the misused
// apps won't always be considered as foreground state.
@@ -4575,52 +4571,10 @@
}
boolean isAnimationRunningSelfOrParent() {
- return inTransitionSelfOrParent()
+ return inTransition()
|| isAnimating(0 /* flags */, ANIMATION_TYPE_WINDOW_ANIMATION);
}
- private boolean shouldFinishAnimatingExit() {
- // Exit animation might be applied soon.
- if (inTransition()) {
- ProtoLog.d(WM_DEBUG_APP_TRANSITIONS, "shouldWaitAnimatingExit: isTransition: %s",
- this);
- return false;
- }
- if (!mDisplayContent.okToAnimate()) {
- return true;
- }
- // Exit animation is running.
- if (isAnimationRunningSelfOrParent()) {
- ProtoLog.d(WM_DEBUG_APP_TRANSITIONS, "shouldWaitAnimatingExit: isAnimating: %s",
- this);
- return false;
- }
- // If the wallpaper is currently behind this app window, we need to change both of
- // them inside of a transaction to avoid artifacts.
- if (mDisplayContent.mWallpaperController.isWallpaperTarget(this)) {
- ProtoLog.d(WM_DEBUG_APP_TRANSITIONS,
- "shouldWaitAnimatingExit: isWallpaperTarget: %s", this);
- return false;
- }
- return true;
- }
-
- /**
- * If this is window is stuck in the animatingExit status, resume clean up procedure blocked
- * by the exit animation.
- */
- void cleanupAnimatingExitWindow() {
- // TODO(b/205335975): WindowManagerService#tryStartExitingAnimation starts an exit animation
- // and set #mAnimationExit. After the exit animation finishes, #onExitAnimationDone shall
- // be called, but there seems to be a case that #onExitAnimationDone is not triggered, so
- // a windows stuck in the animatingExit status.
- if (mAnimatingExit && shouldFinishAnimatingExit()) {
- ProtoLog.w(WM_DEBUG_APP_TRANSITIONS, "Clear window stuck on animatingExit status: %s",
- this);
- onExitAnimationDone();
- }
- }
-
void onExitAnimationDone() {
if (ProtoLog.isEnabled(WM_DEBUG_ANIM, LogLevel.VERBOSE)) {
final AnimationAdapter animationAdapter = mSurfaceAnimator.getAnimation();
@@ -4960,15 +4914,17 @@
@Override
boolean shouldMagnify() {
if (mAttrs.type == TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY
- || mAttrs.type == TYPE_INPUT_METHOD
- || mAttrs.type == TYPE_INPUT_METHOD_DIALOG
|| mAttrs.type == TYPE_MAGNIFICATION_OVERLAY
- || mAttrs.type == TYPE_NAVIGATION_BAR
// It's tempting to wonder: Have we forgotten the rounded corners overlay?
// worry not: it's a fake TYPE_NAVIGATION_BAR_PANEL
|| mAttrs.type == TYPE_NAVIGATION_BAR_PANEL) {
return false;
}
+ if (mAttrs.type == TYPE_INPUT_METHOD
+ || mAttrs.type == TYPE_INPUT_METHOD_DIALOG
+ || mAttrs.type == TYPE_NAVIGATION_BAR) {
+ return mWmService.isMagnifyNavAndImeEnabled();
+ }
if ((mAttrs.privateFlags & PRIVATE_FLAG_NOT_MAGNIFIABLE) != 0) {
return false;
}
@@ -5721,6 +5677,11 @@
}
@Override
+ boolean hasFillingContent() {
+ return true;
+ }
+
+ @Override
boolean showWallpaper() {
if (!isVisibleRequested()
// in multi-window mode, wallpaper is always visible at the back and not tied to
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 7eca487..38923ba 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -46,13 +46,11 @@
import static com.android.server.wm.WindowManagerService.logWithStack;
import static com.android.server.wm.WindowStateAnimatorProto.DRAW_STATE;
import static com.android.server.wm.WindowStateAnimatorProto.SURFACE;
-import static com.android.server.wm.WindowStateAnimatorProto.SYSTEM_DECOR_RECT;
import static com.android.server.wm.WindowSurfaceControllerProto.SHOWN;
import static com.android.window.flags.Flags.setScPropertiesInClient;
import android.content.Context;
import android.graphics.PixelFormat;
-import android.graphics.Rect;
import android.os.Debug;
import android.os.Trace;
import android.util.EventLog;
@@ -110,12 +108,6 @@
float mAlpha = 0;
float mLastAlpha = 0;
- /**
- * This is rectangle of the window's surface that is not covered by
- * system decorations.
- */
- private final Rect mSystemDecorRect = new Rect();
-
// Set to true if, when the window gets displayed, it should perform
// an enter animation.
boolean mEnterAnimationPending;
@@ -587,7 +579,6 @@
proto.end(dumpToken);
}
proto.write(DRAW_STATE, mDrawState);
- mSystemDecorRect.dumpDebug(proto, SYSTEM_DECOR_RECT);
proto.end(token);
}
@@ -605,7 +596,6 @@
pw.print(prefix); pw.print("mDrawState="); pw.print(drawStateToString());
pw.print(prefix); pw.print(" mLastHidden="); pw.println(mLastHidden);
pw.print(prefix); pw.print("mEnterAnimationPending=" + mEnterAnimationPending);
- pw.print(prefix); pw.print("mSystemDecorRect="); mSystemDecorRect.printShortString(pw);
pw.println();
}
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index 7823b92..54454b8 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -254,6 +254,11 @@
return super.handleCompleteDeferredRemoval();
}
+ @Override
+ boolean hasFillingContent() {
+ return true;
+ }
+
/**
* @return The scale for applications running in compatibility mode. Multiply the size in the
* application by this scale will be the size in the screen.
@@ -497,7 +502,7 @@
final ActivityRecord r =
mFixedRotationTransformState.mAssociatedTokens.get(i).asActivityRecord();
// Only care about the transition at Activity/Task level.
- if (r != null && r.inTransitionSelfOrParent() && !r.mDisplayContent.inTransition()) {
+ if (r != null && r.inTransition() && !r.mDisplayContent.inTransition()) {
return true;
}
}
@@ -546,13 +551,16 @@
if (mTransitionController.isShellTransitionsEnabled()
&& asActivityRecord() != null && isVisible()) {
// Trigger an activity level rotation transition.
- Transition transition = mTransitionController.getCollectingTransition();
- if (transition == null) {
- transition = mTransitionController.requestStartTransition(
- mTransitionController.createTransition(WindowManager.TRANSIT_CHANGE),
+ final ActionChain chain =
+ mWmService.mAtmService.mChainTracker.startTransit("cancelFixedRot");
+ if (!chain.isCollecting()) {
+ chain.attachTransition(
+ mTransitionController.createTransition(WindowManager.TRANSIT_CHANGE));
+ mTransitionController.requestStartTransition(chain.getTransition(),
null /* trigger */, null /* remote */, null /* disp */);
}
- transition.collect(this);
+ final Transition transition = chain.getTransition();
+ chain.collect(this);
transition.collectVisibleChange(this);
transition.setReady(mDisplayContent, true);
}
@@ -684,6 +692,11 @@
return super.prepareSync();
}
+ @Override
+ void updateSurfaceVisibility(SurfaceControl.Transaction t) {
+ // Regular window token doesn't change surface visibility.
+ }
+
@CallSuper
@Override
public void dumpDebug(ProtoOutputStream proto, long fieldId,
diff --git a/services/core/java/com/android/server/wm/WindowTracingDataSource.java b/services/core/java/com/android/server/wm/WindowTracingDataSource.java
index b92e525..fe44f0e 100644
--- a/services/core/java/com/android/server/wm/WindowTracingDataSource.java
+++ b/services/core/java/com/android/server/wm/WindowTracingDataSource.java
@@ -18,6 +18,7 @@
import static android.tracing.perfetto.DataSourceParams.PERFETTO_DS_BUFFER_EXHAUSTED_POLICY_STALL_AND_ABORT;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.internal.perfetto.protos.DataSourceConfigOuterClass.DataSourceConfig;
import android.internal.perfetto.protos.WindowmanagerConfig.WindowManagerConfig;
@@ -34,19 +35,42 @@
import java.io.IOException;
import java.lang.ref.WeakReference;
-import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
public final class WindowTracingDataSource extends DataSource<WindowTracingDataSource.Instance,
WindowTracingDataSource.TlsState, Void> {
+
+ @IntDef({
+ com.android.server.wm.WindowTracingDataSource.Status.WAITING_START_EVENT,
+ com.android.server.wm.WindowTracingDataSource.Status
+ .WAITING_START_EVENT_WITH_DEFERRED_STOP,
+ com.android.server.wm.WindowTracingDataSource.Status.WRITING_START_EVENT,
+ com.android.server.wm.WindowTracingDataSource.Status
+ .WRITING_START_EVENT_WITH_DEFERRED_STOP,
+ com.android.server.wm.WindowTracingDataSource.Status.STARTED,
+ com.android.server.wm.WindowTracingDataSource.Status.STOPPED,
+ })
+ @interface Status {
+ int WAITING_START_EVENT = 0;
+ int WAITING_START_EVENT_WITH_DEFERRED_STOP = 1;
+ int WRITING_START_EVENT = 2;
+ int WRITING_START_EVENT_WITH_DEFERRED_STOP = 3;
+ int STARTED_WITH_DEFERRED_STOP = 4;
+ int STARTED = 5;
+ int STOPPED = 6;
+ }
+
public static class TlsState {
public final Config mConfig;
- public final AtomicBoolean mIsStarting = new AtomicBoolean(true);
+ public final AtomicInteger mStatus;
- private TlsState(Config config) {
+ private TlsState(Config config, AtomicInteger status) {
mConfig = config;
+ mStatus = status;
}
}
+
public static class Config {
public final @WindowTracingLogLevel int mLogLevel;
public final @WindowTracingLogFrequency int mLogFrequency;
@@ -61,10 +85,12 @@
public abstract static class Instance extends DataSourceInstance {
public final Config mConfig;
+ public final AtomicInteger mStatus;
public Instance(DataSource dataSource, int instanceIndex, Config config) {
super(dataSource, instanceIndex);
mConfig = config;
+ mStatus = new AtomicInteger(Status.WAITING_START_EVENT);
}
}
@@ -85,6 +111,7 @@
new DataSourceParams.Builder()
.setBufferExhaustedPolicy(
PERFETTO_DS_BUFFER_EXHAUSTED_POLICY_STALL_AND_ABORT)
+ .setPostponeStop(true)
.build();
register(params);
Log.i(TAG, "Registered with perfetto service");
@@ -101,13 +128,15 @@
if (windowTracing != null) {
windowTracing.onStart(mConfig);
}
+
+
}
@Override
protected void onStop(StopCallbackArguments args) {
WindowTracingPerfetto windowTracing = mWindowTracing.get();
if (windowTracing != null) {
- windowTracing.onStop(mConfig);
+ windowTracing.onStop(this);
}
}
};
@@ -119,9 +148,9 @@
try (Instance dsInstance = args.getDataSourceInstanceLocked()) {
if (dsInstance == null) {
// Datasource instance has been removed
- return new TlsState(CONFIG_DEFAULT);
+ return new TlsState(CONFIG_DEFAULT, new AtomicInteger(Status.WAITING_START_EVENT));
}
- return new TlsState(dsInstance.mConfig);
+ return new TlsState(dsInstance.mConfig, dsInstance.mStatus);
}
}
diff --git a/services/core/java/com/android/server/wm/WindowTracingPerfetto.java b/services/core/java/com/android/server/wm/WindowTracingPerfetto.java
index 1b42e13..921d623 100644
--- a/services/core/java/com/android/server/wm/WindowTracingPerfetto.java
+++ b/services/core/java/com/android/server/wm/WindowTracingPerfetto.java
@@ -19,6 +19,8 @@
import android.annotation.Nullable;
import android.internal.perfetto.protos.TracePacketOuterClass.TracePacket;
import android.internal.perfetto.protos.WinscopeExtensionsImplOuterClass.WinscopeExtensionsImpl;
+import android.os.Handler;
+import android.os.Looper;
import android.os.ShellCommand;
import android.os.SystemClock;
import android.util.Log;
@@ -27,7 +29,10 @@
import com.android.internal.annotations.VisibleForTesting;
+import android.os.Trace;
+
import java.io.PrintWriter;
+import java.util.ArrayList;
import java.util.concurrent.atomic.AtomicInteger;
class WindowTracingPerfetto extends WindowTracing {
@@ -106,17 +111,29 @@
@Override
protected void log(String where) {
try {
+ Trace.beginSection("window_tracing_log");
boolean isStartLogEvent = where == WHERE_START_TRACING;
boolean isOnFrameLogEvent = where == WHERE_ON_FRAME;
+ ArrayList<Runnable> pendingStopDones = new ArrayList<Runnable>();
+
mDataSource.trace((context) -> {
WindowTracingDataSource.Config dataSourceConfig =
context.getCustomTlsState().mConfig;
+ AtomicInteger dataSourceStatus =
+ context.getCustomTlsState().mStatus;
+
if (isStartLogEvent) {
- boolean isDataSourceStarting = context.getCustomTlsState()
- .mIsStarting.compareAndSet(true, false);
- if (!isDataSourceStarting) {
+ boolean isWaitingStartEvent = dataSourceStatus.compareAndSet(
+ WindowTracingDataSource.Status.WAITING_START_EVENT,
+ WindowTracingDataSource.Status.WRITING_START_EVENT);
+
+ boolean isWaitingStartEventWithDeferredStop = dataSourceStatus.compareAndSet(
+ WindowTracingDataSource.Status.WAITING_START_EVENT_WITH_DEFERRED_STOP,
+ WindowTracingDataSource.Status.WRITING_START_EVENT_WITH_DEFERRED_STOP);
+
+ if (!isWaitingStartEvent && !isWaitingStartEventWithDeferredStop) {
return;
}
} else if (isOnFrameLogEvent) {
@@ -141,9 +158,30 @@
dumpToProto(os, dataSourceConfig.mLogLevel, where, timestamp);
os.end(tokenExtensionsField);
os.end(tokenWinscopeExtensions);
+
+ dataSourceStatus.compareAndSet(WindowTracingDataSource.Status.WRITING_START_EVENT,
+ WindowTracingDataSource.Status.STARTED);
+
+ dataSourceStatus.compareAndSet(
+ WindowTracingDataSource.Status.WRITING_START_EVENT_WITH_DEFERRED_STOP,
+ WindowTracingDataSource.Status.STARTED_WITH_DEFERRED_STOP);
+
+ if (dataSourceStatus.compareAndSet(
+ WindowTracingDataSource.Status.STARTED_WITH_DEFERRED_STOP,
+ WindowTracingDataSource.Status.STOPPED
+ )) {
+ pendingStopDones.add(context::stopDone);
+ }
});
+
+ for (int i = 0; i < pendingStopDones.size(); ++i) {
+ pendingStopDones.get(i).run();
+ Log.i(TAG, "Stopped session (postponed)");
+ }
} catch (Exception e) {
Log.wtf(TAG, "Exception while tracing state", e);
+ } finally {
+ Trace.endSection();
}
}
@@ -165,22 +203,45 @@
Log.i(TAG, "Started session (frequency=TRANSACTION, log level="
+ config.mLogFrequency + ")");
mCountSessionsOnTransaction.incrementAndGet();
+ } else if (config.mLogFrequency == WindowTracingLogFrequency.SINGLE_DUMP) {
+ Log.i(TAG, "Started session (frequency=SINGLE_DUMP, log level="
+ + config.mLogFrequency + ")");
}
- Log.i(TAG, getStatus());
-
- log(WHERE_START_TRACING);
+ Handler handler = new Handler(Looper.getMainLooper());
+ handler.post(() -> log(WHERE_START_TRACING));
}
- void onStop(WindowTracingDataSource.Config config) {
- if (config.mLogFrequency == WindowTracingLogFrequency.FRAME) {
- Log.i(TAG, "Stopped session (frequency=FRAME)");
- mCountSessionsOnFrame.decrementAndGet();
- Log.i(TAG, "Stopped session (frequency=TRANSACTION)");
- } else if (config.mLogFrequency == WindowTracingLogFrequency.TRANSACTION) {
- mCountSessionsOnTransaction.decrementAndGet();
+ void onStop(WindowTracingDataSource.Instance instance) {
+ if (instance.mStatus.compareAndSet(
+ WindowTracingDataSource.Status.WAITING_START_EVENT,
+ WindowTracingDataSource.Status.WAITING_START_EVENT_WITH_DEFERRED_STOP)) {
+ Log.i(TAG, "Postponed session stop (start event not written yet)");
+ return;
}
- Log.i(TAG, getStatus());
+ if (instance.mStatus.compareAndSet(
+ WindowTracingDataSource.Status.WRITING_START_EVENT,
+ WindowTracingDataSource.Status.WRITING_START_EVENT_WITH_DEFERRED_STOP)) {
+ Log.i(TAG, "Postponed session stop (start event not written yet)");
+ return;
+ }
+
+ if (!instance.mStatus.compareAndSet(WindowTracingDataSource.Status.STARTED,
+ WindowTracingDataSource.Status.STOPPED)) {
+ return;
+ }
+
+ instance.stopDone();
+
+ if (instance.mConfig.mLogFrequency == WindowTracingLogFrequency.FRAME) {
+ Log.i(TAG, "Stopped session (frequency=FRAME)");
+ mCountSessionsOnFrame.decrementAndGet();
+ } else if (instance.mConfig.mLogFrequency == WindowTracingLogFrequency.TRANSACTION) {
+ mCountSessionsOnTransaction.decrementAndGet();
+ Log.i(TAG, "Stopped session (frequency=TRANSACTION)");
+ } else if (instance.mConfig.mLogFrequency == WindowTracingLogFrequency.SINGLE_DUMP) {
+ Log.i(TAG, "Stopped session (frequency=SINGLE_DUMP)");
+ }
}
}
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 24f089a..3bf2204 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -143,7 +143,8 @@
"libstatshidl",
"libstatspull",
"libstatssocket",
- "libstatslog",
+ "libstatslog_inputflinger",
+ "libstatslog_surfaceflinger",
"libschedulerservicehidl",
"libsensorservice",
"libsensorservicehidl",
@@ -192,7 +193,6 @@
"android.hardware.tv.input@1.0",
"android.hardware.tv.input-V3-ndk",
"android.hardware.vibrator-V3-ndk",
- "android.hardware.vr@1.0",
"android.hidl.token@1.0-utils",
"android.frameworks.schedulerservice@1.0",
"android.frameworks.sensorservice@1.0",
diff --git a/services/core/jni/OWNERS b/services/core/jni/OWNERS
index 8052b09..16dd796 100644
--- a/services/core/jni/OWNERS
+++ b/services/core/jni/OWNERS
@@ -31,6 +31,7 @@
per-file com_android_server_am_Freezer.cpp = file:/PERFORMANCE_OWNERS
per-file com_android_server_companion_virtual_InputController.cpp = file:/services/companion/java/com/android/server/companion/virtual/OWNERS
per-file com_android_server_utils_LazyJniRegistrar.cpp = file:/PERFORMANCE_OWNERS
+per-file com_android_server_stats_pull_StatsPullAtomService.cpp = file:/services/core/jni/stats/OWNERS
# Memory
per-file com_android_server_am_OomConnection.cpp = file:/MEMORY_OWNERS
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index d0c491b..48aab9e 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -671,13 +671,14 @@
return;
}
- const DisplayTopologyGraph displayTopology =
+ const base::Result<DisplayTopologyGraph> result =
android_hardware_display_DisplayTopologyGraph_toNative(env, topologyGraph);
- if (input_flags::enable_display_topology_validation() && !displayTopology.isValid()) {
- LOG(ERROR) << "Ignoring Invalid DisplayTopology";
+ if (!result.ok()) {
+ LOG(ERROR) << "Ignoring Invalid DisplayTopology" << result.error();
return;
}
+ const DisplayTopologyGraph& displayTopology = result.value();
mInputManager->getDispatcher().setDisplayTopology(displayTopology);
mInputManager->getChoreographer().setDisplayTopology(displayTopology);
}
diff --git a/services/core/jni/com_android_server_stats_pull_StatsPullAtomService.cpp b/services/core/jni/com_android_server_stats_pull_StatsPullAtomService.cpp
index 46fe595..73240e4 100644
--- a/services/core/jni/com_android_server_stats_pull_StatsPullAtomService.cpp
+++ b/services/core/jni/com_android_server_stats_pull_StatsPullAtomService.cpp
@@ -21,7 +21,7 @@
#include <nativehelper/JNIHelp.h>
#include <stats_event.h>
#include <stats_pull_atom_callback.h>
-#include <statslog.h>
+#include <statslog_surfaceflinger.h>
#include "stats/SurfaceFlingerPuller.h"
@@ -38,10 +38,12 @@
static void initializeNativePullers(JNIEnv* env, jobject javaObject) {
// Surface flinger layer & global info.
gSurfaceFlingerPuller = server::stats::SurfaceFlingerPuller();
- AStatsManager_setPullAtomCallback(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
+ AStatsManager_setPullAtomCallback(android::surfaceflinger::stats::
+ SURFACEFLINGER_STATS_GLOBAL_INFO,
/* metadata= */ nullptr, onSurfaceFlingerPullCallback,
/* cookie= */ nullptr);
- AStatsManager_setPullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO,
+ AStatsManager_setPullAtomCallback(android::surfaceflinger::stats::
+ SURFACEFLINGER_STATS_LAYER_INFO,
/* metadata= */ nullptr, onSurfaceFlingerPullCallback,
/* cookie= */ nullptr);
}
diff --git a/services/core/jni/com_android_server_vr_VrManagerService.cpp b/services/core/jni/com_android_server_vr_VrManagerService.cpp
index c675080..ecf6661 100644
--- a/services/core/jni/com_android_server_vr_VrManagerService.cpp
+++ b/services/core/jni/com_android_server_vr_VrManagerService.cpp
@@ -19,42 +19,19 @@
#include <android_runtime/AndroidRuntime.h>
#include <jni.h>
#include <nativehelper/JNIHelp.h>
-
-#include <android/hardware/vr/1.0/IVr.h>
#include <utils/Errors.h>
#include <utils/Log.h>
namespace android {
-using ::android::hardware::vr::V1_0::IVr;
-
-static sp<IVr> gVr;
-
static void init_native(JNIEnv* /* env */, jclass /* clazz */) {
- // TODO(b/31632518)
- if (gVr != nullptr) {
- // This call path should never be hit.
- ALOGE("%s: May not initialize IVr interface module more than once!", __FUNCTION__);
- return;
- }
-
- gVr = IVr::getService();
- if (gVr == nullptr) {
- ALOGW("%s: Could not open IVr interface", __FUNCTION__);
- return;
- }
-
- gVr->init();
+ ALOGW("%s: Could not open IVr interface as it no longer supported", __FUNCTION__);
+ return;
}
static void setVrMode_native(JNIEnv* /* env */, jclass /* clazz */, jboolean enabled) {
- if (gVr == nullptr) {
- // There is no VR hardware module implemented, do nothing.
- return;
- }
-
- // Call set_vr_mode method, this must be implemented if the HAL exists.
- gVr->setVrMode(static_cast<bool>(enabled));
+ ALOGW("%s: Could not use IVr interface as it no longer supported", __FUNCTION__);
+ return;
}
static const JNINativeMethod method_table[] = {
diff --git a/services/core/jni/gnss/Utils.cpp b/services/core/jni/gnss/Utils.cpp
index 274e3b7..bc7c95c 100644
--- a/services/core/jni/gnss/Utils.cpp
+++ b/services/core/jni/gnss/Utils.cpp
@@ -20,7 +20,6 @@
#include <android/hardware/gnss/1.0/IGnss.h>
#include <android/hardware/gnss/2.0/IGnss.h>
-#include <android_location_flags.h>
#include <utils/SystemClock.h>
/*
* Save a pointer to JavaVm to attach/detach threads executing
@@ -28,8 +27,6 @@
*/
JavaVM* android::ScopedJniThreadAttach::sJvm;
-namespace location_flags = android::location::flags;
-
namespace android {
namespace {
@@ -197,8 +194,7 @@
flags = static_cast<uint32_t>(location.elapsedRealtime.flags);
if (flags & android::hardware::gnss::ElapsedRealtime::HAS_TIMESTAMP_NS) {
- if (location_flags::replace_future_elapsed_realtime_jni() &&
- location.elapsedRealtime.timestampNs > android::elapsedRealtimeNano()) {
+ if (location.elapsedRealtime.timestampNs > android::elapsedRealtimeNano()) {
SET(ElapsedRealtimeNanos, android::elapsedRealtimeNano());
} else {
SET(ElapsedRealtimeNanos, location.elapsedRealtime.timestampNs);
diff --git a/services/core/jni/stats/OWNERS b/services/core/jni/stats/OWNERS
index 03086b3..6e97551 100644
--- a/services/core/jni/stats/OWNERS
+++ b/services/core/jni/stats/OWNERS
@@ -1,5 +1,2 @@
-jeffreyhuang@google.com
-muhammadq@google.com
-singhtejinder@google.com
-tsaichristine@google.com
-yaochen@google.com
+# Bug component: 366902
+file:platform/packages/modules/StatsD:/OWNERS
diff --git a/services/core/jni/stats/SurfaceFlingerPuller.cpp b/services/core/jni/stats/SurfaceFlingerPuller.cpp
index b959798..d7cecfc 100644
--- a/services/core/jni/stats/SurfaceFlingerPuller.cpp
+++ b/services/core/jni/stats/SurfaceFlingerPuller.cpp
@@ -20,7 +20,7 @@
#include <gui/SurfaceComposerClient.h>
#include <log/log.h>
-#include <statslog.h>
+#include <statslog_surfaceflinger.h>
#include <timestatsatomsproto/TimeStatsAtomsProtoHeader.h>
#include <vector>
@@ -29,7 +29,7 @@
namespace server {
namespace stats {
-using android::util::BytesField;
+using android::surfaceflinger::stats::BytesField;
using std::optional;
namespace {
@@ -58,9 +58,9 @@
}
switch (atomTag) {
- case android::util::SURFACEFLINGER_STATS_GLOBAL_INFO:
+ case android::surfaceflinger::stats::SURFACEFLINGER_STATS_GLOBAL_INFO:
return parseGlobalInfoPull(pullDataProto, data);
- case android::util::SURFACEFLINGER_STATS_LAYER_INFO:
+ case android::surfaceflinger::stats::SURFACEFLINGER_STATS_LAYER_INFO:
return parseLayerInfoPull(pullDataProto, data);
default:
ALOGW("Invalid atom id for surfaceflinger pullers: %" PRId32, atomTag);
@@ -92,21 +92,23 @@
return AStatsManager_PULL_SKIP;
}
- android::util::addAStatsEvent(data, android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
- atom.total_frames(), atom.missed_frames(),
- atom.client_composition_frames(), atom.display_on_millis(),
- atom.animation_millis(), atom.event_connection_count(),
- frameDuration.value(), renderEngineTime.value(),
- atom.total_timeline_frames(), atom.total_janky_frames(),
- atom.total_janky_frames_with_long_cpu(),
- atom.total_janky_frames_with_long_gpu(),
- atom.total_janky_frames_sf_unattributed(),
- atom.total_janky_frames_app_unattributed(),
- atom.total_janky_frames_sf_scheduling(),
- atom.total_jank_frames_sf_prediction_error(),
- atom.total_jank_frames_app_buffer_stuffing(),
- atom.display_refresh_rate_bucket(), deadlineMisses.value(),
- predictionErrors.value(), atom.render_rate_bucket());
+ android::surfaceflinger::stats::
+ addAStatsEvent(data,
+ android::surfaceflinger::stats::SURFACEFLINGER_STATS_GLOBAL_INFO,
+ atom.total_frames(), atom.missed_frames(),
+ atom.client_composition_frames(), atom.display_on_millis(),
+ atom.animation_millis(), atom.event_connection_count(),
+ frameDuration.value(), renderEngineTime.value(),
+ atom.total_timeline_frames(), atom.total_janky_frames(),
+ atom.total_janky_frames_with_long_cpu(),
+ atom.total_janky_frames_with_long_gpu(),
+ atom.total_janky_frames_sf_unattributed(),
+ atom.total_janky_frames_app_unattributed(),
+ atom.total_janky_frames_sf_scheduling(),
+ atom.total_jank_frames_sf_prediction_error(),
+ atom.total_jank_frames_app_buffer_stuffing(),
+ atom.display_refresh_rate_bucket(), deadlineMisses.value(),
+ predictionErrors.value(), atom.render_rate_bucket());
}
return AStatsManager_PULL_SUCCESS;
}
@@ -146,24 +148,25 @@
return AStatsManager_PULL_SKIP;
}
- android::util::addAStatsEvent(data, android::util::SURFACEFLINGER_STATS_LAYER_INFO,
- atom.layer_name().c_str(), atom.total_frames(),
- atom.dropped_frames(), present2Present.value(),
- post2present.value(), acquire2Present.value(),
- latch2Present.value(), desired2Present.value(),
- post2Acquire.value(), atom.late_acquire_frames(),
- atom.bad_desired_present_frames(), atom.uid(),
- atom.total_timeline_frames(), atom.total_janky_frames(),
- atom.total_janky_frames_with_long_cpu(),
- atom.total_janky_frames_with_long_gpu(),
- atom.total_janky_frames_sf_unattributed(),
- atom.total_janky_frames_app_unattributed(),
- atom.total_janky_frames_sf_scheduling(),
- atom.total_jank_frames_sf_prediction_error(),
- atom.total_jank_frames_app_buffer_stuffing(),
- atom.display_refresh_rate_bucket(), atom.render_rate_bucket(),
- frameRateVote.value(), appDeadlineMisses.value(),
- atom.game_mode(), present2PresentDelta.value());
+ android::surfaceflinger::stats::
+ addAStatsEvent(data,
+ android::surfaceflinger::stats::SURFACEFLINGER_STATS_LAYER_INFO,
+ atom.layer_name().c_str(), atom.total_frames(),
+ atom.dropped_frames(), present2Present.value(), post2present.value(),
+ acquire2Present.value(), latch2Present.value(),
+ desired2Present.value(), post2Acquire.value(),
+ atom.late_acquire_frames(), atom.bad_desired_present_frames(),
+ atom.uid(), atom.total_timeline_frames(), atom.total_janky_frames(),
+ atom.total_janky_frames_with_long_cpu(),
+ atom.total_janky_frames_with_long_gpu(),
+ atom.total_janky_frames_sf_unattributed(),
+ atom.total_janky_frames_app_unattributed(),
+ atom.total_janky_frames_sf_scheduling(),
+ atom.total_jank_frames_sf_prediction_error(),
+ atom.total_jank_frames_app_buffer_stuffing(),
+ atom.display_refresh_rate_bucket(), atom.render_rate_bucket(),
+ frameRateVote.value(), appDeadlineMisses.value(), atom.game_mode(),
+ present2PresentDelta.value());
}
return AStatsManager_PULL_SUCCESS;
}
diff --git a/services/core/jni/tvinput/JTvInputHal.cpp b/services/core/jni/tvinput/JTvInputHal.cpp
index e482129..a493dfa 100644
--- a/services/core/jni/tvinput/JTvInputHal.cpp
+++ b/services/core/jni/tvinput/JTvInputHal.cpp
@@ -296,7 +296,7 @@
}
void JTvInputHal::onStreamConfigurationsChanged(int deviceId, int cableConnectionStatus) {
- {
+ if (cableConnectionStatus != static_cast<int>(CableConnectionStatus::CONNECTED)) {
Mutex::Autolock autoLock(&mStreamLock);
KeyedVector<int, Connection>& connections = mConnections.editValueFor(deviceId);
for (size_t i = 0; i < connections.size(); ++i) {
diff --git a/services/core/xsd/display-layout-config/schema/current.txt b/services/core/xsd/display-layout-config/schema/current.txt
index 0d2f6a8..28d01a3 100644
--- a/services/core/xsd/display-layout-config/schema/current.txt
+++ b/services/core/xsd/display-layout-config/schema/current.txt
@@ -30,7 +30,9 @@
public class Layout {
ctor public Layout();
method public java.util.List<com.android.server.display.config.layout.Display> getDisplay();
+ method public String getName();
method public java.math.BigInteger getState();
+ method public void setName(String);
method public void setState(java.math.BigInteger);
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index e877b2b..191654c 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -238,6 +238,8 @@
import static android.app.admin.PolicySizeVerifier.MAX_ORG_NAME_LENGTH;
import static android.app.admin.PolicySizeVerifier.MAX_PROFILE_NAME_LENGTH;
import static android.app.admin.PolicySizeVerifier.MAX_SHORT_SUPPORT_MESSAGE_LENGTH;
+import static android.app.admin.PolicyUpdateResult.RESULT_POLICY_CLEARED;
+import static android.app.admin.PolicyUpdateResult.RESULT_POLICY_SET;
import static android.app.admin.ProvisioningException.ERROR_ADMIN_PACKAGE_INSTALLATION_FAILED;
import static android.app.admin.ProvisioningException.ERROR_PRE_CONDITION_FAILED;
import static android.app.admin.ProvisioningException.ERROR_PROFILE_CREATION_FAILED;
@@ -16840,9 +16842,11 @@
}
EnforcingAdmin enforcingAdmin;
- // TODO(b/370472975): enable when we stop policy enforecer callback from blocking the main
- // thread
- if (Flags.setPermissionGrantStateCoexistence()) {
+ if (Flags.setPermissionGrantStateCoexistence() && Flags.dpeBasedOnAsyncApisEnabled()) {
+ // TODO(b/403539755): If admin calls setPermissionGrantState and enforcement fails,
+ // subsequent calls will succeed without retrying enforcement. Need to somehow track
+ // actual enforcement status to disambiguate.
+
enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
admin,
MANAGE_DEVICE_POLICY_RUNTIME_PERMISSIONS,
@@ -16872,26 +16876,21 @@
// exist, or the permission isn't requested by the app, because we could end up with
// inconsistent state between the policy engine and package manager. Also a package
// might get removed or has it's permission updated after we've set the policy.
- if (grantState == PERMISSION_GRANT_STATE_DEFAULT) {
- mDevicePolicyEngine.removeLocalPolicy(
- PolicyDefinition.PERMISSION_GRANT(packageName, permission),
- enforcingAdmin,
- caller.getUserId());
- } else {
- mDevicePolicyEngine.setLocalPolicy(
- PolicyDefinition.PERMISSION_GRANT(packageName, permission),
- enforcingAdmin,
- new IntegerPolicyValue(grantState),
- caller.getUserId());
- }
- int newState = mInjector.binderWithCleanCallingIdentity(() ->
- getPermissionGrantStateForUser(
- packageName, permission, caller, caller.getUserId()));
- if (newState == grantState) {
- callback.sendResult(Bundle.EMPTY);
- } else {
- callback.sendResult(null);
- }
+ CompletableFuture<Integer> setPolicyFuture =
+ grantState == PERMISSION_GRANT_STATE_DEFAULT ?
+ mDevicePolicyEngine.removeLocalPolicy(
+ PolicyDefinition.PERMISSION_GRANT(packageName, permission),
+ enforcingAdmin,
+ caller.getUserId())
+ : mDevicePolicyEngine.setLocalPolicy(
+ PolicyDefinition.PERMISSION_GRANT(packageName, permission),
+ enforcingAdmin,
+ new IntegerPolicyValue(grantState),
+ caller.getUserId());
+
+ setPolicyFuture.thenAccept((result) ->
+ callback.sendResult(result == RESULT_POLICY_SET ||
+ result == RESULT_POLICY_CLEARED? Bundle.EMPTY : null));
} else {
Preconditions.checkCallAuthorization((caller.hasAdminComponent()
&& (isProfileOwner(caller) || isDefaultDeviceOwner(caller)
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java
index 8f80004..9e91388 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java
@@ -68,9 +68,6 @@
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicReference;
final class PolicyEnforcerCallbacks {
@@ -123,24 +120,21 @@
? DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT
: grantState;
- // TODO(b/278710449): stop blocking in the main thread
- BlockingCallback callback = new BlockingCallback();
// TODO: remove canAdminGrantSensorPermissions once we expose a new method in
// permissionController that doesn't need it.
AdminPermissionControlParams permissionParams = new AdminPermissionControlParams(
parsedKey.getPackageName(), parsedKey.getPermissionName(), value,
/* canAdminGrantSensorPermissions= */ true);
+
+ CompletableFuture<Boolean> enforcementFuture = new AndroidFuture<>();
+
getPermissionControllerManager(context, UserHandle.of(userId))
// TODO: remove callingPackage param and stop passing context.getPackageName()
.setRuntimePermissionGrantStateByDeviceAdmin(context.getPackageName(),
- permissionParams, context.getMainExecutor(), callback::trigger);
- try {
- return AndroidFuture.completedFuture(
- callback.await(20_000, TimeUnit.MILLISECONDS));
- } catch (Exception e) {
- // TODO: add logging
- return AndroidFuture.completedFuture(false);
- }
+ permissionParams, context.getMainExecutor(),
+ enforcementFuture::complete);
+
+ return enforcementFuture;
});
}
@@ -231,23 +225,6 @@
});
}
- private static class BlockingCallback {
- private final CountDownLatch mLatch = new CountDownLatch(1);
- private final AtomicReference<Boolean> mValue = new AtomicReference<>();
-
- public void trigger(Boolean value) {
- mValue.set(value);
- mLatch.countDown();
- }
-
- public Boolean await(long timeout, TimeUnit unit) throws InterruptedException {
- if (!mLatch.await(timeout, unit)) {
- Slogf.e(LOG_TAG, "Callback was not received");
- }
- return mValue.get();
- }
- }
-
// TODO: when a local policy exists for a user, this callback will be invoked for this user
// individually as well as for USER_ALL. This can be optimized by separating local and global
// enforcement in the policy engine.
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 4d0baa4..bdb3357 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -28,7 +28,6 @@
import static android.system.OsConstants.O_RDONLY;
import static android.view.Display.DEFAULT_DISPLAY;
-import static com.android.hardware.input.Flags.inputManagerLifecycleSupport;
import static com.android.server.utils.TimingsTraceAndSlog.SYSTEM_SERVER_TIMING_TAG;
import static com.android.tradeinmode.flags.Flags.enableTradeInMode;
@@ -152,6 +151,7 @@
import com.android.server.camera.CameraServiceProxy;
import com.android.server.clipboard.ClipboardService;
import com.android.server.companion.CompanionDeviceManagerService;
+import com.android.server.companion.datatransfer.continuity.TaskContinuityManagerService;
import com.android.server.companion.virtual.VirtualDeviceManagerService;
import com.android.server.compat.PlatformCompat;
import com.android.server.compat.PlatformCompatNative;
@@ -1592,6 +1592,7 @@
ServiceManager.addService("scheduling_policy", new SchedulingPolicyService());
t.traceEnd();
+
// TelecomLoader hooks into classes with defined HFP logic,
// so check for either telephony or microphone.
if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_MICROPHONE)
@@ -1686,12 +1687,8 @@
t.traceEnd();
t.traceBegin("StartInputManagerService");
- if (inputManagerLifecycleSupport()) {
- inputManager = mSystemServiceManager.startService(
- InputManagerService.Lifecycle.class).getService();
- } else {
- inputManager = new InputManagerService(context);
- }
+ inputManager = mSystemServiceManager.startService(
+ InputManagerService.Lifecycle.class).getService();
t.traceEnd();
t.traceBegin("DeviceStateManagerService");
@@ -1712,10 +1709,6 @@
ServiceManager.addService(Context.WINDOW_SERVICE, wm, /* allowIsolated= */ false,
DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PRIORITY_HIGH
| DUMP_FLAG_PROTO);
- if (!inputManagerLifecycleSupport()) {
- ServiceManager.addService(Context.INPUT_SERVICE, inputManager,
- /* allowIsolated= */ false, DUMP_FLAG_PRIORITY_CRITICAL);
- }
t.traceEnd();
t.traceBegin("SetWindowManagerService");
@@ -2017,6 +2010,13 @@
dpms = mSystemServiceManager.startService(DevicePolicyManagerService.Lifecycle.class);
t.traceEnd();
+ // If this flag is disabled, this service is started later.
+ if (android.server.Flags.voiceinteractionmanagerserviceGetResourcesInInitThread()) {
+ t.traceBegin("StartVoiceRecognitionManager");
+ mSystemServiceManager.startService(VoiceInteractionManagerService.class);
+ t.traceEnd();
+ }
+
t.traceBegin("StartStatusBarManagerService");
try {
statusBar = new StatusBarManagerService(context);
@@ -2528,9 +2528,13 @@
// FEATURE_VOICE_RECOGNIZERS feature is set, because it needs to take care
// of initializing various settings. It will internally modify its behavior
// based on that feature.
- t.traceBegin("StartVoiceRecognitionManager");
- mSystemServiceManager.startService(VoiceInteractionManagerService.class);
- t.traceEnd();
+ //
+ // If this flag is enabled, this service will have begun initializing earlier.
+ if (!android.server.Flags.voiceinteractionmanagerserviceGetResourcesInInitThread()) {
+ t.traceBegin("StartVoiceRecognitionManager");
+ mSystemServiceManager.startService(VoiceInteractionManagerService.class);
+ t.traceEnd();
+ }
if (GestureLauncherService.isGestureLauncherEnabled(context.getResources())) {
t.traceBegin("StartGestureLauncher");
@@ -2625,6 +2629,12 @@
t.traceEnd();
}
+ if (android.companion.Flags.enableTaskContinuity()) {
+ t.traceBegin("StartTaskContinuityService");
+ mSystemServiceManager.startService(TaskContinuityManagerService.class);
+ t.traceEnd();
+ }
+
if (context.getResources().getBoolean(R.bool.config_enableVirtualDeviceManager)) {
t.traceBegin("StartVirtualDeviceManager");
mSystemServiceManager.startService(VirtualDeviceManagerService.class);
@@ -2911,8 +2921,7 @@
t.traceEnd();
// OnDevicePersonalizationSystemService
- if (!com.android.server.flags.Flags.enableOdpFeatureGuard()
- || SystemProperties.getBoolean("ro.system_settings.service.odp_enabled", true)) {
+ if (SystemProperties.getBoolean("ro.system_settings.service.odp_enabled", true)) {
t.traceBegin("StartOnDevicePersonalizationSystemService");
mSystemServiceManager.startService(ON_DEVICE_PERSONALIZATION_SYSTEM_SERVICE_CLASS);
t.traceEnd();
@@ -3415,18 +3424,6 @@
reportWtf("Notifying NetworkTimeService running", e);
}
t.traceEnd();
- if (!inputManagerLifecycleSupport()) {
- t.traceBegin("MakeInputManagerServiceReady");
- try {
- // TODO(BT) Pass parameter to input manager
- if (inputManagerF != null) {
- inputManagerF.systemRunning();
- }
- } catch (Throwable e) {
- reportWtf("Notifying InputManagerService running", e);
- }
- t.traceEnd();
- }
t.traceBegin("MakeTelephonyRegistryReady");
try {
if (telephonyRegistryF != null) {
diff --git a/services/java/com/android/server/flags.aconfig b/services/java/com/android/server/flags.aconfig
index f864b6b..227d900 100644
--- a/services/java/com/android/server/flags.aconfig
+++ b/services/java/com/android/server/flags.aconfig
@@ -80,4 +80,11 @@
description: "Whether the Wear Gesture API is available."
bug: "396154116"
is_exported: true
+}
+
+flag {
+ name: "voiceinteractionmanagerservice_get_resources_in_init_thread"
+ namespace: "system_performance"
+ description: "Move VoiceInteractionManagerService getResources away from the boot critical path"
+ bug: "406841419"
}
\ No newline at end of file
diff --git a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
index fc585c9..8e4e997 100644
--- a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
+++ b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
@@ -69,6 +69,8 @@
public final class ProfcollectForwardingService extends SystemService {
public static final String LOG_TAG = "ProfcollectForwardingService";
+ private static final boolean DEBUG = false;
+
private static final String INTENT_UPLOAD_PROFILES =
"com.android.server.profcollect.UPLOAD_PROFILES";
private static final long BG_PROCESS_INTERVAL = TimeUnit.HOURS.toMillis(4); // every 4 hours.
@@ -99,22 +101,30 @@
} else if (ACTION_BATTERY_OKAY.equals(intent.getAction())) {
sIsBatteryLow = false;
} else if (ACTION_SCREEN_ON.equals(intent.getAction())) {
- Log.d(LOG_TAG, "Received broadcast that the device became interactive, was "
- + sIsInteractive);
+ if (DEBUG) {
+ Log.d(LOG_TAG, "Received broadcast that the device became interactive, was "
+ + sIsInteractive);
+ }
sIsInteractive = true;
} else if (ACTION_SCREEN_OFF.equals(intent.getAction())) {
- Log.d(LOG_TAG, "Received broadcast that the device became noninteractive, was "
- + sIsInteractive);
+ if (DEBUG) {
+ Log.d(LOG_TAG, "Received broadcast that the device became noninteractive, was "
+ + sIsInteractive);
+ }
sIsInteractive = false;
} else if (INTENT_UPLOAD_PROFILES.equals(intent.getAction())) {
- Log.d(LOG_TAG, "Received broadcast to pack and upload reports");
+ if (DEBUG) {
+ Log.d(LOG_TAG, "Received broadcast to pack and upload reports");
+ }
createAndUploadReport(sSelfService);
} else if (UsbManager.ACTION_USB_STATE.equals(intent.getAction())) {
boolean isADB = intent.getBooleanExtra(UsbManager.USB_FUNCTION_ADB, false);
if (isADB) {
boolean connected = intent.getBooleanExtra(UsbManager.USB_CONNECTED, false);
- Log.d(LOG_TAG, "Received broadcast that ADB became " + connected
- + ", was " + sAdbActive);
+ if (DEBUG) {
+ Log.d(LOG_TAG, "Received broadcast that ADB became " + connected
+ + ", was " + sAdbActive);
+ }
sAdbActive = connected;
}
}
@@ -141,7 +151,7 @@
final String verityMode = SystemProperties.get("ro.boot.veritymode");
sVerityEnforced = verityMode.equals("enforcing");
if (!sVerityEnforced) {
- Log.d(LOG_TAG, "verity is not enforced: " + verityMode);
+ Log.w(LOG_TAG, "verity is not enforced: " + verityMode);
}
mUploadEnabled =
@@ -177,19 +187,23 @@
UsbManager usbManager = getContext().getSystemService(UsbManager.class);
if (usbManager == null) {
sAdbActive = false;
- Log.d(LOG_TAG, "USBManager is not ready");
+ Log.w(LOG_TAG, "USBManager is not ready");
} else {
sAdbActive = ((usbManager.getCurrentFunctions() & UsbManager.FUNCTION_ADB) == 1);
- Log.d(LOG_TAG, "ADB is " + sAdbActive + " on system startup");
+ if (DEBUG) {
+ Log.d(LOG_TAG, "ADB is " + sAdbActive + " on system startup");
+ }
}
PowerManager powerManager = getContext().getSystemService(PowerManager.class);
if (powerManager == null) {
sIsInteractive = true;
- Log.d(LOG_TAG, "PowerManager is not ready");
+ Log.w(LOG_TAG, "PowerManager is not ready");
} else {
sIsInteractive = powerManager.isInteractive();
- Log.d(LOG_TAG, "Device is interactive " + sIsInteractive + " on system startup");
+ if (DEBUG) {
+ Log.d(LOG_TAG, "Device is interactive " + sIsInteractive + " on system startup");
+ }
}
}
if (phase == PHASE_BOOT_COMPLETED) {
@@ -458,7 +472,9 @@
cm.registerAvailabilityCallback(new CameraManager.AvailabilityCallback() {
@Override
public void onCameraOpened(String cameraId, String packageId) {
- Log.d(LOG_TAG, "Received camera open event from: " + packageId);
+ if (DEBUG) {
+ Log.d(LOG_TAG, "Received camera open event from: " + packageId);
+ }
// Skip face auth since it triggers way too often.
if (packageId.startsWith("client.pid")) {
return;
diff --git a/services/supervision/java/com/android/server/supervision/SupervisionRecoveryInfoStorage.java b/services/supervision/java/com/android/server/supervision/SupervisionRecoveryInfoStorage.java
new file mode 100644
index 0000000..a4664ca
--- /dev/null
+++ b/services/supervision/java/com/android/server/supervision/SupervisionRecoveryInfoStorage.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.supervision;
+
+import android.app.supervision.SupervisionRecoveryInfo;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.os.Environment;
+import android.util.Log;
+
+import java.io.File;
+
+/**
+ * Provides storage and retrieval of device supervision recovery information.
+ *
+ * <p>This class uses {@link SharedPreferences} as a temporary solution for persistent storage of
+ * the recovery email and ID associated with device supervision.
+ *
+ * <p>The storage is managed as a singleton, ensuring a single point of access for recovery
+ * information. Access to the shared preferences is synchronized to ensure thread safety.
+ *
+ * <p>TODO(b/406054267): need to figure out better solutions(binary xml) for persistent storage.
+ */
+public class SupervisionRecoveryInfoStorage {
+ private static final String LOG_TAG = "RecoveryInfoStorage";
+ private static final String PREF_NAME = "supervision_recovery_info";
+ private static final String KEY_EMAIL = "email";
+ private static final String KEY_ID = "id";
+
+ private final SharedPreferences mSharedPreferences;
+
+ private static SupervisionRecoveryInfoStorage sInstance;
+
+ private static final Object sLock = new Object();
+
+ private SupervisionRecoveryInfoStorage(Context context) {
+ Context deviceContext = context.createDeviceProtectedStorageContext();
+ File sharedPrefs = new File(Environment.getDataSystemDirectory(), PREF_NAME);
+ mSharedPreferences = deviceContext.getSharedPreferences(sharedPrefs, Context.MODE_PRIVATE);
+ }
+
+ /**
+ * Gets the singleton instance of {@link SupervisionRecoveryInfoStorage}.
+ *
+ * @param context The application context.
+ * @return The singleton instance.
+ */
+ public static SupervisionRecoveryInfoStorage getInstance(Context context) {
+ synchronized (sLock) {
+ if (sInstance == null) {
+ sInstance = new SupervisionRecoveryInfoStorage(context.getApplicationContext());
+ }
+ return sInstance;
+ }
+ }
+
+ /**
+ * Loads the device supervision recovery information from persistent storage.
+ *
+ * @return The {@link SupervisionRecoveryInfo} if found, otherwise {@code null}.
+ */
+ public SupervisionRecoveryInfo loadRecoveryInfo() {
+ synchronized (sLock) {
+ String email = mSharedPreferences.getString(KEY_EMAIL, null);
+ String id = mSharedPreferences.getString(KEY_ID, null);
+
+ if (email != null || id != null) {
+ SupervisionRecoveryInfo info = new SupervisionRecoveryInfo();
+ info.email = email;
+ info.id = id;
+ return info;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Saves the device supervision recovery information to persistent storage.
+ *
+ * @param recoveryInfo The {@link SupervisionRecoveryInfo} to save or {@code null} to clear the
+ * stored information.
+ */
+ public void saveRecoveryInfo(SupervisionRecoveryInfo recoveryInfo) {
+ synchronized (sLock) {
+ SharedPreferences.Editor editor = mSharedPreferences.edit();
+
+ if (recoveryInfo == null) {
+ editor.remove(KEY_EMAIL);
+ editor.remove(KEY_ID);
+ } else {
+ editor.putString(KEY_EMAIL, recoveryInfo.email);
+ editor.putString(KEY_ID, recoveryInfo.id);
+ }
+ editor.apply();
+ if (!editor.commit()) {
+ Log.e(LOG_TAG, "Failed to save recovery info to SharedPreferences");
+ }
+ }
+ }
+}
diff --git a/services/supervision/java/com/android/server/supervision/SupervisionService.java b/services/supervision/java/com/android/server/supervision/SupervisionService.java
index c419fd2..a21129a 100644
--- a/services/supervision/java/com/android/server/supervision/SupervisionService.java
+++ b/services/supervision/java/com/android/server/supervision/SupervisionService.java
@@ -33,6 +33,7 @@
import android.app.admin.DevicePolicyManagerInternal;
import android.app.supervision.ISupervisionManager;
import android.app.supervision.SupervisionManagerInternal;
+import android.app.supervision.SupervisionRecoveryInfo;
import android.app.supervision.flags.Flags;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -148,9 +149,12 @@
*/
@Override
@Nullable
- public Intent createConfirmSupervisionCredentialsIntent() {
+ public Intent createConfirmSupervisionCredentialsIntent(@UserIdInt int userId) {
enforceAnyPermission(QUERY_USERS, MANAGE_USERS);
- if (!isSupervisionEnabledForUser(mContext.getUserId())) {
+ if (UserHandle.getUserId(Binder.getCallingUid()) != userId) {
+ enforcePermission(INTERACT_ACROSS_USERS);
+ }
+ if (!isSupervisionEnabledForUser(userId)) {
return null;
}
// Verify the supervising user profile exists and has a secure credential set.
@@ -171,6 +175,18 @@
return intent;
}
+ /** Set the Supervision Recovery Info. */
+ @Override
+ public void setSupervisionRecoveryInfo(SupervisionRecoveryInfo recoveryInfo) {
+ SupervisionRecoveryInfoStorage.getInstance(mContext).saveRecoveryInfo(recoveryInfo);
+ }
+
+ /** Returns the Supervision Recovery Info or null if recovery is not set. */
+ @Override
+ public SupervisionRecoveryInfo getSupervisionRecoveryInfo() {
+ return SupervisionRecoveryInfoStorage.getInstance(mContext).loadRecoveryInfo();
+ }
+
@Override
public boolean shouldAllowBypassingSupervisionRoleQualification() {
enforcePermission(MANAGE_ROLE_HOLDERS);
@@ -193,7 +209,7 @@
/**
* Returns true if there are any non-default non-test users.
*
- * This excludes the system and main user(s) as those users are created by default.
+ * <p>This excludes the system and main user(s) as those users are created by default.
*/
private boolean hasNonTestDefaultUsers() {
List<UserInfo> users = mInjector.getUserManagerInternal().getUsers(true);
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java
index 2339a94..41e1ace 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java
@@ -230,7 +230,7 @@
@Test
public void testRemoveImeScreenshot() {
synchronized (ImfLock.class) {
- mVisibilityApplier.removeImeScreenshot(Display.DEFAULT_DISPLAY, mUserId);
+ mVisibilityApplier.removeImeScreenshot(mWindowToken, Display.DEFAULT_DISPLAY, mUserId);
}
verify(mMockImeTargetVisibilityPolicy).removeImeScreenshot(eq(Display.DEFAULT_DISPLAY));
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ImeVisibilityStateComputerTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ImeVisibilityStateComputerTest.java
index aa77919..7ef7e9b 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ImeVisibilityStateComputerTest.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ImeVisibilityStateComputerTest.java
@@ -19,6 +19,7 @@
import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HIDDEN;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
+import static android.view.MotionEvent.TOOL_TYPE_UNKNOWN;
import static android.view.WindowManager.DISPLAY_IME_POLICY_FALLBACK_DISPLAY;
import static android.view.WindowManager.DISPLAY_IME_POLICY_HIDE;
import static android.view.WindowManager.DISPLAY_IME_POLICY_LOCAL;
@@ -26,13 +27,9 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.internal.inputmethod.SoftInputShowHideReason.HIDE_WHEN_INPUT_TARGET_INVISIBLE;
-import static com.android.internal.inputmethod.SoftInputShowHideReason.REMOVE_IME_SCREENSHOT_FROM_IMMS;
-import static com.android.internal.inputmethod.SoftInputShowHideReason.SHOW_IME_SCREENSHOT_FROM_IMMS;
import static com.android.server.inputmethod.ImeVisibilityStateComputer.ImeTargetWindowState;
import static com.android.server.inputmethod.ImeVisibilityStateComputer.ImeVisibilityResult;
import static com.android.server.inputmethod.ImeVisibilityStateComputer.STATE_HIDE_IME_EXPLICIT;
-import static com.android.server.inputmethod.ImeVisibilityStateComputer.STATE_REMOVE_IME_SNAPSHOT;
-import static com.android.server.inputmethod.ImeVisibilityStateComputer.STATE_SHOW_IME_SNAPSHOT;
import static com.android.server.inputmethod.InputMethodManagerService.FALLBACK_DISPLAY_ID;
import static com.android.server.inputmethod.InputMethodManagerService.ImeDisplayValidator;
@@ -47,6 +44,7 @@
import android.view.inputmethod.ImeTracker;
import android.view.inputmethod.InputMethodManager;
+import androidx.annotation.NonNull;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.internal.annotations.GuardedBy;
@@ -275,30 +273,27 @@
}
@Test
- public void testOnInteractiveChanged() {
+ public void testShouldShowImeScreenshot() {
synchronized (ImfLock.class) {
mComputer.getOrCreateWindowState(mWindowToken);
- // Precondition: ensure IME has shown before hiding request.
+ // Precondition: ensure IME has shown before screenshot request.
mComputer.requestImeVisibility(mWindowToken, true);
mComputer.setInputShown(true);
- // No need any visibility change When initially shows IME on the device was interactive.
- ImeVisibilityStateComputer.ImeVisibilityResult result = mComputer.onInteractiveChanged(
- mWindowToken, true /* interactive */);
- assertThat(result).isNull();
+ // No screenshot visibility change when IME initially shown while interactive.
+ Boolean shouldShow = mComputer.shouldShowImeScreenshot(mWindowToken,
+ true /* interactive */);
+ assertThat(shouldShow).isNull();
- // Show the IME screenshot to capture the last IME visible state when the device
- // inactive.
- result = mComputer.onInteractiveChanged(mWindowToken, false /* interactive */);
- assertThat(result).isNotNull();
- assertThat(result.getState()).isEqualTo(STATE_SHOW_IME_SNAPSHOT);
- assertThat(result.getReason()).isEqualTo(SHOW_IME_SCREENSHOT_FROM_IMMS);
+ // Show the screenshot when the device became non-interactive.
+ shouldShow = mComputer.shouldShowImeScreenshot(mWindowToken, false /* interactive */);
+ assertThat(shouldShow).isNotNull();
+ assertThat(shouldShow).isTrue();
- // Remove the IME screenshot when the device became interactive again.
- result = mComputer.onInteractiveChanged(mWindowToken, true /* interactive */);
- assertThat(result).isNotNull();
- assertThat(result.getState()).isEqualTo(STATE_REMOVE_IME_SNAPSHOT);
- assertThat(result.getReason()).isEqualTo(REMOVE_IME_SCREENSHOT_FROM_IMMS);
+ // Remove the screenshot when the device became interactive again.
+ shouldShow = mComputer.shouldShowImeScreenshot(mWindowToken, true /* interactive */);
+ assertThat(shouldShow).isNotNull();
+ assertThat(shouldShow).isFalse();
}
}
@@ -335,9 +330,11 @@
}
@GuardedBy("ImfLock.class")
- private ImeTargetWindowState initImeTargetWindowState(IBinder windowToken) {
- final ImeTargetWindowState state = new ImeTargetWindowState(SOFT_INPUT_STATE_UNCHANGED,
- 0, true, true, true);
+ @NonNull
+ private ImeTargetWindowState initImeTargetWindowState(@NonNull IBinder windowToken) {
+ final var state = new ImeTargetWindowState(SOFT_INPUT_STATE_UNCHANGED,
+ 0 /* windowFlags */, true /* imeFocusChanged */, true /* hasFocusedEditor */,
+ true /* isStartInputByWindowGainFocus */, TOOL_TYPE_UNKNOWN);
mComputer.setWindowState(windowToken, state);
return state;
}
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java
index dd819ee..5925b42 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -1616,7 +1616,6 @@
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_IMPROVE_INSTALL_DONT_KILL)
public void testUpdatePackageSettings02WithOldPaths() throws PackageManagerException {
final PackageSetting testPkgSetting01 =
createPackageSetting(0 /*sharedUserId*/, 0 /*pkgFlags*/);
diff --git a/services/tests/displayservicetests/src/com/android/server/display/AutomaticBrightnessControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
index d881566..6fd0265 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
@@ -196,7 +196,7 @@
mController.configure(AUTO_BRIGHTNESS_ENABLED, null /* configuration= */,
0 /* brightness= */, false /* userChangedBrightness= */, 0 /* adjustment= */,
false /* userChanged= */, DisplayPowerRequest.POLICY_BRIGHT, Display.STATE_ON,
- /* useNormalBrightnessForDoze= */ false, /* shouldResetShortTermModel= */ true);
+ /* useNormalBrightnessForDoze= */ false, /* shouldResetShortTermModel= */ false);
}
@Test
@@ -326,7 +326,7 @@
mController.configure(AUTO_BRIGHTNESS_ENABLED, null /* configuration= */,
0.5f /* brightness= */, true /* userChangedBrightness= */, 0 /* adjustment= */,
false /* userChanged= */, DisplayPowerRequest.POLICY_BRIGHT, Display.STATE_ON,
- /* useNormalBrightnessForDoze= */ false, /* shouldResetShortTermModel= */ true);
+ /* useNormalBrightnessForDoze= */ false, /* shouldResetShortTermModel= */ false);
//Recalculating the spline with RBC enabled, verifying that the short term model is reset,
//and the interaction is learnt in short term model
@@ -400,7 +400,7 @@
mController.configure(AUTO_BRIGHTNESS_ENABLED, null /* configuration= */,
0.51f /* brightness= */, true /* userChangedBrightness= */, 0 /* adjustment= */,
false /* userChanged= */, DisplayPowerRequest.POLICY_BRIGHT, Display.STATE_ON,
- /* useNormalBrightnessForDoze= */ false, /* shouldResetShortTermModel= */ true);
+ /* useNormalBrightnessForDoze= */ false, /* shouldResetShortTermModel= */ false);
when(mBrightnessMappingStrategy.shouldResetShortTermModel(
anyFloat(), anyFloat())).thenReturn(true);
@@ -573,7 +573,7 @@
mController.configure(AUTO_BRIGHTNESS_ENABLED, null /* configuration= */,
0.5f /* brightness= */, true /* userChangedBrightness= */, 0 /* adjustment= */,
false /* userChanged= */, DisplayPowerRequest.POLICY_BRIGHT, Display.STATE_ON,
- /* useNormalBrightnessForDoze= */ false, /* shouldResetShortTermModel= */ true);
+ /* useNormalBrightnessForDoze= */ false, /* shouldResetShortTermModel= */ false);
// There should be a user data point added to the mapper.
verify(mBrightnessMappingStrategy, times(1)).addUserDataPoint(/* lux= */ 1000f,
@@ -583,11 +583,12 @@
// Now let's do the same for idle mode
mController.switchMode(AUTO_BRIGHTNESS_MODE_IDLE, /* sendUpdate= */ true);
- // Called once when switching,
+ // Called once when configuring in setUp(), once when configuring in the test,
+ // once when switching,
// setAmbientLux() is called twice and once in updateAutoBrightness(),
// nextAmbientLightBrighteningTransition() and nextAmbientLightDarkeningTransition() are
// called twice each.
- verify(mBrightnessMappingStrategy, times(8)).getMode();
+ verify(mBrightnessMappingStrategy, times(10)).getMode();
// Called when switching.
verify(mBrightnessMappingStrategy, times(1)).getShortTermModelTimeout();
verify(mBrightnessMappingStrategy, times(1)).getUserBrightness();
@@ -924,9 +925,7 @@
}
@Test
- public void testResetShortTermModelWhenConfigChanges() {
- when(mBrightnessMappingStrategy.setBrightnessConfiguration(any())).thenReturn(true);
-
+ public void testResetShortTermModel() {
mController.configure(AUTO_BRIGHTNESS_ENABLED, null /* configuration= */,
BRIGHTNESS_MAX_FLOAT /* brightness= */, false /* userChangedBrightness= */,
0 /* adjustment= */, false /* userChanged= */, DisplayPowerRequest.POLICY_BRIGHT,
diff --git a/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java b/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java
index 9ec9136..83ce3d2 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java
@@ -92,7 +92,6 @@
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
-import com.android.internal.foldables.FoldGracePeriodProvider;
import com.android.internal.util.test.LocalServiceKeeperRule;
import com.android.server.display.feature.DisplayManagerFlags;
import com.android.server.display.feature.flags.Flags;
@@ -162,7 +161,6 @@
@Mock LogicalDisplayMapper.Listener mListenerMock;
@Mock Context mContextMock;
@Mock FoldSettingProvider mFoldSettingProviderMock;
- @Mock FoldGracePeriodProvider mFoldGracePeriodProvider;
@Mock Resources mResourcesMock;
@Mock IPowerManager mIPowerManagerMock;
@Mock IThermalService mIThermalServiceMock;
@@ -828,41 +826,6 @@
}
@Test
- public void testWaitForSleepWhenFoldSettingSleep() {
- initLogicalDisplayMapper();
- // Test device should not be marked ready for transition immediately, when 'Continue
- // using app on fold' set to 'Never'
- setFoldLockBehaviorSettingValue(SETTING_VALUE_SLEEP_ON_FOLD);
- setGracePeriodAvailability(false);
- FoldableDisplayDevices foldableDisplayDevices = createFoldableDeviceStateToLayoutMap();
-
- finishBootAndFoldDevice();
- foldableDisplayDevices.mInner.setState(STATE_OFF);
- notifyDisplayChanges(foldableDisplayDevices.mOuter);
-
- assertDisplayDisabled(foldableDisplayDevices.mOuter);
- assertDisplayEnabled(foldableDisplayDevices.mInner);
- }
-
- @Test
- public void testSwapDeviceStateWithDelayWhenFoldSettingSleep() {
- initLogicalDisplayMapper();
- // Test device should be marked ready for transition after a delay when 'Continue using
- // app on fold' set to 'Never'
- setFoldLockBehaviorSettingValue(SETTING_VALUE_SLEEP_ON_FOLD);
- setGracePeriodAvailability(false);
- FoldableDisplayDevices foldableDisplayDevices = createFoldableDeviceStateToLayoutMap();
-
- finishBootAndFoldDevice();
- foldableDisplayDevices.mInner.setState(STATE_OFF);
- notifyDisplayChanges(foldableDisplayDevices.mOuter);
- advanceTime(TIMEOUT_STATE_TRANSITION_MILLIS);
-
- assertDisplayEnabled(foldableDisplayDevices.mOuter);
- assertDisplayDisabled(foldableDisplayDevices.mInner);
- }
-
- @Test
public void testDisplaySwappedAfterDeviceStateChange_windowManagerIsNotified() {
initLogicalDisplayMapper();
FoldableDisplayDevices foldableDisplayDevices = createFoldableDeviceStateToLayoutMap();
@@ -903,29 +866,11 @@
}
@Test
- public void testDoNotWaitForSleepWhenFoldSettingStayAwake() {
- initLogicalDisplayMapper();
- // Test device should be marked ready for transition immediately when 'Continue using app
- // on fold' set to 'Always'
- setFoldLockBehaviorSettingValue(SETTING_VALUE_STAY_AWAKE_ON_FOLD);
- setGracePeriodAvailability(false);
- FoldableDisplayDevices foldableDisplayDevices = createFoldableDeviceStateToLayoutMap();
-
- finishBootAndFoldDevice();
- foldableDisplayDevices.mInner.setState(STATE_OFF);
- notifyDisplayChanges(foldableDisplayDevices.mOuter);
-
- assertDisplayEnabled(foldableDisplayDevices.mOuter);
- assertDisplayDisabled(foldableDisplayDevices.mInner);
- }
-
- @Test
public void testDoNotWaitForSleepWhenFoldSettingSelectiveStayAwake() {
initLogicalDisplayMapper();
// Test device should be marked ready for transition immediately when 'Continue using app
// on fold' set to 'Swipe up to continue'
setFoldLockBehaviorSettingValue(SETTING_VALUE_SELECTIVE_STAY_AWAKE);
- setGracePeriodAvailability(true);
FoldableDisplayDevices foldableDisplayDevices = createFoldableDeviceStateToLayoutMap();
finishBootAndFoldDevice();
@@ -937,56 +882,6 @@
}
@Test
- public void testWaitForSleepWhenGracePeriodSettingDisabled() {
- initLogicalDisplayMapper();
- // Test device should not be marked ready for transition immediately when 'Continue using
- // app on fold' set to 'Swipe up to continue' but Grace Period flag is disabled
- setFoldLockBehaviorSettingValue(SETTING_VALUE_SELECTIVE_STAY_AWAKE);
- setGracePeriodAvailability(false);
- FoldableDisplayDevices foldableDisplayDevices = createFoldableDeviceStateToLayoutMap();
-
- finishBootAndFoldDevice();
- foldableDisplayDevices.mInner.setState(STATE_OFF);
- notifyDisplayChanges(foldableDisplayDevices.mOuter);
-
- assertDisplayDisabled(foldableDisplayDevices.mOuter);
- assertDisplayEnabled(foldableDisplayDevices.mInner);
- }
-
- @Test
- public void testWaitForSleepWhenTransitionDisplayStaysOn() {
- initLogicalDisplayMapper();
- // Test device should not be marked ready for transition immediately, when 'Continue
- // using app on fold' set to 'Always' but not all transitioning displays are OFF.
- setFoldLockBehaviorSettingValue(SETTING_VALUE_STAY_AWAKE_ON_FOLD);
- setGracePeriodAvailability(false);
- FoldableDisplayDevices foldableDisplayDevices = createFoldableDeviceStateToLayoutMap();
-
- finishBootAndFoldDevice();
- notifyDisplayChanges(foldableDisplayDevices.mOuter);
-
- assertDisplayDisabled(foldableDisplayDevices.mOuter);
- assertDisplayEnabled(foldableDisplayDevices.mInner);
- }
-
- @Test
- public void testSwapDeviceStateWithDelayWhenTransitionDisplayStaysOn() {
- initLogicalDisplayMapper();
- // Test device should be marked ready for transition after a delay, when 'Continue using
- // app on fold' set to 'Never' but not all transitioning displays are OFF.
- setFoldLockBehaviorSettingValue(SETTING_VALUE_SLEEP_ON_FOLD);
- setGracePeriodAvailability(false);
- FoldableDisplayDevices foldableDisplayDevices = createFoldableDeviceStateToLayoutMap();
-
- finishBootAndFoldDevice();
- notifyDisplayChanges(foldableDisplayDevices.mOuter);
- advanceTime(TIMEOUT_STATE_TRANSITION_MILLIS);
-
- assertDisplayEnabled(foldableDisplayDevices.mOuter);
- assertDisplayDisabled(foldableDisplayDevices.mInner);
- }
-
- @Test
public void testDeviceStateLocked() {
initLogicalDisplayMapper();
DisplayDevice device1 = createDisplayDevice(TYPE_INTERNAL, 600, 800,
@@ -1301,7 +1196,6 @@
*/
private void initLogicalDisplayMapper() {
mLogicalDisplayMapper = new LogicalDisplayMapper(mContextMock, mFoldSettingProviderMock,
- mFoldGracePeriodProvider,
mDisplayDeviceRepo,
mListenerMock, new DisplayManagerService.SyncRoot(), mHandler,
mDeviceStateToLayoutMapSpy, mFlagsMock, mSyntheticModeManagerMock,
@@ -1329,10 +1223,6 @@
mDisplayGroupAllocatorSpy.initLater(mContextMock);
}
- private void setGracePeriodAvailability(boolean isGracePeriodEnabled) {
- when(mFoldGracePeriodProvider.isEnabled()).thenReturn(isGracePeriodEnabled);
- }
-
private void setFoldLockBehaviorSettingValue(String foldLockBehaviorSettingValue) {
when(mFoldSettingProviderMock.shouldSleepOnFold()).thenReturn(false);
when(mFoldSettingProviderMock.shouldStayAwakeOnFold()).thenReturn(false);
diff --git a/services/tests/displayservicetests/src/com/android/server/display/VirtualDisplayAdapterTest.java b/services/tests/displayservicetests/src/com/android/server/display/VirtualDisplayAdapterTest.java
index 10bea7d..4ee7f24 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/VirtualDisplayAdapterTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/VirtualDisplayAdapterTest.java
@@ -25,6 +25,7 @@
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -73,6 +74,8 @@
private static final float DEFAULT_BRIGHTNESS = 0.34f;
private static final float DIM_BRIGHTNESS = 0.12f;
+ private static final int CALLBACK_TIMEOUT_MILLIS = 3000;
+
@Rule
public final TestableContext mContext = new TestableContext(
InstrumentationRegistry.getInstrumentation().getContext());
@@ -437,7 +440,7 @@
stateOnRunnable.run();
verify(mMockSufaceControlDisplayFactory)
.setDisplayPowerMode(displayToken, SurfaceControl.POWER_MODE_NORMAL);
- verify(mMockCallback).onResumed();
+ verify(mMockCallback, timeout(CALLBACK_TIMEOUT_MILLIS)).onResumed();
// Requesting the same display state is a no-op
Runnable stateOnSecondRunnable = device.requestDisplayStateLocked(
@@ -453,7 +456,7 @@
stateOffRunnable.run();
verify(mMockSufaceControlDisplayFactory)
.setDisplayPowerMode(displayToken, SurfaceControl.POWER_MODE_OFF);
- verify(mMockCallback).onPaused();
+ verify(mMockCallback, timeout(CALLBACK_TIMEOUT_MILLIS)).onPaused();
}
@EnableFlags(
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java
index 037768d..ca0e604 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java
@@ -624,7 +624,8 @@
mDisplayBrightnessStrategySelector = new DisplayBrightnessStrategySelector(mContext,
mInjector, DISPLAY_ID, mDisplayManagerFlags, mDisplayDeviceConfig);
mDisplayBrightnessStrategySelector
- .setAllowAutoBrightnessWhileDozing(mDisplayOffloadSession);
+ .setAllowAutoBrightnessWhileDozing(mDisplayOffloadSession,
+ /* useNormalBrightnessForDoze= */ false);
assertTrue(mDisplayBrightnessStrategySelector.isAllowAutoBrightnessWhileDozing());
}
@@ -636,7 +637,8 @@
mDisplayBrightnessStrategySelector = new DisplayBrightnessStrategySelector(mContext,
mInjector, DISPLAY_ID, mDisplayManagerFlags, mDisplayDeviceConfig);
mDisplayBrightnessStrategySelector
- .setAllowAutoBrightnessWhileDozing(mDisplayOffloadSession);
+ .setAllowAutoBrightnessWhileDozing(mDisplayOffloadSession,
+ /* useNormalBrightnessForDoze= */ false);
assertFalse(mDisplayBrightnessStrategySelector.isAllowAutoBrightnessWhileDozing());
}
@@ -648,7 +650,8 @@
mDisplayBrightnessStrategySelector = new DisplayBrightnessStrategySelector(mContext,
mInjector, DISPLAY_ID, mDisplayManagerFlags, mDisplayDeviceConfig);
mDisplayBrightnessStrategySelector
- .setAllowAutoBrightnessWhileDozing(mDisplayOffloadSession);
+ .setAllowAutoBrightnessWhileDozing(mDisplayOffloadSession,
+ /* useNormalBrightnessForDoze= */ false);
assertFalse(mDisplayBrightnessStrategySelector.isAllowAutoBrightnessWhileDozing());
}
@@ -657,7 +660,8 @@
when(mResources.getBoolean(R.bool.config_allowAutoBrightnessWhileDozing)).thenReturn(true);
mDisplayBrightnessStrategySelector = new DisplayBrightnessStrategySelector(mContext,
mInjector, DISPLAY_ID, mDisplayManagerFlags, mDisplayDeviceConfig);
- mDisplayBrightnessStrategySelector.setAllowAutoBrightnessWhileDozing(null);
+ mDisplayBrightnessStrategySelector.setAllowAutoBrightnessWhileDozing(null,
+ /* useNormalBrightnessForDoze= */ false);
assertTrue(mDisplayBrightnessStrategySelector.isAllowAutoBrightnessWhileDozing());
}
@@ -671,7 +675,24 @@
// are disabled
when(mDisplayManagerFlags.isDisplayOffloadEnabled()).thenReturn(false);
mDisplayBrightnessStrategySelector
- .setAllowAutoBrightnessWhileDozing(mDisplayOffloadSession);
+ .setAllowAutoBrightnessWhileDozing(mDisplayOffloadSession,
+ /* useNormalBrightnessForDoze= */ false);
+ assertTrue(mDisplayBrightnessStrategySelector.isAllowAutoBrightnessWhileDozing());
+ }
+
+ @Test
+ public void setAllowAutoBrightnessWhileDozing_enabledWhenUseNormalBrightnessForDoze() {
+ // This is the case for dream screen on. This overrides the decision of Displayoffload.
+ // i.e. even if Displayoffload doesn't allow auto-brightness while doze, if the request is
+ // made for dream-screen-on, the auto-brightness is allowed.
+ when(mDisplayManagerFlags.isDisplayOffloadEnabled()).thenReturn(true);
+ when(mDisplayOffloadSession.allowAutoBrightnessInDoze()).thenReturn(false);
+ when(mResources.getBoolean(R.bool.config_allowAutoBrightnessWhileDozing)).thenReturn(true);
+ mDisplayBrightnessStrategySelector = new DisplayBrightnessStrategySelector(mContext,
+ mInjector, DISPLAY_ID, mDisplayManagerFlags, mDisplayDeviceConfig);
+ mDisplayBrightnessStrategySelector
+ .setAllowAutoBrightnessWhileDozing(mDisplayOffloadSession,
+ /* useNormalBrightnessForDoze= */ true);
assertTrue(mDisplayBrightnessStrategySelector.isAllowAutoBrightnessWhileDozing());
}
}
diff --git a/services/tests/dreamservicetests/Android.bp b/services/tests/dreamservicetests/Android.bp
index cb9a09d..a5e1f2e 100644
--- a/services/tests/dreamservicetests/Android.bp
+++ b/services/tests/dreamservicetests/Android.bp
@@ -16,10 +16,10 @@
"androidx.test.ext.truth",
"flag-junit",
"frameworks-base-testutils",
- "mockito-target-minus-junit4",
- "services.core",
"mockingservicestests-utils-mockito",
+ "mockito-target-minus-junit4",
"platform-test-annotations",
+ "services.core",
"servicestests-utils",
"testables",
],
diff --git a/services/tests/dreamservicetests/src/com/android/server/dreams/DreamServiceTest.java b/services/tests/dreamservicetests/src/com/android/server/dreams/DreamServiceTest.java
index 265b74d..6ffba16 100644
--- a/services/tests/dreamservicetests/src/com/android/server/dreams/DreamServiceTest.java
+++ b/services/tests/dreamservicetests/src/com/android/server/dreams/DreamServiceTest.java
@@ -34,6 +34,7 @@
import android.content.res.TypedArray;
import android.os.Looper;
import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.service.dreams.DreamService;
import android.service.dreams.Flags;
import android.service.dreams.IDreamOverlayCallback;
@@ -46,6 +47,7 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
@@ -53,6 +55,9 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
public class DreamServiceTest {
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
private static final String TEST_PACKAGE_NAME = "com.android.frameworks.dreamservicetests";
private TestableLooper mTestableLooper;
@@ -191,7 +196,6 @@
}
@Test
- @EnableFlags(Flags.FLAG_DREAM_WAKE_REDIRECT)
public void testRedirect() throws Exception {
TestDreamEnvironment environment = new TestDreamEnvironment.Builder(mTestableLooper)
.setDreamOverlayPresent(true)
@@ -248,7 +252,7 @@
environment.comeToFront();
mTestableLooper.processAllMessages();
- // Overlay client receives call.
+ // Overlay client does not receives call.
verify(environment.getDreamOverlayClient(), never()).comeToFront();
}
}
diff --git a/services/tests/mockingservicestests/jni/Android.bp b/services/tests/mockingservicestests/jni/Android.bp
index 746e4f5..ccf97ef 100644
--- a/services/tests/mockingservicestests/jni/Android.bp
+++ b/services/tests/mockingservicestests/jni/Android.bp
@@ -57,7 +57,6 @@
"android.hardware.graphics.common@1.2",
"android.hardware.graphics.mapper@4.0",
"android.hardware.ir@1.0",
- "android.hardware.vr@1.0",
"android.hidl.token@1.0-utils",
],
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
index f0e61ec..2882a46 100644
--- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
@@ -150,7 +150,6 @@
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
-import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
import android.platform.test.flag.junit.SetFlagsRule;
@@ -437,7 +436,6 @@
*/
private void disableFlagsNotSetByAnnotation() {
try {
- mSetFlagsRule.disableFlags(Flags.FLAG_START_USER_BEFORE_SCHEDULED_ALARMS);
mSetFlagsRule.disableFlags(Flags.FLAG_ACQUIRE_WAKELOCK_BEFORE_SEND);
} catch (FlagSetException fse) {
// Expected if the test about to be run requires this enabled.
@@ -3838,7 +3836,6 @@
mUidFrozenStateCallback.onUidFrozenStateChanged(uids, frozenStates);
}
- @DisableFlags(Flags.FLAG_START_USER_BEFORE_SCHEDULED_ALARMS)
@Test
public void exactListenerAlarmsRemovedOnFrozen() {
mockChangeEnabled(EXACT_LISTENER_ALARMS_DROPPED_ON_CACHED, true);
@@ -3869,7 +3866,6 @@
assertEquals(6, mService.mAlarmStore.size());
}
- @DisableFlags(Flags.FLAG_START_USER_BEFORE_SCHEDULED_ALARMS)
@Test
public void alarmCountOnListenerFrozen() {
mockChangeEnabled(EXACT_LISTENER_ALARMS_DROPPED_ON_CACHED, true);
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java
index 1ac6df2..982c9d1 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java
@@ -43,7 +43,6 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
import static com.android.server.am.ActivityManagerInternalTest.CustomThread;
import static com.android.server.am.ActivityManagerService.Injector;
-import static com.android.server.am.Flags.FLAG_AVOID_RESOLVING_TYPE;
import static com.android.server.am.ProcessList.NETWORK_STATE_BLOCK;
import static com.android.server.am.ProcessList.NETWORK_STATE_NO_CHANGE;
import static com.android.server.am.ProcessList.NETWORK_STATE_UNBLOCK;
@@ -968,7 +967,6 @@
null));
}
- @RequiresFlagsEnabled(FLAG_AVOID_RESOLVING_TYPE)
@Test
@SuppressWarnings("GuardedBy")
public void testBroadcastStickyIntent_verifyTypeNotResolved() throws Exception {
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ApplicationThreadDeferredTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationThreadDeferredTest.java
index 8f8c1ac..9a8f3e4 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/ApplicationThreadDeferredTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationThreadDeferredTest.java
@@ -73,7 +73,7 @@
@Test
public void testDeferredUnpaused() throws Exception {
IApplicationThread base = mock(IApplicationThread.class);
- ApplicationThreadDeferred thread = new ApplicationThreadDeferred(base, true);
+ ApplicationThreadDeferred thread = new ApplicationThreadDeferred(base);
callDeferredApis(thread);
verifyDeferredApis(base, 1);
}
@@ -83,7 +83,7 @@
@Test
public void testDeferredPaused() throws Exception {
IApplicationThread base = mock(IApplicationThread.class);
- ApplicationThreadDeferred thread = new ApplicationThreadDeferred(base, true);
+ ApplicationThreadDeferred thread = new ApplicationThreadDeferred(base);
thread.onProcessPaused();
callDeferredApis(thread);
callDeferredApis(thread);
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
index 9484c5c..509a21a 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
@@ -242,8 +242,12 @@
mock(BatteryStatsService.class));
setFieldValue(ActivityManagerService.class, mService, "mInjector",
new ActivityManagerService.Injector(mContext));
- setFieldValue(ActivityManagerService.class, mService, "mPhantomProcessList",
- new PhantomProcessList(mService));
+
+ PhantomProcessList ppl = mock(PhantomProcessList.class);
+ doNothing().when(ppl).setProcessGroupForPhantomProcessOfApp(any(ProcessRecord.class),
+ anyInt());
+ setFieldValue(ActivityManagerService.class, mService, "mPhantomProcessList", ppl);
+
doReturn(mock(AppOpsManager.class)).when(mService).getAppOpsManager();
doCallRealMethod().when(mService).enqueueOomAdjTargetLocked(any(ProcessRecord.class));
doCallRealMethod().when(mService).updateOomAdjPendingTargetsLocked(OOM_ADJ_REASON_ACTIVITY);
@@ -930,8 +934,6 @@
PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ, SCHED_GROUP_DEFAULT, "fg-service-act");
assertBfsl(app);
- if (!Flags.followUpOomadjUpdates()) return;
-
final ArgumentCaptor<Long> followUpTimeCaptor = ArgumentCaptor.forClass(Long.class);
verify(mService.mHandler).sendEmptyMessageAtTime(
eq(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG), followUpTimeCaptor.capture());
@@ -967,8 +969,6 @@
assertEquals(PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ + 2, app.mState.getSetAdj());
- if (!Flags.followUpOomadjUpdates()) return;
-
final ArgumentCaptor<Long> followUpTimeCaptor = ArgumentCaptor.forClass(Long.class);
verify(mService.mHandler).sendEmptyMessageAtTime(
eq(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG), followUpTimeCaptor.capture());
@@ -1105,8 +1105,6 @@
assertProcStates(app, PROCESS_STATE_LAST_ACTIVITY, PREVIOUS_APP_ADJ,
SCHED_GROUP_BACKGROUND, "previous");
- if (!Flags.followUpOomadjUpdates()) return;
-
final ArgumentCaptor<Long> followUpTimeCaptor = ArgumentCaptor.forClass(Long.class);
verify(mService.mHandler).sendEmptyMessageAtTime(eq(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG),
followUpTimeCaptor.capture());
@@ -1162,8 +1160,6 @@
SCHED_GROUP_BACKGROUND, "previous");
}
- if (!Flags.followUpOomadjUpdates()) return;
-
for (int i = 0; i < numberOfApps; i++) {
final ArgumentCaptor<Long> followUpTimeCaptor = ArgumentCaptor.forClass(Long.class);
verify(mService.mHandler).sendEmptyMessageAtTime(eq(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG),
@@ -1666,13 +1662,7 @@
setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(client, app, service);
- final int expectedAdj;
- if (Flags.addModifyRawOomAdjServiceLevel()) {
- expectedAdj = SERVICE_ADJ;
- } else {
- expectedAdj = CACHED_APP_MIN_ADJ;
- }
- assertEquals(expectedAdj, app.mState.getSetAdj());
+ assertEquals(SERVICE_ADJ, app.mState.getSetAdj());
}
@SuppressWarnings("GuardedBy")
@@ -1965,8 +1955,6 @@
assertProcStates(app, PROCESS_STATE_LAST_ACTIVITY, PREVIOUS_APP_ADJ,
SCHED_GROUP_BACKGROUND, "recent-provider");
- if (!Flags.followUpOomadjUpdates()) return;
-
final ArgumentCaptor<Long> followUpTimeCaptor = ArgumentCaptor.forClass(Long.class);
verify(mService.mHandler).sendEmptyMessageAtTime(eq(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG),
followUpTimeCaptor.capture());
@@ -3460,8 +3448,6 @@
assertProcStates(app, PROCESS_STATE_SERVICE, SERVICE_ADJ, SCHED_GROUP_BACKGROUND,
"started-services");
- if (!Flags.followUpOomadjUpdates()) return;
-
final ArgumentCaptor<Long> followUpTimeCaptor = ArgumentCaptor.forClass(Long.class);
verify(mService.mHandler).sendEmptyMessageAtTime(
eq(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG), followUpTimeCaptor.capture());
@@ -3604,8 +3590,6 @@
assertProcStates(app2, PROCESS_STATE_LAST_ACTIVITY, PREVIOUS_APP_ADJ,
SCHED_GROUP_BACKGROUND, "recent-provider");
- if (!Flags.followUpOomadjUpdates()) return;
-
final ArgumentCaptor<Long> followUpTimeCaptor = ArgumentCaptor.forClass(Long.class);
verify(mService.mHandler, atLeastOnce()).sendEmptyMessageAtTime(
eq(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG), followUpTimeCaptor.capture());
@@ -3659,16 +3643,8 @@
setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(app, client);
- if (Flags.raiseBoundUiServiceThreshold()) {
- assertProcStates(app, PROCESS_STATE_SERVICE, SERVICE_ADJ, SCHED_GROUP_BACKGROUND,
- "service");
- } else {
- final int expectedAdj = mService.mConstants.USE_TIERED_CACHED_ADJ
- ? sFirstUiCachedAdj : sFirstCachedAdj;
- assertProcStates(app, PROCESS_STATE_SERVICE, expectedAdj, SCHED_GROUP_BACKGROUND,
- "cch-bound-ui-services");
- assertNoImplicitCpuTime(app);
- }
+ assertProcStates(app, PROCESS_STATE_SERVICE, SERVICE_ADJ, SCHED_GROUP_BACKGROUND,
+ "service");
}
@SuppressWarnings("GuardedBy")
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ServiceBindingOomAdjPolicyTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ServiceBindingOomAdjPolicyTest.java
index 6fb7a9d..d23fe34 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/ServiceBindingOomAdjPolicyTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/ServiceBindingOomAdjPolicyTest.java
@@ -18,6 +18,7 @@
import static android.app.ActivityManager.PROCESS_CAPABILITY_CPU_TIME;
import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_MICROPHONE;
+import static android.app.ActivityManager.PROCESS_CAPABILITY_IMPLICIT_CPU_TIME;
import static android.app.ActivityManager.PROCESS_CAPABILITY_NONE;
import static android.app.ActivityManager.PROCESS_STATE_CACHED_EMPTY;
import static android.app.ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
@@ -68,8 +69,6 @@
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
-import android.platform.test.annotations.RequiresFlagsDisabled;
-import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.platform.test.flag.junit.SetFlagsRule;
@@ -327,7 +326,6 @@
}
@Test
- @RequiresFlagsEnabled(com.android.server.am.Flags.FLAG_UNFREEZE_BIND_POLICY_FIX)
@DisableFlags(Flags.FLAG_CPU_TIME_CAPABILITY_BASED_FREEZE_POLICY)
public void testServiceDistinctBindingOomAdjShouldNotFreeze() throws Exception {
// Enable the flags.
@@ -420,7 +418,6 @@
}
@Test
- @RequiresFlagsEnabled(com.android.server.am.Flags.FLAG_UNFREEZE_BIND_POLICY_FIX)
@DisableFlags(Flags.FLAG_CPU_TIME_CAPABILITY_BASED_FREEZE_POLICY)
public void testServiceDistinctBindingOomAdjAllowOomManagement() throws Exception {
// Enable the flags.
@@ -500,7 +497,6 @@
}
@Test
- @RequiresFlagsEnabled(com.android.server.am.Flags.FLAG_UNFREEZE_BIND_POLICY_FIX)
@DisableFlags(Flags.FLAG_CPU_TIME_CAPABILITY_BASED_FREEZE_POLICY)
public void testServiceDistinctBindingOomAdjWaivePriority_propagateUnfreeze() throws Exception {
// Enable the flags.
@@ -579,11 +575,8 @@
}
@Test
- @RequiresFlagsEnabled({
- Flags.FLAG_UNFREEZE_BIND_POLICY_FIX,
- Flags.FLAG_SERVICE_BINDING_OOM_ADJ_POLICY
- })
- @EnableFlags(Flags.FLAG_CPU_TIME_CAPABILITY_BASED_FREEZE_POLICY)
+ @EnableFlags({Flags.FLAG_SERVICE_BINDING_OOM_ADJ_POLICY,
+ Flags.FLAG_CPU_TIME_CAPABILITY_BASED_FREEZE_POLICY})
public void testServiceDistinctBindingOomAdj_propagateCpuTimeCapability() throws Exception {
// Note that PROCESS_CAPABILITY_CPU_TIME is special and should be propagated even when
// BIND_INCLUDE_CAPABILITIES is not present.
@@ -623,52 +616,6 @@
}
@Test
- @RequiresFlagsDisabled(com.android.server.am.Flags.FLAG_UNFREEZE_BIND_POLICY_FIX)
- public void testServiceDistinctBindingOomAdjWaivePriority() throws Exception {
- // Enable the flags.
- mSetFlagsRule.enableFlags(Flags.FLAG_SERVICE_BINDING_OOM_ADJ_POLICY);
-
- // Verify that there should be 0 oom adj update for binding
- // because we're using the BIND_WAIVE_PRIORITY;
- // but for the unbinding, because client is better than service, we can't skip it safely.
- performTestServiceDistinctBindingOomAdj(TEST_APP1_PID, TEST_APP1_UID,
- PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
- PROCESS_CAPABILITY_NONE, TEST_APP1_NAME,
- this::setHasForegroundServices,
- TEST_APP2_PID, TEST_APP2_UID, PROCESS_STATE_HOME,
- HOME_APP_ADJ, PROCESS_CAPABILITY_NONE, TEST_APP2_NAME, TEST_SERVICE2_NAME,
- this::setHomeProcess,
- BIND_AUTO_CREATE | BIND_WAIVE_PRIORITY,
- never(), atLeastOnce());
-
- // Verify that there should be 0 oom adj update
- // because we're using the BIND_WAIVE_PRIORITY;
- performTestServiceDistinctBindingOomAdj(TEST_APP1_PID, TEST_APP1_UID,
- PROCESS_STATE_HOME, HOME_APP_ADJ, PROCESS_CAPABILITY_NONE, TEST_APP1_NAME,
- this::setHomeProcess,
- TEST_APP2_PID, TEST_APP2_UID, PROCESS_STATE_FOREGROUND_SERVICE,
- PERCEPTIBLE_APP_ADJ, PROCESS_CAPABILITY_NONE, TEST_APP2_NAME, TEST_SERVICE2_NAME,
- this::setHasForegroundServices,
- BIND_AUTO_CREATE | BIND_WAIVE_PRIORITY,
- never(), never());
-
- // Disable the flags.
- mSetFlagsRule.disableFlags(Flags.FLAG_SERVICE_BINDING_OOM_ADJ_POLICY);
-
- // Verify that there should be at least 1 oom adj update
- // because the client is more important.
- performTestServiceDistinctBindingOomAdj(TEST_APP1_PID, TEST_APP1_UID,
- PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
- PROCESS_CAPABILITY_NONE, TEST_APP1_NAME,
- this::setHasForegroundServices,
- TEST_APP2_PID, TEST_APP2_UID, PROCESS_STATE_HOME,
- HOME_APP_ADJ, PROCESS_CAPABILITY_NONE, TEST_APP2_NAME, TEST_SERVICE2_NAME,
- this::setHomeProcess,
- BIND_AUTO_CREATE,
- atLeastOnce(), atLeastOnce());
- }
-
- @Test
public void testServiceDistinctBindingOomAdjNoIncludeCapabilities() throws Exception {
// Enable the flags.
mSetFlagsRule.enableFlags(Flags.FLAG_SERVICE_BINDING_OOM_ADJ_POLICY);
@@ -764,6 +711,200 @@
atLeastOnce(), atLeastOnce());
}
+ @Test
+ @EnableFlags(Flags.FLAG_CPU_TIME_CAPABILITY_BASED_FREEZE_POLICY)
+ public void testServiceDistinctBindingOomAdjCpuTime() throws Exception {
+ // Enable the flags.
+ mSetFlagsRule.enableFlags(Flags.FLAG_SERVICE_BINDING_OOM_ADJ_POLICY);
+
+ // Verify the CPU_TIME capability triggers an update.
+ performTestServiceDistinctBindingOomAdj(TEST_APP1_PID, TEST_APP1_UID,
+ PROCESS_STATE_CACHED_EMPTY, CACHED_APP_MIN_ADJ, PROCESS_CAPABILITY_CPU_TIME,
+ TEST_APP1_NAME, null,
+ TEST_APP2_PID, TEST_APP2_UID, PROCESS_STATE_CACHED_EMPTY,
+ CACHED_APP_MIN_ADJ, PROCESS_CAPABILITY_NONE, TEST_APP2_NAME, TEST_SERVICE2_NAME,
+ null,
+ BIND_AUTO_CREATE,
+ atLeastOnce(), atLeastOnce());
+
+ // Disable the flags.
+ mSetFlagsRule.disableFlags(Flags.FLAG_SERVICE_BINDING_OOM_ADJ_POLICY);
+
+ // Verify that there should be at least 1 oom adj update
+ performTestServiceDistinctBindingOomAdj(TEST_APP1_PID, TEST_APP1_UID,
+ PROCESS_STATE_CACHED_EMPTY, CACHED_APP_MIN_ADJ, PROCESS_CAPABILITY_CPU_TIME,
+ TEST_APP1_NAME, null,
+ TEST_APP2_PID, TEST_APP2_UID, PROCESS_STATE_CACHED_EMPTY,
+ CACHED_APP_MIN_ADJ, PROCESS_CAPABILITY_NONE, TEST_APP2_NAME, TEST_SERVICE2_NAME,
+ null,
+ BIND_AUTO_CREATE,
+ atLeastOnce(), atLeastOnce());
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_CPU_TIME_CAPABILITY_BASED_FREEZE_POLICY)
+ public void testServiceDistinctBindingOomAdjCpuTime_hostHasCpuTime() throws Exception {
+ // Enable the flags.
+ mSetFlagsRule.enableFlags(Flags.FLAG_SERVICE_BINDING_OOM_ADJ_POLICY);
+
+ // Verify the CPU_TIME capability does not trigger an update if the host has already it.
+ performTestServiceDistinctBindingOomAdj(TEST_APP1_PID, TEST_APP1_UID,
+ PROCESS_STATE_CACHED_EMPTY, CACHED_APP_MIN_ADJ, PROCESS_CAPABILITY_CPU_TIME,
+ TEST_APP1_NAME, null,
+ TEST_APP2_PID, TEST_APP2_UID, PROCESS_STATE_CACHED_EMPTY,
+ CACHED_APP_MIN_ADJ, PROCESS_CAPABILITY_CPU_TIME, TEST_APP2_NAME, TEST_SERVICE2_NAME,
+ null,
+ BIND_AUTO_CREATE,
+ never(), atLeastOnce());
+
+ // Disable the flags.
+ mSetFlagsRule.disableFlags(Flags.FLAG_SERVICE_BINDING_OOM_ADJ_POLICY);
+
+ // Verify that there should be at least 1 oom adj update
+ performTestServiceDistinctBindingOomAdj(TEST_APP1_PID, TEST_APP1_UID,
+ PROCESS_STATE_CACHED_EMPTY, CACHED_APP_MIN_ADJ, PROCESS_CAPABILITY_CPU_TIME,
+ TEST_APP1_NAME, null,
+ TEST_APP2_PID, TEST_APP2_UID, PROCESS_STATE_CACHED_EMPTY,
+ CACHED_APP_MIN_ADJ, PROCESS_CAPABILITY_CPU_TIME, TEST_APP2_NAME, TEST_SERVICE2_NAME,
+ null,
+ BIND_AUTO_CREATE,
+ atLeastOnce(), atLeastOnce());
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_CPU_TIME_CAPABILITY_BASED_FREEZE_POLICY)
+ public void testServiceDistinctBindingOomAdjCpuTime_hostHasImplicitCpuTime() throws Exception {
+ // Enable the flags.
+ mSetFlagsRule.enableFlags(Flags.FLAG_SERVICE_BINDING_OOM_ADJ_POLICY);
+
+ // Verify the CPU_TIME capability still triggers an update even if the host has the
+ // IMPLICIT_CPU_TIME.
+ performTestServiceDistinctBindingOomAdj(TEST_APP1_PID, TEST_APP1_UID,
+ PROCESS_STATE_CACHED_EMPTY, CACHED_APP_MIN_ADJ, PROCESS_CAPABILITY_CPU_TIME,
+ TEST_APP1_NAME, null,
+ TEST_APP2_PID, TEST_APP2_UID, PROCESS_STATE_CACHED_EMPTY,
+ CACHED_APP_MIN_ADJ, PROCESS_CAPABILITY_IMPLICIT_CPU_TIME, TEST_APP2_NAME,
+ TEST_SERVICE2_NAME,
+ null,
+ BIND_AUTO_CREATE,
+ atLeastOnce(), atLeastOnce());
+
+ // Disable the flags.
+ mSetFlagsRule.disableFlags(Flags.FLAG_SERVICE_BINDING_OOM_ADJ_POLICY);
+
+ // Verify that there should be at least 1 oom adj update
+ performTestServiceDistinctBindingOomAdj(TEST_APP1_PID, TEST_APP1_UID,
+ PROCESS_STATE_CACHED_EMPTY, CACHED_APP_MIN_ADJ, PROCESS_CAPABILITY_CPU_TIME,
+ TEST_APP1_NAME, null,
+ TEST_APP2_PID, TEST_APP2_UID, PROCESS_STATE_CACHED_EMPTY,
+ CACHED_APP_MIN_ADJ, PROCESS_CAPABILITY_IMPLICIT_CPU_TIME, TEST_APP2_NAME,
+ TEST_SERVICE2_NAME,
+ null,
+ BIND_AUTO_CREATE,
+ atLeastOnce(), atLeastOnce());
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_CPU_TIME_CAPABILITY_BASED_FREEZE_POLICY)
+ public void testServiceDistinctBindingOomAdjImplicitCpuTime() throws Exception {
+ // Enable the flags.
+ mSetFlagsRule.enableFlags(Flags.FLAG_SERVICE_BINDING_OOM_ADJ_POLICY);
+
+ // Verify the IMPLICIT_CPU_TIME capability triggers an update.
+ performTestServiceDistinctBindingOomAdj(TEST_APP1_PID, TEST_APP1_UID,
+ PROCESS_STATE_CACHED_EMPTY, CACHED_APP_MIN_ADJ,
+ PROCESS_CAPABILITY_IMPLICIT_CPU_TIME,
+ TEST_APP1_NAME, null,
+ TEST_APP2_PID, TEST_APP2_UID, PROCESS_STATE_CACHED_EMPTY,
+ CACHED_APP_MIN_ADJ, PROCESS_CAPABILITY_NONE, TEST_APP2_NAME, TEST_SERVICE2_NAME,
+ null,
+ BIND_AUTO_CREATE,
+ atLeastOnce(), atLeastOnce());
+
+ // Disable the flags.
+ mSetFlagsRule.disableFlags(Flags.FLAG_SERVICE_BINDING_OOM_ADJ_POLICY);
+
+ // Verify that there should be at least 1 oom adj update
+ performTestServiceDistinctBindingOomAdj(TEST_APP1_PID, TEST_APP1_UID,
+ PROCESS_STATE_CACHED_EMPTY, CACHED_APP_MIN_ADJ,
+ PROCESS_CAPABILITY_IMPLICIT_CPU_TIME,
+ TEST_APP1_NAME, null,
+ TEST_APP2_PID, TEST_APP2_UID, PROCESS_STATE_CACHED_EMPTY,
+ CACHED_APP_MIN_ADJ, PROCESS_CAPABILITY_NONE, TEST_APP2_NAME, TEST_SERVICE2_NAME,
+ null,
+ BIND_AUTO_CREATE,
+ atLeastOnce(), atLeastOnce());
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_CPU_TIME_CAPABILITY_BASED_FREEZE_POLICY)
+ public void testServiceDistinctBindingOomAdjImplicitCpuTime_hostHasCpuTime() throws Exception {
+ // Enable the flags.
+ mSetFlagsRule.enableFlags(Flags.FLAG_SERVICE_BINDING_OOM_ADJ_POLICY);
+
+ // Verify the IMPLICIT_CPU_TIME capability still triggers an update even if the host has the
+ // CPU_TIME.
+ performTestServiceDistinctBindingOomAdj(TEST_APP1_PID, TEST_APP1_UID,
+ PROCESS_STATE_CACHED_EMPTY, CACHED_APP_MIN_ADJ,
+ PROCESS_CAPABILITY_IMPLICIT_CPU_TIME,
+ TEST_APP1_NAME, null,
+ TEST_APP2_PID, TEST_APP2_UID, PROCESS_STATE_CACHED_EMPTY,
+ CACHED_APP_MIN_ADJ, PROCESS_CAPABILITY_CPU_TIME, TEST_APP2_NAME, TEST_SERVICE2_NAME,
+ null,
+ BIND_AUTO_CREATE,
+ atLeastOnce(), atLeastOnce());
+
+ // Disable the flags.
+ mSetFlagsRule.disableFlags(Flags.FLAG_SERVICE_BINDING_OOM_ADJ_POLICY);
+
+ // Verify that there should be at least 1 oom adj update
+ performTestServiceDistinctBindingOomAdj(TEST_APP1_PID, TEST_APP1_UID,
+ PROCESS_STATE_CACHED_EMPTY, CACHED_APP_MIN_ADJ,
+ PROCESS_CAPABILITY_IMPLICIT_CPU_TIME,
+ TEST_APP1_NAME, null,
+ TEST_APP2_PID, TEST_APP2_UID, PROCESS_STATE_CACHED_EMPTY,
+ CACHED_APP_MIN_ADJ, PROCESS_CAPABILITY_CPU_TIME, TEST_APP2_NAME, TEST_SERVICE2_NAME,
+ null,
+ BIND_AUTO_CREATE,
+ atLeastOnce(), atLeastOnce());
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_CPU_TIME_CAPABILITY_BASED_FREEZE_POLICY)
+ public void testServiceDistinctBindingOomAdjImplicitCpuTime_hostHasImplicitCpuTime()
+ throws Exception {
+ // Enable the flags.
+ mSetFlagsRule.enableFlags(Flags.FLAG_SERVICE_BINDING_OOM_ADJ_POLICY);
+
+ // Verify the IMPLICIT_CPU_TIME capability does not trigger an update if the host has
+ // already it.
+ performTestServiceDistinctBindingOomAdj(TEST_APP1_PID, TEST_APP1_UID,
+ PROCESS_STATE_CACHED_EMPTY, CACHED_APP_MIN_ADJ,
+ PROCESS_CAPABILITY_IMPLICIT_CPU_TIME,
+ TEST_APP1_NAME, null,
+ TEST_APP2_PID, TEST_APP2_UID, PROCESS_STATE_CACHED_EMPTY,
+ CACHED_APP_MIN_ADJ, PROCESS_CAPABILITY_IMPLICIT_CPU_TIME, TEST_APP2_NAME,
+ TEST_SERVICE2_NAME,
+ null,
+ BIND_AUTO_CREATE,
+ never(), atLeastOnce());
+
+ // Disable the flags.
+ mSetFlagsRule.disableFlags(Flags.FLAG_SERVICE_BINDING_OOM_ADJ_POLICY);
+
+ // Verify that there should be at least 1 oom adj update
+ performTestServiceDistinctBindingOomAdj(TEST_APP1_PID, TEST_APP1_UID,
+ PROCESS_STATE_CACHED_EMPTY, CACHED_APP_MIN_ADJ,
+ PROCESS_CAPABILITY_IMPLICIT_CPU_TIME,
+ TEST_APP1_NAME, null,
+ TEST_APP2_PID, TEST_APP2_UID, PROCESS_STATE_CACHED_EMPTY,
+ CACHED_APP_MIN_ADJ, PROCESS_CAPABILITY_IMPLICIT_CPU_TIME, TEST_APP2_NAME,
+ TEST_SERVICE2_NAME,
+ null,
+ BIND_AUTO_CREATE,
+ atLeastOnce(), atLeastOnce());
+ }
+
@SuppressWarnings("GuardedBy")
private void performTestServiceDistinctBindingOomAdj(int clientPid, int clientUid,
int clientProcState, int clientAdj, int clientCap, String clientPackageName,
diff --git a/services/tests/mockingservicestests/src/com/android/server/backup/BackupManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/BackupManagerServiceTest.java
index f1f4a0e..8538586 100644
--- a/services/tests/mockingservicestests/src/com/android/server/backup/BackupManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/backup/BackupManagerServiceTest.java
@@ -115,6 +115,7 @@
this)
.strictness(Strictness.LENIENT)
.spyStatic(UserBackupManagerService.class)
+ .spyStatic(UserManager.class)
.spyStatic(DumpUtils.class)
.startMocking();
doReturn(mSystemUserBackupManagerService).when(
@@ -124,6 +125,9 @@
() -> UserBackupManagerService.createAndInitializeService(eq(NON_SYSTEM_USER),
any(), any(), any()));
+ // Assume non-headless mode by default.
+ mockHeadlessSystemUserMode(false);
+
when(mNonSystemUserBackupManagerService.getUserId()).thenReturn(NON_SYSTEM_USER);
when(mUserManagerMock.getUserInfo(UserHandle.USER_SYSTEM)).thenReturn(mUserInfoMock);
when(mUserManagerMock.getUserInfo(NON_SYSTEM_USER)).thenReturn(mUserInfoMock);
@@ -722,6 +726,7 @@
when(mUserManagerMock.getMainUser()).thenReturn(UserHandle.of(NON_SYSTEM_USER));
assertFalse(mService.isBackupServiceActive(NON_SYSTEM_USER));
+ mockHeadlessSystemUserMode(true);
simulateUserUnlocked(UserHandle.USER_SYSTEM);
assertTrue(mService.isBackupServiceActive(NON_SYSTEM_USER));
@@ -736,6 +741,7 @@
when(mUserManagerMock.getMainUser()).thenReturn(UserHandle.of(NON_SYSTEM_USER));
assertFalse(mService.isBackupServiceActive(NON_SYSTEM_USER));
+ mockHeadlessSystemUserMode(true);
simulateUserUnlocked(UserHandle.USER_SYSTEM);
assertFalse(mService.isUserReadyForBackup(UserHandle.USER_SYSTEM));
@@ -755,6 +761,9 @@
}
private void createBackupManagerServiceAndUnlockSystemUser() {
+ // Assume non-headless mode for standard system user tests.
+ mockHeadlessSystemUserMode(false);
+
mService = new BackupManagerServiceTestable(mContextMock);
createBackupServiceLifecycle(mContextMock, mService);
simulateUserUnlocked(UserHandle.USER_SYSTEM);
@@ -765,6 +774,9 @@
* start a new service after mocking the 'main' user.
*/
private void setMockMainUserAndCreateBackupManagerService(int userId) {
+ // Assume headless mode for tests involving a non-system main user explicitly.
+ mockHeadlessSystemUserMode(true);
+
when(mUserManagerMock.getMainUser()).thenReturn(UserHandle.of(userId));
mService = new BackupManagerServiceTestable(mContextMock);
createBackupServiceLifecycle(mContextMock, mService);
@@ -800,6 +812,10 @@
return new File(sTestDir, "rememberActivated-" + userId);
}
+ private static void mockHeadlessSystemUserMode(boolean isHeadless) {
+ doReturn(isHeadless).when(UserManager::isHeadlessSystemUserMode);
+ }
+
private static void mockDumpPermissionsGranted(boolean granted) {
doReturn(granted)
.when(() -> DumpUtils.checkDumpAndUsageStatsPermission(any(), any(), any()));
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 6d78def..6b4bb46 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
@@ -1420,7 +1420,6 @@
@Test
public void testValidateLocation_futureLocation() {
- mSetFlagsRule.enableFlags(Flags.FLAG_LOCATION_VALIDATION);
Location location = createLocation(NAME, mRandom);
mProvider.setProviderLocation(location);
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundUserSoundNotifierTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundUserSoundNotifierTest.java
index 29af7d2..4dd5b10 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundUserSoundNotifierTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundUserSoundNotifierTest.java
@@ -41,12 +41,10 @@
import android.media.AudioPlaybackConfiguration;
import android.media.PlayerProxy;
import android.media.audiopolicy.AudioPolicy;
-import android.multiuser.Flags;
import android.os.Build;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
-import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.util.ArraySet;
@@ -151,11 +149,8 @@
final int fgUserId = mSpiedContext.getUserId();
int bgUserId = fgUserId + 1;
int bgUserUid = bgUserId * 100000;
- if (Flags.multipleAlarmNotificationsSupport()) {
- mBackgroundUserSoundNotifier.mNotificationClientUids.add(bgUserUid);
- } else {
- mBackgroundUserSoundNotifier.mNotificationClientUid = bgUserUid;
- }
+ mBackgroundUserSoundNotifier.mNotificationClientUids.add(bgUserUid);
+
AudioManager mockAudioManager = mock(AudioManager.class);
when(mSpiedContext.getSystemService(AudioManager.class)).thenReturn(mockAudioManager);
@@ -277,7 +272,6 @@
notification.actions[0].title);
}
- @RequiresFlagsEnabled({Flags.FLAG_MULTIPLE_ALARM_NOTIFICATIONS_SUPPORT})
@Test
public void testMultipleAlarmsSameUid_OneNotificationCreated() throws RemoteException {
assumeTrue(UserManager.supportsMultipleUsers());
@@ -308,7 +302,6 @@
eq(UserHandle.of(fgUserId)));
}
- @RequiresFlagsEnabled({Flags.FLAG_MULTIPLE_ALARM_NOTIFICATIONS_SUPPORT})
@Test
public void testMultipleAlarmsDifferentUsers_multipleNotificationsCreated()
throws RemoteException {
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceMockedTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceMockedTest.java
index 651bc18..f5cff43 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceMockedTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceMockedTest.java
@@ -87,6 +87,8 @@
import com.android.server.pm.UserManagerService.UserData;
import com.android.server.storage.DeviceStorageMonitorInternal;
+import com.google.common.truth.Expect;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
@@ -146,6 +148,8 @@
private static final String PRIVATE_PROFILE_NAME = "TestPrivateProfile";
+ @Rule public final Expect expect = Expect.create();
+
@Rule
public final ExtendedMockitoRule mExtendedMockitoRule = new ExtendedMockitoRule.Builder(this)
.spyStatic(UserManager.class)
@@ -1062,6 +1066,159 @@
assertThat(mUmi.getUserToLogoutCurrentUserTo()).isEqualTo(OTHER_USER_ID);
}
+ @Test
+ public void testGetOwnerName() {
+ assertThat(mUms.getOwnerName()).isNotEmpty();
+ }
+
+ @Test
+ public void testGetGuestName() {
+ assertThat(mUms.getGuestName()).isNotEmpty();
+ }
+
+ @Test
+ public void testUserWithName_null() {
+ assertThat(mUms.userWithName(null)).isNull();
+ }
+
+ private int getCurrentNumberOfUser0Allocations() {
+ return mUms.mUser0Allocations == null ? 0 : mUms.mUser0Allocations.get();
+ }
+
+ /**
+ * Tests what happens when {@code userWithName} is called with a {@link UserInfo} that has a
+ * explicit (non-{@code null}) name.
+ */
+ @Test
+ public void testUserWithName_hasExplicitName() {
+ int initialAllocations = getCurrentNumberOfUser0Allocations();
+
+ var systemUser = new UserInfo(UserHandle.USER_SYSTEM, "James Bond", /* flags= */ 0);
+ expect.withMessage("userWithName(%s)", systemUser).that(mUms.userWithName(systemUser))
+ .isSameInstanceAs(systemUser);
+ expect.withMessage("system.name").that(systemUser.name).isEqualTo("James Bond");
+ expect.withMessage("number of system user allocations after systemUser call")
+ .that(getCurrentNumberOfUser0Allocations()).isEqualTo(initialAllocations);
+
+ var mainUser = new UserInfo(007, "James Bond", UserInfo.FLAG_MAIN);
+ expect.withMessage("userWithName(%s)", mainUser).that(mUms.userWithName(mainUser))
+ .isSameInstanceAs(mainUser);
+ expect.withMessage("mainUser.name").that(mainUser.name).isEqualTo("James Bond");
+ expect.withMessage("number of system user allocations after non-systemUser call")
+ .that(getCurrentNumberOfUser0Allocations()).isEqualTo(initialAllocations);
+
+ var guestUser = new UserInfo(007, "James Bond", UserInfo.FLAG_GUEST);
+ expect.withMessage("userWithName(%s)", guestUser).that(mUms.userWithName(guestUser))
+ .isSameInstanceAs(guestUser);
+ expect.withMessage("guest.name").that(guestUser.name).isEqualTo("James Bond");
+ expect.withMessage("number of system user allocations after non-systemUser call")
+ .that(getCurrentNumberOfUser0Allocations()).isEqualTo(initialAllocations);
+
+ var normalUser = new UserInfo(007, "James Bond", /* flags= */ 0);
+ expect.withMessage("userWithName(%s)", systemUser).that(mUms.userWithName(normalUser))
+ .isSameInstanceAs(normalUser);
+ expect.withMessage("normalUser.name").that(normalUser.name).isEqualTo("James Bond");
+ expect.withMessage("number of system user allocations after non-systemUser call")
+ .that(getCurrentNumberOfUser0Allocations()).isEqualTo(initialAllocations);
+ }
+
+ /**
+ * Tests what happens when {@code userWithName} is called with a {@link UserInfo} that has a
+ * {@code null} name.
+ */
+ @Test
+ public void testUserWithName_withDefaultName() {
+ int initialAllocations = getCurrentNumberOfUser0Allocations();
+
+ var systemUser = new UserInfo(UserHandle.USER_SYSTEM, /* name= */ null, /* flags= */ 0);
+ UserInfo systemUserWithName = mUms.userWithName(systemUser);
+ assertWithMessage("userWithName(systemUser)").that(systemUserWithName).isNotNull();
+ expect.withMessage("userWithName(systemUser)").that(systemUserWithName)
+ .isNotSameInstanceAs(systemUser);
+ expect.withMessage("systemUserWithName.name").that(systemUserWithName.name)
+ .isEqualTo(mUms.getOwnerName());
+ expect.withMessage("system.name").that(systemUser.name).isNull();
+
+ // Allocation should only increase for USER_SYSTEM
+ int expectedAllocations = mUms.mUser0Allocations == null ? 0 : initialAllocations + 1;
+ expect.withMessage("number of system user allocations after systemUser call")
+ .that(getCurrentNumberOfUser0Allocations()).isEqualTo(expectedAllocations);
+
+ var mainUser = new UserInfo(42, /* name= */ null, UserInfo.FLAG_MAIN);
+ UserInfo mainUserWithName = mUms.userWithName(mainUser);
+ assertWithMessage("userWithName(mainUser)").that(mainUserWithName).isNotNull();
+ expect.withMessage("userWithName(mainUser)").that(mainUserWithName)
+ .isNotSameInstanceAs(mainUser);
+ expect.withMessage("mainUserWithName.name").that(mainUserWithName.name)
+ .isEqualTo(mUms.getOwnerName());
+ expect.withMessage("mainUser.name").that(mainUser.name).isNull();
+ expect.withMessage("number of system user allocations after non-systemUser call")
+ .that(getCurrentNumberOfUser0Allocations()).isEqualTo(expectedAllocations);
+
+ var guestUser = new UserInfo(42, /* name= */ null, UserInfo.FLAG_GUEST);
+ UserInfo guestUserWithName = mUms.userWithName(guestUser);
+ assertWithMessage("userWithName(guestUser)").that(guestUserWithName).isNotNull();
+ expect.withMessage("userWithName(guestUser)").that(guestUserWithName)
+ .isNotSameInstanceAs(guestUser);
+ expect.withMessage("mainUserWithName.name").that(guestUserWithName.name)
+ .isEqualTo(mUms.getGuestName());
+ expect.withMessage("guestUser.name").that(guestUser.name).isNull();
+ expect.withMessage("number of system user allocations after non-systemUser call")
+ .that(getCurrentNumberOfUser0Allocations()).isEqualTo(expectedAllocations);
+
+ var normalUser = new UserInfo(42, /* name= */ null, /* flags= */ 0);
+ UserInfo normalUserWithName = mUms.userWithName(normalUser);
+ assertWithMessage("userWithName(normalUser)").that(normalUserWithName).isNotNull();
+ expect.withMessage("userWithName(normalUser)").that(normalUserWithName)
+ .isSameInstanceAs(normalUser);
+ expect.withMessage("normalUserWithName.name").that(normalUserWithName.name).isNull();
+ expect.withMessage("normalUser.name").that(normalUser.name).isNull();
+ expect.withMessage("number of system user allocations after non-systemUser call")
+ .that(getCurrentNumberOfUser0Allocations()).isEqualTo(expectedAllocations);
+ }
+
+ @Test
+ public void testGetName_null() {
+ assertThrows(NullPointerException.class, () -> mUms.getName(null));
+ }
+
+ /** Tests what happens when the {@link UserInfo} has a explicit (non-{@code null}) name. */
+ @Test
+ public void testGetName_withExplicitName() {
+ String name = "Bond, James Bond!";
+
+ var systemUser = new UserInfo(UserHandle.USER_SYSTEM, name, /* flags= */ 0);
+ expect.withMessage("name of system user").that(mUms.getName(systemUser)).isEqualTo(name);
+
+ var mainUser = new UserInfo(42, name, UserInfo.FLAG_MAIN);
+ expect.withMessage("name of main user").that(mUms.getName(mainUser)).isEqualTo(name);
+
+ var guestUser = new UserInfo(42, name, UserInfo.FLAG_GUEST);
+ expect.withMessage("name of guest user").that(mUms.getName(guestUser)).isEqualTo(name);
+
+ var normalUser = new UserInfo(42, name, /* flags=*/ 0);
+ expect.withMessage("name of normal user").that(mUms.getName(normalUser)).isEqualTo(name);
+ }
+
+ /** Tests what happens when the {@link UserInfo} has a {@code null} name. */
+ @Test
+ public void testGetName_withDefaultNames() {
+ var systemUser = new UserInfo(UserHandle.USER_SYSTEM, /* name= */ null, /* flags= */ 0);
+ expect.withMessage("name of system user").that(mUms.getName(systemUser))
+ .isEqualTo(mUms.getOwnerName());
+
+ var mainUser = new UserInfo(42, /* name= */ null, UserInfo.FLAG_MAIN);
+ expect.withMessage("name of main user").that(mUms.getName(mainUser))
+ .isEqualTo(mUms.getOwnerName());
+
+ var guestUser = new UserInfo(42, /* name= */ null, UserInfo.FLAG_GUEST);
+ expect.withMessage("name of guest user").that(mUms.getName(guestUser))
+ .isEqualTo(mUms.getGuestName());
+
+ var normalUser = new UserInfo(42, /* name= */ null, /* flags= */ 0);
+ expect.withMessage("name of normal user").that(mUms.getName(normalUser)).isNull();
+ }
+
/**
* Returns true if the user's XML file has Default restrictions
* @param userId Id of the user.
diff --git a/services/tests/powerservicetests/Android.bp b/services/tests/powerservicetests/Android.bp
index 2f06331..ca966cf 100644
--- a/services/tests/powerservicetests/Android.bp
+++ b/services/tests/powerservicetests/Android.bp
@@ -12,15 +12,17 @@
],
static_libs: [
- "truth",
+ "TestParameterInjector",
"flag-junit",
"frameworks-base-testutils",
+ "perfetto_trace_java_protos",
"platform-compat-test-rules",
"platform-test-annotations",
"services.core",
"servicestests-utils",
"testables",
- "TestParameterInjector",
+ "truth",
+ "truth-liteproto-extension",
],
libs: [
@@ -33,8 +35,8 @@
platform_apis: true,
test_suites: [
- "device-tests",
"automotive-tests",
+ "device-tests",
],
certificate: "platform",
diff --git a/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java b/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java
index 94ce723..0535895 100644
--- a/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java
+++ b/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java
@@ -42,6 +42,7 @@
import static org.mockito.Mockito.when;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.ActivityManagerInternal;
import android.app.AppOpsManager;
import android.content.Context;
@@ -111,6 +112,8 @@
private static final int OWNER_WORK_SOURCE_UID_2 = 3457;
private static final int PID = 5678;
+ private static final int FAKE_NANO_TIME = 123456;
+
@Mock private BatterySaverStateMachine mBatterySaverStateMachineMock;
@Mock private PowerManagerService.NativeWrapper mNativeWrapperMock;
@Mock private Notifier mNotifierMock;
@@ -125,6 +128,7 @@
@Mock private DisplayManagerInternal mDisplayManagerInternal;
@Mock private ActivityManagerInternal mActivityManagerInternal;
@Mock private WakeLockLog mWakeLockLog;
+ @Mock private WakelockTracer mWakelockTracer;
@Mock private IBatteryStats mBatteryStats;
@@ -876,6 +880,7 @@
@Test
public void testOnWakeLockListener_FullWakeLock_ProcessesOnHandler() throws RemoteException {
+ when(mPowerManagerFlags.isAppWakelockDataSourceEnabled()).thenReturn(true);
when(mPowerManagerFlags.improveWakelockLatency()).thenReturn(true);
createNotifier();
@@ -884,7 +889,7 @@
throw new RemoteException("Just testing");
}
};
- clearInvocations(mWakeLockLog, mBatteryStats, mAppOpsManager);
+ clearInvocations(mWakeLockLog, mBatteryStats, mAppOpsManager, mWakelockTracer);
final int uid = 1234;
final int pid = 5678;
@@ -894,6 +899,10 @@
"my.package.name", uid, pid, /* workSource= */ null, /* historyTag= */ null,
exceptingCallback);
+ // Tracing is done synchronously.
+ verify(mWakelockTracer).onWakelockEvent(false, "wakelockTag", uid, pid,
+ PowerManager.SCREEN_BRIGHT_WAKE_LOCK, null, FAKE_NANO_TIME);
+
// No interaction because we expect that to happen in async
verifyNoMoreInteractions(mWakeLockLog, mBatteryStats, mAppOpsManager);
@@ -905,13 +914,17 @@
verify(mAppOpsManager).finishOp(AppOpsManager.OP_WAKE_LOCK, uid,
"my.package.name", null);
- clearInvocations(mWakeLockLog, mBatteryStats, mAppOpsManager);
+ clearInvocations(mWakeLockLog, mBatteryStats, mAppOpsManager, mWakelockTracer);
// Acquire the wakelock
mNotifier.onWakeLockAcquired(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "wakelockTag",
"my.package.name", uid, pid, /* workSource= */ null, /* historyTag= */ null,
exceptingCallback);
+ // Tracing is done synchronously.
+ verify(mWakelockTracer).onWakelockEvent(true, "wakelockTag", uid, pid,
+ PowerManager.SCREEN_BRIGHT_WAKE_LOCK, null, FAKE_NANO_TIME);
+
// No interaction because we expect that to happen in async
verifyNoMoreInteractions(mWakeLockLog, mBatteryStats, mAppOpsManager);
@@ -942,6 +955,26 @@
}
@Test
+ public void testOnWakeLockListener_TracingDisabled() throws RemoteException {
+ when(mPowerManagerFlags.isAppWakelockDataSourceEnabled()).thenReturn(false);
+ createNotifier();
+
+ clearInvocations(mWakelockTracer);
+
+ final int uid = 1234;
+ final int pid = 5678;
+
+ // Release the wakelock
+ mNotifier.onWakeLockReleased(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "wakelockTag",
+ "my.package.name", uid, pid, /* workSource= */ null, /* historyTag= */ null,
+ /* callback= */ null);
+
+ // No interaction because the flag is disabled.
+ mTestLooper.dispatchAll();
+ verifyNoMoreInteractions(mWakelockTracer);
+ }
+
+ @Test
public void testOnWakeLockListener_FullWakeLock_ProcessesInSync() throws RemoteException {
createNotifier();
@@ -1297,11 +1330,21 @@
}
@Override
+ public long nanoTime() {
+ return FAKE_NANO_TIME;
+ }
+
+ @Override
public @NonNull WakeLockLog getWakeLockLog(Context context) {
return mWakeLockLog;
}
@Override
+ public @Nullable WakelockTracer getWakelockTracer(Looper looper) {
+ return mWakelockTracer;
+ }
+
+ @Override
public AppOpsManager getAppOpsManager(Context context) {
return mAppOpsManager;
}
diff --git a/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java
index 7921c7a..478aa02 100644
--- a/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java
+++ b/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java
@@ -112,7 +112,6 @@
import androidx.test.core.app.ApplicationProvider;
import com.android.internal.app.IBatteryStats;
-import com.android.internal.foldables.FoldGracePeriodProvider;
import com.android.internal.util.test.FakeSettingsProvider;
import com.android.server.LocalServices;
import com.android.server.SystemService;
@@ -187,7 +186,6 @@
@Mock private VirtualDeviceManagerInternal mVirtualDeviceManagerInternalMock;
@Mock private DreamManagerInternal mDreamManagerInternalMock;
@Mock private PowerManagerService.NativeWrapper mNativeWrapperMock;
- @Mock private FoldGracePeriodProvider mFoldGracePeriodProvider;
@Mock private Notifier mNotifierMock;
@Mock private WirelessChargerDetector mWirelessChargerDetectorMock;
@Mock private AmbientDisplayConfiguration mAmbientDisplayConfigurationMock;
@@ -398,11 +396,6 @@
DeviceConfigParameterProvider createDeviceConfigParameterProvider() {
return mDeviceParameterProvider;
}
-
- @Override
- FoldGracePeriodProvider createFoldGracePeriodProvider() {
- return mFoldGracePeriodProvider;
- }
});
return mService;
}
@@ -569,25 +562,7 @@
}
@Test
- public void testWakefulnessSleep_SoftSleepFlag_NoWakelocks() {
- when(mFoldGracePeriodProvider.isEnabled()).thenReturn(false);
-
- createService();
- // Start with AWAKE state
- startSystem();
- assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);
-
- // Take a nap and verify we go to sleep.
- mService.getBinderServiceInstance().goToSleep(mClock.now(),
- PowerManager.GO_TO_SLEEP_REASON_APPLICATION,
- PowerManager.GO_TO_SLEEP_FLAG_SOFT_SLEEP);
- assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_DOZING);
- }
-
- @Test
public void testWakefulnessAwakeShowKeyguard_SoftSleepFlag_NoWakelocks() {
- when(mFoldGracePeriodProvider.isEnabled()).thenReturn(true);
-
createService();
// Start with AWAKE state
startSystem();
@@ -617,11 +592,12 @@
null /* workSource */, null /* historyTag */, Display.INVALID_DISPLAY,
null /* callback */);
- // Take a nap and verify we go to sleep.
+ // Take a nap and verify do not sleep
mService.getBinderServiceInstance().goToSleep(mClock.now(),
PowerManager.GO_TO_SLEEP_REASON_APPLICATION,
PowerManager.GO_TO_SLEEP_FLAG_SOFT_SLEEP);
- assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_DOZING);
+ assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);
+ verify(mNotifierMock).showDismissibleKeyguard();
}
@Test
@@ -1329,6 +1305,36 @@
assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_DOZING);
}
+ @Test
+ public void testDreamPreventedWhenNotPowered_proximityPositive_afterTimeout_staysAwake() {
+ AtomicReference<DisplayManagerInternal.DisplayPowerCallbacks> callback =
+ new AtomicReference<>();
+ doAnswer((Answer<Void>) invocation -> {
+ callback.set(invocation.getArgument(0));
+ return null;
+ }).when(mDisplayManagerInternalMock).initPowerManagement(any(), any(), any());
+ when(mBatteryManagerInternalMock.isPowered(anyInt())).thenReturn(false);
+ Settings.Secure.putInt(mContextSpy.getContentResolver(),
+ Settings.Secure.SCREENSAVER_ACTIVATE_ON_SLEEP, 1);
+ Settings.Secure.putInt(mContextSpy.getContentResolver(),
+ Settings.Secure.SCREENSAVER_RESTRICT_TO_WIRELESS_CHARGING, 1);
+
+ doAnswer(inv -> {
+ when(mDreamManagerInternalMock.isDreaming()).thenReturn(true);
+ return null;
+ }).when(mDreamManagerInternalMock).startDream(anyBoolean(), anyString());
+
+ setMinimumScreenOffTimeoutConfig(5);
+ createService();
+ startSystem();
+ callback.get().onProximityPositive();
+
+ assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);
+
+ advanceTime(15000);
+ assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);
+ }
+
@EnableFlags(android.companion.virtualdevice.flags.Flags.FLAG_DEVICE_AWARE_DISPLAY_POWER)
@SuppressWarnings("GuardedBy")
@Test
@@ -1693,6 +1699,7 @@
setAttentiveTimeout(100);
createService();
startSystem();
+ setPluggedIn(true);
advanceTime(50);
verify(mInattentiveSleepWarningControllerMock, atLeastOnce()).show();
when(mInattentiveSleepWarningControllerMock.isShown()).thenReturn(true);
@@ -2992,6 +2999,26 @@
}
@Test
+ @RequiresFlagsEnabled({Flags.FLAG_FORCE_DISABLE_WAKELOCKS})
+ public void testDisableWakelocks_whenForced() {
+ createService();
+ startSystem();
+
+ WakeLock wakeLock = acquireWakeLock("forceDisableTestWakeLock",
+ PowerManager.PARTIAL_WAKE_LOCK, Display.INVALID_DISPLAY);
+ assertThat(wakeLock.mDisabled).isFalse();
+ advanceTime(1000);
+
+ mService.setForceDisableWakelocksInternal(true);
+ advanceTime(1000);
+ assertThat(wakeLock.mDisabled).isTrue();
+
+ mService.setForceDisableWakelocksInternal(false);
+ advanceTime(1000);
+ assertThat(wakeLock.mDisabled).isFalse();
+ }
+
+ @Test
@RequiresFlagsEnabled({Flags.FLAG_DISABLE_FROZEN_PROCESS_WAKELOCKS})
public void testDisableWakelocks_whenFrozen() {
createService();
diff --git a/services/tests/powerservicetests/src/com/android/server/power/ScreenTimeoutOverridePolicyTest.java b/services/tests/powerservicetests/src/com/android/server/power/ScreenTimeoutOverridePolicyTest.java
new file mode 100644
index 0000000..bf9e567
--- /dev/null
+++ b/services/tests/powerservicetests/src/com/android/server/power/ScreenTimeoutOverridePolicyTest.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.power;
+
+
+import static android.os.PowerManagerInternal.WAKEFULNESS_ASLEEP;
+import static android.os.PowerManagerInternal.WAKEFULNESS_AWAKE;
+import static android.os.PowerManagerInternal.WAKEFULNESS_DOZING;
+import static android.os.PowerManagerInternal.WAKEFULNESS_DREAMING;
+
+import static com.android.server.power.PowerManagerService.WAKE_LOCK_BUTTON_BRIGHT;
+import static com.android.server.power.PowerManagerService.WAKE_LOCK_SCREEN_BRIGHT;
+import static com.android.server.power.PowerManagerService.WAKE_LOCK_SCREEN_DIM;
+import static com.android.server.power.PowerManagerService.WAKE_LOCK_SCREEN_TIMEOUT_OVERRIDE;
+import static com.android.server.power.ScreenTimeoutOverridePolicy.RELEASE_REASON_NON_INTERACTIVE;
+import static com.android.server.power.ScreenTimeoutOverridePolicy.RELEASE_REASON_SCREEN_LOCK;
+import static com.android.server.power.ScreenTimeoutOverridePolicy.RELEASE_REASON_UNKNOWN;
+import static com.android.server.power.ScreenTimeoutOverridePolicy.RELEASE_REASON_USER_ACTIVITY_ACCESSIBILITY;
+import static com.android.server.power.ScreenTimeoutOverridePolicy.RELEASE_REASON_USER_ACTIVITY_ATTENTION;
+import static com.android.server.power.ScreenTimeoutOverridePolicy.RELEASE_REASON_USER_ACTIVITY_BUTTON;
+import static com.android.server.power.ScreenTimeoutOverridePolicy.RELEASE_REASON_USER_ACTIVITY_OTHER;
+import static com.android.server.power.ScreenTimeoutOverridePolicy.RELEASE_REASON_USER_ACTIVITY_TOUCH;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.content.ContextWrapper;
+import android.content.res.Resources;
+import android.os.PowerManager;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests for {@link com.android.server.power.ScreenTimeoutOverridePolicy}.
+ *
+ * Build/Install/Run:
+ * atest PowerServiceTests:ScreenTimeoutOverridePolicyTest
+ */
+public class ScreenTimeoutOverridePolicyTest {
+ private static final int SCREEN_TIMEOUT_OVERRIDE = 10;
+ private ContextWrapper mContextSpy;
+ private Resources mResourcesSpy;
+ private ScreenTimeoutOverridePolicy mScreenTimeoutOverridePolicy;
+
+ private ScreenTimeoutOverridePolicy.PolicyCallback mPolicyCallback;
+ private int mReleaseReason = RELEASE_REASON_UNKNOWN;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mContextSpy = spy(new ContextWrapper(ApplicationProvider.getApplicationContext()));
+ mResourcesSpy = spy(mContextSpy.getResources());
+ when(mContextSpy.getResources()).thenReturn(mResourcesSpy);
+ when(mResourcesSpy.getInteger(com.android.internal.R.integer.config_screenTimeoutOverride))
+ .thenReturn(SCREEN_TIMEOUT_OVERRIDE);
+
+ mPolicyCallback = reason -> mReleaseReason = reason;
+ mScreenTimeoutOverridePolicy =
+ new ScreenTimeoutOverridePolicy(
+ mContextSpy, SCREEN_TIMEOUT_OVERRIDE, mPolicyCallback);
+ }
+
+ @Test
+ public void testUserActivity() {
+ mScreenTimeoutOverridePolicy.onUserActivity(WAKE_LOCK_SCREEN_TIMEOUT_OVERRIDE,
+ PowerManager.USER_ACTIVITY_EVENT_ATTENTION);
+ verifyReason(RELEASE_REASON_USER_ACTIVITY_ATTENTION);
+
+ mScreenTimeoutOverridePolicy.onUserActivity(WAKE_LOCK_SCREEN_TIMEOUT_OVERRIDE,
+ PowerManager.USER_ACTIVITY_EVENT_OTHER);
+ verifyReason(RELEASE_REASON_USER_ACTIVITY_OTHER);
+
+ mScreenTimeoutOverridePolicy.onUserActivity(WAKE_LOCK_SCREEN_TIMEOUT_OVERRIDE,
+ PowerManager.USER_ACTIVITY_EVENT_BUTTON);
+ verifyReason(RELEASE_REASON_USER_ACTIVITY_BUTTON);
+
+ mScreenTimeoutOverridePolicy.onUserActivity(WAKE_LOCK_SCREEN_TIMEOUT_OVERRIDE,
+ PowerManager.USER_ACTIVITY_EVENT_TOUCH);
+ verifyReason(RELEASE_REASON_USER_ACTIVITY_TOUCH);
+
+ mScreenTimeoutOverridePolicy.onUserActivity(WAKE_LOCK_SCREEN_TIMEOUT_OVERRIDE,
+ PowerManager.USER_ACTIVITY_EVENT_ACCESSIBILITY);
+ verifyReason(RELEASE_REASON_USER_ACTIVITY_ACCESSIBILITY);
+ }
+
+ @Test
+ public void testScreenWakeLock() {
+ mScreenTimeoutOverridePolicy.checkScreenWakeLock(WAKE_LOCK_SCREEN_TIMEOUT_OVERRIDE);
+ verifyReason(RELEASE_REASON_UNKNOWN);
+
+ mScreenTimeoutOverridePolicy.checkScreenWakeLock(
+ WAKE_LOCK_SCREEN_TIMEOUT_OVERRIDE | WAKE_LOCK_SCREEN_BRIGHT);
+ verifyReason(RELEASE_REASON_SCREEN_LOCK);
+
+ mScreenTimeoutOverridePolicy.checkScreenWakeLock(
+ WAKE_LOCK_SCREEN_TIMEOUT_OVERRIDE | WAKE_LOCK_SCREEN_DIM);
+ verifyReason(RELEASE_REASON_SCREEN_LOCK);
+
+ mScreenTimeoutOverridePolicy.checkScreenWakeLock(
+ WAKE_LOCK_SCREEN_TIMEOUT_OVERRIDE | WAKE_LOCK_BUTTON_BRIGHT);
+ verifyReason(RELEASE_REASON_SCREEN_LOCK);
+ }
+
+
+ @Test
+ public void testWakefulnessChange() {
+ mScreenTimeoutOverridePolicy.onWakefulnessChange(
+ WAKE_LOCK_SCREEN_TIMEOUT_OVERRIDE, WAKEFULNESS_AWAKE);
+ verifyReason(RELEASE_REASON_UNKNOWN);
+
+ mScreenTimeoutOverridePolicy.onWakefulnessChange(
+ WAKE_LOCK_SCREEN_TIMEOUT_OVERRIDE, WAKEFULNESS_DREAMING);
+ verifyReason(RELEASE_REASON_UNKNOWN);
+
+ mScreenTimeoutOverridePolicy.onWakefulnessChange(
+ WAKE_LOCK_SCREEN_TIMEOUT_OVERRIDE, WAKEFULNESS_ASLEEP);
+ verifyReason(RELEASE_REASON_NON_INTERACTIVE);
+
+ mScreenTimeoutOverridePolicy.onWakefulnessChange(
+ WAKE_LOCK_SCREEN_TIMEOUT_OVERRIDE, WAKEFULNESS_DOZING);
+ verifyReason(RELEASE_REASON_NON_INTERACTIVE);
+ }
+
+ private void verifyReason(int expectedReason) {
+ assertThat(mReleaseReason).isEqualTo(expectedReason);
+ mReleaseReason = RELEASE_REASON_UNKNOWN;
+ }
+}
diff --git a/services/tests/powerservicetests/src/com/android/server/power/WakelockTracerTest.java b/services/tests/powerservicetests/src/com/android/server/power/WakelockTracerTest.java
new file mode 100644
index 0000000..9691b5d
--- /dev/null
+++ b/services/tests/powerservicetests/src/com/android/server/power/WakelockTracerTest.java
@@ -0,0 +1,302 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.power;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.extensions.proto.LiteProtoTruth.assertThat;
+
+import static perfetto.protos.AppWakelockData.AppWakelockInfo;
+import static perfetto.protos.InternedDataOuterClass.InternedData;
+import static perfetto.protos.TracePacketOuterClass.TracePacket;
+
+import android.os.test.TestLooper;
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoOutputStream;
+
+import com.google.protobuf.InvalidProtocolBufferException;
+
+import org.junit.Test;
+
+import perfetto.protos.AppWakelockConfig;
+import perfetto.protos.DataSourceConfigOuterClass;
+
+/** Tests for {@link com.android.server.power.WakelockTracer} */
+public class WakelockTracerTest {
+ private TestLooper mTestLooper = new TestLooper();
+ private WakelockTracer mTracer = new WakelockTracer(
+ mTestLooper.getLooper(), /* params= */ null);
+
+ private ProtoInputStream emptyConfig() {
+ return new ProtoInputStream(
+ DataSourceConfigOuterClass.DataSourceConfig.newBuilder().build().toByteArray());
+ }
+
+ private ProtoInputStream makeConfig(int thresholdMs, int delayMs, boolean dropPid) {
+ return new ProtoInputStream(
+ DataSourceConfigOuterClass.DataSourceConfig.newBuilder()
+ .setAppWakelocksConfig(
+ AppWakelockConfig.AppWakelocksConfig.newBuilder()
+ .setFilterDurationBelowMs(thresholdMs)
+ .setWriteDelayMs(delayMs)
+ .setDropOwnerPid(dropPid)
+ .build())
+ .build()
+ .toByteArray());
+ }
+
+ private long timeAtMillis(long millis) {
+ return millis * 1_000_000L;
+ }
+
+ private long encodeTimestamp(long millis, boolean acquired) {
+ return timeAtMillis(millis) << 1 | (acquired ? 1 : 0);
+ }
+
+ private TracePacket write(
+ WakelockTracer.Instance instance, WakelockTracer.IncrementalState state, long nanoTime)
+ throws InvalidProtocolBufferException {
+ ProtoOutputStream stream = new ProtoOutputStream();
+ instance.write(stream, state, nanoTime);
+ return TracePacket.parseFrom(stream.getBytes());
+ }
+
+ @Test
+ public void testWakelockUnderThreshold() throws InvalidProtocolBufferException {
+ WakelockTracer.Instance instance = mTracer.createInstance(makeConfig(50, 1000, false), 0);
+ WakelockTracer.IncrementalState state = new WakelockTracer.IncrementalState();
+
+ instance.onWakelockEvent(true, "foo", 1, 2, 3, null, timeAtMillis(0));
+ instance.onWakelockEvent(false, "foo", 1, 2, 3, null, timeAtMillis(10));
+
+ TracePacket packet = write(instance, state, timeAtMillis(100));
+
+ // Wakelock is under threshold, so it is not written.
+ assertThat(packet.hasAppWakelockBundle()).isFalse();
+ }
+
+ @Test
+ public void testWakelockAboveThreshold() throws InvalidProtocolBufferException {
+ WakelockTracer.Instance instance = mTracer.createInstance(makeConfig(50, 1000, false), 0);
+ WakelockTracer.IncrementalState state = new WakelockTracer.IncrementalState();
+
+ instance.onWakelockEvent(true, "foo", 1, 2, 3, null, timeAtMillis(10));
+ instance.onWakelockEvent(false, "foo", 1, 2, 3, null, timeAtMillis(100));
+
+ TracePacket packet = write(instance, state, timeAtMillis(100));
+
+ // Wakelock is above threshold, so it is written.
+ assertThat(packet.getAppWakelockBundle().getInternIdList()).containsExactly(1, 1);
+ assertThat(packet.getAppWakelockBundle().getEncodedTsList())
+ .containsExactly(encodeTimestamp(0, true), encodeTimestamp(90, false));
+ }
+
+ @Test
+ public void testInternedDataFormat() throws InvalidProtocolBufferException {
+ WakelockTracer.Instance instance = mTracer.createInstance(makeConfig(50, 1000, false), 0);
+ WakelockTracer.IncrementalState state = new WakelockTracer.IncrementalState();
+
+ instance.onWakelockEvent(false, "foo", 1, 2, 3, null, timeAtMillis(0));
+
+ TracePacket packet = write(instance, state, timeAtMillis(100));
+
+ assertThat(packet.getInternedData())
+ .isEqualTo(
+ InternedData.newBuilder()
+ .addAppWakelockInfo(
+ AppWakelockInfo.newBuilder()
+ .setIid(1)
+ .setTag("foo")
+ .setOwnerUid(1)
+ .setOwnerPid(2)
+ .setFlags(3)
+ .build())
+ .build());
+ }
+
+ @Test
+ public void testInternedDataUniqueness() throws InvalidProtocolBufferException {
+ WakelockTracer.Instance instance = mTracer.createInstance(makeConfig(50, 1000, false), 0);
+ WakelockTracer.IncrementalState state = new WakelockTracer.IncrementalState();
+
+ // Each wakelock below changes one field from the first.
+ instance.onWakelockEvent(false, "foo", 1, 2, 3, null, timeAtMillis(0));
+ instance.onWakelockEvent(false, "bar", 1, 2, 3, null, timeAtMillis(1));
+ instance.onWakelockEvent(false, "foo", 9, 2, 3, null, timeAtMillis(2));
+ instance.onWakelockEvent(false, "foo", 1, 9, 3, null, timeAtMillis(3));
+ instance.onWakelockEvent(false, "foo", 1, 2, 9, null, timeAtMillis(4));
+
+ // The wakelock below is a repeat, to show interned id re-use.
+ instance.onWakelockEvent(false, "foo", 9, 2, 3, null, timeAtMillis(5));
+
+ // The wakelock below is a repeat with different acquire/time values.
+ instance.onWakelockEvent(true, "foo", 9, 2, 3, null, timeAtMillis(6));
+
+ TracePacket packet = write(instance, state, timeAtMillis(100));
+
+ // Five unique locks written first, then two repeats of the third item.
+ assertThat(packet.getAppWakelockBundle().getInternIdList())
+ .containsExactly(1, 2, 3, 4, 5, 3, 3);
+ assertThat(packet.getAppWakelockBundle().getEncodedTsList())
+ .containsExactly(
+ encodeTimestamp(0, false),
+ encodeTimestamp(1, false),
+ encodeTimestamp(2, false),
+ encodeTimestamp(3, false),
+ encodeTimestamp(4, false),
+ encodeTimestamp(5, false),
+ encodeTimestamp(6, true));
+ }
+
+ @Test
+ public void testDropOwnerPid() throws InvalidProtocolBufferException {
+ WakelockTracer.Instance instance = mTracer.createInstance(makeConfig(50, 1000, true), 0);
+ WakelockTracer.IncrementalState state = new WakelockTracer.IncrementalState();
+
+ instance.onWakelockEvent(true, "foo", 1, 2, 3, null, timeAtMillis(0));
+ instance.onWakelockEvent(true, "foo", 1, 4, 3, null, timeAtMillis(10));
+ instance.onWakelockEvent(false, "foo", 1, 2, 3, null, timeAtMillis(20));
+ instance.onWakelockEvent(false, "foo", 1, 4, 3, null, timeAtMillis(90));
+
+ TracePacket packet = write(instance, state, timeAtMillis(100));
+
+ // With dropOwnerPid, both pairing and interning excludes the pid. Above, the
+ // second and third events will pair and be below threshold with different
+ // pids, and the output will use a single intern id for first/fourth event.
+ assertThat(packet.getAppWakelockBundle().getInternIdList()).containsExactly(1, 1);
+ assertThat(packet.getAppWakelockBundle().getEncodedTsList())
+ .containsExactly(encodeTimestamp(0, true), encodeTimestamp(90, false));
+ }
+
+ @Test
+ public void testStartTooRecent() throws InvalidProtocolBufferException {
+ WakelockTracer.Instance instance = mTracer.createInstance(makeConfig(50, 1000, false), 0);
+ WakelockTracer.IncrementalState state = new WakelockTracer.IncrementalState();
+
+ instance.onWakelockEvent(true, "foo", 1, 2, 3, null, timeAtMillis(0));
+
+ TracePacket packet = write(instance, state, timeAtMillis(10));
+
+ // The acquire event is too recent to know if the dur > threshold, don't write.
+ assertThat(packet).isEqualTo(TracePacket.newBuilder().build());
+ assertThat(packet.hasAppWakelockBundle()).isFalse();
+ }
+
+ @Test
+ public void testStartOldEnough() throws InvalidProtocolBufferException {
+ WakelockTracer.Instance instance = mTracer.createInstance(makeConfig(50, 1000, false), 0);
+ WakelockTracer.IncrementalState state = new WakelockTracer.IncrementalState();
+
+ instance.onWakelockEvent(true, "foo", 1, 2, 3, null, timeAtMillis(0));
+
+ TracePacket packet = write(instance, state, timeAtMillis(100));
+
+ // The acquire event is 100ms old, it must be > 50ms threshold.
+ assertThat(packet.hasAppWakelockBundle()).isTrue();
+ }
+
+ @Test
+ public void testUnmatchedRelease() throws InvalidProtocolBufferException {
+ WakelockTracer.Instance instance = mTracer.createInstance(makeConfig(50, 1000, false), 0);
+ WakelockTracer.IncrementalState state = new WakelockTracer.IncrementalState();
+
+ instance.onWakelockEvent(false, "foo", 1, 2, 3, null, timeAtMillis(0));
+ instance.onWakelockEvent(false, "bar", 1, 2, 3, null, timeAtMillis(90));
+
+ TracePacket packet = write(instance, state, timeAtMillis(100));
+
+ // End events should be written if there's no matching start (the start may have been
+ // before the trace, or written due to already aging out. The end event does not care
+ // about how recent it is like the acquire event.
+ assertThat(packet.getAppWakelockBundle().getInternIdList()).containsExactly(1, 2);
+ }
+
+ @Test
+ public void testSchedulesSingleWrite() throws InvalidProtocolBufferException {
+ WakelockTracer.Instance instance = mTracer.createInstance(makeConfig(50, 1000, false), 0);
+
+ instance.onWakelockEvent(true, "foo", 1, 2, 3, null, timeAtMillis(0));
+ instance.onWakelockEvent(true, "foo", 1, 2, 3, null, timeAtMillis(0));
+ instance.onWakelockEvent(true, "foo", 1, 2, 3, null, timeAtMillis(0));
+
+ // The write shouldn't dispatch until time has elapsed.
+ assertThat(mTestLooper.dispatchAll()).isEqualTo(0);
+
+ // There should only be one write scheduled at a time.
+ mTestLooper.moveTimeForward(1000);
+ assertThat(mTestLooper.dispatchAll()).isEqualTo(1);
+ }
+
+ @Test
+ public void testEmptyConfig() throws InvalidProtocolBufferException {
+ WakelockTracer.Instance instance = mTracer.createInstance(emptyConfig(), 0);
+ WakelockTracer.IncrementalState state = new WakelockTracer.IncrementalState();
+
+ instance.onWakelockEvent(true, "foo", 1, 2, 3, null, timeAtMillis(0));
+ instance.onWakelockEvent(false, "foo", 1, 2, 3, null, timeAtMillis(1));
+
+ // Default is threshold of zero, so a 1ms wakelock should be written.
+ TracePacket packet = write(instance, state, timeAtMillis(100));
+ assertThat(packet.getAppWakelockBundle().getInternIdList()).containsExactly(1, 1);
+
+ // Default delay is 5000ms, so expect handler to trigger after that time.
+ mTestLooper.moveTimeForward(4000);
+ assertThat(mTestLooper.dispatchAll()).isEqualTo(0);
+
+ mTestLooper.moveTimeForward(1000);
+ assertThat(mTestLooper.dispatchAll()).isEqualTo(1);
+ }
+
+ @Test
+ public void testResetPendingWhenEmpty() throws InvalidProtocolBufferException {
+ WakelockTracer.Instance instance = mTracer.createInstance(makeConfig(50, 1000, false), 0);
+ WakelockTracer.IncrementalState state = new WakelockTracer.IncrementalState();
+
+ instance.onWakelockEvent(true, "foo", 1, 2, 3, null, timeAtMillis(0));
+
+ // Simulate a delay and write.
+ mTestLooper.moveTimeForward(1000);
+ assertThat(mTestLooper.dispatchAll()).isEqualTo(1);
+ write(instance, state, timeAtMillis(1000));
+
+ // After further delay, no additional dispatches.
+ mTestLooper.moveTimeForward(1000);
+ assertThat(mTestLooper.dispatchAll()).isEqualTo(0);
+
+ // After a new event and delay, expect a new dispatch.
+ instance.onWakelockEvent(true, "foo", 1, 2, 3, null, timeAtMillis(1000));
+ mTestLooper.moveTimeForward(1000);
+ assertThat(mTestLooper.dispatchAll()).isEqualTo(1);
+ }
+
+ @Test
+ public void testScheduleWriteWhenNonEmpty() throws InvalidProtocolBufferException {
+ WakelockTracer.Instance instance = mTracer.createInstance(makeConfig(50, 1000, false), 0);
+ WakelockTracer.IncrementalState state = new WakelockTracer.IncrementalState();
+
+ instance.onWakelockEvent(true, "foo", 1, 2, 3, null, timeAtMillis(1000));
+
+ // Simulate a delay and write.
+ mTestLooper.moveTimeForward(1000);
+ assertThat(mTestLooper.dispatchAll()).isEqualTo(1);
+ write(instance, state, timeAtMillis(1000));
+
+ // Since the event was too new, it wasn't written above. The call to write should
+ // internally schedule another write (as will be dispatched here).
+ mTestLooper.moveTimeForward(1000);
+ assertThat(mTestLooper.dispatchAll()).isEqualTo(1);
+ }
+}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsBinderCallStatsTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsBinderCallStatsTest.java
deleted file mode 100644
index c0d33cb..0000000
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsBinderCallStatsTest.java
+++ /dev/null
@@ -1,147 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.power.stats;
-
-import static org.junit.Assert.assertEquals;
-
-import android.os.Binder;
-import android.os.Process;
-import android.util.ArraySet;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.internal.os.BinderCallsStats;
-import com.android.internal.os.BinderTransactionNameResolver;
-
-import org.junit.Test;
-
-import java.util.ArrayList;
-import java.util.Collection;
-
-/**
- * Test cases for android.os.BatteryStats, system server Binder call stats.
- */
-@SmallTest
-@android.platform.test.annotations.DisabledOnRavenwood(blockedBy = BinderCallsStats.class)
-public class BatteryStatsBinderCallStatsTest {
-
- private static final int TRANSACTION_CODE1 = 100;
- private static final int TRANSACTION_CODE2 = 101;
-
- /**
- * Test BatteryStatsImpl.Uid.noteBinderCallStats.
- */
- @Test
- public void testNoteBinderCallStats() throws Exception {
- final MockClock clocks = new MockClock(); // holds realtime and uptime in ms
- MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
-
- int callingUid = Process.FIRST_APPLICATION_UID + 1;
- int workSourceUid = Process.FIRST_APPLICATION_UID + 1;
-
- Collection<BinderCallsStats.CallStat> callStats = new ArrayList<>();
- BinderCallsStats.CallStat stat1 = new BinderCallsStats.CallStat(callingUid,
- MockBinder.class, TRANSACTION_CODE1, true /*screenInteractive */);
- stat1.incrementalCallCount = 21;
- stat1.recordedCallCount = 5;
- stat1.cpuTimeMicros = 1000;
- callStats.add(stat1);
-
- bi.noteBinderCallStats(workSourceUid, 42, callStats);
-
- callStats.clear();
- BinderCallsStats.CallStat stat2 = new BinderCallsStats.CallStat(callingUid,
- MockBinder.class, TRANSACTION_CODE1, true /*screenInteractive */);
- stat2.incrementalCallCount = 9;
- stat2.recordedCallCount = 8;
- stat2.cpuTimeMicros = 500;
- callStats.add(stat2);
-
- bi.noteBinderCallStats(workSourceUid, 8, callStats);
-
- BatteryStatsImpl.Uid uid = bi.getUidStatsLocked(workSourceUid);
- assertEquals(42 + 8, uid.getBinderCallCount());
-
- BinderTransactionNameResolver resolver = new BinderTransactionNameResolver();
- ArraySet<BatteryStatsImpl.BinderCallStats> stats = uid.getBinderCallStats();
- assertEquals(1, stats.size());
- BatteryStatsImpl.BinderCallStats value = stats.valueAt(0);
- value.ensureMethodName(resolver);
- assertEquals("testMethod", value.getMethodName());
- assertEquals(21 + 9, value.callCount);
- assertEquals(8, value.recordedCallCount);
- assertEquals(500, value.recordedCpuTimeMicros);
- }
-
- @Test
- public void testProportionalSystemServiceUsage_noStatsForSomeMethods() throws Exception {
- final MockClock clocks = new MockClock(); // holds realtime and uptime in ms
- MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
-
- int callingUid = Process.FIRST_APPLICATION_UID + 1;
- int workSourceUid1 = Process.FIRST_APPLICATION_UID + 1;
- int workSourceUid2 = Process.FIRST_APPLICATION_UID + 2;
- int workSourceUid3 = Process.FIRST_APPLICATION_UID + 3;
-
- Collection<BinderCallsStats.CallStat> callStats = new ArrayList<>();
- BinderCallsStats.CallStat stat1a = new BinderCallsStats.CallStat(callingUid,
- MockBinder.class, TRANSACTION_CODE1, true /*screenInteractive */);
- stat1a.incrementalCallCount = 10;
- stat1a.recordedCallCount = 5;
- stat1a.cpuTimeMicros = 1000;
- callStats.add(stat1a);
-
- BinderCallsStats.CallStat stat1b = new BinderCallsStats.CallStat(callingUid,
- MockBinder.class, TRANSACTION_CODE2, true /*screenInteractive */);
- stat1b.incrementalCallCount = 30;
- stat1b.recordedCallCount = 15;
- stat1b.cpuTimeMicros = 1500;
- callStats.add(stat1b);
-
- bi.noteBinderCallStats(workSourceUid1, 65, callStats);
-
- // No recorded stats for some methods. Must use the global average.
- callStats.clear();
- BinderCallsStats.CallStat stat2 = new BinderCallsStats.CallStat(callingUid,
- MockBinder.class, TRANSACTION_CODE1, true /*screenInteractive */);
- stat2.incrementalCallCount = 10;
- callStats.add(stat2);
-
- bi.noteBinderCallStats(workSourceUid2, 40, callStats);
-
- // No stats for any calls. Must use the global average
- callStats.clear();
- bi.noteBinderCallStats(workSourceUid3, 50, callStats);
-
- bi.updateSystemServiceCallStats();
-
- double prop1 = bi.getUidStatsLocked(workSourceUid1).getProportionalSystemServiceUsage();
- double prop2 = bi.getUidStatsLocked(workSourceUid2).getProportionalSystemServiceUsage();
- double prop3 = bi.getUidStatsLocked(workSourceUid3).getProportionalSystemServiceUsage();
-
- assertEquals(0.419, prop1, 0.01);
- assertEquals(0.258, prop2, 0.01);
- assertEquals(0.323, prop3, 0.01);
- assertEquals(1.000, prop1 + prop2 + prop3, 0.01);
- }
-
- private static class MockBinder extends Binder {
- public static String getDefaultTransactionName(int txCode) {
- return txCode == TRANSACTION_CODE1 ? "testMethod" : "unknown";
- }
- }
-}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsCpuTimesTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsCpuTimesTest.java
index b73895c..c5d0eab 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsCpuTimesTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsCpuTimesTest.java
@@ -95,8 +95,6 @@
@Mock
KernelCpuUidClusterTimeReader mCpuUidClusterTimeReader;
@Mock
- SystemServerCpuThreadReader mSystemServerCpuThreadReader;
- @Mock
BatteryStatsImpl.UserInfoProvider mUserInfoProvider;
private MockClock mClocks;
@@ -118,7 +116,6 @@
.setKernelCpuUidFreqTimeReader(mCpuUidFreqTimeReader)
.setKernelCpuUidActiveTimeReader(mCpuUidActiveTimeReader)
.setKernelCpuUidClusterTimeReader(mCpuUidClusterTimeReader)
- .setSystemServerCpuThreadReader(mSystemServerCpuThreadReader)
.setUserInfoProvider(mUserInfoProvider);
}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerCalculatorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerCalculatorTest.java
index ee58d7e..3263b15 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerCalculatorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerCalculatorTest.java
@@ -90,8 +90,6 @@
@Mock
private KernelCpuUidTimeReader.KernelCpuUidActiveTimeReader mMockKerneCpuUidActiveTimeReader;
@Mock
- private SystemServerCpuThreadReader mMockSystemServerCpuThreadReader;
- @Mock
private KernelSingleUidTimeReader mMockKernelSingleUidTimeReader;
private boolean[] mSupportedPowerBuckets;
@@ -110,8 +108,7 @@
.setKernelCpuUidClusterTimeReader(mMockKernelCpuUidClusterTimeReader)
.setKernelCpuUidUserSysTimeReader(mMockKernelCpuUidUserSysTimeReader)
.setKernelCpuUidActiveTimeReader(mMockKerneCpuUidActiveTimeReader)
- .setKernelSingleUidTimeReader(mMockKernelSingleUidTimeReader)
- .setSystemServerCpuThreadReader(mMockSystemServerCpuThreadReader);
+ .setKernelSingleUidTimeReader(mMockKernelSingleUidTimeReader);
}
@Test
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/MockBatteryStatsImpl.java b/services/tests/powerstatstests/src/com/android/server/power/stats/MockBatteryStatsImpl.java
index 8c3bd17..0b8fbb0 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/MockBatteryStatsImpl.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/MockBatteryStatsImpl.java
@@ -242,12 +242,6 @@
return this;
}
- public MockBatteryStatsImpl setSystemServerCpuThreadReader(
- SystemServerCpuThreadReader systemServerCpuThreadReader) {
- mSystemServerCpuThreadReader = systemServerCpuThreadReader;
- return this;
- }
-
public MockBatteryStatsImpl setUserInfoProvider(UserInfoProvider provider) {
mUserInfoProvider = provider;
return this;
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/SystemServerCpuThreadReaderTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/SystemServerCpuThreadReaderTest.java
deleted file mode 100644
index d67d408..0000000
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/SystemServerCpuThreadReaderTest.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.power.stats;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.platform.test.ravenwood.RavenwoodRule;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.internal.os.KernelSingleProcessCpuThreadReader;
-
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.io.IOException;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-@android.platform.test.annotations.DisabledOnRavenwood(reason = "Kernel dependency")
-public class SystemServerCpuThreadReaderTest {
- @Rule
- public final RavenwoodRule mRavenwood = new RavenwoodRule();
-
- @Test
- public void testReadDelta() throws IOException {
- int pid = 42;
-
- MockCpuTimeInStateReader mockReader = new MockCpuTimeInStateReader(4);
- // Units are nanoseconds
- mockReader.setAggregatedTaskCpuFreqTimes(new String[] {
- "0:1000000000 2000000000 3000000000:4000000000",
- "1:100000000 200000000 300000000:400000000",
- });
-
- SystemServerCpuThreadReader reader = new SystemServerCpuThreadReader(pid, mockReader);
- reader.setBinderThreadNativeTids(new int[] {1, 3});
-
- // The first invocation of readDelta populates the "last" snapshot
- SystemServerCpuThreadReader.SystemServiceCpuThreadTimes systemServiceCpuThreadTimes =
- reader.readDelta();
-
- assertThat(systemServiceCpuThreadTimes.threadCpuTimesUs)
- .isEqualTo(new long[] {1100000, 2200000, 3300000, 4400000});
- assertThat(systemServiceCpuThreadTimes.binderThreadCpuTimesUs)
- .isEqualTo(new long[] {100000, 200000, 300000, 400000});
-
- mockReader.setAggregatedTaskCpuFreqTimes(new String[] {
- "0:1010000000 2020000000 3030000000:4040000000",
- "1:101000000 202000000 303000000:404000000",
- });
-
- // The second invocation gets the actual delta
- systemServiceCpuThreadTimes = reader.readDelta();
-
- assertThat(systemServiceCpuThreadTimes.threadCpuTimesUs)
- .isEqualTo(new long[] {11000, 22000, 33000, 44000});
- assertThat(systemServiceCpuThreadTimes.binderThreadCpuTimesUs)
- .isEqualTo(new long[] {1000, 2000, 3000, 4000});
- }
-
- public static class MockCpuTimeInStateReader implements
- KernelSingleProcessCpuThreadReader.CpuTimeInStateReader {
- private final int mCpuFrequencyCount;
- private String[] mAggregatedTaskCpuFreqTimes;
-
- MockCpuTimeInStateReader(int frequencyCount) {
- mCpuFrequencyCount = frequencyCount;
- }
-
- @Override
- public int getCpuFrequencyCount() {
- return mCpuFrequencyCount;
- }
-
- @Override
- public boolean startTrackingProcessCpuTimes(int tgid) {
- return true;
- }
-
- public boolean startAggregatingTaskCpuTimes(int pid, int aggregationKey) {
- return true;
- }
-
- public void setAggregatedTaskCpuFreqTimes(String[] mAggregatedTaskCpuFreqTimes) {
- this.mAggregatedTaskCpuFreqTimes = mAggregatedTaskCpuFreqTimes;
- }
-
- public String[] getAggregatedTaskCpuFreqTimes(int pid) {
- return mAggregatedTaskCpuFreqTimes;
- }
- }
-}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/SystemServicePowerCalculatorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/SystemServicePowerCalculatorTest.java
deleted file mode 100644
index 1ff347f..0000000
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/SystemServicePowerCalculatorTest.java
+++ /dev/null
@@ -1,270 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.power.stats;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import android.os.BatteryConsumer;
-import android.os.Binder;
-import android.os.Process;
-import android.platform.test.annotations.RequiresFlagsDisabled;
-import android.platform.test.flag.junit.CheckFlagsRule;
-import android.platform.test.flag.junit.DeviceFlagsValueProvider;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.internal.os.BinderCallsStats;
-import com.android.internal.os.KernelCpuSpeedReader;
-import com.android.internal.os.KernelCpuUidTimeReader;
-import com.android.internal.os.KernelSingleUidTimeReader;
-import com.android.internal.os.PowerProfile;
-import com.android.internal.power.EnergyConsumerStats;
-import com.android.server.power.optimization.Flags;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collection;
-
-@SmallTest
-@SuppressWarnings("GuardedBy")
-public class SystemServicePowerCalculatorTest {
- @Rule(order = 0)
- public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
-
- private static final double PRECISION = 0.000001;
- private static final int APP_UID1 = 100;
- private static final int APP_UID2 = 200;
-
- @Rule(order = 1)
- public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
- .setAveragePower(PowerProfile.POWER_CPU_ACTIVE, 720)
- .setCpuScalingPolicy(0, new int[]{0, 1}, new int[]{100, 200})
- .setCpuScalingPolicy(2, new int[]{2, 3}, new int[]{300, 400})
- .setAveragePowerForCpuScalingPolicy(0, 360)
- .setAveragePowerForCpuScalingPolicy(2, 480)
- .setAveragePowerForCpuScalingStep(0, 0, 300)
- .setAveragePowerForCpuScalingStep(0, 1, 400)
- .setAveragePowerForCpuScalingStep(2, 0, 500)
- .setAveragePowerForCpuScalingStep(2, 1, 600);
-
- @Mock
- private BatteryStatsImpl.UserInfoProvider mMockUserInfoProvider;
- @Mock
- private KernelCpuUidTimeReader.KernelCpuUidClusterTimeReader mMockKernelCpuUidClusterTimeReader;
- @Mock
- private KernelCpuUidTimeReader.KernelCpuUidFreqTimeReader mMockCpuUidFreqTimeReader;
- @Mock
- private KernelCpuUidTimeReader.KernelCpuUidUserSysTimeReader mMockKernelCpuUidUserSysTimeReader;
- @Mock
- private KernelCpuUidTimeReader.KernelCpuUidActiveTimeReader mMockKerneCpuUidActiveTimeReader;
- @Mock
- private SystemServerCpuThreadReader mMockSystemServerCpuThreadReader;
- @Mock
- private KernelSingleUidTimeReader mMockKernelSingleUidTimeReader;
-
- private final KernelCpuSpeedReader[] mMockKernelCpuSpeedReaders = new KernelCpuSpeedReader[]{
- mock(KernelCpuSpeedReader.class),
- mock(KernelCpuSpeedReader.class),
- };
-
- private MockBatteryStatsImpl mMockBatteryStats;
-
- @Before
- public void setUp() throws IOException {
- MockitoAnnotations.initMocks(this);
- mMockBatteryStats = mStatsRule.getBatteryStats()
- .setUserInfoProvider(mMockUserInfoProvider)
- .setKernelCpuSpeedReaders(mMockKernelCpuSpeedReaders)
- .setKernelCpuUidFreqTimeReader(mMockCpuUidFreqTimeReader)
- .setKernelCpuUidClusterTimeReader(mMockKernelCpuUidClusterTimeReader)
- .setKernelCpuUidUserSysTimeReader(mMockKernelCpuUidUserSysTimeReader)
- .setKernelCpuUidActiveTimeReader(mMockKerneCpuUidActiveTimeReader)
- .setKernelSingleUidTimeReader(mMockKernelSingleUidTimeReader)
- .setSystemServerCpuThreadReader(mMockSystemServerCpuThreadReader);
- }
-
- @Test
- @RequiresFlagsDisabled(Flags.FLAG_DISABLE_SYSTEM_SERVICE_POWER_ATTR)
- public void testPowerProfileBasedModel() {
- prepareBatteryStats(null);
-
- SystemServicePowerCalculator calculator = new SystemServicePowerCalculator(
- mStatsRule.getCpuScalingPolicies(), mStatsRule.getPowerProfile());
-
- mStatsRule.apply(new CpuPowerCalculator(mStatsRule.getCpuScalingPolicies(),
- mStatsRule.getPowerProfile()), calculator);
-
- assertThat(mStatsRule.getUidBatteryConsumer(APP_UID1)
- .getConsumedPower(BatteryConsumer.POWER_COMPONENT_SYSTEM_SERVICES))
- .isWithin(PRECISION).of(1.888888);
- assertThat(mStatsRule.getUidBatteryConsumer(APP_UID2)
- .getConsumedPower(BatteryConsumer.POWER_COMPONENT_SYSTEM_SERVICES))
- .isWithin(PRECISION).of(17.0);
- assertThat(mStatsRule.getUidBatteryConsumer(Process.SYSTEM_UID)
- .getConsumedPower(BatteryConsumer.POWER_COMPONENT_REATTRIBUTED_TO_OTHER_CONSUMERS))
- .isWithin(PRECISION).of(-18.888888);
- assertThat(mStatsRule.getDeviceBatteryConsumer()
- .getConsumedPower(BatteryConsumer.POWER_COMPONENT_SYSTEM_SERVICES))
- .isWithin(PRECISION).of(18.888888);
- assertThat(mStatsRule.getAppsBatteryConsumer()
- .getConsumedPower(BatteryConsumer.POWER_COMPONENT_SYSTEM_SERVICES))
- .isWithin(PRECISION).of(18.888888);
- }
-
- @Test
- @RequiresFlagsDisabled(Flags.FLAG_DISABLE_SYSTEM_SERVICE_POWER_ATTR)
- public void testMeasuredEnergyBasedModel() {
- final boolean[] supportedPowerBuckets =
- new boolean[EnergyConsumerStats.NUMBER_STANDARD_POWER_BUCKETS];
- supportedPowerBuckets[EnergyConsumerStats.POWER_BUCKET_CPU] = true;
- mStatsRule.getBatteryStats()
- .initEnergyConsumerStatsLocked(supportedPowerBuckets, new String[0]);
-
- prepareBatteryStats(new long[]{50000000, 100000000});
-
- SystemServicePowerCalculator calculator = new SystemServicePowerCalculator(
- mStatsRule.getCpuScalingPolicies(), mStatsRule.getPowerProfile());
-
- mStatsRule.apply(new CpuPowerCalculator(mStatsRule.getCpuScalingPolicies(),
- mStatsRule.getPowerProfile()), calculator);
-
- assertThat(mStatsRule.getUidBatteryConsumer(APP_UID1)
- .getConsumedPower(BatteryConsumer.POWER_COMPONENT_SYSTEM_SERVICES))
- .isWithin(PRECISION).of(2.105425);
- assertThat(mStatsRule.getUidBatteryConsumer(APP_UID2)
- .getConsumedPower(BatteryConsumer.POWER_COMPONENT_SYSTEM_SERVICES))
- .isWithin(PRECISION).of(18.948825);
- assertThat(mStatsRule.getUidBatteryConsumer(Process.SYSTEM_UID)
- .getConsumedPower(BatteryConsumer.POWER_COMPONENT_REATTRIBUTED_TO_OTHER_CONSUMERS))
- .isWithin(PRECISION).of(-21.054250);
- assertThat(mStatsRule.getDeviceBatteryConsumer()
- .getConsumedPower(BatteryConsumer.POWER_COMPONENT_SYSTEM_SERVICES))
- .isWithin(PRECISION).of(21.054250);
- assertThat(mStatsRule.getAppsBatteryConsumer()
- .getConsumedPower(BatteryConsumer.POWER_COMPONENT_SYSTEM_SERVICES))
- .isWithin(PRECISION).of(21.054250);
- }
-
- private void prepareBatteryStats(long[] clusterChargesUc) {
- when(mMockUserInfoProvider.exists(anyInt())).thenReturn(true);
-
- when(mMockKernelCpuSpeedReaders[0].readDelta()).thenReturn(new long[]{1000, 2000});
- when(mMockKernelCpuSpeedReaders[1].readDelta()).thenReturn(new long[]{3000, 4000});
-
- when(mMockCpuUidFreqTimeReader.perClusterTimesAvailable()).thenReturn(false);
-
- mStatsRule.setTime(1000, 1000);
-
- // Initialize active CPU time
- doAnswer(invocation -> {
- final KernelCpuUidTimeReader.Callback<Long> callback = invocation.getArgument(0);
- callback.onUidCpuTime(APP_UID1, 1000L);
- callback.onUidCpuTime(APP_UID2, 3000L);
- callback.onUidCpuTime(Process.SYSTEM_UID, 5000L);
- return null;
- }).when(mMockKerneCpuUidActiveTimeReader).readAbsolute(any());
-
- mStatsRule.getBatteryStats().updateCpuTimeLocked(true, true, null);
-
- mStatsRule.setTime(2000, 2000);
-
- // User/System CPU time in microseconds
- doAnswer(invocation -> {
- final KernelCpuUidTimeReader.Callback<long[]> callback = invocation.getArgument(1);
- callback.onUidCpuTime(APP_UID1, new long[]{1_000_000, 2_000_000});
- callback.onUidCpuTime(APP_UID2, new long[]{3_000_000, 4_000_000});
- callback.onUidCpuTime(Process.SYSTEM_UID, new long[]{60_000_000, 80_000_000});
- return null;
- }).when(mMockKernelCpuUidUserSysTimeReader).readDelta(anyBoolean(), any());
-
- // Active CPU time in milliseconds
- doAnswer(invocation -> {
- final KernelCpuUidTimeReader.Callback<Long> callback = invocation.getArgument(0);
- callback.onUidCpuTime(APP_UID1, 2111L);
- callback.onUidCpuTime(APP_UID2, 6333L);
- callback.onUidCpuTime(Process.SYSTEM_UID, 15000L);
- return null;
- }).when(mMockKerneCpuUidActiveTimeReader).readAbsolute(any());
-
- // Per-cluster CPU time in milliseconds
- doAnswer(invocation -> {
- final KernelCpuUidTimeReader.Callback<long[]> callback = invocation.getArgument(1);
- callback.onUidCpuTime(APP_UID1, new long[]{1111, 2222});
- callback.onUidCpuTime(APP_UID2, new long[]{3333, 4444});
- callback.onUidCpuTime(Process.SYSTEM_UID, new long[]{50_000, 80_000});
- return null;
- }).when(mMockKernelCpuUidClusterTimeReader).readDelta(anyBoolean(), any());
-
- when(mMockKernelSingleUidTimeReader.singleUidCpuTimesAvailable()).thenReturn(true);
-
- // Per-frequency CPU time
- doAnswer(invocation -> {
- final KernelCpuUidTimeReader.Callback<long[]> callback = invocation.getArgument(1);
- callback.onUidCpuTime(APP_UID1, new long[]{1100, 11, 2200, 22});
- callback.onUidCpuTime(APP_UID2, new long[]{3300, 33, 4400, 44});
- callback.onUidCpuTime(Process.SYSTEM_UID, new long[]{20_000, 30_000, 40_000, 40_000});
- return null;
- }).when(mMockCpuUidFreqTimeReader).readDelta(anyBoolean(), any());
-
- // System service CPU time
- final SystemServerCpuThreadReader.SystemServiceCpuThreadTimes threadTimes =
- new SystemServerCpuThreadReader.SystemServiceCpuThreadTimes();
- threadTimes.binderThreadCpuTimesUs =
- new long[]{20_000_000, 30_000_000, 40_000_000, 50_000_000};
-
- when(mMockSystemServerCpuThreadReader.readDelta()).thenReturn(threadTimes);
-
- int transactionCode = 42;
-
- Collection<BinderCallsStats.CallStat> callStats = new ArrayList<>();
- BinderCallsStats.CallStat stat1 = new BinderCallsStats.CallStat(APP_UID1,
- Binder.class, transactionCode, true /*screenInteractive */);
- stat1.incrementalCallCount = 100;
- stat1.recordedCallCount = 100;
- stat1.cpuTimeMicros = 1_000_000;
- callStats.add(stat1);
-
- mMockBatteryStats.noteBinderCallStats(APP_UID1, 100, callStats);
-
- callStats.clear();
- BinderCallsStats.CallStat stat2 = new BinderCallsStats.CallStat(APP_UID2,
- Binder.class, transactionCode, true /*screenInteractive */);
- stat2.incrementalCallCount = 100;
- stat2.recordedCallCount = 100;
- stat2.cpuTimeMicros = 9_000_000;
- callStats.add(stat2);
-
- mMockBatteryStats.noteBinderCallStats(APP_UID2, 100, callStats);
-
- mMockBatteryStats.updateCpuTimeLocked(true, true, clusterChargesUc);
-
- mMockBatteryStats.prepareForDumpLocked();
- }
-}
diff --git a/services/tests/servicestests/jni/Android.bp b/services/tests/servicestests/jni/Android.bp
index b465ff2..76b9c85 100644
--- a/services/tests/servicestests/jni/Android.bp
+++ b/services/tests/servicestests/jni/Android.bp
@@ -57,7 +57,6 @@
"android.hardware.graphics.common@1.2",
"android.hardware.graphics.mapper@4.0",
"android.hardware.ir@1.0",
- "android.hardware.vr@1.0",
"android.hidl.token@1.0-utils",
],
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
index 457d8a9..fb89733 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
@@ -60,6 +60,7 @@
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -1682,15 +1683,6 @@
}
@Test
- @DisableFlags(android.view.accessibility.Flags.FLAG_RESTORE_A11Y_SECURE_SETTINGS_ON_HSUM_DEVICE)
- public void restoreShortcutTargetsAssumeUser0_qs_a11yQsTargetsRestored() {
- assumeTrue("The test is setup to run as a user 0",
- mTestableContext.getUserId() == UserHandle.USER_SYSTEM);
- restoreShortcutTargets_qs_a11yQsTargetsRestored();
- }
-
- @Test
- @EnableFlags(android.view.accessibility.Flags.FLAG_RESTORE_A11Y_SECURE_SETTINGS_ON_HSUM_DEVICE)
public void restoreShortcutTargets_qs_a11yQsTargetsRestored() {
String daltonizerTile =
AccessibilityShortcutController.DALTONIZER_COMPONENT_NAME.flattenToString();
@@ -1771,15 +1763,6 @@
}
@Test
- @DisableFlags(android.view.accessibility.Flags.FLAG_RESTORE_A11Y_SECURE_SETTINGS_ON_HSUM_DEVICE)
- public void restoreShortcutTargetsAssumeUser0_hardware_targetsMerged() {
- assumeTrue("The test is setup to run as a user 0",
- mTestableContext.getUserId() == UserHandle.USER_SYSTEM);
- restoreShortcutTargets_hardware_targetsMerged();
- }
-
- @Test
- @EnableFlags(android.view.accessibility.Flags.FLAG_RESTORE_A11Y_SECURE_SETTINGS_ON_HSUM_DEVICE)
public void restoreShortcutTargets_hardware_targetsMerged() {
mFakePermissionEnforcer.grant(Manifest.permission.MANAGE_ACCESSIBILITY);
final String servicePrevious = TARGET_ALWAYS_ON_A11Y_SERVICE.flattenToString();
@@ -1804,15 +1787,6 @@
}
@Test
- @DisableFlags(android.view.accessibility.Flags.FLAG_RESTORE_A11Y_SECURE_SETTINGS_ON_HSUM_DEVICE)
- public void restoreShortcutTargetsAssumeUser0_hardware_alreadyHadDefaultService_doesNotClear() {
- assumeTrue("The test is setup to run as a user 0",
- mTestableContext.getUserId() == UserHandle.USER_SYSTEM);
- restoreShortcutTargets_hardware_alreadyHadDefaultService_doesNotClear();
- }
-
- @Test
- @EnableFlags(android.view.accessibility.Flags.FLAG_RESTORE_A11Y_SECURE_SETTINGS_ON_HSUM_DEVICE)
public void restoreShortcutTargets_hardware_alreadyHadDefaultService_doesNotClear() {
final String serviceDefault = TARGET_STANDARD_A11Y_SERVICE_NAME;
mTestableContext.getOrCreateTestableResources().addOverride(
@@ -1838,15 +1812,6 @@
}
@Test
- @DisableFlags(android.view.accessibility.Flags.FLAG_RESTORE_A11Y_SECURE_SETTINGS_ON_HSUM_DEVICE)
- public void restoreShortcutTargetsAsUser0_hardware_noDefaultService_clearsDefaultService() {
- assumeTrue("The test is setup to run as a user 0",
- mTestableContext.getUserId() == UserHandle.USER_SYSTEM);
- restoreShortcutTargets_hardware_didNotHaveDefaultService_clearsDefaultService();
- }
-
- @Test
- @EnableFlags(android.view.accessibility.Flags.FLAG_RESTORE_A11Y_SECURE_SETTINGS_ON_HSUM_DEVICE)
public void restoreShortcutTargets_hardware_didNotHaveDefaultService_clearsDefaultService() {
final String serviceDefault = TARGET_STANDARD_A11Y_SERVICE_NAME;
final String serviceRestored = TARGET_ALWAYS_ON_A11Y_SERVICE.flattenToString();
@@ -1871,15 +1836,6 @@
}
@Test
- @DisableFlags(android.view.accessibility.Flags.FLAG_RESTORE_A11Y_SECURE_SETTINGS_ON_HSUM_DEVICE)
- public void restoreShortcutTargetsAssumeUser0_hardware_nullSetting_clearsDefaultService() {
- assumeTrue("The test is setup to run as a user 0",
- mTestableContext.getUserId() == UserHandle.USER_SYSTEM);
- restoreShortcutTargets_hardware_nullSetting_clearsDefaultService();
- }
-
- @Test
- @EnableFlags(android.view.accessibility.Flags.FLAG_RESTORE_A11Y_SECURE_SETTINGS_ON_HSUM_DEVICE)
public void restoreShortcutTargets_hardware_nullSetting_clearsDefaultService() {
final String serviceDefault = TARGET_STANDARD_A11Y_SERVICE_NAME;
final String serviceRestored = TARGET_ALWAYS_ON_A11Y_SERVICE.flattenToString();
@@ -2128,8 +2084,9 @@
@Test
public void switchUser_callsUserInitializationCompleteCallback() throws RemoteException {
mA11yms.mUserInitializationCompleteCallbacks.add(mUserInitializationCompleteCallback);
-
int newUserId = mA11yms.getCurrentUserIdLocked() + 1;
+ when(mMockSecurityPolicy.resolveProfileParentLocked(anyInt())).thenReturn(newUserId);
+
mA11yms.switchUser(newUserId);
mTestableLooper.processAllMessages();
@@ -2143,6 +2100,7 @@
AccessibilityManagerService.Lifecycle lifecycle =
new AccessibilityManagerService.Lifecycle(mTestableContext, mA11yms);
int newUserId = mA11yms.getCurrentUserIdLocked() + 1;
+ when(mMockSecurityPolicy.resolveProfileParentLocked(anyInt())).thenReturn(newUserId);
lifecycle.onUserSwitching(
new SystemService.TargetUser(new UserInfo(0, "USER", 0)),
@@ -2153,12 +2111,28 @@
}
@Test
+ @EnableFlags(Flags.FLAG_MANAGER_LIFECYCLE_USER_CHANGE)
+ public void switchUser_sameParent_noChange() throws RemoteException {
+ mA11yms.mUserInitializationCompleteCallbacks.add(mUserInitializationCompleteCallback);
+ int newUserId = mA11yms.getCurrentUserIdLocked() + 1;
+ when(mMockSecurityPolicy.resolveProfileParentLocked(anyInt())).thenReturn(
+ mA11yms.getCurrentUserIdLocked());
+
+ mA11yms.switchUser(newUserId);
+ mTestableLooper.processAllMessages();
+
+ verify(mUserInitializationCompleteCallback, never())
+ .onUserInitializationComplete(newUserId);
+ }
+
+ @Test
@DisableFlags(Flags.FLAG_MANAGER_LIFECYCLE_USER_CHANGE)
public void intent_user_switched_switchesUser() throws RemoteException {
mA11yms.mUserInitializationCompleteCallbacks.add(mUserInitializationCompleteCallback);
int newUserId = mA11yms.getCurrentUserIdLocked() + 1;
final Intent intent = new Intent(Intent.ACTION_USER_SWITCHED);
intent.putExtra(Intent.EXTRA_USER_HANDLE, newUserId);
+ when(mMockSecurityPolicy.resolveProfileParentLocked(anyInt())).thenReturn(newUserId);
sendBroadcastToAccessibilityManagerService(intent, mA11yms.getCurrentUserIdLocked());
mTestableLooper.processAllMessages();
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickControllerTest.java
index 63231b0..f838e9b 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickControllerTest.java
@@ -18,6 +18,7 @@
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+import static com.android.server.accessibility.autoclick.AutoclickController.CONTINUOUS_SCROLL_INTERVAL;
import static com.android.server.accessibility.autoclick.AutoclickTypePanel.AUTOCLICK_TYPE_RIGHT_CLICK;
import static com.android.server.testutils.MockitoUtilsKt.eq;
@@ -59,22 +60,30 @@
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
+import java.util.ArrayList;
+import java.util.List;
+
/** Test cases for {@link AutoclickController}. */
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
public class AutoclickControllerTest {
- @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
- @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+ @Rule
+ public final MockitoRule mMockitoRule = MockitoJUnit.rule();
@Rule
public TestableContext mTestableContext =
new TestableContext(getInstrumentation().getContext());
private TestableLooper mTestableLooper;
- @Mock private AccessibilityTraceManager mMockTrace;
- @Mock private WindowManager mMockWindowManager;
+ @Mock
+ private AccessibilityTraceManager mMockTrace;
+ @Mock
+ private WindowManager mMockWindowManager;
private AutoclickController mController;
+ private MotionEventCaptor mMotionEventCaptor;
private static class MotionEventCaptor extends BaseEventStreamTransformation {
public MotionEvent downEvent;
@@ -82,31 +91,44 @@
public MotionEvent buttonReleaseEvent;
public MotionEvent upEvent;
public MotionEvent moveEvent;
+ public MotionEvent cancelEvent;
public int eventCount = 0;
+ private List<MotionEvent> mEventList = new ArrayList<>();
+
@Override
public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
MotionEvent eventCopy = MotionEvent.obtain(event);
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
downEvent = eventCopy;
- eventCount++;
break;
case MotionEvent.ACTION_BUTTON_PRESS:
buttonPressEvent = eventCopy;
- eventCount++;
break;
case MotionEvent.ACTION_BUTTON_RELEASE:
buttonReleaseEvent = eventCopy;
- eventCount++;
break;
case MotionEvent.ACTION_UP:
upEvent = eventCopy;
- eventCount++;
break;
case MotionEvent.ACTION_MOVE:
moveEvent = eventCopy;
- eventCount++;
break;
+ case MotionEvent.ACTION_CANCEL:
+ cancelEvent = eventCopy;
+ break;
+ default:
+ return;
+ }
+ mEventList.add(eventCopy);
+ eventCount++;
+ }
+
+ public void assertCapturedEvents(int... actionsInOrder) {
+ assertThat(eventCount).isEqualTo(mEventList.size());
+ for (int i = 0; i < eventCount; i++) {
+ assertThat(actionsInOrder[i])
+ .isEqualTo(mEventList.get(i).getAction());
}
}
}
@@ -677,31 +699,21 @@
@Test
public void sendClick_clickType_leftClick() {
- MotionEventCaptor motionEventCaptor = new MotionEventCaptor();
- mController.setNext(motionEventCaptor);
-
- injectFakeMouseActionHoverMoveEvent();
- // Set delay to zero so click is scheduled to run immediately.
- mController.mClickScheduler.updateDelay(0);
+ initializeAutoclick();
// Send hover move event.
injectFakeMouseMoveEvent(/* x= */ 30f, /* y= */ 0, MotionEvent.ACTION_HOVER_MOVE);
mTestableLooper.processAllMessages();
// Verify left click sent.
- assertThat(motionEventCaptor.downEvent).isNotNull();
- assertThat(motionEventCaptor.downEvent.getButtonState()).isEqualTo(
+ assertThat(mMotionEventCaptor.downEvent).isNotNull();
+ assertThat(mMotionEventCaptor.downEvent.getButtonState()).isEqualTo(
MotionEvent.BUTTON_PRIMARY);
}
@Test
public void sendClick_clickType_rightClick() {
- MotionEventCaptor motionEventCaptor = new MotionEventCaptor();
- mController.setNext(motionEventCaptor);
-
- injectFakeMouseActionHoverMoveEvent();
- // Set delay to zero so click is scheduled to run immediately.
- mController.mClickScheduler.updateDelay(0);
+ initializeAutoclick();
// Set click type to right click.
mController.clickPanelController.handleAutoclickTypeChange(
@@ -714,20 +726,15 @@
mTestableLooper.processAllMessages();
// Verify right click sent.
- assertThat(motionEventCaptor.downEvent).isNotNull();
- assertThat(motionEventCaptor.downEvent.getButtonState()).isEqualTo(
+ assertThat(mMotionEventCaptor.downEvent).isNotNull();
+ assertThat(mMotionEventCaptor.downEvent.getButtonState()).isEqualTo(
MotionEvent.BUTTON_SECONDARY);
}
@Test
@EnableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_AUTOCLICK_INDICATOR)
public void sendClick_clickType_scroll_showsScrollPanelOnlyOnce() {
- MotionEventCaptor motionEventCaptor = new MotionEventCaptor();
- mController.setNext(motionEventCaptor);
-
- injectFakeMouseActionHoverMoveEvent();
- // Set delay to zero so click is scheduled to run immediately.
- mController.mClickScheduler.updateDelay(0);
+ initializeAutoclick();
// Set click type to scroll.
mController.clickPanelController.handleAutoclickTypeChange(
@@ -743,7 +750,7 @@
// Verify scroll panel is shown once.
verify(mockScrollPanel, times(1)).show(anyFloat(), anyFloat());
- assertThat(motionEventCaptor.downEvent).isNull();
+ assertThat(mMotionEventCaptor.downEvent).isNull();
// Second significant hover move event to trigger another autoclick.
injectFakeMouseMoveEvent(/* x= */ 100f, /* y= */ 100f, MotionEvent.ACTION_HOVER_MOVE);
@@ -751,7 +758,7 @@
// Verify scroll panel is still only shown once (not called again).
verify(mockScrollPanel, times(1)).show(anyFloat(), anyFloat());
- assertThat(motionEventCaptor.downEvent).isNull();
+ assertThat(mMotionEventCaptor.downEvent).isNull();
}
@Test
@@ -774,12 +781,7 @@
@Test
@EnableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_AUTOCLICK_INDICATOR)
public void hoverOnAutoclickPanel_rightClickType_forceTriggerLeftClick() {
- MotionEventCaptor motionEventCaptor = new MotionEventCaptor();
- mController.setNext(motionEventCaptor);
-
- injectFakeMouseActionHoverMoveEvent();
- // Set delay to zero so click is scheduled to run immediately.
- mController.mClickScheduler.updateDelay(0);
+ initializeAutoclick();
// Set click type to right click.
mController.clickPanelController.handleAutoclickTypeChange(
@@ -794,13 +796,96 @@
mTestableLooper.processAllMessages();
// Verify left click is sent due to the mouse hovering the panel.
- assertThat(motionEventCaptor.downEvent).isNotNull();
- assertThat(motionEventCaptor.downEvent.getButtonState()).isEqualTo(
+ assertThat(mMotionEventCaptor.downEvent).isNotNull();
+ assertThat(mMotionEventCaptor.downEvent.getButtonState()).isEqualTo(
MotionEvent.BUTTON_PRIMARY);
}
@Test
@EnableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_AUTOCLICK_INDICATOR)
+ public void hoverOnAutoclickPanel_dragClickType_forceTriggerLeftClick() {
+ initializeAutoclick();
+
+ // Set click type to right click.
+ mController.clickPanelController.handleAutoclickTypeChange(
+ AutoclickTypePanel.AUTOCLICK_TYPE_DRAG);
+ // Set mouse to hover panel.
+ AutoclickTypePanel mockAutoclickTypePanel = mock(AutoclickTypePanel.class);
+ when(mockAutoclickTypePanel.isHovered()).thenReturn(true);
+ mController.mAutoclickTypePanel = mockAutoclickTypePanel;
+
+ // Send hover move event.
+ injectFakeMouseMoveEvent(/* x= */ 30f, /* y= */ 100f, MotionEvent.ACTION_HOVER_MOVE);
+ mTestableLooper.processAllMessages();
+
+ // Verify both the down and up left click events are sent due to the mouse hovering the
+ // panel.
+ assertThat(mMotionEventCaptor.downEvent).isNotNull();
+ assertThat(mMotionEventCaptor.downEvent.getButtonState()).isEqualTo(
+ MotionEvent.BUTTON_PRIMARY);
+ assertThat(mMotionEventCaptor.upEvent).isNotNull();
+ assertThat(mController.isDraggingForTesting()).isFalse();
+ }
+
+ @Test
+ @EnableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_AUTOCLICK_INDICATOR)
+ public void hoverOnAutoclickPanel_scrollClickType_forceTriggerLeftClick() {
+ initializeAutoclick();
+
+ // Set click type to scrolling click.
+ mController.clickPanelController.handleAutoclickTypeChange(
+ AutoclickTypePanel.AUTOCLICK_TYPE_SCROLL);
+ // Set mouse to hover panel.
+ AutoclickTypePanel mockAutoclickTypePanel = mock(AutoclickTypePanel.class);
+ when(mockAutoclickTypePanel.isHovered()).thenReturn(true);
+ mController.mAutoclickTypePanel = mockAutoclickTypePanel;
+
+ // Send hover move event.
+ injectFakeMouseMoveEvent(/* x= */ 30f, /* y= */ 100f, MotionEvent.ACTION_HOVER_MOVE);
+ mTestableLooper.processAllMessages();
+
+ // Verify left click is sent and the scroll panel is hidden due to the mouse hovering the
+ // panel.
+ assertThat(mMotionEventCaptor.downEvent).isNotNull();
+ assertThat(mMotionEventCaptor.downEvent.getButtonState()).isEqualTo(
+ MotionEvent.BUTTON_PRIMARY);
+ assertThat(mController.mAutoclickScrollPanel.isVisible()).isFalse();
+ }
+
+ @Test
+ @EnableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_AUTOCLICK_INDICATOR)
+ public void hoverOnAutoclickPanel_useDefaultCursorArea() {
+ initializeAutoclick();
+
+ // Set an extra large cursor area size and enable ignore minor cursor movement.
+ int customSize = 250;
+ Settings.Secure.putIntForUser(mTestableContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_AUTOCLICK_CURSOR_AREA_SIZE,
+ customSize,
+ mTestableContext.getUserId());
+ mController.onChangeForTesting(/* selfChange= */ true,
+ Settings.Secure.getUriFor(
+ Settings.Secure.ACCESSIBILITY_AUTOCLICK_CURSOR_AREA_SIZE));
+ enableIgnoreMinorCursorMovement();
+
+ // Set mouse to hover panel.
+ AutoclickTypePanel mockAutoclickTypePanel = mock(AutoclickTypePanel.class);
+ when(mockAutoclickTypePanel.isHovered()).thenReturn(true);
+ mController.mAutoclickTypePanel = mockAutoclickTypePanel;
+
+ // Send a hover move event that's within than the cursor area size. Normally because
+ // ignoreMinorCursorMovement is enabled this wouldn't trigger a click. But since the panel
+ // is hovered a click is expected.
+ injectFakeMouseMoveEvent(/* x= */ 30f, /* y= */ 0, MotionEvent.ACTION_HOVER_MOVE);
+ mTestableLooper.processAllMessages();
+
+ // Verify the expected left click.
+ assertThat(mMotionEventCaptor.eventCount).isEqualTo(
+ getNumEventsExpectedFromClick(/* numClicks= */ 1));
+ }
+
+ @Test
+ @EnableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_AUTOCLICK_INDICATOR)
public void sendClick_updateLastCursorAndScrollAtThatLocation() {
// Set up event capturer to track scroll events.
ScrollEventCaptor scrollCaptor = new ScrollEventCaptor();
@@ -992,12 +1077,7 @@
@Test
@EnableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_AUTOCLICK_INDICATOR)
public void sendClick_clickType_doubleclick_triggerClickTwice() {
- MotionEventCaptor motionEventCaptor = new MotionEventCaptor();
- mController.setNext(motionEventCaptor);
-
- injectFakeMouseActionHoverMoveEvent();
- // Set delay to zero so click is scheduled to run immediately.
- mController.mClickScheduler.updateDelay(0);
+ initializeAutoclick();
// Set click type to double click.
mController.clickPanelController.handleAutoclickTypeChange(
@@ -1010,22 +1090,17 @@
mTestableLooper.processAllMessages();
// Verify left click sent.
- assertThat(motionEventCaptor.downEvent).isNotNull();
- assertThat(motionEventCaptor.downEvent.getButtonState()).isEqualTo(
+ assertThat(mMotionEventCaptor.downEvent).isNotNull();
+ assertThat(mMotionEventCaptor.downEvent.getButtonState()).isEqualTo(
MotionEvent.BUTTON_PRIMARY);
- assertThat(motionEventCaptor.eventCount).isEqualTo(
+ assertThat(mMotionEventCaptor.eventCount).isEqualTo(
getNumEventsExpectedFromClick(/* numClicks= */ 2));
}
@Test
@EnableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_AUTOCLICK_INDICATOR)
public void sendClick_clickType_drag_simulateDragging() {
- MotionEventCaptor motionEventCaptor = new MotionEventCaptor();
- mController.setNext(motionEventCaptor);
-
- injectFakeMouseActionHoverMoveEvent();
- // Set delay to zero so click is scheduled to run immediately.
- mController.mClickScheduler.updateDelay(0);
+ initializeAutoclick();
// Set click type to drag click.
mController.clickPanelController.handleAutoclickTypeChange(
@@ -1035,55 +1110,50 @@
mTestableLooper.processAllMessages();
// Verify only two motion events were sent.
- assertThat(motionEventCaptor.eventCount).isEqualTo(2);
+ assertThat(mMotionEventCaptor.eventCount).isEqualTo(2);
// Verify both events have the same down time.
- assertThat(motionEventCaptor.downEvent).isNotNull();
- assertThat(motionEventCaptor.buttonPressEvent).isNotNull();
- assertThat(motionEventCaptor.downEvent.getDownTime()).isEqualTo(
- motionEventCaptor.buttonPressEvent.getDownTime());
+ assertThat(mMotionEventCaptor.downEvent).isNotNull();
+ assertThat(mMotionEventCaptor.buttonPressEvent).isNotNull();
+ assertThat(mMotionEventCaptor.downEvent.getDownTime()).isEqualTo(
+ mMotionEventCaptor.buttonPressEvent.getDownTime());
// Move the mouse again to simulate dragging and verify the new mouse event is
// transformed to a MOVE action and its down time matches the drag initiating click's
// down time.
injectFakeMouseMoveEvent(/* x= */ 40, /* y= */ 0, MotionEvent.ACTION_HOVER_MOVE);
mTestableLooper.processAllMessages();
- assertThat(motionEventCaptor.eventCount).isEqualTo(3);
- assertThat(motionEventCaptor.moveEvent).isNotNull();
- assertThat(motionEventCaptor.moveEvent.getDownTime()).isEqualTo(
- motionEventCaptor.downEvent.getDownTime());
+ assertThat(mMotionEventCaptor.eventCount).isEqualTo(3);
+ assertThat(mMotionEventCaptor.moveEvent).isNotNull();
+ assertThat(mMotionEventCaptor.moveEvent.getDownTime()).isEqualTo(
+ mMotionEventCaptor.downEvent.getDownTime());
// Move the mouse again further now to simulate ending the drag session.
- motionEventCaptor.moveEvent = null;
- motionEventCaptor.eventCount = 0;
+ mMotionEventCaptor.moveEvent = null;
+ mMotionEventCaptor.eventCount = 0;
injectFakeMouseMoveEvent(/* x= */ 300, /* y= */ 300, MotionEvent.ACTION_HOVER_MOVE);
mTestableLooper.processAllMessages();
// Verify the final 3 clicks were sent: the 1 move event + 2 up type events to end the drag.
- assertThat(motionEventCaptor.eventCount).isEqualTo(3);
+ assertThat(mMotionEventCaptor.eventCount).isEqualTo(3);
// Verify each event matches the same down time as the initiating drag click.
- assertThat(motionEventCaptor.moveEvent).isNotNull();
- assertThat(motionEventCaptor.moveEvent.getDownTime()).isEqualTo(
- motionEventCaptor.downEvent.getDownTime());
- assertThat(motionEventCaptor.buttonReleaseEvent).isNotNull();
- assertThat(motionEventCaptor.buttonReleaseEvent.getDownTime()).isEqualTo(
- motionEventCaptor.downEvent.getDownTime());
- assertThat(motionEventCaptor.upEvent).isNotNull();
- assertThat(motionEventCaptor.upEvent.getDownTime()).isEqualTo(
- motionEventCaptor.downEvent.getDownTime());
+ assertThat(mMotionEventCaptor.moveEvent).isNotNull();
+ assertThat(mMotionEventCaptor.moveEvent.getDownTime()).isEqualTo(
+ mMotionEventCaptor.downEvent.getDownTime());
+ assertThat(mMotionEventCaptor.buttonReleaseEvent).isNotNull();
+ assertThat(mMotionEventCaptor.buttonReleaseEvent.getDownTime()).isEqualTo(
+ mMotionEventCaptor.downEvent.getDownTime());
+ assertThat(mMotionEventCaptor.upEvent).isNotNull();
+ assertThat(mMotionEventCaptor.upEvent.getDownTime()).isEqualTo(
+ mMotionEventCaptor.downEvent.getDownTime());
}
@Test
@EnableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_AUTOCLICK_INDICATOR)
public void sendClick_clickType_drag_keyEventCancelsDrag() {
- MotionEventCaptor motionEventCaptor = new MotionEventCaptor();
- mController.setNext(motionEventCaptor);
-
- injectFakeMouseActionHoverMoveEvent();
- // Set delay to zero so click is scheduled to run immediately.
- mController.mClickScheduler.updateDelay(0);
+ initializeAutoclick();
// Set click type to drag click.
mController.clickPanelController.handleAutoclickTypeChange(
@@ -1104,21 +1174,16 @@
assertThat(mController.isDraggingForTesting()).isFalse();
// Verify the ACTION_UP was sent for alerting the system that dragging has ended.
- assertThat(motionEventCaptor.upEvent).isNotNull();
- assertThat(motionEventCaptor.downEvent).isNotNull();
- assertThat(motionEventCaptor.upEvent.getDownTime()).isEqualTo(
- motionEventCaptor.downEvent.getDownTime());
+ assertThat(mMotionEventCaptor.upEvent).isNotNull();
+ assertThat(mMotionEventCaptor.downEvent).isNotNull();
+ assertThat(mMotionEventCaptor.upEvent.getDownTime()).isEqualTo(
+ mMotionEventCaptor.downEvent.getDownTime());
}
@Test
@EnableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_AUTOCLICK_INDICATOR)
public void sendClick_clickType_drag_clickTypeDoesNotRevertAfterFirstClick() {
- MotionEventCaptor motionEventCaptor = new MotionEventCaptor();
- mController.setNext(motionEventCaptor);
-
- injectFakeMouseActionHoverMoveEvent();
- // Set delay to zero so click is scheduled to run immediately.
- mController.mClickScheduler.updateDelay(0);
+ initializeAutoclick();
// Set ACCESSIBILITY_AUTOCLICK_REVERT_TO_LEFT_CLICK to true.
Settings.Secure.putIntForUser(mTestableContext.getContentResolver(),
@@ -1143,6 +1208,130 @@
verify(mockAutoclickTypePanel, Mockito.never()).resetSelectedClickType();
}
+ @Test
+ @EnableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_AUTOCLICK_INDICATOR)
+ public void sendClick_clickType_longPress_triggerPressAndHold() {
+ MotionEventCaptor motionEventCaptor = new MotionEventCaptor();
+ mController.setNext(motionEventCaptor);
+
+ injectFakeMouseActionHoverMoveEvent();
+ // Set delay to zero so click is scheduled to run immediately.
+ mController.mClickScheduler.updateDelay(0);
+ // Set click type to long press.
+ mController.clickPanelController.handleAutoclickTypeChange(
+ AutoclickTypePanel.AUTOCLICK_TYPE_LONG_PRESS);
+ AutoclickTypePanel mockAutoclickTypePanel = mock(AutoclickTypePanel.class);
+ mController.mAutoclickTypePanel = mockAutoclickTypePanel;
+
+ // Send hover move event.
+ injectFakeMouseMoveEvent(/* x= */ 30f, /* y= */ 0, MotionEvent.ACTION_HOVER_MOVE);
+ mTestableLooper.processAllMessages();
+ assertThat(motionEventCaptor.downEvent).isNotNull();
+ assertThat(motionEventCaptor.downEvent.getButtonState()).isEqualTo(
+ MotionEvent.BUTTON_PRIMARY);
+ assertThat(motionEventCaptor.upEvent).isNull();
+
+ // When all messages (with delays) are processed.
+ mTestableLooper.moveTimeForward(mController.LONG_PRESS_TIMEOUT);
+ mTestableLooper.processAllMessages();
+ assertThat(motionEventCaptor.upEvent).isNotNull();
+ motionEventCaptor.assertCapturedEvents(
+ MotionEvent.ACTION_DOWN, MotionEvent.ACTION_BUTTON_PRESS,
+ MotionEvent.ACTION_BUTTON_RELEASE, MotionEvent.ACTION_UP);
+ }
+
+ @Test
+ @EnableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_AUTOCLICK_INDICATOR)
+ public void sendClick_clickType_longPress_interruptCancelsLongPress() {
+ MotionEventCaptor motionEventCaptor = new MotionEventCaptor();
+ mController.setNext(motionEventCaptor);
+
+ injectFakeMouseActionHoverMoveEvent();
+ // Set delay to zero so click is scheduled to run immediately.
+ mController.mClickScheduler.updateDelay(0);
+ // Set click type to long press.
+ mController.clickPanelController.handleAutoclickTypeChange(
+ AutoclickTypePanel.AUTOCLICK_TYPE_LONG_PRESS);
+ AutoclickTypePanel mockAutoclickTypePanel = mock(AutoclickTypePanel.class);
+ mController.mAutoclickTypePanel = mockAutoclickTypePanel;
+
+ // Send hover move event.
+ injectFakeMouseMoveEvent(/* x= */ 100f, /* y= */ 0, MotionEvent.ACTION_HOVER_MOVE);
+ mTestableLooper.processAllMessages();
+ assertThat(motionEventCaptor.downEvent).isNotNull();
+ assertThat(motionEventCaptor.downEvent.getButtonState()).isEqualTo(
+ MotionEvent.BUTTON_PRIMARY);
+ assertThat(motionEventCaptor.upEvent).isNull();
+ assertThat(mController.hasOngoingLongPressForTesting()).isTrue();
+ assertThat(motionEventCaptor.cancelEvent).isNull();
+
+ // Send another hover move event to interrupt the long press.
+ mTestableLooper.moveTimeForward(mController.LONG_PRESS_TIMEOUT / 2);
+ mController.clickPanelController.handleAutoclickTypeChange(
+ AutoclickTypePanel.AUTOCLICK_TYPE_LEFT_CLICK);
+ injectFakeMouseMoveEvent(/* x= */ 0, /* y= */ 30f, MotionEvent.ACTION_HOVER_MOVE);
+ mController.mClickScheduler.run();
+ mTestableLooper.processAllMessages();
+ assertThat(motionEventCaptor.cancelEvent).isNotNull();
+ assertThat(mController.hasOngoingLongPressForTesting()).isFalse();
+ }
+
+ @Test
+ @EnableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_AUTOCLICK_INDICATOR)
+ public void continuousScroll_completeLifecycle() {
+ // Set up event capturer to track scroll events.
+ ScrollEventCaptor scrollCaptor = new ScrollEventCaptor();
+ mController.setNext(scrollCaptor);
+
+ // Initialize controller.
+ injectFakeMouseActionHoverMoveEvent();
+
+ // Set cursor position.
+ float expectedX = 100f;
+ float expectedY = 200f;
+ mController.mScrollCursorX = expectedX;
+ mController.mScrollCursorY = expectedY;
+
+ // Start scrolling by hovering UP button.
+ mController.mScrollPanelController.onHoverButtonChange(
+ AutoclickScrollPanel.DIRECTION_UP, true);
+
+ // Verify initial hover state and event.
+ assertThat(mController.mHoveredDirection).isEqualTo(AutoclickScrollPanel.DIRECTION_UP);
+ assertThat(scrollCaptor.eventCount).isEqualTo(1);
+ assertThat(scrollCaptor.scrollEvent.getAxisValue(MotionEvent.AXIS_VSCROLL)).isGreaterThan(
+ 0);
+
+ // Simulate continuous scrolling by triggering runnable.
+ scrollCaptor.eventCount = 0;
+
+ // Advance time by CONTINUOUS_SCROLL_INTERVAL (30ms) and process messages.
+ mTestableLooper.moveTimeForward(CONTINUOUS_SCROLL_INTERVAL);
+ mTestableLooper.processAllMessages();
+
+ // Advance time again to trigger second scroll event.
+ mTestableLooper.moveTimeForward(CONTINUOUS_SCROLL_INTERVAL);
+ mTestableLooper.processAllMessages();
+
+ // Verify multiple scroll events were generated.
+ assertThat(scrollCaptor.eventCount).isEqualTo(2);
+ assertThat(scrollCaptor.scrollEvent.getX()).isEqualTo(expectedX);
+ assertThat(scrollCaptor.scrollEvent.getY()).isEqualTo(expectedY);
+
+ // Stop scrolling by un-hovering the button.
+ mController.mScrollPanelController.onHoverButtonChange(
+ AutoclickScrollPanel.DIRECTION_UP, false);
+
+ // Verify direction is reset.
+ assertThat(mController.mHoveredDirection).isEqualTo(AutoclickScrollPanel.DIRECTION_NONE);
+
+ // Verify no more scroll events are generated after stopping.
+ int countBeforeRunnable = scrollCaptor.eventCount;
+ mTestableLooper.moveTimeForward(CONTINUOUS_SCROLL_INTERVAL);
+ mTestableLooper.processAllMessages();
+ assertThat(scrollCaptor.eventCount).isEqualTo(countBeforeRunnable);
+ }
+
/**
* =========================================================================
* Helper Functions
@@ -1206,4 +1395,13 @@
private int getNumEventsExpectedFromClick(int numClicks) {
return numClicks * 4;
}
+
+ private void initializeAutoclick() {
+ mMotionEventCaptor = new MotionEventCaptor();
+ mController.setNext(mMotionEventCaptor);
+
+ injectFakeMouseActionHoverMoveEvent();
+ // Set delay to zero so click is scheduled to run immediately.
+ mController.mClickScheduler.updateDelay(0);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickScrollPanelTest.java b/services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickScrollPanelTest.java
index bbfecf9..94ae105 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickScrollPanelTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickScrollPanelTest.java
@@ -146,7 +146,7 @@
// Test hover move.
reset(mMockScrollPanelController);
triggerHoverEvent(mUpButton, MotionEvent.ACTION_HOVER_MOVE);
- verify(mMockScrollPanelController).onHoverButtonChange(
+ verify(mMockScrollPanelController, never()).onHoverButtonChange(
eq(AutoclickScrollPanel.DIRECTION_UP), eq(/* hovered= */ true));
// Test hover exit.
@@ -184,7 +184,7 @@
triggerHoverEvent(mUpButton, MotionEvent.ACTION_HOVER_ENTER);
triggerHoverEvent(mUpButton, MotionEvent.ACTION_HOVER_MOVE);
triggerHoverEvent(mUpButton, MotionEvent.ACTION_HOVER_MOVE);
- verify(mMockScrollPanelController, times(3)).onHoverButtonChange(
+ verify(mMockScrollPanelController, times(1)).onHoverButtonChange(
eq(AutoclickScrollPanel.DIRECTION_UP), eq(true));
// Case 2. Move from left button to exit button.
@@ -192,17 +192,17 @@
triggerHoverEvent(mLeftButton, MotionEvent.ACTION_HOVER_ENTER);
triggerHoverEvent(mLeftButton, MotionEvent.ACTION_HOVER_MOVE);
triggerHoverEvent(mLeftButton, MotionEvent.ACTION_HOVER_EXIT);
- triggerHoverEvent(mExitButton, MotionEvent.ACTION_HOVER_MOVE);
triggerHoverEvent(mExitButton, MotionEvent.ACTION_HOVER_ENTER);
+ triggerHoverEvent(mExitButton, MotionEvent.ACTION_HOVER_MOVE);
triggerHoverEvent(mExitButton, MotionEvent.ACTION_HOVER_EXIT);
- // Verify left button events - 2 'true' calls (enter+move) and 1 'false' call (exit).
- verify(mMockScrollPanelController, times(2)).onHoverButtonChange(
+ // Verify left button events - 1 'true' call (enter) and 1 'false' call (exit).
+ verify(mMockScrollPanelController, times(1)).onHoverButtonChange(
eq(AutoclickScrollPanel.DIRECTION_LEFT), eq(/* hovered= */ true));
verify(mMockScrollPanelController).onHoverButtonChange(
eq(AutoclickScrollPanel.DIRECTION_LEFT), eq(/* hovered= */ false));
- // Verify exit button events - hover_move is ignored so 1 'true' call (enter) and 1
- // 'false' call (exit).
+
+ // Verify exit button events - 1 'true' call (enter) and 1 'false' call (exit).
verify(mMockScrollPanelController).onHoverButtonChange(
eq(AutoclickScrollPanel.DIRECTION_EXIT), eq(/* hovered= */ true));
verify(mMockScrollPanelController).onHoverButtonChange(
@@ -241,8 +241,8 @@
mScrollPanel.show(cursorX, cursorY);
// Verify view is added to window manager.
- verify(mMockWindowManager).addView(any(), any(WindowManager.LayoutParams.class));
-
+ verify(mMockWindowManager).addView(eq(mScrollPanel.getContentViewForTesting()),
+ any(WindowManager.LayoutParams.class));
// Verify panel is visible.
assertThat(mScrollPanel.isVisible()).isTrue();
}
@@ -268,7 +268,8 @@
assertThat(mScrollPanel.isVisible()).isTrue();
// Verify view was added twice to window manager.
- verify(mMockWindowManager, times(2)).addView(any(), any(WindowManager.LayoutParams.class));
+ verify(mMockWindowManager, times(2)).addView(eq(mScrollPanel.getContentViewForTesting()),
+ any(WindowManager.LayoutParams.class));
}
@Test
@@ -335,6 +336,21 @@
assertThat(params.y).isLessThan(cursorY);
}
+ @Test
+ public void exitButton_click_hidesPanel() {
+ float cursorX = 300;
+ float cursorY = 300;
+
+ mScrollPanel.show(cursorX, cursorY);
+ assertThat(mScrollPanel.isVisible()).isTrue();
+
+ // Simulate clicking the exit button.
+ mExitButton.performClick();
+
+ // Verify the panel is hidden.
+ assertThat(mScrollPanel.isVisible()).isFalse();
+ }
+
// Helper method to simulate a hover event on a view.
private void triggerHoverEvent(View view, int action) {
MotionEvent event = MotionEvent.obtain(
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickScrollPointIndicatorTest.java b/services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickScrollPointIndicatorTest.java
new file mode 100644
index 0000000..5180172
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickScrollPointIndicatorTest.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.accessibility.autoclick;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableContext;
+import android.testing.TestableLooper;
+import android.view.WindowManager;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+/** Test cases for {@link AutoclickScrollPointIndicator}. */
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+public class AutoclickScrollPointIndicatorTest {
+ @Rule
+ public final MockitoRule mMockitoRule = MockitoJUnit.rule();
+
+ @Rule
+ public TestableContext mTestableContext =
+ new TestableContext(getInstrumentation().getContext());
+
+ @Mock
+ private WindowManager mMockWindowManager;
+
+ private AutoclickScrollPointIndicator mPointIndicator;
+
+ @Before
+ public void setUp() {
+ mTestableContext.addMockSystemService(Context.WINDOW_SERVICE, mMockWindowManager);
+ mPointIndicator = new AutoclickScrollPointIndicator(mTestableContext);
+ }
+
+ @Test
+ public void initialState_isNotVisible() {
+ assertThat(mPointIndicator.isVisible()).isFalse();
+ }
+
+ @Test
+ public void show_addsViewToWindowManager() {
+ float testX = 100.0f;
+ float testY = 200.0f;
+
+ mPointIndicator.show(testX, testY);
+
+ // Verify view is added to window manager.
+ verify(mMockWindowManager).addView(eq(mPointIndicator),
+ any(WindowManager.LayoutParams.class));
+
+ // Verify isVisible reflects correct state.
+ assertThat(mPointIndicator.isVisible()).isTrue();
+ }
+
+ @Test
+ public void show_alreadyVisible_doesNotAddAgain() {
+ float testX = 100.0f;
+ float testY = 200.0f;
+
+ // Show twice.
+ mPointIndicator.show(testX, testY);
+ mPointIndicator.show(testX, testY);
+
+ // Verify addView was only called once.
+ verify(mMockWindowManager, times(1)).addView(any(), any());
+ }
+
+ @Test
+ public void hide_removesViewFromWindowManager() {
+ float testX = 100.0f;
+ float testY = 200.0f;
+
+ // First show the indicator.
+ mPointIndicator.show(testX, testY);
+
+ // Then hide it.
+ mPointIndicator.hide();
+
+ // Verify view is removed from window manager.
+ verify(mMockWindowManager).removeView(eq(mPointIndicator));
+
+ // Verify indicator is hidden.
+ assertThat(mPointIndicator.isVisible()).isFalse();
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java
index 5922b12..14d6ccc 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java
@@ -166,43 +166,33 @@
mHandler = new TestHandler();
mTouchExplorer = new TouchExplorer(mContext, mMockAms, null, mHandler);
mTouchExplorer.setNext(mCaptor);
- // Start TouchExplorer in the state where it has already reset InputDispatcher so that
- // all tests do not start with an irrelevant ACTION_CANCEL.
- mTouchExplorer.setHasResetInputDispatcherState(true);
}
@Test
public void testOneFingerMove_shouldInjectHoverEvents() {
- triggerTouchExplorationWithOneFingerDownMoveUp();
+ goFromStateClearTo(STATE_TOUCH_EXPLORING_1FINGER);
+ // Wait for transiting to touch exploring state.
+ mHandler.fastForward(2 * USER_INTENT_TIMEOUT);
+ moveEachPointers(mLastEvent, p(10, 10));
+ send(mLastEvent);
+ goToStateClearFrom(STATE_TOUCH_EXPLORING_1FINGER);
assertCapturedEvents(ACTION_HOVER_ENTER, ACTION_HOVER_MOVE, ACTION_HOVER_EXIT);
assertState(STATE_TOUCH_EXPLORING);
}
@Test
- @EnableFlags(Flags.FLAG_RESET_INPUT_DISPATCHER_BEFORE_FIRST_TOUCH_EXPLORATION)
- public void testStartTouchExploration_shouldResetInputDispatcherStateWithActionCancel() {
- // Start TouchExplorer in the state where it has *not yet* reset InputDispatcher.
- mTouchExplorer.setHasResetInputDispatcherState(false);
- // Trigger touch exploration twice, with a handler fast-forward in between so TouchExplorer
- // treats these as two separate interactions.
- triggerTouchExplorationWithOneFingerDownMoveUp();
+ @EnableFlags(Flags.FLAG_TOUCH_EXPLORER_USE_VIRTUAL_DEVICE_ID)
+ public void testOneFingerMove_injectedEventsUseVirtualDeviceId() {
+ goFromStateClearTo(STATE_TOUCH_EXPLORING_1FINGER);
+ // Wait for transiting to touch exploring state.
mHandler.fastForward(2 * USER_INTENT_TIMEOUT);
- triggerTouchExplorationWithOneFingerDownMoveUp();
+ goToStateClearFrom(STATE_TOUCH_EXPLORING_1FINGER);
- assertCapturedEvents(
- ACTION_CANCEL, // Only one ACTION_CANCEL before the first touch exploration
- ACTION_HOVER_ENTER, ACTION_HOVER_MOVE, ACTION_HOVER_EXIT,
- ACTION_HOVER_ENTER, ACTION_HOVER_MOVE, ACTION_HOVER_EXIT);
- assertState(STATE_TOUCH_EXPLORING);
- }
-
- private void triggerTouchExplorationWithOneFingerDownMoveUp() {
- send(downEvent());
- // Fast forward so that TouchExplorer's timeouts transition us to the touch exploring state.
- mHandler.fastForward(2 * USER_INTENT_TIMEOUT);
- moveEachPointers(mLastEvent, p(10, 10));
- send(mLastEvent);
- send(upEvent());
+ assertThat(getCapturedEvents()).hasSize(2);
+ assertThat(getCapturedEvents().get(0).getDeviceId()).isEqualTo(
+ EventDispatcher.VIRTUAL_TOUCHSCREEN_DEVICE_ID);
+ assertThat(getCapturedEvents().get(1).getDeviceId()).isEqualTo(
+ EventDispatcher.VIRTUAL_TOUCHSCREEN_DEVICE_ID);
}
/**
@@ -360,6 +350,7 @@
public void upEventWhenInTwoFingerMove_clearsState() {
goFromStateClearTo(STATE_MOVING_2FINGERS);
+ send(pointerUpEvent());
send(upEvent());
assertState(STATE_CLEAR);
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java
index 9ec99c6..708b3c0 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java
@@ -65,6 +65,7 @@
import android.test.mock.MockContentResolver;
import android.view.DisplayInfo;
import android.view.MagnificationSpec;
+import android.view.View;
import android.view.accessibility.MagnificationAnimationCallback;
import android.widget.Scroller;
@@ -1293,22 +1294,133 @@
}
@Test
- public void requestRectOnScreen_disabledByPrefSetting_doesNothing() {
+ public void requestRectOnScreen_followTypingDisabledByPrefSetting_undefined_doNothing() {
+ requestRectOnScreen_followTypingDisabledByPrefSettingHelper_doNothing(
+ View.RECTANGLE_ON_SCREEN_REQUEST_SOURCE_UNDEFINED);
+ }
+
+ @Test
+ public void requestRectOnScreen_followTypingDisabledByPrefSetting_textCursor_doNothing() {
+ requestRectOnScreen_followTypingDisabledByPrefSettingHelper_doNothing(
+ View.RECTANGLE_ON_SCREEN_REQUEST_SOURCE_TEXT_CURSOR);
+ }
+
+ private void requestRectOnScreen_followTypingDisabledByPrefSettingHelper_doNothing(int source) {
register(DISPLAY_0);
zoomIn2xToMiddle(DISPLAY_0);
+ mMessageCapturingHandler.sendAllMessages();
Mockito.reset(mMockWindowManager);
MagnificationSpec startSpec = getCurrentMagnificationSpec(DISPLAY_0);
- MagnificationSpec expectedEndSpec = getMagnificationSpec(2.0f, 0, 0);
mFullScreenMagnificationController.setMagnificationFollowTypingEnabled(false);
- mFullScreenMagnificationController.onRectangleOnScreenRequested(DISPLAY_0, 0, 0, 1, 1);
+ mFullScreenMagnificationController.onRectangleOnScreenRequested(DISPLAY_0, 0, 0, 1, 1,
+ source);
+ mMessageCapturingHandler.sendAllMessages();
assertThat(getCurrentMagnificationSpec(DISPLAY_0), closeTo(startSpec));
- verify(mMockWindowManager, never()).setMagnificationSpec(eq(DISPLAY_0),
+ verify(mMockWindowManager, never()).setMagnificationSpec(eq(DISPLAY_0), any());
+ }
+
+ @Test
+ public void requestRectOnScreen_followTypingEnabledByPrefSetting_undefined_moves() {
+ requestRectOnScreen_followTypingEnabledByPrefSettingHelper_moves(
+ View.RECTANGLE_ON_SCREEN_REQUEST_SOURCE_UNDEFINED);
+ }
+
+ @Test
+ public void requestRectOnScreen_followTypingEnabledByPrefSetting_textCursor_moves() {
+ requestRectOnScreen_followTypingEnabledByPrefSettingHelper_moves(
+ View.RECTANGLE_ON_SCREEN_REQUEST_SOURCE_TEXT_CURSOR);
+ }
+
+ private void requestRectOnScreen_followTypingEnabledByPrefSettingHelper_moves(int source) {
+ register(DISPLAY_0);
+ zoomIn2xToMiddle(DISPLAY_0);
+ mMessageCapturingHandler.sendAllMessages();
+ Mockito.reset(mMockWindowManager);
+ MagnificationSpec expectedEndSpec = getMagnificationSpec(2.0f, 0, 0);
+ mFullScreenMagnificationController.setMagnificationFollowTypingEnabled(true);
+
+ mFullScreenMagnificationController.onRectangleOnScreenRequested(DISPLAY_0, 0, 0, 1, 1,
+ source);
+ mMessageCapturingHandler.sendAllMessages();
+
+ assertThat(getCurrentMagnificationSpec(DISPLAY_0), closeTo(expectedEndSpec));
+ verify(mMockWindowManager).setMagnificationSpec(eq(DISPLAY_0),
argThat(closeTo(expectedEndSpec)));
}
@Test
+ public void requestRectOnScreen_followTypingEnabledByPrefSetting_scrollOnly_doNothing() {
+ register(DISPLAY_0);
+ zoomIn2xToMiddle(DISPLAY_0);
+ mMessageCapturingHandler.sendAllMessages();
+ Mockito.reset(mMockWindowManager);
+ MagnificationSpec startSpec = getCurrentMagnificationSpec(DISPLAY_0);
+ mFullScreenMagnificationController.setMagnificationFollowTypingEnabled(true);
+
+ mFullScreenMagnificationController.onRectangleOnScreenRequested(DISPLAY_0, 0, 0, 1, 1,
+ View.RECTANGLE_ON_SCREEN_REQUEST_SOURCE_SCROLL_ONLY);
+ mMessageCapturingHandler.sendAllMessages();
+
+ assertThat(getCurrentMagnificationSpec(DISPLAY_0), closeTo(startSpec));
+ verify(mMockWindowManager, never()).setMagnificationSpec(eq(DISPLAY_0), any());
+ }
+
+ @Test
+ public void requestRectOnScreen_followFocusDisabledByPrefSetting_focus_doNothing() {
+ register(DISPLAY_0);
+ zoomIn2xToMiddle(DISPLAY_0);
+ mMessageCapturingHandler.sendAllMessages();
+ Mockito.reset(mMockWindowManager);
+ MagnificationSpec startSpec = getCurrentMagnificationSpec(DISPLAY_0);
+ mFullScreenMagnificationController.setMagnificationFollowKeyboardEnabled(false);
+
+ mFullScreenMagnificationController.onRectangleOnScreenRequested(DISPLAY_0, 0, 0, 1, 1,
+ View.RECTANGLE_ON_SCREEN_REQUEST_SOURCE_INPUT_FOCUS);
+ mMessageCapturingHandler.sendAllMessages();
+
+ assertThat(getCurrentMagnificationSpec(DISPLAY_0), closeTo(startSpec));
+ verify(mMockWindowManager, never()).setMagnificationSpec(eq(DISPLAY_0), any());
+ }
+
+ @Test
+ public void requestRectOnScreen_followKeyboardEnabledByPrefSetting_focus_moves() {
+ register(DISPLAY_0);
+ zoomIn2xToMiddle(DISPLAY_0);
+ mMessageCapturingHandler.sendAllMessages();
+ Mockito.reset(mMockWindowManager);
+ MagnificationSpec expectedEndSpec = getMagnificationSpec(2.0f, 0, 0);
+ mFullScreenMagnificationController.setMagnificationFollowKeyboardEnabled(true);
+
+ mFullScreenMagnificationController.onRectangleOnScreenRequested(DISPLAY_0, 0, 0, 1, 1,
+ View.RECTANGLE_ON_SCREEN_REQUEST_SOURCE_INPUT_FOCUS);
+ mMessageCapturingHandler.sendAllMessages();
+
+ assertThat(getCurrentMagnificationSpec(DISPLAY_0), closeTo(expectedEndSpec));
+ verify(mMockWindowManager).setMagnificationSpec(eq(DISPLAY_0),
+ argThat(closeTo(expectedEndSpec)));
+ }
+
+
+ @Test
+ public void requestRectOnScreen_followKeyboardEnabledByPrefSetting_scrollOnly_doNothing() {
+ register(DISPLAY_0);
+ zoomIn2xToMiddle(DISPLAY_0);
+ mMessageCapturingHandler.sendAllMessages();
+ Mockito.reset(mMockWindowManager);
+ MagnificationSpec startSpec = getCurrentMagnificationSpec(DISPLAY_0);
+ mFullScreenMagnificationController.setMagnificationFollowKeyboardEnabled(true);
+
+ mFullScreenMagnificationController.onRectangleOnScreenRequested(DISPLAY_0, 0, 0, 1, 1,
+ View.RECTANGLE_ON_SCREEN_REQUEST_SOURCE_SCROLL_ONLY);
+ mMessageCapturingHandler.sendAllMessages();
+
+ assertThat(getCurrentMagnificationSpec(DISPLAY_0), closeTo(startSpec));
+ verify(mMockWindowManager, never()).setMagnificationSpec(eq(DISPLAY_0), any());
+ }
+
+ @Test
public void testRequestRectOnScreen_rectCanFitOnScreen_pansToGetRectOnScreen() {
for (int i = 0; i < DISPLAY_COUNT; i++) {
requestRectOnScreen_rectCanFitOnScreen_pansToGetRectOnScreen(i);
@@ -1331,14 +1443,14 @@
}
@Test
- public void testRequestRectOnScreen_garbageInput_doesNothing() {
+ public void testRequestRectOnScreen_garbageInput_doNothing() {
for (int i = 0; i < DISPLAY_COUNT; i++) {
- requestRectOnScreen_garbageInput_doesNothing(i);
+ requestRectOnScreen_garbageInput_doNothing(i);
resetMockWindowManager();
}
}
- private void requestRectOnScreen_garbageInput_doesNothing(int displayId) {
+ private void requestRectOnScreen_garbageInput_doNothing(int displayId) {
register(displayId);
zoomIn2xToMiddle(displayId);
mMessageCapturingHandler.sendAllMessages();
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationConnectionManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationConnectionManagerTest.java
index 06ebe6e..60572dd 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationConnectionManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationConnectionManagerTest.java
@@ -18,6 +18,12 @@
import static com.android.server.accessibility.magnification.MockMagnificationConnection.TEST_DISPLAY;
import static com.android.server.accessibility.magnification.MockMagnificationConnection.TEST_DISPLAY_2;
+import static com.android.server.accessibility.magnification.MockMagnificationConnection.TEST_SOURCE_INPUT_FOCUS;
+import static com.android.server.accessibility.magnification.MockMagnificationConnection.TEST_SOURCE_SCROLL_ONLY;
+import static com.android.server.accessibility.magnification.MockMagnificationConnection.TEST_SOURCE_TEXT_CURSOR;
+import static com.android.server.accessibility.magnification.MockMagnificationConnection.TEST_SOURCE_UNDEFINED;
+
+import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -314,15 +320,106 @@
MagnificationScaleProvider.MAX_SCALE);
}
+ @Test
+ public void enableWindowMagnification_trackingTypingFocusEnabledByDefault() {
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
+
+ assertThat(mMagnificationConnectionManager.isTrackingTypingFocusEnabled(TEST_DISPLAY))
+ .isTrue();
+ }
+
+ @Test
+ public void disableTrackingTypingFocus_wasEnabled_trackingDisabled() {
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
+
+ mMagnificationConnectionManager.setTrackingTypingFocusEnabled(TEST_DISPLAY, false);
+
+ assertThat(mMagnificationConnectionManager.isTrackingTypingFocusEnabled(TEST_DISPLAY))
+ .isFalse();
+ }
+
+ @Test
+ public void imeBecomesVisible_trackingTypingFocusWasEnabled_trackingStaysEnabled() {
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
+
+ mMagnificationConnectionManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
+
+ assertThat(mMagnificationConnectionManager.isTrackingTypingFocusEnabled(TEST_DISPLAY))
+ .isTrue();
+ }
+
+ @Test
+ public void imeBecomesVisible_trackingTypingFocusWasDisabled_trackingEnabled() {
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
+ mMagnificationConnectionManager.setTrackingTypingFocusEnabled(TEST_DISPLAY, false);
+
+ mMagnificationConnectionManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
+
+ assertThat(mMagnificationConnectionManager.isTrackingTypingFocusEnabled(TEST_DISPLAY))
+ .isTrue();
+ }
+
+ @Test
+ public void imeBecomesInvisible_trackingTypingFocusWasEnabled_trackingStaysEnabled() {
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
+ mMagnificationConnectionManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
+
+ mMagnificationConnectionManager.onImeWindowVisibilityChanged(TEST_DISPLAY, false);
+
+ assertThat(mMagnificationConnectionManager.isTrackingTypingFocusEnabled(TEST_DISPLAY))
+ .isTrue();
+ }
+
+ @Test
+ public void restartWindowMagnification_trackingTypingFocusWasDisabled_trackingEnabled() {
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
+ mMagnificationConnectionManager.setTrackingTypingFocusEnabled(TEST_DISPLAY, false);
+
+ mMagnificationConnectionManager.disableWindowMagnification(TEST_DISPLAY, false);
+ // Enabling a window magnifier again will turn on the tracking typing focus functionality.
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, NaN, NaN, NaN);
+
+ assertThat(mMagnificationConnectionManager.isTrackingTypingFocusEnabled(TEST_DISPLAY))
+ .isTrue();
+ }
+
+ @Test
+ public void onMoveWindowMagnification_trackingTypingFocusWasEnabled_trackingDisabled()
+ throws RemoteException {
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
+
+ mMockConnection.getConnectionCallback().onMove(TEST_DISPLAY);
+
+ assertThat(mMagnificationConnectionManager.isTrackingTypingFocusEnabled(TEST_DISPLAY))
+ .isFalse();
+ }
+
+ @Test
+ public void processScroll_trackingTypingFocusWasEnabled_trackingDisabled() {
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
+
+ mMagnificationConnectionManager.processScroll(TEST_DISPLAY, 10f, 10f);
+
+ assertThat(mMagnificationConnectionManager.isTrackingTypingFocusEnabled(TEST_DISPLAY))
+ .isFalse();
+ }
+
@FlakyTest(bugId = 297879435)
@Test
- public void logTrackingTypingFocus_processScroll_logDuration() {
+ public void processScroll_trackingTypingFocusWasEnabled_logTrackingTypingFocusWithDuration() {
MagnificationConnectionManager spyMagnificationConnectionManager = spy(
mMagnificationConnectionManager);
spyMagnificationConnectionManager.enableWindowMagnification(
TEST_DISPLAY, 3.0f, 50f, 50f);
- spyMagnificationConnectionManager.onImeWindowVisibilityChanged(
- TEST_DISPLAY, /* shown */ true);
spyMagnificationConnectionManager.processScroll(TEST_DISPLAY, 10f, 10f);
@@ -330,93 +427,53 @@
}
@Test
- public void onRectangleOnScreenRequested_trackingDisabledByOnDrag_withoutMovingMagnifier()
+ public void onRectangleOnScreenRequested_trackingDisabled_undefined_doNotMoveMagnifier()
throws RemoteException {
- mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
- mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
- mMagnificationConnectionManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
- final Region outRegion = new Region();
- mMagnificationConnectionManager.getMagnificationSourceBounds(TEST_DISPLAY, outRegion);
- final Rect requestedRect = outRegion.getBounds();
- requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10);
- mMockConnection.getConnectionCallback().onMove(TEST_DISPLAY);
-
- mMagnificationConnectionManager.onRectangleOnScreenRequested(TEST_DISPLAY,
- requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
-
- verify(mMockConnection.getConnection(), never())
- .moveWindowMagnifierToPosition(anyInt(), anyFloat(), anyFloat(), any());
- }
-
-
- @Test
- public void onRectangleOnScreenRequested_trackingDisabledByScroll_withoutMovingMagnifier()
- throws RemoteException {
- final float distanceX = 10f;
- final float distanceY = 10f;
- mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
- mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
- mMagnificationConnectionManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
- final Region outRegion = new Region();
- mMagnificationConnectionManager.getMagnificationSourceBounds(TEST_DISPLAY, outRegion);
- final Rect requestedRect = outRegion.getBounds();
- requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10);
- mMagnificationConnectionManager.processScroll(TEST_DISPLAY, distanceX, distanceY);
-
- mMagnificationConnectionManager.onRectangleOnScreenRequested(TEST_DISPLAY,
- requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
-
- verify(mMockConnection.getConnection(), never())
- .moveWindowMagnifierToPosition(anyInt(), anyFloat(), anyFloat(), any());
+ onRectangleOnScreenRequested_trackingDisabledHelper_doNotMoveMagnifier(
+ TEST_SOURCE_UNDEFINED);
}
@Test
- public void onRectangleOnScreenRequested_requestRectangleInBound_withoutMovingMagnifier()
+ public void onRectangleOnScreenRequested_trackingDisabled_textCursor_doNotMoveMagnifier()
throws RemoteException {
+ onRectangleOnScreenRequested_trackingDisabledHelper_doNotMoveMagnifier(
+ TEST_SOURCE_TEXT_CURSOR);
+ }
+
+ private void onRectangleOnScreenRequested_trackingDisabledHelper_doNotMoveMagnifier(
+ int source) throws RemoteException {
mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
mMagnificationConnectionManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
- final Region outRegion = new Region();
- mMagnificationConnectionManager.getMagnificationSourceBounds(TEST_DISPLAY, outRegion);
- final Rect requestedRect = outRegion.getBounds();
- requestedRect.inset(-10, -10);
+ mMagnificationConnectionManager.setTrackingTypingFocusEnabled(TEST_DISPLAY, false);
- mMagnificationConnectionManager.onRectangleOnScreenRequested(TEST_DISPLAY,
- requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
+ final Region curRegion = new Region();
+ mMagnificationConnectionManager.getMagnificationSourceBounds(TEST_DISPLAY, curRegion);
+ requestRectOnScreen(mMagnificationConnectionManager, TEST_DISPLAY, /* inBounds= */ false,
+ source);
verify(mMockConnection.getConnection(), never())
.moveWindowMagnifierToPosition(anyInt(), anyFloat(), anyFloat(), any());
- }
- @Test
- public void onRectangleOnScreenRequested_imeVisibilityDefaultInvisible_withoutMovingMagnifier()
- throws RemoteException {
- mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
- mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
- final Region outRegion = new Region();
- mMagnificationConnectionManager.getMagnificationSourceBounds(TEST_DISPLAY, outRegion);
- final Rect requestedRect = outRegion.getBounds();
- requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10);
-
- mMagnificationConnectionManager.onRectangleOnScreenRequested(TEST_DISPLAY,
- requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
-
- verify(mMockConnection.getConnection(), never())
- .moveWindowMagnifierToPosition(anyInt(), anyFloat(), anyFloat(), any());
+ // Also verify the source bounds not changed
+ final Region newRegion = new Region();
+ mMagnificationConnectionManager.getMagnificationSourceBounds(TEST_DISPLAY, newRegion);
+ assertEquals(curRegion, newRegion);
}
@Test
- public void onRectangleOnScreenRequested_trackingEnabledByDefault_movingMagnifier()
+ public void onRectangleOnScreenRequested_trackingDisabled_inputFocus_moveMagnifier()
throws RemoteException {
+ // The magnifier should move to follow keyboard focus even if tracking typing is disabled.
mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
mMagnificationConnectionManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
- final Region outRegion = new Region();
- mMagnificationConnectionManager.getMagnificationSourceBounds(TEST_DISPLAY, outRegion);
- final Rect requestedRect = outRegion.getBounds();
- requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10);
+ mMagnificationConnectionManager.setTrackingTypingFocusEnabled(TEST_DISPLAY, false);
+ mMagnificationConnectionManager.setMagnificationFollowKeyboardEnabled(true);
- mMagnificationConnectionManager.onRectangleOnScreenRequested(TEST_DISPLAY,
- requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
+ final Region curRegion = new Region();
+ mMagnificationConnectionManager.getMagnificationSourceBounds(TEST_DISPLAY, curRegion);
+ Rect requestedRect = requestRectOnScreen(mMagnificationConnectionManager, TEST_DISPLAY,
+ /* inBounds= */ false, TEST_SOURCE_INPUT_FOCUS);
verify(mMockConnection.getConnection()).moveWindowMagnifierToPosition(eq(TEST_DISPLAY),
eq(requestedRect.exactCenterX()), eq(requestedRect.exactCenterY()),
@@ -424,39 +481,125 @@
}
@Test
- public void onRectangleOnScreenRequested_imeInvisible_withoutMovingMagnifier()
+ public void onRectangleOnScreenRequested_imeInvisibleByDefault_undefined_doNotMoveMagnifier()
throws RemoteException {
+ onRectangleOnScreenRequested_imeInvisibleByDefaultHelper_doNotMoveMagnifier(
+ TEST_SOURCE_UNDEFINED);
+ }
+
+ @Test
+ public void onRectangleOnScreenRequested_imeInvisibleByDefault_textCursor_doNotMoveMagnifier()
+ throws RemoteException {
+ onRectangleOnScreenRequested_imeInvisibleByDefaultHelper_doNotMoveMagnifier(
+ TEST_SOURCE_TEXT_CURSOR);
+ }
+
+ private void onRectangleOnScreenRequested_imeInvisibleByDefaultHelper_doNotMoveMagnifier(
+ int source) throws RemoteException {
+ // By default the IME visibility is false.
+ assertThat(mMagnificationConnectionManager.isImeVisible(TEST_DISPLAY)).isFalse();
+
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
+
+ requestRectOnScreen(mMagnificationConnectionManager, TEST_DISPLAY, /* inBounds= */ false,
+ source);
+
+ verify(mMockConnection.getConnection(), never())
+ .moveWindowMagnifierToPosition(anyInt(), anyFloat(), anyFloat(), any());
+ }
+
+ @Test
+ public void onRectangleOnScreenRequested_imeInvisibleByDefault_inputFocus_moveMagnifier()
+ throws RemoteException {
+ // By default the IME visibility is false.
+ assertThat(mMagnificationConnectionManager.isImeVisible(TEST_DISPLAY)).isFalse();
+
+ // The magnifier should move to follow keyboard focus even if the IME visibility is false.
+ mMagnificationConnectionManager.setMagnificationFollowKeyboardEnabled(true);
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
+
+ Rect requestedRect = requestRectOnScreen(mMagnificationConnectionManager, TEST_DISPLAY,
+ /* inBounds= */ false, TEST_SOURCE_INPUT_FOCUS);
+
+ verify(mMockConnection.getConnection()).moveWindowMagnifierToPosition(eq(TEST_DISPLAY),
+ eq(requestedRect.exactCenterX()), eq(requestedRect.exactCenterY()),
+ any(IRemoteMagnificationAnimationCallback.class));
+ }
+
+ @Test
+ public void onRectangleOnScreenRequested_imeBecomesVisible_undefined_moveMagnifier()
+ throws RemoteException {
+ onRectangleOnScreenRequested_imeBecomesVisibleHelper_moveMagnifier(TEST_SOURCE_UNDEFINED);
+ }
+
+ @Test
+ public void onRectangleOnScreenRequested_imeBecomesVisible_textCursor_moveMagnifier()
+ throws RemoteException {
+ onRectangleOnScreenRequested_imeBecomesVisibleHelper_moveMagnifier(TEST_SOURCE_TEXT_CURSOR);
+ }
+
+ private void onRectangleOnScreenRequested_imeBecomesVisibleHelper_moveMagnifier(int source)
+ throws RemoteException {
+ // The essential factors to decide whether the magnifier needs to move to follow typing
+ // focus are:
+ // 1. trackingTypingFocus is enabled
+ // 2. IME is visible
+ // 3. the requested rectangle is not in the current source bound
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
+
+ mMagnificationConnectionManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
+ Rect requestedRect = requestRectOnScreen(
+ mMagnificationConnectionManager, TEST_DISPLAY, /* inBounds= */ false, source);
+
+ verify(mMockConnection.getConnection()).moveWindowMagnifierToPosition(eq(TEST_DISPLAY),
+ eq(requestedRect.exactCenterX()), eq(requestedRect.exactCenterY()),
+ any(IRemoteMagnificationAnimationCallback.class));
+ }
+
+ @Test
+ public void onRectangleOnScreenRequested_imeBecomesInvisible_undefined_doNotMoveMagnifier()
+ throws RemoteException {
+ onRectangleOnScreenRequested_imeBecomesInvisibleHelper_doNotMoveMagnifier(
+ TEST_SOURCE_UNDEFINED);
+ }
+
+ @Test
+ public void onRectangleOnScreenRequested_imeBecomesInvisible_textCursor_doNotMoveMagnifier()
+ throws RemoteException {
+ onRectangleOnScreenRequested_imeBecomesInvisibleHelper_doNotMoveMagnifier(
+ TEST_SOURCE_TEXT_CURSOR);
+ }
+
+ private void onRectangleOnScreenRequested_imeBecomesInvisibleHelper_doNotMoveMagnifier(
+ int source) throws RemoteException {
mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
mMagnificationConnectionManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
- final Region outRegion = new Region();
- mMagnificationConnectionManager.getMagnificationSourceBounds(TEST_DISPLAY, outRegion);
- final Rect requestedRect = outRegion.getBounds();
- requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10);
+
mMagnificationConnectionManager.onImeWindowVisibilityChanged(TEST_DISPLAY, false);
-
- mMagnificationConnectionManager.onRectangleOnScreenRequested(TEST_DISPLAY,
- requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
+ requestRectOnScreen(mMagnificationConnectionManager, TEST_DISPLAY, /* inBounds= */ false,
+ source);
verify(mMockConnection.getConnection(), never())
.moveWindowMagnifierToPosition(anyInt(), anyFloat(), anyFloat(), any());
}
@Test
- public void onRectangleOnScreenRequested_trackingEnabledByDragAndReset_movingMagnifier()
+ public void onRectangleOnScreenRequested_imeBecomesInvisible_inputFocus_moveMagnifier()
throws RemoteException {
+ // The magnifier should move to follow keyboard focus even if the IME visibility changes to
+ // false.
mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
mMagnificationConnectionManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
- mMockConnection.getConnectionCallback().onMove(TEST_DISPLAY);
- mMagnificationConnectionManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
- final Region outRegion = new Region();
- mMagnificationConnectionManager.getMagnificationSourceBounds(TEST_DISPLAY, outRegion);
- final Rect requestedRect = outRegion.getBounds();
- requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10);
+ mMagnificationConnectionManager.setMagnificationFollowKeyboardEnabled(true);
- mMagnificationConnectionManager.onRectangleOnScreenRequested(TEST_DISPLAY,
- requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
+ mMagnificationConnectionManager.onImeWindowVisibilityChanged(TEST_DISPLAY, false);
+ Rect requestedRect = requestRectOnScreen(mMagnificationConnectionManager, TEST_DISPLAY,
+ /* inBounds= */ false, TEST_SOURCE_INPUT_FOCUS);
verify(mMockConnection.getConnection()).moveWindowMagnifierToPosition(eq(TEST_DISPLAY),
eq(requestedRect.exactCenterX()), eq(requestedRect.exactCenterY()),
@@ -464,64 +607,124 @@
}
@Test
- public void onRectangleOnScreenRequested_followTypingIsDisabled_withoutMovingMagnifier() {
+ public void onRectangleOnScreenRequested_rectangleInBound_undefined_doNotMoveMagnifier()
+ throws RemoteException {
+ onRectangleOnScreenRequested_rectangleInBoundHelper_doNotMoveMagnifier(
+ TEST_SOURCE_UNDEFINED);
+ }
+
+ @Test
+ public void onRectangleOnScreenRequested_rectangleInBound_textCursor_doNotMoveMagnifier()
+ throws RemoteException {
+ onRectangleOnScreenRequested_rectangleInBoundHelper_doNotMoveMagnifier(
+ TEST_SOURCE_TEXT_CURSOR);
+ }
+
+ private void onRectangleOnScreenRequested_rectangleInBoundHelper_doNotMoveMagnifier(int source)
+ throws RemoteException {
mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
mMagnificationConnectionManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
- final Region beforeRegion = new Region();
- mMagnificationConnectionManager.getMagnificationSourceBounds(TEST_DISPLAY, beforeRegion);
- final Rect requestedRect = beforeRegion.getBounds();
- requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10);
+
+ requestRectOnScreen(mMagnificationConnectionManager, TEST_DISPLAY, /* inBounds= */ true,
+ source);
+
+ verify(mMockConnection.getConnection(), never())
+ .moveWindowMagnifierToPosition(anyInt(), anyFloat(), anyFloat(), any());
+ }
+
+ @Test
+ public void onRectangleOnScreenRequested_rectangleInBound_inputFocus_doNotMoveMagnifier()
+ throws RemoteException {
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
+ mMagnificationConnectionManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
+ mMagnificationConnectionManager.setMagnificationFollowKeyboardEnabled(true);
+
+ requestRectOnScreen(mMagnificationConnectionManager, TEST_DISPLAY, /* inBounds= */ true,
+ TEST_SOURCE_INPUT_FOCUS);
+
+ verify(mMockConnection.getConnection(), never())
+ .moveWindowMagnifierToPosition(anyInt(), anyFloat(), anyFloat(), any());
+ }
+
+ @Test
+ public void onRectangleOnScreenRequested_followTypingDisabled_undefined_doNotMoveMagnifier()
+ throws RemoteException {
+ onRectangleOnScreenRequested_followTypingDisabledHelper_doNotMoveMagnifier(
+ TEST_SOURCE_UNDEFINED);
+ }
+
+ @Test
+ public void onRectangleOnScreenRequested_followTypingDisabled_textCursor_doNotMoveMagnifier()
+ throws RemoteException {
+ onRectangleOnScreenRequested_followTypingDisabledHelper_doNotMoveMagnifier(
+ TEST_SOURCE_TEXT_CURSOR);
+ }
+
+ private void onRectangleOnScreenRequested_followTypingDisabledHelper_doNotMoveMagnifier(
+ int source) throws RemoteException {
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
+ mMagnificationConnectionManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
mMagnificationConnectionManager.setMagnificationFollowTypingEnabled(false);
- mMagnificationConnectionManager.onRectangleOnScreenRequested(TEST_DISPLAY,
- requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
+ requestRectOnScreen(mMagnificationConnectionManager, TEST_DISPLAY, /* inBounds= */ false,
+ source);
- final Region afterRegion = new Region();
- mMagnificationConnectionManager.getMagnificationSourceBounds(TEST_DISPLAY, afterRegion);
- assertEquals(afterRegion, beforeRegion);
+ verify(mMockConnection.getConnection(), never())
+ .moveWindowMagnifierToPosition(anyInt(), anyFloat(), anyFloat(), any());
}
@Test
- public void onRectangleOnScreenRequested_trackingDisabled_withoutMovingMagnifier() {
+ public void onRectangleOnScreenRequested_default_inputFocus_doNotMoveMagnifier()
+ throws RemoteException {
+ // Keyboard following behavior should currently be false by default.
+ assertFalse(mMagnificationConnectionManager.isMagnificationFollowKeyboardEnabled());
+
mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
mMagnificationConnectionManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
- mMagnificationConnectionManager.setTrackingTypingFocusEnabled(TEST_DISPLAY, false);
- final Region beforeRegion = new Region();
- mMagnificationConnectionManager.getMagnificationSourceBounds(TEST_DISPLAY, beforeRegion);
- final Rect requestedRect = beforeRegion.getBounds();
- requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10);
- mMagnificationConnectionManager.onRectangleOnScreenRequested(TEST_DISPLAY,
- requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
+ // The magnifier should not move even if all the preconditions for typing following are met.
+ requestRectOnScreen(
+ mMagnificationConnectionManager, TEST_DISPLAY, /* inBounds= */ false,
+ TEST_SOURCE_INPUT_FOCUS);
- final Region afterRegion = new Region();
- mMagnificationConnectionManager.getMagnificationSourceBounds(TEST_DISPLAY, afterRegion);
- assertEquals(afterRegion, beforeRegion);
+ verify(mMockConnection.getConnection(), never())
+ .moveWindowMagnifierToPosition(anyInt(), anyFloat(), anyFloat(), any());
}
@Test
- public void onRectangleOnScreenRequested_trackingDisabledAndEnabledMagnifier_movingMagnifier()
+ public void onRectangleOnScreenRequested_followFocusDisabled_inputFocus_doNotMoveMagnifier()
throws RemoteException {
mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
mMagnificationConnectionManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
- mMagnificationConnectionManager.setTrackingTypingFocusEnabled(TEST_DISPLAY, false);
- final Region beforeRegion = new Region();
- mMagnificationConnectionManager.getMagnificationSourceBounds(TEST_DISPLAY, beforeRegion);
- final Rect requestedRect = beforeRegion.getBounds();
- requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10);
- mMagnificationConnectionManager.disableWindowMagnification(TEST_DISPLAY, false);
- // Enabling a window magnifier again will turn on the tracking typing focus functionality.
- mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, NaN, NaN, NaN);
+ mMagnificationConnectionManager.setMagnificationFollowKeyboardEnabled(false);
- mMagnificationConnectionManager.onRectangleOnScreenRequested(TEST_DISPLAY,
- requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
+ requestRectOnScreen(mMagnificationConnectionManager, TEST_DISPLAY, /* inBounds= */ false,
+ TEST_SOURCE_INPUT_FOCUS);
- verify(mMockConnection.getConnection()).moveWindowMagnifierToPosition(eq(TEST_DISPLAY),
- eq(requestedRect.exactCenterX()), eq(requestedRect.exactCenterY()),
- any(IRemoteMagnificationAnimationCallback.class));
+ verify(mMockConnection.getConnection(), never())
+ .moveWindowMagnifierToPosition(anyInt(), anyFloat(), anyFloat(), any());
+ }
+
+ @Test
+ public void onRectangleOnScreenRequested_scrollOnly_doNotMoveMagnifier()
+ throws RemoteException {
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
+ mMagnificationConnectionManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
+ mMagnificationConnectionManager.setMagnificationFollowTypingEnabled(true);
+ mMagnificationConnectionManager.setMagnificationFollowKeyboardEnabled(true);
+
+ requestRectOnScreen(
+ mMagnificationConnectionManager, TEST_DISPLAY, /* inBounds= */ false,
+ TEST_SOURCE_SCROLL_ONLY);
+
+ verify(mMockConnection.getConnection(), never())
+ .moveWindowMagnifierToPosition(anyInt(), anyFloat(), anyFloat(), any());
}
@Test
@@ -869,5 +1072,19 @@
/* flags */ 0);
}
-
+ private Rect requestRectOnScreen(
+ MagnificationConnectionManager manager, int displayId, boolean inBounds, int source) {
+ final Region curRegion = new Region();
+ manager.getMagnificationSourceBounds(displayId, curRegion);
+ final Rect requestedRect = curRegion.getBounds();
+ if (inBounds) {
+ requestedRect.inset(-10, -10);
+ } else {
+ requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10);
+ }
+ manager.onRectangleOnScreenRequested(displayId,
+ requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom,
+ source);
+ return requestedRect;
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
index 6607054..c159e74 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
@@ -62,12 +62,17 @@
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.test.TestLooper;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
import android.test.mock.MockContentResolver;
import android.testing.DexmakerShareClassLoaderRule;
import android.util.DisplayMetrics;
import android.view.Display;
import android.view.DisplayInfo;
+import android.view.View;
+import android.view.accessibility.Flags;
import android.view.accessibility.IRemoteMagnificationAnimationCallback;
import android.view.accessibility.MagnificationAnimationCallback;
import android.widget.Scroller;
@@ -89,6 +94,7 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -112,6 +118,20 @@
private static final Region INITIAL_SCREEN_MAGNIFICATION_REGION =
new Region(0, 0, 500, 600);
private static final Rect TEST_RECT = new Rect(0, 50, 100, 51);
+
+ @ClassRule
+ public static final SetFlagsRule.ClassRule mSetFlagsClassRule = new SetFlagsRule.ClassRule();
+ @Rule public final SetFlagsRule mSetFlagsRule = mSetFlagsClassRule.createSetFlagsRule();
+
+ private static final int TEST_SOURCE_UNDEFINED =
+ View.RECTANGLE_ON_SCREEN_REQUEST_SOURCE_UNDEFINED;
+ private static final int TEST_SOURCE_TEXT_CURSOR =
+ View.RECTANGLE_ON_SCREEN_REQUEST_SOURCE_TEXT_CURSOR;
+ private static final int TEST_SOURCE_INPUT_FOCUS =
+ View.RECTANGLE_ON_SCREEN_REQUEST_SOURCE_INPUT_FOCUS;
+ private static final int TEST_SOURCE_SCROLL_ONLY =
+ View.RECTANGLE_ON_SCREEN_REQUEST_SOURCE_SCROLL_ONLY;
+
private static final float MAGNIFIED_CENTER_X = 100;
private static final float MAGNIFIED_CENTER_Y = 200;
private static final float DEFAULT_SCALE = 3f;
@@ -1760,12 +1780,31 @@
UiChangesForAccessibilityCallbacks callbacks = getUiChangesForAccessibilityCallbacks();
callbacks.onRectangleOnScreenRequested(TEST_DISPLAY,
- TEST_RECT.left, TEST_RECT.top, TEST_RECT.right, TEST_RECT.bottom);
+ TEST_RECT.left, TEST_RECT.top, TEST_RECT.right, TEST_RECT.bottom,
+ TEST_SOURCE_UNDEFINED);
verify(mScreenMagnificationController).onRectangleOnScreenRequested(eq(TEST_DISPLAY),
- eq(TEST_RECT.left), eq(TEST_RECT.top), eq(TEST_RECT.right), eq(TEST_RECT.bottom));
+ eq(TEST_RECT.left), eq(TEST_RECT.top), eq(TEST_RECT.right), eq(TEST_RECT.bottom),
+ eq(TEST_SOURCE_UNDEFINED));
verify(mMagnificationConnectionManager, never()).onRectangleOnScreenRequested(anyInt(),
- anyInt(), anyInt(), anyInt(), anyInt());
+ anyInt(), anyInt(), anyInt(), anyInt(), anyInt());
+ }
+
+ @Test
+ public void onRectangleOnScreenRequested_windowIsActivated_windowDispatchEvent()
+ throws RemoteException {
+ setMagnificationEnabled(MODE_WINDOW);
+ UiChangesForAccessibilityCallbacks callbacks = getUiChangesForAccessibilityCallbacks();
+
+ callbacks.onRectangleOnScreenRequested(TEST_DISPLAY,
+ TEST_RECT.left, TEST_RECT.top, TEST_RECT.right, TEST_RECT.bottom,
+ TEST_SOURCE_UNDEFINED);
+
+ verify(mMagnificationConnectionManager).onRectangleOnScreenRequested(eq(TEST_DISPLAY),
+ eq(TEST_RECT.left), eq(TEST_RECT.top), eq(TEST_RECT.right), eq(TEST_RECT.bottom),
+ eq(TEST_SOURCE_UNDEFINED));
+ verify(mScreenMagnificationController, never()).onRectangleOnScreenRequested(anyInt(),
+ anyInt(), anyInt(), anyInt(), anyInt(), anyInt());
}
@Test
@@ -1777,12 +1816,13 @@
UiChangesForAccessibilityCallbacks callbacks = getUiChangesForAccessibilityCallbacks();
callbacks.onRectangleOnScreenRequested(TEST_DISPLAY,
- TEST_RECT.left, TEST_RECT.top, TEST_RECT.right, TEST_RECT.bottom);
+ TEST_RECT.left, TEST_RECT.top, TEST_RECT.right, TEST_RECT.bottom,
+ TEST_SOURCE_UNDEFINED);
verify(mScreenMagnificationController, never()).onRectangleOnScreenRequested(anyInt(),
- anyInt(), anyInt(), anyInt(), anyInt());
+ anyInt(), anyInt(), anyInt(), anyInt(), anyInt());
verify(mMagnificationConnectionManager, never()).onRectangleOnScreenRequested(anyInt(),
- anyInt(), anyInt(), anyInt(), anyInt());
+ anyInt(), anyInt(), anyInt(), anyInt(), anyInt());
}
@Test
@@ -1790,12 +1830,13 @@
UiChangesForAccessibilityCallbacks callbacks = getUiChangesForAccessibilityCallbacks();
callbacks.onRectangleOnScreenRequested(TEST_DISPLAY,
- TEST_RECT.left, TEST_RECT.top, TEST_RECT.right, TEST_RECT.bottom);
+ TEST_RECT.left, TEST_RECT.top, TEST_RECT.right, TEST_RECT.bottom,
+ TEST_SOURCE_UNDEFINED);
verify(mScreenMagnificationController, never()).onRectangleOnScreenRequested(
- eq(TEST_DISPLAY), anyInt(), anyInt(), anyInt(), anyInt());
+ eq(TEST_DISPLAY), anyInt(), anyInt(), anyInt(), anyInt(), anyInt());
verify(mMagnificationConnectionManager, never()).onRectangleOnScreenRequested(anyInt(),
- anyInt(), anyInt(), anyInt(), anyInt());
+ anyInt(), anyInt(), anyInt(), anyInt(), anyInt());
}
@Test
@@ -1805,12 +1846,82 @@
UiChangesForAccessibilityCallbacks callbacks = getUiChangesForAccessibilityCallbacks();
callbacks.onRectangleOnScreenRequested(TEST_DISPLAY,
- TEST_RECT.left, TEST_RECT.top, TEST_RECT.right, TEST_RECT.bottom);
+ TEST_RECT.left, TEST_RECT.top, TEST_RECT.right, TEST_RECT.bottom,
+ TEST_SOURCE_UNDEFINED);
verify(mScreenMagnificationController, never()).onRectangleOnScreenRequested(
- eq(TEST_DISPLAY), anyInt(), anyInt(), anyInt(), anyInt());
+ eq(TEST_DISPLAY), anyInt(), anyInt(), anyInt(), anyInt(), anyInt());
verify(mMagnificationConnectionManager, never()).onRectangleOnScreenRequested(anyInt(),
- anyInt(), anyInt(), anyInt(), anyInt());
+ anyInt(), anyInt(), anyInt(), anyInt(), anyInt());
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_REQUEST_RECTANGLE_WITH_SOURCE)
+ public void onRectangleOnScreenRequested_flagOff_scrollOnly_dispatchWithUndefinedSource() {
+ onRectangleOnScreenRequested_flagOffHelper_dispatchWithUndefinedSource(
+ TEST_SOURCE_SCROLL_ONLY);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_REQUEST_RECTANGLE_WITH_SOURCE)
+ public void onRectangleOnScreenRequested_flagOn_scrollOnly_dispatchWithScrollOnlySource() {
+ onRectangleOnScreenRequested_flagOnHelper_dispatchWithExpectedSource(
+ TEST_SOURCE_SCROLL_ONLY);
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_REQUEST_RECTANGLE_WITH_SOURCE)
+ public void onRectangleOnScreenRequested_flagOff_textCursor_dispatchWithUndefinedSource() {
+ onRectangleOnScreenRequested_flagOffHelper_dispatchWithUndefinedSource(
+ TEST_SOURCE_TEXT_CURSOR);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_REQUEST_RECTANGLE_WITH_SOURCE)
+ public void onRectangleOnScreenRequested_flagOn_textCursor_dispatchWithTextCursorSource() {
+ onRectangleOnScreenRequested_flagOnHelper_dispatchWithExpectedSource(
+ TEST_SOURCE_TEXT_CURSOR);
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_REQUEST_RECTANGLE_WITH_SOURCE)
+ public void onRectangleOnScreenRequested_flagOff_inputFocus_dispatchWithUndefinedSource() {
+ onRectangleOnScreenRequested_flagOffHelper_dispatchWithUndefinedSource(
+ TEST_SOURCE_INPUT_FOCUS);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_REQUEST_RECTANGLE_WITH_SOURCE)
+ public void onRectangleOnScreenRequested_flagOn_inputFocus_dispatchWithInputFocusSource() {
+ onRectangleOnScreenRequested_flagOnHelper_dispatchWithExpectedSource(
+ TEST_SOURCE_INPUT_FOCUS);
+ }
+
+ private void onRectangleOnScreenRequested_flagOffHelper_dispatchWithUndefinedSource(
+ int source) {
+ mMagnificationController.onFullScreenMagnificationActivationState(TEST_DISPLAY,
+ true);
+ UiChangesForAccessibilityCallbacks callbacks = getUiChangesForAccessibilityCallbacks();
+
+ callbacks.onRectangleOnScreenRequested(TEST_DISPLAY,
+ TEST_RECT.left, TEST_RECT.top, TEST_RECT.right, TEST_RECT.bottom, source);
+
+ verify(mScreenMagnificationController).onRectangleOnScreenRequested(eq(TEST_DISPLAY),
+ eq(TEST_RECT.left), eq(TEST_RECT.top), eq(TEST_RECT.right), eq(TEST_RECT.bottom),
+ eq(TEST_SOURCE_UNDEFINED));
+ }
+
+ private void onRectangleOnScreenRequested_flagOnHelper_dispatchWithExpectedSource(int source) {
+ mMagnificationController.onFullScreenMagnificationActivationState(TEST_DISPLAY,
+ true);
+ UiChangesForAccessibilityCallbacks callbacks = getUiChangesForAccessibilityCallbacks();
+
+ callbacks.onRectangleOnScreenRequested(TEST_DISPLAY,
+ TEST_RECT.left, TEST_RECT.top, TEST_RECT.right, TEST_RECT.bottom, source);
+
+ verify(mScreenMagnificationController).onRectangleOnScreenRequested(eq(TEST_DISPLAY),
+ eq(TEST_RECT.left), eq(TEST_RECT.top), eq(TEST_RECT.right), eq(TEST_RECT.bottom),
+ eq(source));
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MockMagnificationConnection.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MockMagnificationConnection.java
index 35b6c90..9cf688e 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MockMagnificationConnection.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MockMagnificationConnection.java
@@ -31,6 +31,7 @@
import android.os.IBinder;
import android.os.RemoteException;
import android.view.Display;
+import android.view.View;
import android.view.accessibility.IMagnificationConnection;
import android.view.accessibility.IMagnificationConnectionCallback;
import android.view.accessibility.IRemoteMagnificationAnimationCallback;
@@ -46,6 +47,14 @@
public static final int TEST_DISPLAY = Display.DEFAULT_DISPLAY;
public static final int TEST_DISPLAY_2 = Display.DEFAULT_DISPLAY + 1;
+ public static final int TEST_SOURCE_UNDEFINED =
+ View.RECTANGLE_ON_SCREEN_REQUEST_SOURCE_UNDEFINED;
+ public static final int TEST_SOURCE_SCROLL_ONLY =
+ View.RECTANGLE_ON_SCREEN_REQUEST_SOURCE_SCROLL_ONLY;
+ public static final int TEST_SOURCE_TEXT_CURSOR =
+ View.RECTANGLE_ON_SCREEN_REQUEST_SOURCE_TEXT_CURSOR;
+ public static final int TEST_SOURCE_INPUT_FOCUS =
+ View.RECTANGLE_ON_SCREEN_REQUEST_SOURCE_INPUT_FOCUS;
private final List mValidDisplayIds;
private final IMagnificationConnection mConnection;
private final Binder mBinder;
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
index e51f437..49fbaf6 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
@@ -19,7 +19,6 @@
import static android.Manifest.permission.MANAGE_BIOMETRIC;
import static android.Manifest.permission.TEST_BIOMETRIC;
import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
-import static android.adaptiveauth.Flags.FLAG_REPORT_BIOMETRIC_AUTH_ATTEMPTS;
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_NONE;
import static android.hardware.biometrics.BiometricConstants.BIOMETRIC_ERROR_CANCELED;
import static android.hardware.biometrics.BiometricConstants.BIOMETRIC_SUCCESS;
@@ -475,7 +474,6 @@
@Test
public void testRegisterAuthenticationStateListener_callsFaceService() throws Exception {
- mSetFlagsRule.enableFlags(FLAG_REPORT_BIOMETRIC_AUTH_ATTEMPTS);
setInternalAndTestBiometricPermissions(mContext, true /* hasPermission */);
mAuthService = new AuthService(mContext, mInjector);
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java
index a5d6a19..6254714 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java
@@ -269,6 +269,37 @@
}
@Test
+ public void testOnErrorReceived_errorCancelled() throws Exception {
+ setupFingerprint(0 /* id */, FingerprintSensorProperties.TYPE_REAR);
+ setupFace(1 /* id */, false /* confirmationAlwaysRequired */,
+ mock(IBiometricAuthenticator.class));
+ final AuthSession session = createAuthSession(mSensors,
+ false /* checkDevicePolicyManager */,
+ Authenticators.BIOMETRIC_STRONG,
+ TEST_REQUEST_ID,
+ 0 /* operationId */,
+ 0 /* userId */);
+ session.goToInitialState();
+ for (BiometricSensor sensor : session.mPreAuthInfo.eligibleSensors) {
+ assertEquals(BiometricSensor.STATE_WAITING_FOR_COOKIE, sensor.getSensorState());
+ session.onCookieReceived(
+ session.mPreAuthInfo.eligibleSensors.get(sensor.id).getCookie());
+ }
+ assertTrue(session.allCookiesReceived());
+ assertEquals(STATE_AUTH_STARTED, session.getState());
+
+ //Either sensors getting cancelled should cancel all sensors.
+ final int cookie1 = session.mPreAuthInfo.eligibleSensors.get(0).getCookie();
+ session.onErrorReceived(0, cookie1, BiometricConstants.BIOMETRIC_ERROR_CANCELED, 0);
+ for (BiometricSensor sensor : session.mPreAuthInfo.eligibleSensors) {
+ assertEquals(BiometricSensor.STATE_CANCELING, sensor.getSensorState());
+ }
+
+ verify(mClientReceiver).onError(anyInt(), anyInt(), anyInt());
+ verify(mStatusBarService).hideAuthenticationDialog(anyLong());
+ }
+
+ @Test
public void testOnErrorReceivedBeforeOnDialogAnimatedIn() throws RemoteException {
final int fingerprintId = 0;
final int faceId = 1;
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java
index 78c9372..2492687 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java
@@ -16,7 +16,6 @@
package com.android.server.biometrics.sensors.face.aidl;
-import static android.adaptiveauth.Flags.FLAG_REPORT_BIOMETRIC_AUTH_ATTEMPTS;
import static android.hardware.biometrics.BiometricConstants.BIOMETRIC_ERROR_CANCELED;
import static android.hardware.biometrics.BiometricFaceConstants.FACE_ACQUIRED_START;
import static android.hardware.biometrics.BiometricFaceConstants.FACE_ACQUIRED_TOO_DARK;
@@ -265,11 +264,11 @@
}
@Test
- public void cancelsAuthWhenNotInForeground() throws Exception {
+ public void cancelsAuthWhenNotInForegroundAndNotVisible() throws Exception {
final ActivityManager.RunningTaskInfo topTask = new ActivityManager.RunningTaskInfo();
topTask.topActivity = new ComponentName("other", "thing");
+ topTask.isVisible = false;
when(mActivityTaskManager.getTasks(anyInt())).thenReturn(List.of(topTask));
- when(mHal.authenticateWithContext(anyLong(), any())).thenReturn(mCancellationSignal);
final FaceAuthenticationClient client = createClient();
client.start(mCallback);
@@ -282,6 +281,40 @@
}
@Test
+ public void successfulAuthWhenInForegroundAndNotVisible() throws Exception {
+ final ActivityManager.RunningTaskInfo topTask = new ActivityManager.RunningTaskInfo();
+ topTask.topActivity = new ComponentName("test-owner", "cls");
+ topTask.isVisible = false;
+ when(mActivityTaskManager.getTasks(anyInt())).thenReturn(List.of(topTask));
+
+ final FaceAuthenticationClient client = createClient();
+ client.start(mCallback);
+ client.onAuthenticated(new Face("friendly", 1 /* faceId */, 2 /* deviceId */),
+ true /* authenticated */, new ArrayList<>());
+
+ verify(mCallback).onClientFinished(client, true);
+ }
+
+ @Test
+ public void successfulAuthWhenNotInForegroundAndVisible() throws Exception {
+ final ActivityManager.RunningTaskInfo topTask = new ActivityManager.RunningTaskInfo();
+ topTask.topActivity = new ComponentName("other", "thing");
+ topTask.isVisible = false;
+ final ActivityManager.RunningTaskInfo currentTask = new ActivityManager.RunningTaskInfo();
+ currentTask.topActivity = new ComponentName("test-owner", "cls");
+ currentTask.isVisible = true;
+
+ when(mActivityTaskManager.getTasks(anyInt())).thenReturn(List.of(topTask, currentTask));
+
+ final FaceAuthenticationClient client = createClient();
+ client.start(mCallback);
+ client.onAuthenticated(new Face("friendly", 1 /* faceId */, 2 /* deviceId */),
+ true /* authenticated */, new ArrayList<>());
+
+ verify(mCallback).onClientFinished(client, true);
+ }
+
+ @Test
public void testOnAuthenticatedFalseWhenListenerIsNull() throws RemoteException {
final FaceAuthenticationClient client = createClientWithNullListener();
client.start(mCallback);
@@ -412,7 +445,6 @@
@Test
public void testAuthenticationStateListeners_onAuthenticationSucceeded()
throws RemoteException {
- mSetFlagsRule.enableFlags(FLAG_REPORT_BIOMETRIC_AUTH_ATTEMPTS);
final FaceAuthenticationClient client = createClient();
client.start(mCallback);
client.onAuthenticated(new Face("friendly", 1 /* faceId */, 2 /* deviceId */),
@@ -430,7 +462,6 @@
@Test
public void testAuthenticationStateListeners_onAuthenticationFailed() throws RemoteException {
- mSetFlagsRule.enableFlags(FLAG_REPORT_BIOMETRIC_AUTH_ATTEMPTS);
final FaceAuthenticationClient client = createClient();
client.start(mCallback);
client.onAuthenticated(new Face("friendly", 1 /* faceId */, 2 /* deviceId */),
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java
index 7e1d421..df43a18 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java
@@ -16,7 +16,6 @@
package com.android.server.biometrics.sensors.fingerprint.aidl;
-import static android.adaptiveauth.Flags.FLAG_REPORT_BIOMETRIC_AUTH_ATTEMPTS;
import static android.hardware.biometrics.BiometricConstants.BIOMETRIC_ERROR_CANCELED;
import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_START;
import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_TOO_FAST;
@@ -96,7 +95,6 @@
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
-import java.time.Clock;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -158,8 +156,6 @@
@Mock
private AuthSessionCoordinator mAuthSessionCoordinator;
@Mock
- private Clock mClock;
- @Mock
private LockoutTracker mLockoutTracker;
@Captor
private ArgumentCaptor<OperationContextExt> mOperationContextCaptor;
@@ -558,7 +554,6 @@
@Test
public void testAuthenticationStateListeners_onAuthenticationSucceeded()
throws RemoteException {
- mSetFlagsRule.enableFlags(FLAG_REPORT_BIOMETRIC_AUTH_ATTEMPTS);
final FingerprintAuthenticationClient client = createClient();
client.start(mCallback);
client.onAuthenticated(new Fingerprint("friendly", 1 /* fingerId */,
@@ -575,7 +570,6 @@
@Test
public void testAuthenticationStateListeners_onAuthenticationFailed() throws RemoteException {
- mSetFlagsRule.enableFlags(FLAG_REPORT_BIOMETRIC_AUTH_ATTEMPTS);
final FingerprintAuthenticationClient client = createClient();
client.start(mCallback);
client.onAuthenticated(new Fingerprint("friendly", 1 /* fingerId */,
@@ -590,9 +584,10 @@
}
@Test
- public void cancelsAuthWhenNotInForeground() throws Exception {
+ public void cancelsAuthWhenNotInForegroundAndNotVisible() throws Exception {
final ActivityManager.RunningTaskInfo topTask = new ActivityManager.RunningTaskInfo();
topTask.topActivity = new ComponentName("other", "thing");
+ topTask.isVisible = false;
when(mActivityTaskManager.getTasks(anyInt())).thenReturn(List.of(topTask));
final FingerprintAuthenticationClient client = createClientWithoutBackgroundAuth();
@@ -602,12 +597,53 @@
mLooper.moveTimeForward(10);
mLooper.dispatchAll();
+
verify(mCancellationSignal, never()).cancel();
verify(mClientMonitorCallbackConverter)
.onError(anyInt(), anyInt(), eq(BIOMETRIC_ERROR_CANCELED), anyInt());
}
@Test
+ public void successfulAuthWhenInForegroundAndNotVisible() throws Exception {
+ final ActivityManager.RunningTaskInfo topTask = new ActivityManager.RunningTaskInfo();
+ topTask.topActivity = new ComponentName("test-owner", "cls");
+ topTask.isVisible = false;
+ when(mActivityTaskManager.getTasks(anyInt())).thenReturn(List.of(topTask));
+
+ final FingerprintAuthenticationClient client = createClientWithoutBackgroundAuth();
+ client.start(mCallback);
+ client.onAuthenticated(new Fingerprint("friendly", 1 /* fingerId */, 2 /* deviceId */),
+ true /* authenticated */, new ArrayList<>());
+
+ mLooper.moveTimeForward(10);
+ mLooper.dispatchAll();
+
+ verify(mCallback).onClientFinished(client, true);
+ }
+
+ @Test
+ public void successfulAuthWhenNotInForegroundAndVisible() throws Exception {
+ final ActivityManager.RunningTaskInfo topTask = new ActivityManager.RunningTaskInfo();
+ topTask.topActivity = new ComponentName("other", "thing");
+ topTask.isVisible = false;
+ final ActivityManager.RunningTaskInfo currentTask = new ActivityManager.RunningTaskInfo();
+ currentTask.topActivity = new ComponentName("test-owner", "cls");
+ currentTask.isVisible = true;
+
+ when(mActivityTaskManager.getTasks(anyInt())).thenReturn(List.of(topTask, currentTask));
+
+ final FingerprintAuthenticationClient client = createClientWithoutBackgroundAuth();
+ client.start(mCallback);
+ client.onAuthenticated(new Fingerprint("friendly", 1 /* fingerId */, 2 /* deviceId */),
+ true /* authenticated */, new ArrayList<>());
+
+ mLooper.moveTimeForward(10);
+ mLooper.dispatchAll();
+
+ verify(mCallback).onClientFinished(client, true);
+ }
+
+ @Test
public void testResetLockoutOnAuthSuccess_nonBiometricPrompt() throws RemoteException {
final FingerprintAuthenticationClient client = createClient(1 /* version */,
true /* allowBackgroundAuthentication */, false /* isBiometricPrompt */,
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java
index 178e7ec..72e40eb 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java
@@ -28,6 +28,7 @@
import android.content.AttributionSource;
import android.hardware.display.DisplayManagerInternal;
import android.hardware.input.IInputManager;
+import android.hardware.input.InputManager;
import android.hardware.input.InputManagerGlobal;
import android.os.Binder;
import android.os.Handler;
@@ -87,9 +88,9 @@
final InputController.DeviceCreationThreadVerifier threadVerifier = () -> true;
mInputController = new InputController(mNativeWrapperMock,
new Handler(TestableLooper.get(this).getLooper()),
+ InstrumentationRegistry.getTargetContext().getSystemService(InputManager.class),
InstrumentationRegistry.getTargetContext().getSystemService(WindowManager.class),
- AttributionSource.myAttributionSource(),
- threadVerifier);
+ AttributionSource.myAttributionSource(), threadVerifier);
}
void setUpDisplay(int displayId) {
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/ViewConfigurationControllerTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/ViewConfigurationControllerTest.java
new file mode 100644
index 0000000..b8cbef2
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/ViewConfigurationControllerTest.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.companion.virtual;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.companion.virtual.ViewConfigurationParams;
+import android.companion.virtualdevice.flags.Flags;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.om.OverlayConstraint;
+import android.content.om.OverlayManager;
+import android.content.om.OverlayManagerTransaction;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.annotations.Presubmit;
+import android.platform.test.flag.junit.SetFlagsRule;
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.List;
+
+@Presubmit
+@RunWith(AndroidTestingRunner.class)
+@EnableFlags({Flags.FLAG_VIEWCONFIGURATION_APIS,
+ android.content.res.Flags.FLAG_DIMENSION_FRRO})
+public class ViewConfigurationControllerTest {
+
+ private static final int DEVICE_ID = 5;
+
+ @Rule
+ public SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
+ private ViewConfigurationController mViewConfigurationController;
+ @Mock
+ private OverlayManager mOverlayManagerMock;
+ @Captor
+ private ArgumentCaptor<OverlayManagerTransaction> mTransactionArgumentCaptor;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ Context context = Mockito.spy(new ContextWrapper(
+ InstrumentationRegistry.getInstrumentation().getTargetContext()));
+ when(context.getSystemService(OverlayManager.class)).thenReturn(mOverlayManagerMock);
+ mViewConfigurationController = new ViewConfigurationController(context);
+ }
+
+ @Test
+ public void applyViewConfigurationParams_enablesOverlay() throws Exception {
+ mViewConfigurationController.applyViewConfigurationParams(DEVICE_ID, createParams());
+
+ verify(mOverlayManagerMock).commit(mTransactionArgumentCaptor.capture());
+ OverlayManagerTransaction transaction = mTransactionArgumentCaptor.getValue();
+ List<OverlayManagerTransaction.Request> requests = new ArrayList<>();
+ transaction.getRequests().forEachRemaining(requests::add);
+ assertThat(requests).hasSize(2);
+ OverlayManagerTransaction.Request request0 = requests.get(0);
+ assertEquals(OverlayManagerTransaction.Request.TYPE_REGISTER_FABRICATED, request0.type);
+ List<OverlayConstraint> constraints0 = request0.constraints;
+ assertThat(constraints0).hasSize(0);
+ OverlayManagerTransaction.Request request1 = requests.get(1);
+ List<OverlayConstraint> constraints1 = request1.constraints;
+ assertEquals(OverlayManagerTransaction.Request.TYPE_SET_ENABLED, request1.type);
+ assertThat(constraints1).hasSize(1);
+ OverlayConstraint constraint = constraints1.get(0);
+ assertEquals(OverlayConstraint.TYPE_DEVICE_ID, constraint.getType());
+ assertEquals(DEVICE_ID, constraint.getValue());
+ }
+
+ @Test
+ public void close_disablesOverlay() throws Exception {
+ mViewConfigurationController.applyViewConfigurationParams(DEVICE_ID, createParams());
+ clearInvocations(mOverlayManagerMock);
+
+ mViewConfigurationController.close();
+
+ verify(mOverlayManagerMock).commit(mTransactionArgumentCaptor.capture());
+ OverlayManagerTransaction transaction = mTransactionArgumentCaptor.getValue();
+ List<OverlayManagerTransaction.Request> requests = new ArrayList<>();
+ transaction.getRequests().forEachRemaining(requests::add);
+ assertThat(requests).hasSize(1);
+ OverlayManagerTransaction.Request request = requests.get(0);
+ assertEquals(OverlayManagerTransaction.Request.TYPE_UNREGISTER_FABRICATED, request.type);
+ List<OverlayConstraint> constraints = request.constraints;
+ assertThat(constraints).hasSize(0);
+ }
+
+ private static ViewConfigurationParams createParams() {
+ return new ViewConfigurationParams.Builder()
+ .setTapTimeoutDuration(Duration.ofMillis(10L))
+ .setDoubleTapTimeoutDuration(Duration.ofMillis(10L))
+ .setDoubleTapMinTimeDuration(Duration.ofMillis(10L))
+ .setScrollFriction(10f)
+ .setMinimumFlingVelocityDpPerSecond(10f)
+ .setMaximumFlingVelocityDpPerSecond(10f)
+ .setTouchSlopDp(10f)
+ .build();
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
index ab7b4da..5fb6f02 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
@@ -56,6 +56,7 @@
import android.companion.virtual.IVirtualDeviceActivityListener;
import android.companion.virtual.IVirtualDeviceIntentInterceptor;
import android.companion.virtual.IVirtualDeviceSoundEffectListener;
+import android.companion.virtual.ViewConfigurationParams;
import android.companion.virtual.VirtualDeviceManager;
import android.companion.virtual.VirtualDeviceParams;
import android.companion.virtual.audio.IAudioConfigChangedCallback;
@@ -80,6 +81,7 @@
import android.hardware.display.IVirtualDisplayCallback;
import android.hardware.display.VirtualDisplayConfig;
import android.hardware.input.IInputManager;
+import android.hardware.input.InputManager;
import android.hardware.input.VirtualDpadConfig;
import android.hardware.input.VirtualKeyEvent;
import android.hardware.input.VirtualKeyboardConfig;
@@ -104,6 +106,7 @@
import android.os.RemoteException;
import android.os.WorkSource;
import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
import android.platform.test.flag.junit.SetFlagsRule;
import android.testing.AndroidTestingRunner;
@@ -136,6 +139,7 @@
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
+import java.time.Duration;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@@ -280,6 +284,8 @@
private ApplicationInfo mApplicationInfoMock;
@Mock
IInputManager mIInputManagerMock;
+ @Mock
+ private ViewConfigurationController mViewConfigurationControllerMock;
private Intent createRestrictedActivityBlockedIntent(Set<String> displayCategories,
String targetDisplayCategory) {
@@ -310,7 +316,6 @@
return blockedAppIntent;
}
-
private ActivityInfo getActivityInfo(
String packageName, String name, boolean displayOnRemoteDevices,
String requiredDisplayCategory) {
@@ -331,7 +336,6 @@
LocalServices.removeServiceForTest(DisplayManagerInternal.class);
LocalServices.addService(DisplayManagerInternal.class, mDisplayManagerInternalMock);
- doNothing().when(mInputManagerInternalMock).setMouseScalingEnabled(anyBoolean(), anyInt());
doNothing().when(mInputManagerInternalMock).setPointerIconVisible(anyBoolean(), anyInt());
LocalServices.removeServiceForTest(InputManagerInternal.class);
LocalServices.addService(InputManagerInternal.class, mInputManagerInternalMock);
@@ -376,7 +380,9 @@
// Allow virtual devices to be created on the looper thread for testing.
final InputController.DeviceCreationThreadVerifier threadVerifier = () -> true;
mInputController = new InputController(mNativeWrapperMock,
- new Handler(TestableLooper.get(this).getLooper()), mWindowManager,
+ new Handler(TestableLooper.get(this).getLooper()),
+ InstrumentationRegistry.getInstrumentation().getTargetContext().getSystemService(
+ InputManager.class), mWindowManager,
AttributionSource.myAttributionSource(), threadVerifier);
mCameraAccessController =
new CameraAccessController(mContext, mLocalService, mCameraAccessBlockedCallback);
@@ -1421,7 +1427,6 @@
verify(mNativeWrapperMock).writeRelativeEvent(fd, x, y, eventTimeNanos);
}
-
@Test
public void sendScrollEvent_noFd() {
assertThat(mDeviceImpl.sendScrollEvent(BINDER,
@@ -1451,7 +1456,6 @@
verify(mNativeWrapperMock).writeScrollEvent(fd, x, y, eventTimeNanos);
}
-
@Test
public void sendTouchEvent_noFd() {
assertThat(mDeviceImpl.sendTouchEvent(BINDER,
@@ -1835,6 +1839,34 @@
.isEqualTo(mDeviceImpl.getPersistentDeviceId());
}
+ @Test
+ @EnableFlags(Flags.FLAG_VIEWCONFIGURATION_APIS)
+ public void createVirtualDeviceWithViewConfigurationParams_appliesParams() {
+ ViewConfigurationParams viewConfigurationParams = new ViewConfigurationParams.Builder()
+ .setTapTimeoutDuration(Duration.ofMillis(10L))
+ .setDoubleTapTimeoutDuration(Duration.ofMillis(10L))
+ .setDoubleTapMinTimeDuration(Duration.ofMillis(10L))
+ .setScrollFriction(10f)
+ .setMinimumFlingVelocityDpPerSecond(10f)
+ .setMaximumFlingVelocityDpPerSecond(10f)
+ .setTouchSlopDp(10f)
+ .build();
+ VirtualDeviceParams virtualDeviceParams =
+ new VirtualDeviceParams.Builder()
+ .setViewConfigurationParams(viewConfigurationParams)
+ .build();
+
+ createVirtualDevice(VIRTUAL_DEVICE_ID_1, DEVICE_OWNER_UID_1, virtualDeviceParams);
+ verify(mViewConfigurationControllerMock).applyViewConfigurationParams(
+ eq(VIRTUAL_DEVICE_ID_1), eq(viewConfigurationParams));
+ }
+
+ @Test
+ public void closeVirtualDevice_closesViewConfigurationController() {
+ mDeviceImpl.close();
+ verify(mViewConfigurationControllerMock).close();
+ }
+
private VirtualDeviceImpl createVirtualDevice(int virtualDeviceId, int ownerUid) {
VirtualDeviceParams params = new VirtualDeviceParams.Builder()
.setBlockedActivities(BLOCKED_ACTIVITIES)
@@ -1862,7 +1894,8 @@
mRunningAppsChangedCallback,
params,
new DisplayManagerGlobal(mIDisplayManager),
- new VirtualCameraController(DEVICE_POLICY_DEFAULT, virtualDeviceId));
+ new VirtualCameraController(DEVICE_POLICY_DEFAULT, virtualDeviceId),
+ mViewConfigurationControllerMock);
mVdms.addVirtualDevice(virtualDeviceImpl);
assertThat(virtualDeviceImpl.getAssociationId()).isEqualTo(mAssociationInfo.getId());
assertThat(virtualDeviceImpl.getPersistentDeviceId())
@@ -1908,7 +1941,7 @@
private AssociationInfo createAssociationInfo(int associationId, String deviceProfile,
CharSequence displayName) {
- return new AssociationInfo(associationId, /* userId= */ 0, /* packageName=*/ null,
+ return new AssociationInfo(associationId, /* userId= */ 0, /* packageName= */ null,
MacAddress.BROADCAST_ADDRESS, displayName, deviceProfile,
/* associatedDevice= */ null, /* selfManaged= */ true,
/* notifyOnDeviceNearby= */ false, /* revoked= */ false, /* pending= */ false,
diff --git a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java
index 4a05ea6..c4713d7 100644
--- a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java
@@ -74,6 +74,8 @@
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.test.TestLooper;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.Presubmit;
import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
@@ -87,6 +89,8 @@
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
+import com.android.internal.R;
+import com.android.media.projection.flags.Flags;
import com.android.server.LocalServices;
import com.android.server.testutils.OffsettableClock;
import com.android.server.wm.WindowManagerInternal;
@@ -1187,6 +1191,42 @@
assertThat(projection.getDisplayId()).isEqualTo(200);
}
+ @Test
+ @EnableFlags(Flags.FLAG_RECORDING_OVERLAY)
+ public void createProjectionForOverlay_forUnknownCaller_isNotSet() throws Exception {
+ mContext.getOrCreateTestableResources().addOverride(
+ R.string.config_defaultContextualSearchPackageName, "test.something");
+ MediaProjectionManagerService.MediaProjection projection =
+ createProjectionPreconditions(mService);
+ projection.setRecordingOverlay(true);
+
+ assertThat(projection.isRecordingOverlay()).isFalse();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_RECORDING_OVERLAY)
+ public void createProjectionForOverlay_forContextualSearch() throws Exception {
+ mContext.getOrCreateTestableResources().addOverride(
+ R.string.config_defaultContextualSearchPackageName, PACKAGE_NAME);
+ MediaProjectionManagerService.MediaProjection projection =
+ createProjectionPreconditions(mService);
+ projection.setRecordingOverlay(true);
+
+ assertThat(projection.isRecordingOverlay()).isTrue();
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_RECORDING_OVERLAY)
+ public void createProjectionForOverlay_withoutFlag() throws Exception {
+ mContext.getOrCreateTestableResources().addOverride(
+ R.string.config_defaultContextualSearchPackageName, PACKAGE_NAME);
+ MediaProjectionManagerService.MediaProjection projection = createProjectionPreconditions(
+ mService);
+ projection.setRecordingOverlay(true);
+
+ assertThat(projection.isRecordingOverlay()).isFalse();
+ }
+
private void verifySetSessionWithContent(@RecordContent int content) {
verify(mWindowManagerInternal, atLeastOnce()).setContentRecordingSession(
mSessionCaptor.capture());
diff --git a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionMetricsLoggerTest.java b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionMetricsLoggerTest.java
index c727bb6..86ca6f4 100644
--- a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionMetricsLoggerTest.java
+++ b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionMetricsLoggerTest.java
@@ -21,6 +21,7 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.view.ContentRecordingSession.RECORD_CONTENT_BELOW_OVERLAY;
import static android.view.ContentRecordingSession.RECORD_CONTENT_DISPLAY;
import static android.view.ContentRecordingSession.RECORD_CONTENT_TASK;
@@ -45,6 +46,7 @@
import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_USER_SWITCH;
import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_TARGET_CHANGED__TARGET_TYPE__TARGET_TYPE_APP_TASK;
import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_TARGET_CHANGED__TARGET_TYPE__TARGET_TYPE_DISPLAY;
+import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_TARGET_CHANGED__TARGET_TYPE__TARGET_TYPE_OVERLAY;
import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_TARGET_CHANGED__TARGET_TYPE__TARGET_TYPE_UNKNOWN;
import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_TARGET_CHANGED__TARGET_WINDOWING_MODE__WINDOWING_MODE_FREEFORM;
import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_TARGET_CHANGED__TARGET_WINDOWING_MODE__WINDOWING_MODE_FULLSCREEN;
@@ -643,10 +645,13 @@
mExpect.that(mLogger.contentToRecordToTargetType(RECORD_CONTENT_DISPLAY))
.isEqualTo(MEDIA_PROJECTION_TARGET_CHANGED__TARGET_TYPE__TARGET_TYPE_DISPLAY);
+ mExpect.that(mLogger.contentToRecordToTargetType(RECORD_CONTENT_BELOW_OVERLAY))
+ .isEqualTo(MEDIA_PROJECTION_TARGET_CHANGED__TARGET_TYPE__TARGET_TYPE_OVERLAY);
+
mExpect.that(mLogger.contentToRecordToTargetType(RECORD_CONTENT_TASK))
.isEqualTo(MEDIA_PROJECTION_TARGET_CHANGED__TARGET_TYPE__TARGET_TYPE_APP_TASK);
- mExpect.that(mLogger.contentToRecordToTargetType(2))
+ mExpect.that(mLogger.contentToRecordToTargetType(4))
.isEqualTo(MEDIA_PROJECTION_TARGET_CHANGED__TARGET_TYPE__TARGET_TYPE_UNKNOWN);
mExpect.that(mLogger.contentToRecordToTargetType(-1))
diff --git a/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java
index 07e4152..d7068c1 100644
--- a/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java
@@ -422,47 +422,82 @@
@Test
@EnableFlags({Flags.FLAG_ALLOW_THERMAL_THRESHOLDS_CALLBACK})
public void testNotifyThrottling_headroomCallback() throws Exception {
- assertTrue(mService.mService.registerThermalHeadroomListener(mHeadroomListener));
- Thread.sleep(CALLBACK_TIMEOUT_MILLI_SEC);
- resetListenerMock();
+ TemperatureWatcher watcher = mService.mTemperatureWatcher;
+ // Reduce the inactivity threshold to speed up testing
+ watcher.mInactivityThresholdMillis = 500;
+
int status = Temperature.THROTTLING_SEVERE;
+ // if no listener, temperature update should not trigger headroom polling
+ Temperature newSkin = new Temperature(37, Temperature.TYPE_SKIN, "skin1", status);
+ mFakeHal.mGetCurrentTemperaturesCalled.set(0);
+ mFakeHal.mCallback.onTemperatureChanged(newSkin);
+ assertEquals(0, mFakeHal.mGetCurrentTemperaturesCalled.get());
+
+ assertTrue(mService.mService.registerThermalHeadroomListener(mHeadroomListener));
+ // sleep until the polling from registration is done
+ Thread.sleep(1000);
+
+ resetListenerMock();
mFakeHal.updateTemperatureList();
- // Should not notify on non-skin type
+ // Should not notify on non-skin type should not trigger headroom polling nor notification
Temperature newBattery = new Temperature(37, Temperature.TYPE_BATTERY, "batt", status);
+ mFakeHal.updateTemperatureList(newBattery);
+ mFakeHal.mGetCurrentTemperaturesCalled.set(0);
mFakeHal.mCallback.onTemperatureChanged(newBattery);
verify(mHeadroomListener, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
.times(0)).onHeadroomChange(anyFloat(), anyFloat(), anyInt(), any());
+ assertEquals(0, mFakeHal.mGetCurrentTemperaturesCalled.get());
resetListenerMock();
// Notify headroom on skin temperature change
- Temperature newSkin = new Temperature(37, Temperature.TYPE_SKIN, "skin1", status);
+ newSkin = new Temperature(37, Temperature.TYPE_SKIN, "skin1", status);
+ mFakeHal.updateTemperatureList(newSkin);
+ mFakeHal.mGetCurrentTemperaturesCalled.set(0);
mFakeHal.mCallback.onTemperatureChanged(newSkin);
verify(mHeadroomListener, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
.times(1)).onHeadroomChange(eq(0.9f), anyFloat(), anyInt(),
eq(new float[]{Float.NaN, 0.6666667f, 0.8333333f, 1.0f, 1.1666666f, 1.3333334f,
1.5f}));
+ assertEquals(1, mFakeHal.mGetCurrentTemperaturesCalled.get());
resetListenerMock();
// Same or similar temperature should not trigger in a short period
mFakeHal.mCallback.onTemperatureChanged(newSkin);
- newSkin = new Temperature(36.9f, Temperature.TYPE_SKIN, "skin1", status);
+ Thread.sleep(1000);
+ newSkin = new Temperature(36.99f, Temperature.TYPE_SKIN, "skin1", status);
+ mFakeHal.updateTemperatureList(newSkin);
mFakeHal.mCallback.onTemperatureChanged(newSkin);
- newSkin = new Temperature(37.1f, Temperature.TYPE_SKIN, "skin1", status);
+ Thread.sleep(1000);
+ newSkin = new Temperature(37.01f, Temperature.TYPE_SKIN, "skin1", status);
+ mFakeHal.updateTemperatureList(newSkin);
mFakeHal.mCallback.onTemperatureChanged(newSkin);
verify(mHeadroomListener, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
.times(0)).onHeadroomChange(anyFloat(), anyFloat(), anyInt(), any());
resetListenerMock();
+ Thread.sleep(1000);
- // Significant temperature should trigger in a short period
+ // Significant temperature change should trigger in a short period
newSkin = new Temperature(34f, Temperature.TYPE_SKIN, "skin1", status);
+ mFakeHal.updateTemperatureList(newSkin);
mFakeHal.mCallback.onTemperatureChanged(newSkin);
verify(mHeadroomListener, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
.times(1)).onHeadroomChange(eq(0.8f), anyFloat(), anyInt(),
eq(new float[]{Float.NaN, 0.6666667f, 0.8333333f, 1.0f, 1.1666666f, 1.3333334f,
1.5f}));
resetListenerMock();
+ Thread.sleep(1000);
newSkin = new Temperature(40f, Temperature.TYPE_SKIN, "skin1", status);
+ mFakeHal.updateTemperatureList(newSkin);
+ mFakeHal.mCallback.onTemperatureChanged(newSkin);
+ verify(mHeadroomListener, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+ .times(1)).onHeadroomChange(eq(1.0f), anyFloat(), anyInt(),
+ eq(new float[]{Float.NaN, 0.6666667f, 0.8333333f, 1.0f, 1.1666666f, 1.3333334f,
+ 1.5f}));
+ resetListenerMock();
+ Thread.sleep(ThermalManagerService.HEADROOM_CALLBACK_MIN_INTERVAL_MILLIS);
+
+ // Non-significant temperature change but after long time can also trigger
mFakeHal.mCallback.onTemperatureChanged(newSkin);
verify(mHeadroomListener, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
.times(1)).onHeadroomChange(eq(1.0f), anyFloat(), anyInt(),
@@ -559,15 +594,12 @@
@DisableFlags({Flags.FLAG_ALLOW_THERMAL_HAL_SKIN_FORECAST})
public void testGetThermalHeadroom_handlerUpdateTemperatures()
throws RemoteException, InterruptedException {
- // test that handler will at least enqueue one message to periodically read temperatures
- // even if there is sample seeded from HAL temperature callback
String temperatureName = "skin1";
Temperature temperature = new Temperature(100, Temperature.TYPE_SKIN, temperatureName,
Temperature.THROTTLING_NONE);
mFakeHal.mCallback.onTemperatureChanged(temperature);
float headroom = mService.mService.getThermalHeadroom(0);
- // the callback temperature 100C (headroom > 1.0f) sample should have been appended by the
- // immediately scheduled fake HAL current temperatures read (mSkin1, mSkin2), and because
+ // callback temperature will not update the samples used for headroom calculation. Since
// there are less samples for prediction, the latest temperature mSkin1 is used to calculate
// headroom (mSkin2 has no threshold), which is 0.6f (28C vs threshold 40C).
assertEquals(0.6f, headroom, 0.01f);
@@ -687,14 +719,8 @@
@Test
@EnableFlags({Flags.FLAG_ALLOW_THERMAL_THRESHOLDS_CALLBACK,
Flags.FLAG_ALLOW_THERMAL_HEADROOM_THRESHOLDS})
- public void testTemperatureWatcherUpdateSevereThresholds() throws Exception {
- assertTrue(mService.mService.registerThermalHeadroomListener(mHeadroomListener));
- verify(mHeadroomListener, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
- .times(1)).onHeadroomChange(eq(0.6f), eq(0.6f), anyInt(),
- aryEq(new float[]{Float.NaN, 0.6666667f, 0.8333333f, 1.0f, 1.1666666f, 1.3333334f,
- 1.5f}));
- resetListenerMock();
- TemperatureWatcher watcher = mService.mTemperatureWatcher;
+ public void testTemperatureWatcherUpdateSevereThresholds_withoutHeadroomListener() {
+ // if no listeners, threshold update should not trigger headroom calculation
TemperatureThreshold newThreshold = new TemperatureThreshold();
newThreshold.name = "skin1";
newThreshold.type = Temperature.TYPE_SKIN;
@@ -702,6 +728,39 @@
newThreshold.hotThrottlingThresholds = new float[]{
Float.NaN, 43.0f, 46.0f, 49.0f, Float.NaN, Float.NaN, Float.NaN
};
+ mFakeHal.mGetCurrentTemperaturesCalled.set(0);
+ mFakeHal.mCallback.onThresholdChanged(newThreshold);
+ assertEquals(0, mFakeHal.mGetCurrentTemperaturesCalled.get());
+ }
+
+ @Test
+ @EnableFlags({Flags.FLAG_ALLOW_THERMAL_THRESHOLDS_CALLBACK,
+ Flags.FLAG_ALLOW_THERMAL_HEADROOM_THRESHOLDS})
+ public void testTemperatureWatcherUpdateSevereThresholds_withHeadroomListener()
+ throws Exception {
+ TemperatureWatcher watcher = mService.mTemperatureWatcher;
+ // Reduce the inactivity threshold to speed up testing
+ watcher.mInactivityThresholdMillis = 500;
+ mFakeHal.mGetCurrentTemperaturesCalled.set(0);
+ assertTrue(mService.mService.registerThermalHeadroomListener(mHeadroomListener));
+ verify(mHeadroomListener, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+ .times(1)).onHeadroomChange(eq(0.6f), eq(0.6f), anyInt(),
+ aryEq(new float[]{Float.NaN, 0.6666667f, 0.8333333f, 1.0f, 1.1666666f, 1.3333334f,
+ 1.5f}));
+ assertEquals(1, mFakeHal.mGetCurrentTemperaturesCalled.get());
+
+ // sleep until the polling from registration is done
+ Thread.sleep(1000);
+
+ resetListenerMock();
+ TemperatureThreshold newThreshold = new TemperatureThreshold();
+ newThreshold.name = "skin1";
+ newThreshold.type = Temperature.TYPE_SKIN;
+ // significant change in threshold (> 0.3C) should trigger a callback
+ newThreshold.hotThrottlingThresholds = new float[]{
+ Float.NaN, 43.0f, 46.0f, 49.0f, Float.NaN, Float.NaN, Float.NaN
+ };
+ mFakeHal.mGetCurrentTemperaturesCalled.set(0);
mFakeHal.mCallback.onThresholdChanged(newThreshold);
synchronized (watcher.mSamples) {
Float threshold = watcher.mSevereThresholds.get("skin1");
@@ -714,10 +773,10 @@
verify(mHeadroomListener, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
.times(1)).onHeadroomChange(eq(0.3f), eq(0.3f), anyInt(),
aryEq(new float[]{Float.NaN, 0.8f, 0.9f, 1.0f, Float.NaN, Float.NaN, Float.NaN}));
+ assertEquals(1, mFakeHal.mGetCurrentTemperaturesCalled.get());
resetListenerMock();
// same or similar threshold callback data within a second should not trigger callback
- mFakeHal.mCallback.onThresholdChanged(newThreshold);
newThreshold.hotThrottlingThresholds = new float[]{
Float.NaN, 43.1f, 45.9f, 49.0f, Float.NaN, Float.NaN, Float.NaN
};
@@ -831,35 +890,6 @@
}
@Test
- public void testTemperatureWatcherGetForecast() throws RemoteException {
- TemperatureWatcher watcher = mService.mTemperatureWatcher;
-
- ArrayList<TemperatureWatcher.Sample> samples = new ArrayList<>();
-
- // Add a single sample
- samples.add(watcher.createSampleForTesting(0, 25.0f));
- watcher.mSamples.put("skin1", samples);
-
- // Because there are not enough samples to compute the linear regression,
- // no matter how far ahead we forecast, we should receive the same value
- assertEquals(0.5f, watcher.getForecast(0), 0.0f);
- assertEquals(0.5f, watcher.getForecast(5), 0.0f);
-
- // Add some time-series data
- for (int i = 1; i < 20; ++i) {
- samples.add(watcher.createSampleForTesting(1000 * i, 25.0f + 0.5f * i));
- }
-
- // Now the forecast should vary depending on how far ahead we are trying to predict
- assertEquals(0.9f, watcher.getForecast(4), 0.02f);
- assertEquals(1.0f, watcher.getForecast(10), 0.02f);
-
- // If there are no thresholds, then we shouldn't receive a headroom value
- watcher.mSevereThresholds.erase();
- assertTrue(Float.isNaN(watcher.getForecast(0)));
- }
-
- @Test
public void testTemperatureWatcherGetForecastUpdate() throws Exception {
TemperatureWatcher watcher = mService.mTemperatureWatcher;
diff --git a/services/tests/servicestests/src/com/android/server/security/authenticationpolicy/AuthenticationPolicyServiceTest.java b/services/tests/servicestests/src/com/android/server/security/authenticationpolicy/AuthenticationPolicyServiceTest.java
index e7763ae..7eafbae 100644
--- a/services/tests/servicestests/src/com/android/server/security/authenticationpolicy/AuthenticationPolicyServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/security/authenticationpolicy/AuthenticationPolicyServiceTest.java
@@ -16,7 +16,6 @@
package com.android.server.security.authenticationpolicy;
-import static android.adaptiveauth.Flags.FLAG_REPORT_BIOMETRIC_AUTH_ATTEMPTS;
import static android.security.authenticationpolicy.AuthenticationPolicyManager.ERROR_UNSUPPORTED;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_ADAPTIVE_AUTH_REQUEST;
@@ -109,8 +108,6 @@
public void setUp() {
MockitoAnnotations.initMocks(this);
- mSetFlagsRule.enableFlags(FLAG_REPORT_BIOMETRIC_AUTH_ATTEMPTS);
-
mContext = spy(ApplicationProvider.getApplicationContext());
assumeTrue("Adaptive auth is disabled on device",
diff --git a/services/tests/servicestests/src/com/android/server/supervision/SupervisionServiceTest.kt b/services/tests/servicestests/src/com/android/server/supervision/SupervisionServiceTest.kt
index 02b9744..d2675ae 100644
--- a/services/tests/servicestests/src/com/android/server/supervision/SupervisionServiceTest.kt
+++ b/services/tests/servicestests/src/com/android/server/supervision/SupervisionServiceTest.kt
@@ -20,6 +20,7 @@
import android.app.KeyguardManager
import android.app.admin.DevicePolicyManager
import android.app.admin.DevicePolicyManagerInternal
+import android.app.supervision.SupervisionRecoveryInfo
import android.app.supervision.flags.Flags
import android.content.BroadcastReceiver
import android.content.ComponentName
@@ -70,8 +71,7 @@
@Mock private lateinit var mockDpmInternal: DevicePolicyManagerInternal
- @Mock
- private lateinit var mockKeyguardManager: KeyguardManager
+ @Mock private lateinit var mockKeyguardManager: KeyguardManager
@Mock private lateinit var mockPackageManager: PackageManager
@Mock private lateinit var mockUserManagerInternal: UserManagerInternal
@@ -265,7 +265,8 @@
whenever(mockUserManagerInternal.getSupervisingProfileId()).thenReturn(SUPERVISING_USER_ID)
whenever(mockKeyguardManager.isDeviceSecure(SUPERVISING_USER_ID)).thenReturn(true)
- val intent = checkNotNull(service.createConfirmSupervisionCredentialsIntent())
+ val intent =
+ checkNotNull(service.createConfirmSupervisionCredentialsIntent(context.getUserId()))
assertThat(intent.action).isEqualTo(ACTION_CONFIRM_SUPERVISION_CREDENTIALS)
assertThat(intent.getPackage()).isEqualTo("com.android.settings")
}
@@ -276,7 +277,7 @@
whenever(mockUserManagerInternal.getSupervisingProfileId()).thenReturn(SUPERVISING_USER_ID)
whenever(mockKeyguardManager.isDeviceSecure(SUPERVISING_USER_ID)).thenReturn(true)
- assertThat(service.createConfirmSupervisionCredentialsIntent()).isNull()
+ assertThat(service.createConfirmSupervisionCredentialsIntent(context.getUserId())).isNull()
}
@Test
@@ -284,7 +285,7 @@
service.mInternal.setSupervisionEnabledForUser(context.getUserId(), true)
whenever(mockUserManagerInternal.getSupervisingProfileId()).thenReturn(UserHandle.USER_NULL)
- assertThat(service.createConfirmSupervisionCredentialsIntent()).isNull()
+ assertThat(service.createConfirmSupervisionCredentialsIntent(context.getUserId())).isNull()
}
@Test
@@ -293,7 +294,7 @@
whenever(mockUserManagerInternal.getSupervisingProfileId()).thenReturn(SUPERVISING_USER_ID)
whenever(mockKeyguardManager.isDeviceSecure(SUPERVISING_USER_ID)).thenReturn(false)
- assertThat(service.createConfirmSupervisionCredentialsIntent()).isNull()
+ assertThat(service.createConfirmSupervisionCredentialsIntent(context.getUserId())).isNull()
}
fun shouldAllowBypassingSupervisionRoleQualification_returnsTrue() {
@@ -326,6 +327,22 @@
assertThat(service.isSupervisionEnabledForUser(USER_ID)).isFalse()
}
+ @Test
+ fun setSupervisionRecoveryInfo() {
+ assertThat(service.supervisionRecoveryInfo).isNull()
+
+ val recoveryInfo =
+ SupervisionRecoveryInfo().apply {
+ email = "test_email"
+ id = "test_id"
+ }
+ service.setSupervisionRecoveryInfo(recoveryInfo)
+
+ assertThat(service.supervisionRecoveryInfo).isNotNull()
+ assertThat(service.supervisionRecoveryInfo.email).isEqualTo(recoveryInfo.email)
+ assertThat(service.supervisionRecoveryInfo.id).isEqualTo(recoveryInfo.id)
+ }
+
private val systemSupervisionPackage: String
get() = context.getResources().getString(R.string.config_systemSupervision)
@@ -348,16 +365,18 @@
}
private fun addDefaultAndTestUsers() {
- val userInfos = userData.map { (userId, flags) ->
- UserInfo(userId, "user" + userId, USER_ICON, flags, USER_TYPE)
- }
+ val userInfos =
+ userData.map { (userId, flags) ->
+ UserInfo(userId, "user" + userId, USER_ICON, flags, USER_TYPE)
+ }
whenever(mockUserManagerInternal.getUsers(any())).thenReturn(userInfos)
}
private fun addDefaultAndFullUsers() {
- val userInfos = userData.map { (userId, flags) ->
- UserInfo(userId, "user" + userId, USER_ICON, flags, USER_TYPE)
- } + UserInfo(USER_ID, "user" + USER_ID, USER_ICON, FLAG_FULL, USER_TYPE)
+ val userInfos =
+ userData.map { (userId, flags) ->
+ UserInfo(userId, "user" + userId, USER_ICON, flags, USER_TYPE)
+ } + UserInfo(USER_ID, "user" + USER_ID, USER_ICON, FLAG_FULL, USER_TYPE)
whenever(mockUserManagerInternal.getUsers(any())).thenReturn(userInfos)
}
@@ -367,11 +386,12 @@
const val SUPERVISING_USER_ID = 10
const val USER_ICON = "user_icon"
const val USER_TYPE = "fake_user_type"
- val userData: Map<Int, Int> = mapOf(
- USER_SYSTEM to FLAG_SYSTEM,
- MIN_SECONDARY_USER_ID to FLAG_MAIN,
- (MIN_SECONDARY_USER_ID + 1) to (FLAG_FULL or FLAG_FOR_TESTING)
- )
+ val userData: Map<Int, Int> =
+ mapOf(
+ USER_SYSTEM to FLAG_SYSTEM,
+ MIN_SECONDARY_USER_ID to FLAG_MAIN,
+ (MIN_SECONDARY_USER_ID + 1) to (FLAG_FULL or FLAG_FOR_TESTING),
+ )
}
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java
index 1114365..db111bf 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java
@@ -128,8 +128,7 @@
@Parameters(name = "{0}")
public static List<FlagsParameterization> getParams() {
- return FlagsParameterization.allCombinationsOf(
- android.app.Flags.FLAG_CHECK_AUTOGROUP_BEFORE_POST);
+ return FlagsParameterization.allCombinationsOf();
}
public GroupHelperTest(FlagsParameterization flags) {
@@ -357,28 +356,6 @@
}
@Test
- @DisableFlags(android.app.Flags.FLAG_CHECK_AUTOGROUP_BEFORE_POST)
- public void testAddSummary_alwaysAutogroup() {
- final String pkg = "package";
- final String autogroupKey = getExpectedAutogroupKey(
- getNotificationRecord(pkg, 0, String.valueOf(0), UserHandle.SYSTEM));
- for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
- mGroupHelper.onNotificationPosted(
- getNotificationRecord(pkg, i, String.valueOf(i), UserHandle.SYSTEM), false);
- }
- verify(mCallback, times(1)).addAutoGroupSummary(
- anyInt(), eq(pkg), anyString(), eq(autogroupKey),
- anyInt(), eq(getNotificationAttributes(BASE_FLAGS)));
- verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString(), eq(autogroupKey),
- anyBoolean());
- verify(mCallback, never()).removeAutoGroup(anyString());
- verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
- verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyString(),
- any());
- }
-
- @Test
- @EnableFlags(android.app.Flags.FLAG_CHECK_AUTOGROUP_BEFORE_POST)
public void testAddSummary() {
final String pkg = "package";
final String autogroupKey = getExpectedAutogroupKey(
@@ -402,32 +379,6 @@
}
@Test
- @DisableFlags(android.app.Flags.FLAG_CHECK_AUTOGROUP_BEFORE_POST)
- public void testAddSummary_oneChildOngoing_summaryOngoing_alwaysAutogroup() {
- final String pkg = "package";
- final String autogroupKey = getExpectedAutogroupKey(
- getNotificationRecord(pkg, 0, String.valueOf(0), UserHandle.SYSTEM));
- for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
- NotificationRecord r = getNotificationRecord(pkg, i, String.valueOf(i),
- UserHandle.SYSTEM);
- if (i == 0) {
- r.getNotification().flags |= FLAG_ONGOING_EVENT;
- }
- mGroupHelper.onNotificationPosted(r, false);
- }
- verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
- eq(autogroupKey), anyInt(),
- eq(getNotificationAttributes(BASE_FLAGS | FLAG_ONGOING_EVENT)));
- verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString(), anyString(),
- anyBoolean());
- verify(mCallback, never()).removeAutoGroup(anyString());
- verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
- verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyString(),
- any());
- }
-
- @Test
- @EnableFlags(android.app.Flags.FLAG_CHECK_AUTOGROUP_BEFORE_POST)
public void testAddSummary_oneChildOngoing_summaryOngoing() {
final String pkg = "package";
final String autogroupKey = getExpectedAutogroupKey(
@@ -452,31 +403,6 @@
}
@Test
- @DisableFlags(android.app.Flags.FLAG_CHECK_AUTOGROUP_BEFORE_POST)
- public void testAddSummary_oneChildAutoCancel_summaryNotAutoCancel_alwaysAutogroup() {
- final String pkg = "package";
- final String autogroupKey = getExpectedAutogroupKey(
- getNotificationRecord(pkg, 0, String.valueOf(0), UserHandle.SYSTEM));
- for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
- NotificationRecord r = getNotificationRecord(pkg, i, String.valueOf(i),
- UserHandle.SYSTEM);
- if (i == 0) {
- r.getNotification().flags |= FLAG_AUTO_CANCEL;
- }
- mGroupHelper.onNotificationPosted(r, false);
- }
- verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
- eq(autogroupKey), anyInt(), eq(getNotificationAttributes(BASE_FLAGS)));
- verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString(), anyString(),
- anyBoolean());
- verify(mCallback, never()).removeAutoGroup(anyString());
- verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
- verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyString(),
- any());
- }
-
- @Test
- @EnableFlags(android.app.Flags.FLAG_CHECK_AUTOGROUP_BEFORE_POST)
public void testAddSummary_oneChildAutoCancel_summaryNotAutoCancel() {
final String pkg = "package";
final String autogroupKey = getExpectedAutogroupKey(
@@ -500,30 +426,6 @@
}
@Test
- @DisableFlags(android.app.Flags.FLAG_CHECK_AUTOGROUP_BEFORE_POST)
- public void testAddSummary_allChildrenAutoCancel_summaryAutoCancel_alwaysAutogroup() {
- final String pkg = "package";
- final String autogroupKey = getExpectedAutogroupKey(
- getNotificationRecord(pkg, 0, String.valueOf(0), UserHandle.SYSTEM));
- for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
- NotificationRecord r = getNotificationRecord(pkg, i, String.valueOf(i),
- UserHandle.SYSTEM);
- r.getNotification().flags |= FLAG_AUTO_CANCEL;
- mGroupHelper.onNotificationPosted(r, false);
- }
- verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
- eq(autogroupKey), anyInt(),
- eq(getNotificationAttributes(BASE_FLAGS | FLAG_AUTO_CANCEL)));
- verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString(), eq(autogroupKey),
- anyBoolean());
- verify(mCallback, never()).removeAutoGroup(anyString());
- verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
- verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyString(),
- any());
- }
-
- @Test
- @EnableFlags(android.app.Flags.FLAG_CHECK_AUTOGROUP_BEFORE_POST)
public void testAddSummary_allChildrenAutoCancel_summaryAutoCancel() {
final String pkg = "package";
final String autogroupKey = getExpectedAutogroupKey(
@@ -546,33 +448,6 @@
}
@Test
- @DisableFlags(android.app.Flags.FLAG_CHECK_AUTOGROUP_BEFORE_POST)
- public void testAddSummary_summaryAutoCancelNoClear_alwaysAutogroup() {
- final String pkg = "package";
- final String autogroupKey = getExpectedAutogroupKey(
- getNotificationRecord(pkg, 0, String.valueOf(0), UserHandle.SYSTEM));
- for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
- NotificationRecord r = getNotificationRecord(pkg, i, String.valueOf(i),
- UserHandle.SYSTEM);
- r.getNotification().flags |= FLAG_AUTO_CANCEL;
- if (i == 0) {
- r.getNotification().flags |= FLAG_NO_CLEAR;
- }
- mGroupHelper.onNotificationPosted(r, false);
- }
- verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
- eq(autogroupKey), anyInt(),
- eq(getNotificationAttributes(BASE_FLAGS | FLAG_AUTO_CANCEL | FLAG_NO_CLEAR)));
- verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString(), eq(autogroupKey),
- anyBoolean());
- verify(mCallback, never()).removeAutoGroup(anyString());
- verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
- verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyString(),
- any());
- }
-
- @Test
- @EnableFlags(android.app.Flags.FLAG_CHECK_AUTOGROUP_BEFORE_POST)
public void testAddSummary_summaryAutoCancelNoClear() {
final String pkg = "package";
final String autogroupKey = getExpectedAutogroupKey(
@@ -949,40 +824,7 @@
}
@Test
- @EnableFlags(FLAG_NOTIFICATION_FORCE_GROUPING)
- @DisableFlags(android.app.Flags.FLAG_CHECK_AUTOGROUP_BEFORE_POST)
- public void testDropToZeroRemoveGroup_disableFlag() {
- final String pkg = "package";
- ArrayList<NotificationRecord> posted = new ArrayList<>();
- for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
- NotificationRecord r = getNotificationRecord(pkg, i, String.valueOf(i),
- UserHandle.SYSTEM);
- posted.add(r);
- mGroupHelper.onNotificationPosted(r, false);
- }
- verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(), anyString(),
- anyInt(), eq(getNotificationAttributes(BASE_FLAGS)));
- verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString(), anyString(),
- anyBoolean());
- verify(mCallback, never()).removeAutoGroup(anyString());
- verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
- Mockito.reset(mCallback);
-
- for (int i = 0; i < AUTOGROUP_AT_COUNT - 1; i++) {
- mGroupHelper.onNotificationRemoved(posted.remove(0), new ArrayList<>(), false);
- }
- verify(mCallback, never()).removeAutoGroup(anyString());
- verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
- Mockito.reset(mCallback);
-
- mGroupHelper.onNotificationRemoved(posted.remove(0), new ArrayList<>(), false);
- verify(mCallback, never()).removeAutoGroup(anyString());
- verify(mCallback, times(1)).removeAutoGroupSummary(anyInt(), anyString(), anyString());
- }
-
- @Test
- @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING,
- android.app.Flags.FLAG_CHECK_AUTOGROUP_BEFORE_POST})
+ @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING})
public void testDropToZeroRemoveGroup() {
final String pkg = "package";
ArrayList<NotificationRecord> posted = new ArrayList<>();
@@ -1013,40 +855,6 @@
}
@Test
- @DisableFlags(android.app.Flags.FLAG_CHECK_AUTOGROUP_BEFORE_POST)
- public void testAppStartsGrouping_disableFlag() {
- final String pkg = "package";
- ArrayList<NotificationRecord> posted = new ArrayList<>();
- for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
- NotificationRecord r = getNotificationRecord(pkg, i, String.valueOf(i),
- UserHandle.SYSTEM);
- posted.add(r);
- mGroupHelper.onNotificationPosted(r, false);
- }
- verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
- anyString(), anyInt(), eq(getNotificationAttributes(BASE_FLAGS)));
- verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString(), anyString(),
- anyBoolean());
- verify(mCallback, never()).removeAutoGroup(anyString());
- verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
- Mockito.reset(mCallback);
-
- for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
- final NotificationRecord r = getNotificationRecord(pkg, i, String.valueOf(i),
- UserHandle.SYSTEM, "app group", false);
- r.getSbn().setOverrideGroupKey("autogrouped");
- mGroupHelper.onNotificationPosted(r, true);
- verify(mCallback, times(1)).removeAutoGroup(r.getKey());
- if (i < AUTOGROUP_AT_COUNT - 1) {
- verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(),
- anyString());
- }
- }
- verify(mCallback, times(1)).removeAutoGroupSummary(anyInt(), anyString(), anyString());
- }
-
- @Test
- @EnableFlags(android.app.Flags.FLAG_CHECK_AUTOGROUP_BEFORE_POST)
public void testAppStartsGrouping() {
final String pkg = "package";
ArrayList<NotificationRecord> posted = new ArrayList<>();
@@ -1079,51 +887,7 @@
}
@Test
- @EnableFlags(FLAG_NOTIFICATION_FORCE_GROUPING)
- @DisableFlags(android.app.Flags.FLAG_CHECK_AUTOGROUP_BEFORE_POST)
- public void testNewNotificationsAddedToAutogroup_ifOriginalNotificationsCanceled_alwaysGroup() {
- final String pkg = "package";
- ArrayList<NotificationRecord> posted = new ArrayList<>();
- for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
- NotificationRecord r = getNotificationRecord(pkg, i, String.valueOf(i),
- UserHandle.SYSTEM);
- posted.add(r);
- mGroupHelper.onNotificationPosted(r, false);
- }
- verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
- anyString(), anyInt(), eq(getNotificationAttributes(BASE_FLAGS)));
- verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString(), anyString(),
- anyBoolean());
- verify(mCallback, never()).removeAutoGroup(anyString());
- verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
- Mockito.reset(mCallback);
-
- for (int i = posted.size() - 2; i >= 0; i--) {
- mGroupHelper.onNotificationRemoved(posted.remove(i), new ArrayList<>(), false);
- }
- verify(mCallback, never()).removeAutoGroup(anyString());
- verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
- Mockito.reset(mCallback);
-
- // Add new notification; it should be autogrouped even though the total count is
- // < AUTOGROUP_AT_COUNT
- final NotificationRecord r = getNotificationRecord(pkg, 5, String.valueOf(5),
- UserHandle.SYSTEM);
- final String autogroupKey = getExpectedAutogroupKey(r);
- posted.add(r);
- assertThat(mGroupHelper.onNotificationPosted(r, true)).isFalse();
- verify(mCallback, times(1)).addAutoGroup(r.getKey(), autogroupKey, true);
- verify(mCallback, never()).removeAutoGroup(anyString());
- verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
- verify(mCallback).updateAutogroupSummary(anyInt(), anyString(), anyString(),
- eq(getNotificationAttributes(BASE_FLAGS)));
- verify(mCallback, never()).addAutoGroupSummary(anyInt(), anyString(), anyString(),
- anyString(), anyInt(), any());
- }
-
- @Test
- @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING,
- android.app.Flags.FLAG_CHECK_AUTOGROUP_BEFORE_POST})
+ @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING})
public void testNewNotificationsAddedToAutogroup_ifOriginalNotificationsCanceled() {
final String pkg = "package";
ArrayList<NotificationRecord> posted = new ArrayList<>();
@@ -1166,45 +930,7 @@
}
@Test
- @DisableFlags(android.app.Flags.FLAG_CHECK_AUTOGROUP_BEFORE_POST)
- @EnableFlags(Flags.FLAG_AUTOGROUP_SUMMARY_ICON_UPDATE)
- public void testAddSummary_sameIcon_sameColor_alwaysAutogroup() {
- final String pkg = "package";
- final Icon icon = mock(Icon.class);
- when(icon.sameAs(icon)).thenReturn(true);
- final int iconColor = Color.BLUE;
- final NotificationAttributes attr = new NotificationAttributes(BASE_FLAGS, icon, iconColor,
- DEFAULT_VISIBILITY, DEFAULT_GROUP_ALERT, TEST_CHANNEL_ID);
-
- // Add notifications with same icon and color
- for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
- NotificationRecord r = getNotificationRecord(
- getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM, null, icon, iconColor));
- mGroupHelper.onNotificationPosted(r, false);
- }
- // Check that the summary would have the same icon and color
- verify(mCallback, times(1)).addAutoGroupSummary(
- anyInt(), eq(pkg), anyString(), anyString(), anyInt(), eq(attr));
- verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString(), anyString(),
- anyBoolean());
- verify(mCallback, never()).removeAutoGroup(anyString());
- verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
-
- // After auto-grouping, add new notification with the same color
- NotificationRecord r = getNotificationRecord(
- getSbn(pkg, AUTOGROUP_AT_COUNT, String.valueOf(AUTOGROUP_AT_COUNT),
- UserHandle.SYSTEM,null, icon, iconColor));
- mGroupHelper.onNotificationPosted(r, true);
-
- // Check that the summary was updated
- //NotificationAttributes newAttr = new NotificationAttributes(BASE_FLAGS, icon, iconColor);
- verify(mCallback, times(1)).updateAutogroupSummary(anyInt(), anyString(), anyString(),
- eq(attr));
- }
-
- @Test
- @EnableFlags({Flags.FLAG_AUTOGROUP_SUMMARY_ICON_UPDATE,
- android.app.Flags.FLAG_CHECK_AUTOGROUP_BEFORE_POST})
+ @EnableFlags({Flags.FLAG_AUTOGROUP_SUMMARY_ICON_UPDATE})
public void testAddSummary_sameIcon_sameColor() {
final String pkg = "package";
final Icon icon = mock(Icon.class);
@@ -1239,57 +965,7 @@
}
@Test
- @DisableFlags(android.app.Flags.FLAG_CHECK_AUTOGROUP_BEFORE_POST)
- @EnableFlags(Flags.FLAG_AUTOGROUP_SUMMARY_ICON_UPDATE)
- public void testAddSummary_diffIcon_diffColor_disableFlag() {
- final String pkg = "package";
- final Icon initialIcon = mock(Icon.class);
- when(initialIcon.sameAs(initialIcon)).thenReturn(true);
- final int initialIconColor = Color.BLUE;
-
- // Spy GroupHelper for getMonochromeAppIcon
- final Icon monochromeIcon = mock(Icon.class);
- when(monochromeIcon.sameAs(monochromeIcon)).thenReturn(true);
- GroupHelper groupHelper = spy(mGroupHelper);
- doReturn(monochromeIcon).when(groupHelper).getMonochromeAppIcon(eq(pkg));
-
- final NotificationAttributes initialAttr = new NotificationAttributes(BASE_FLAGS,
- initialIcon, initialIconColor, DEFAULT_VISIBILITY, DEFAULT_GROUP_ALERT,
- TEST_CHANNEL_ID);
-
- // Add notifications with same icon and color
- for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
- NotificationRecord r = getNotificationRecord(
- getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM, null,
- initialIcon, initialIconColor));
- groupHelper.onNotificationPosted(r, false);
- }
- // Check that the summary would have the same icon and color
- verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
- anyString(), anyInt(), eq(initialAttr));
- verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString(), anyString(),
- anyBoolean());
- verify(mCallback, never()).removeAutoGroup(anyString());
- verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
-
- // After auto-grouping, add new notification with a different color
- final Icon newIcon = mock(Icon.class);
- final int newIconColor = Color.YELLOW;
- NotificationRecord r = getNotificationRecord(getSbn(pkg, AUTOGROUP_AT_COUNT,
- String.valueOf(AUTOGROUP_AT_COUNT), UserHandle.SYSTEM, null, newIcon,
- newIconColor));
- groupHelper.onNotificationPosted(r, true);
-
- // Summary should be updated to the default color and the icon to the monochrome icon
- NotificationAttributes newAttr = new NotificationAttributes(BASE_FLAGS, monochromeIcon,
- COLOR_DEFAULT, DEFAULT_VISIBILITY, DEFAULT_GROUP_ALERT, TEST_CHANNEL_ID);
- verify(mCallback, times(1)).updateAutogroupSummary(anyInt(), anyString(), anyString(),
- eq(newAttr));
- }
-
- @Test
- @EnableFlags({Flags.FLAG_AUTOGROUP_SUMMARY_ICON_UPDATE,
- android.app.Flags.FLAG_CHECK_AUTOGROUP_BEFORE_POST})
+ @EnableFlags({Flags.FLAG_AUTOGROUP_SUMMARY_ICON_UPDATE})
public void testAddSummary_diffIcon_diffColor() {
final String pkg = "package";
final Icon initialIcon = mock(Icon.class);
@@ -1337,48 +1013,7 @@
}
@Test
- @DisableFlags(android.app.Flags.FLAG_CHECK_AUTOGROUP_BEFORE_POST)
- @EnableFlags(Flags.FLAG_AUTOGROUP_SUMMARY_ICON_UPDATE)
- public void testAddSummary_diffVisibility_alwaysAutogroup() {
- final String pkg = "package";
- final Icon icon = mock(Icon.class);
- when(icon.sameAs(icon)).thenReturn(true);
- final int iconColor = Color.BLUE;
- final NotificationAttributes attr = new NotificationAttributes(BASE_FLAGS, icon, iconColor,
- VISIBILITY_PRIVATE, DEFAULT_GROUP_ALERT, TEST_CHANNEL_ID);
-
- // Add notifications with same icon and color and default visibility (private)
- for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
- NotificationRecord r = getNotificationRecord(
- getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM, null,
- icon, iconColor));
- mGroupHelper.onNotificationPosted(r, false);
- }
- // Check that the summary has private visibility
- verify(mCallback, times(1)).addAutoGroupSummary(
- anyInt(), eq(pkg), anyString(), anyString(), anyInt(), eq(attr));
-
- verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString(), anyString(),
- anyBoolean());
- verify(mCallback, never()).removeAutoGroup(anyString());
- verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
-
- // After auto-grouping, add new notification with public visibility
- NotificationRecord r = getNotificationRecord(getSbn(pkg, AUTOGROUP_AT_COUNT,
- String.valueOf(AUTOGROUP_AT_COUNT), UserHandle.SYSTEM, null, icon, iconColor));
- r.getNotification().visibility = VISIBILITY_PUBLIC;
- mGroupHelper.onNotificationPosted(r, true);
-
- // Check that the summary visibility was updated
- NotificationAttributes newAttr = new NotificationAttributes(BASE_FLAGS, icon, iconColor,
- VISIBILITY_PUBLIC, DEFAULT_GROUP_ALERT, TEST_CHANNEL_ID);
- verify(mCallback, times(1)).updateAutogroupSummary(anyInt(), anyString(), anyString(),
- eq(newAttr));
- }
-
- @Test
- @EnableFlags({Flags.FLAG_AUTOGROUP_SUMMARY_ICON_UPDATE,
- android.app.Flags.FLAG_CHECK_AUTOGROUP_BEFORE_POST})
+ @EnableFlags({Flags.FLAG_AUTOGROUP_SUMMARY_ICON_UPDATE})
public void testAddSummary_diffVisibility() {
final String pkg = "package";
final Icon icon = mock(Icon.class);
@@ -1422,44 +1057,6 @@
}
@Test
- @EnableFlags(Flags.FLAG_AUTOGROUP_SUMMARY_ICON_UPDATE)
- @DisableFlags(FLAG_NOTIFICATION_FORCE_GROUPING)
- public void testAutoGrouped_diffIcon_diffColor_removeChild_updateTo_sameIcon_sameColor() {
- final String pkg = "package";
- final Icon initialIcon = mock(Icon.class);
- when(initialIcon.sameAs(initialIcon)).thenReturn(true);
- final int initialIconColor = Color.BLUE;
- final NotificationAttributes initialAttr = new NotificationAttributes(
- GroupHelper.FLAG_INVALID, initialIcon, initialIconColor, DEFAULT_VISIBILITY,
- DEFAULT_GROUP_ALERT, TEST_CHANNEL_ID);
-
- // Add AUTOGROUP_AT_COUNT-1 notifications with same icon and color
- ArrayList<NotificationRecord> notifications = new ArrayList<>();
- for (int i = 0; i < AUTOGROUP_AT_COUNT - 1; i++) {
- NotificationRecord r = getNotificationRecord(
- getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM, null,
- initialIcon, initialIconColor));
- notifications.add(r);
- }
- // And an additional notification with different icon and color
- final int lastIdx = AUTOGROUP_AT_COUNT - 1;
- NotificationRecord newRec = getNotificationRecord(getSbn(pkg, lastIdx,
- String.valueOf(lastIdx), UserHandle.SYSTEM, null, mock(Icon.class),
- Color.YELLOW));
- notifications.add(newRec);
- for (NotificationRecord r: notifications) {
- mGroupHelper.onNotificationPosted(r, false);
- }
-
- // Remove last notification (the only one with different icon and color)
- mGroupHelper.onNotificationRemoved(notifications.get(lastIdx));
-
- // Summary should be updated to the common icon and color
- verify(mCallback, times(1)).updateAutogroupSummary(anyInt(), anyString(), anyString(),
- eq(initialAttr));
- }
-
- @Test
@EnableFlags({Flags.FLAG_AUTOGROUP_SUMMARY_ICON_UPDATE,
FLAG_NOTIFICATION_FORCE_GROUPING})
public void testAutoGrouped_diffIcon_diffColor_removeChild_updateTo_sameIcon_sameColor_forceGrouping() {
@@ -1968,97 +1565,8 @@
verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
}
-
@Test
- @EnableFlags(FLAG_NOTIFICATION_FORCE_GROUPING)
- @DisableFlags(android.app.Flags.FLAG_CHECK_AUTOGROUP_BEFORE_POST)
- public void testAddAggregateSummary_mixUngroupedAndAbusive_alwaysAutogroup() {
- final String pkg = "package";
- final String expectedGroupKey = GroupHelper.getFullAggregateGroupKey(pkg,
- AGGREGATE_GROUP_KEY + "AlertingSection", UserHandle.SYSTEM.getIdentifier());
- // Post ungrouped notifications => create autogroup
- for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
- mGroupHelper.onNotificationPosted(
- getNotificationRecord(pkg, i, String.valueOf(i), UserHandle.SYSTEM), false);
- }
- verify(mCallback, times(1)).addAutoGroupSummary(
- anyInt(), eq(pkg), anyString(), eq(expectedGroupKey),
- anyInt(), eq(getNotificationAttributes(BASE_FLAGS)));
- verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString(), eq(expectedGroupKey),
- anyBoolean());
- verify(mCallback, never()).removeAutoGroup(anyString());
- verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
- verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyString(),
- any());
-
- reset(mCallback);
-
- // Post group notifications without summaries => add to autogroup
- final List<NotificationRecord> notificationList = new ArrayList<>();
- final ArrayMap<String, NotificationRecord> summaryByGroup = new ArrayMap<>();
- final int id = AUTOGROUP_AT_COUNT;
- NotificationRecord r = getNotificationRecord(pkg, id, String.valueOf(id),
- UserHandle.SYSTEM, "testGrp " + id, false);
- notificationList.add(r);
- mGroupHelper.onNotificationPostedWithDelay(r, notificationList, summaryByGroup);
-
- // Check that the new notification was added
- verify(mCallback, times(1)).addAutoGroup(eq(r.getKey()),
- eq(expectedGroupKey), eq(true));
- verify(mCallback, times(1)).updateAutogroupSummary(anyInt(), eq(pkg),
- eq(expectedGroupKey), any());
- verify(mCallback, never()).addAutoGroupSummary(anyInt(), anyString(), anyString(),
- anyString(), anyInt(), any());
- verify(mCallback, never()).removeAutoGroup(anyString());
- verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
- }
-
- @Test
- @EnableFlags(FLAG_NOTIFICATION_FORCE_GROUPING)
- @DisableFlags(android.app.Flags.FLAG_CHECK_AUTOGROUP_BEFORE_POST)
- public void testUpdateAggregateSummary_postUngroupedAfterForcedGrouping_alwaysAutogroup() {
- final String pkg = "package";
- final String expectedGroupKey = GroupHelper.getFullAggregateGroupKey(pkg,
- AGGREGATE_GROUP_KEY + "AlertingSection", UserHandle.SYSTEM.getIdentifier());
- final List<NotificationRecord> notificationList = new ArrayList<>();
- final ArrayMap<String, NotificationRecord> summaryByGroup = new ArrayMap<>();
- // Post group notifications without summaries => force autogroup
- for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
- NotificationRecord r = getNotificationRecord(pkg, i, String.valueOf(i),
- UserHandle.SYSTEM, "testGrp " + i, false);
- notificationList.add(r);
- mGroupHelper.onNotificationPostedWithDelay(r, notificationList, summaryByGroup);
- }
- verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
- eq(expectedGroupKey), anyInt(), eq(getNotificationAttributes(BASE_FLAGS)));
- verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString(),
- eq(expectedGroupKey), eq(true));
- verify(mCallback, never()).removeAutoGroup(anyString());
- verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
- verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyString(),
- any());
-
- reset(mCallback);
-
- // Post ungrouped notification => update autogroup
- final int id = AUTOGROUP_AT_COUNT;
- NotificationRecord r = getNotificationRecord(pkg, id, String.valueOf(id),
- UserHandle.SYSTEM);
- mGroupHelper.onNotificationPosted(r, true);
-
- verify(mCallback, times(1)).addAutoGroup(eq(r.getKey()),
- eq(expectedGroupKey), eq(true));
- verify(mCallback, times(1)).updateAutogroupSummary(anyInt(), eq(pkg),
- eq(expectedGroupKey), eq(getNotificationAttributes(BASE_FLAGS)));
- verify(mCallback, never()).addAutoGroupSummary(anyInt(), anyString(), anyString(),
- anyString(), anyInt(), any());
- verify(mCallback, never()).removeAutoGroup(anyString());
- verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
- }
-
- @Test
- @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING,
- android.app.Flags.FLAG_CHECK_AUTOGROUP_BEFORE_POST})
+ @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING})
public void testUpdateAggregateSummary_postUngroupedAfterForcedGrouping() {
final String pkg = "package";
final String expectedGroupKey = GroupHelper.getFullAggregateGroupKey(pkg,
@@ -2383,8 +1891,7 @@
}
@Test
- @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING, FLAG_NOTIFICATION_FORCE_GROUP_SINGLETONS,
- android.app.Flags.FLAG_CHECK_AUTOGROUP_BEFORE_POST})
+ @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING, FLAG_NOTIFICATION_FORCE_GROUP_SINGLETONS})
public void testUpdateToUngroupableSection_afterAutogroup_isUngrouped() {
final String pkg = "package";
final List<NotificationRecord> notificationList = new ArrayList<>();
@@ -2427,8 +1934,7 @@
}
@Test
- @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING, FLAG_NOTIFICATION_FORCE_GROUP_SINGLETONS,
- android.app.Flags.FLAG_CHECK_AUTOGROUP_BEFORE_POST})
+ @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING, FLAG_NOTIFICATION_FORCE_GROUP_SINGLETONS})
public void testUpdateToUngroupableSection_onRemoved_isUngrouped() {
final String pkg = "package";
final List<NotificationRecord> notificationList = new ArrayList<>();
@@ -2518,8 +2024,7 @@
}
@Test
- @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING, FLAG_NOTIFICATION_FORCE_GROUP_SINGLETONS,
- android.app.Flags.FLAG_CHECK_AUTOGROUP_BEFORE_POST})
+ @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING, FLAG_NOTIFICATION_FORCE_GROUP_SINGLETONS})
public void testRepostWithNewChannel_afterAutogrouping_isRegrouped() {
final String pkg = "package";
final List<NotificationRecord> notificationList = new ArrayList<>();
@@ -2579,8 +2084,7 @@
}
@Test
- @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING, FLAG_NOTIFICATION_FORCE_GROUP_SINGLETONS,
- android.app.Flags.FLAG_CHECK_AUTOGROUP_BEFORE_POST})
+ @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING, FLAG_NOTIFICATION_FORCE_GROUP_SINGLETONS})
public void testRepostWithNewChannel_afterForceGrouping_isRegrouped() {
final String pkg = "package";
final String groupName = "testGroup";
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelExtractorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelExtractorTest.java
index 8de2b9c..f76e4e5 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelExtractorTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelExtractorTest.java
@@ -151,7 +151,6 @@
}
@Test
- @EnableFlags(Flags.FLAG_RESTRICT_AUDIO_ATTRIBUTES_CALL)
public void testAudioAttributes_callStyleCanUseCallUsage() throws RemoteException {
NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_HIGH);
channel.setSound(Uri.EMPTY, new AudioAttributes.Builder()
@@ -175,7 +174,6 @@
}
@Test
- @EnableFlags(Flags.FLAG_RESTRICT_AUDIO_ATTRIBUTES_CALL)
public void testAudioAttributes_nonCallStyleCannotUseCallUsage() throws RemoteException {
NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_HIGH);
channel.setSound(Uri.EMPTY, new AudioAttributes.Builder()
@@ -196,7 +194,6 @@
}
@Test
- @EnableFlags(Flags.FLAG_RESTRICT_AUDIO_ATTRIBUTES_ALARM)
public void testAudioAttributes_alarmCategoryCanUseAlarmUsage() throws RemoteException {
NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_HIGH);
channel.setSound(Uri.EMPTY, new AudioAttributes.Builder()
@@ -216,7 +213,6 @@
}
@Test
- @EnableFlags(Flags.FLAG_RESTRICT_AUDIO_ATTRIBUTES_ALARM)
public void testAudioAttributes_nonAlarmCategoryCannotUseAlarmUsage() throws RemoteException {
NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_HIGH);
channel.setSound(Uri.EMPTY, new AudioAttributes.Builder()
@@ -237,7 +233,6 @@
}
@Test
- @EnableFlags(Flags.FLAG_RESTRICT_AUDIO_ATTRIBUTES_MEDIA)
public void testAudioAttributes_noMediaUsage() throws RemoteException {
NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_HIGH);
channel.setSound(Uri.EMPTY, new AudioAttributes.Builder()
@@ -258,7 +253,6 @@
}
@Test
- @EnableFlags(Flags.FLAG_RESTRICT_AUDIO_ATTRIBUTES_MEDIA)
public void testAudioAttributes_noUnknownUsage() throws RemoteException {
NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_HIGH);
channel.setSound(Uri.EMPTY, new AudioAttributes.Builder()
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 3e43501..93ad84f 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -24,7 +24,6 @@
import static android.app.ActivityManagerInternal.ServiceNotificationPolicy.NOT_FOREGROUND_SERVICE;
import static android.app.ActivityManagerInternal.ServiceNotificationPolicy.SHOW_IMMEDIATELY;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
-import static android.app.Flags.FLAG_KEYGUARD_PRIVATE_NOTIFICATIONS;
import static android.app.Flags.FLAG_NM_SUMMARIZATION;
import static android.app.Flags.FLAG_SORT_SECTION_BY_TIME;
import static android.app.Notification.EXTRA_ALLOW_DURING_SETUP;
@@ -142,10 +141,8 @@
import static com.android.server.am.PendingIntentRecord.FLAG_ACTIVITY_SENDER;
import static com.android.server.am.PendingIntentRecord.FLAG_BROADCAST_SENDER;
import static com.android.server.am.PendingIntentRecord.FLAG_SERVICE_SENDER;
-import static com.android.server.notification.Flags.FLAG_ALL_NOTIFS_NEED_TTL;
import static com.android.server.notification.Flags.FLAG_LOG_CACHED_POSTS;
import static com.android.server.notification.Flags.FLAG_MANAGED_SERVICES_CONCURRENT_MULTIUSER;
-import static com.android.server.notification.Flags.FLAG_REJECT_OLD_NOTIFICATIONS;
import static com.android.server.notification.GroupHelper.AUTOGROUP_KEY;
import static com.android.server.notification.NotificationManagerService.BITMAP_DURATION;
import static com.android.server.notification.NotificationManagerService.DEFAULT_MAX_NOTIFICATION_ENQUEUE_RATE;
@@ -896,10 +893,6 @@
}
assertNotNull("package intent receiver should exist", mPackageIntentReceiver);
assertNotNull("User receiver should exist", mUserIntentReceiver);
- if (!Flags.allNotifsNeedTtl()) {
- assertNotNull("Notification timeout receiver should exist",
- mNotificationTimeoutReceiver);
- }
// Pretend the shortcut exists
List<ShortcutInfo> shortcutInfos = new ArrayList<>();
@@ -1498,32 +1491,6 @@
}
@Test
- @DisableFlags(FLAG_ALL_NOTIFS_NEED_TTL)
- public void testLimitTimeOutBroadcast() {
- NotificationChannel channel = new NotificationChannel("id", "name",
- NotificationManager.IMPORTANCE_HIGH);
- Notification.Builder nb = new Notification.Builder(mContext, channel.getId())
- .setContentTitle("foo")
- .setSmallIcon(android.R.drawable.sym_def_app_icon)
- .setTimeoutAfter(1);
-
- StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, 8, "tag", mUid, 0,
- nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
- NotificationRecord r = new NotificationRecord(mContext, sbn, channel);
-
- mService.scheduleTimeoutLocked(r);
- ArgumentCaptor<PendingIntent> captor = ArgumentCaptor.forClass(PendingIntent.class);
- verify(mAlarmManager).setExactAndAllowWhileIdle(anyInt(), anyLong(), captor.capture());
- assertEquals(PackageManagerService.PLATFORM_PACKAGE_NAME,
- captor.getValue().getIntent().getPackage());
-
- mService.cancelScheduledTimeoutLocked(r);
- verify(mAlarmManager).cancel(captor.capture());
- assertEquals(PackageManagerService.PLATFORM_PACKAGE_NAME,
- captor.getValue().getIntent().getPackage());
- }
-
- @Test
public void testDefaultAssistant_overrideDefault() {
final int userId = mContext.getUserId();
final String testComponent = "package/class";
@@ -2931,8 +2898,7 @@
}
@Test
- @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING,
- android.app.Flags.FLAG_CHECK_AUTOGROUP_BEFORE_POST})
+ @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING})
public void testOnlyForceGroupIfNeeded_newNotification_notAutogrouped() {
NotificationRecord r = generateNotificationRecord(mTestNotificationChannel, 0, null, false);
when(mGroupHelper.onNotificationPosted(any(), anyBoolean())).thenReturn(false);
@@ -2951,8 +2917,7 @@
}
@Test
- @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING,
- android.app.Flags.FLAG_CHECK_AUTOGROUP_BEFORE_POST})
+ @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING})
public void testOnlyForceGroupIfNeeded_newNotification_wasAutogrouped() {
NotificationRecord r = generateNotificationRecord(mTestNotificationChannel, 0, null, false);
when(mGroupHelper.onNotificationPosted(any(), anyBoolean())).thenReturn(true);
@@ -2971,8 +2936,7 @@
}
@Test
- @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING,
- android.app.Flags.FLAG_CHECK_AUTOGROUP_BEFORE_POST})
+ @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING})
public void testRemoveScheduledForceGroup_onNotificationCanceled() throws Exception {
NotificationRecord r = generateNotificationRecord(mTestNotificationChannel, 0, "tag", null,
false);
@@ -3134,8 +3098,7 @@
}
@Test
- @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING,
- android.app.Flags.FLAG_CHECK_AUTOGROUP_BEFORE_POST})
+ @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING})
public void testScheduleGroupHelperWithDelay_onChildNotificationCanceled() throws Exception {
// Post summary + 2 child notification
final String originalGroupName = "originalGroup";
@@ -3174,8 +3137,7 @@
}
@Test
- @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING,
- android.app.Flags.FLAG_CHECK_AUTOGROUP_BEFORE_POST})
+ @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING})
public void testCleanupScheduleGroupHelperWithDelay_onAllNotificationCanceled()
throws Exception {
// Post summary + 2 child notification
@@ -6754,7 +6716,6 @@
}
@Test
- @EnableFlags(android.app.Flags.FLAG_CHECK_AUTOGROUP_BEFORE_POST)
public void testAutogroupSuppressSort_noSort() throws Exception {
final NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
mService.addNotification(r);
@@ -6764,7 +6725,6 @@
}
@Test
- @EnableFlags(android.app.Flags.FLAG_CHECK_AUTOGROUP_BEFORE_POST)
public void testAutogroupOnPost_skipManualSort() throws Exception {
final NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
mService.addNotification(r);
@@ -7019,15 +6979,7 @@
}
private void simulateNotificationTimeout(String notificationKey) {
- if (Flags.allNotifsNeedTtl()) {
- mService.mNotificationManagerPrivate.timeoutNotification(notificationKey);
- } else {
- final Bundle extras = new Bundle();
- extras.putString(EXTRA_KEY, notificationKey);
- final Intent intent = new Intent(ACTION_NOTIFICATION_TIMEOUT);
- intent.putExtras(extras);
- mNotificationTimeoutReceiver.onReceive(getContext(), intent);
- }
+ mService.mNotificationManagerPrivate.timeoutNotification(notificationKey);
}
@Test
@@ -17222,7 +17174,6 @@
}
@Test
- @EnableFlags(FLAG_KEYGUARD_PRIVATE_NOTIFICATIONS)
public void testSetPrivateNotificationsAllowed() throws Exception {
when(mContext.checkCallingPermission(CONTROL_KEYGUARD_SECURE_NOTIFICATIONS))
.thenReturn(PERMISSION_GRANTED);
@@ -17434,7 +17385,6 @@
}
@Test
- @EnableFlags(android.app.Flags.FLAG_RESTRICT_AUDIO_ATTRIBUTES_MEDIA)
public void testRestrictAudioAttributes_listenersGetCorrectAttributes() throws Exception {
NotificationChannel sound = new NotificationChannel("a", "a", IMPORTANCE_DEFAULT);
sound.setSound(Uri.EMPTY, new AudioAttributes.Builder().setUsage(USAGE_MEDIA).build());
@@ -17461,7 +17411,6 @@
}
@Test
- @EnableFlags(FLAG_ALL_NOTIFS_NEED_TTL)
public void testFixNotification_missingTtl() throws Exception {
Notification n = new Notification.Builder(mContext, mTestNotificationChannel.getId())
.setSmallIcon(android.R.drawable.sym_def_app_icon)
@@ -17473,7 +17422,6 @@
}
@Test
- @EnableFlags(FLAG_ALL_NOTIFS_NEED_TTL)
public void testFixNotification_doesNotOverwriteTtl() throws Exception {
Notification n = new Notification.Builder(mContext, mTestNotificationChannel.getId())
.setSmallIcon(android.R.drawable.sym_def_app_icon)
@@ -17486,7 +17434,6 @@
}
@Test
- @EnableFlags(FLAG_REJECT_OLD_NOTIFICATIONS)
public void testRejectOldNotification_oldWhen() throws Exception {
Notification n = new Notification.Builder(mContext, mTestNotificationChannel.getId())
.setSmallIcon(android.R.drawable.sym_def_app_icon)
@@ -17501,7 +17448,6 @@
}
@Test
- @EnableFlags(FLAG_REJECT_OLD_NOTIFICATIONS)
public void testRejectOldNotification_mediumOldWhen() throws Exception {
Notification n = new Notification.Builder(mContext, mTestNotificationChannel.getId())
.setSmallIcon(android.R.drawable.sym_def_app_icon)
@@ -17516,7 +17462,6 @@
}
@Test
- @EnableFlags(FLAG_REJECT_OLD_NOTIFICATIONS)
public void testRejectOldNotification_zeroWhen() throws Exception {
Notification n = new Notification.Builder(mContext, mTestNotificationChannel.getId())
.setSmallIcon(android.R.drawable.sym_def_app_icon)
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
index e9cf036..2e5d7cb 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
@@ -1681,7 +1681,6 @@
}
@Test
- @EnableFlags(Flags.FLAG_RESTRICT_AUDIO_ATTRIBUTES_ALARM)
public void updateChannel_nullAudioAttributes() {
StatusBarNotification sbn = getStyledNotification(true, true, true,
new Notification.DecoratedCustomViewStyle());
@@ -1693,7 +1692,6 @@
}
@Test
- @EnableFlags(Flags.FLAG_RESTRICT_AUDIO_ATTRIBUTES_ALARM)
public void updateChannel_nonNullAudioAttributes() {
StatusBarNotification sbn = getStyledNotification(true, true, true,
new Notification.DecoratedCustomViewStyle());
diff --git a/services/tests/wmtests/src/com/android/server/policy/CombinationKeyTests.java b/services/tests/wmtests/src/com/android/server/policy/CombinationKeyTests.java
deleted file mode 100644
index e473a06..0000000
--- a/services/tests/wmtests/src/com/android/server/policy/CombinationKeyTests.java
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.server.policy;
-
-import static android.view.KeyEvent.KEYCODE_POWER;
-import static android.view.KeyEvent.KEYCODE_VOLUME_DOWN;
-import static android.view.KeyEvent.KEYCODE_VOLUME_UP;
-
-import static com.android.server.policy.PhoneWindowManager.POWER_VOLUME_UP_BEHAVIOR_GLOBAL_ACTIONS;
-import static com.android.server.policy.PhoneWindowManager.POWER_VOLUME_UP_BEHAVIOR_MUTE;
-
-import android.platform.test.annotations.DisableFlags;
-import android.platform.test.annotations.Presubmit;
-import android.view.ViewConfiguration;
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.MediumTest;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * Test class for combination key shortcuts.
- *
- * Build/Install/Run:
- * atest WmTests:CombinationKeyTests
- */
-@Presubmit
-@MediumTest
-@RunWith(AndroidJUnit4.class)
-@DisableFlags(com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER_MULTI_KEY_GESTURES)
-public class CombinationKeyTests extends ShortcutKeyTestBase {
- private static final long A11Y_KEY_HOLD_MILLIS = 3500;
-
- @Before
- public void setUp() {
- setUpPhoneWindowManager();
- mPhoneWindowManager.overrideStatusBarManagerInternal();
- }
-
- /**
- * Power-VolDown to take screenshot.
- */
- @Test
- public void testPowerVolumeDown() {
- sendKeyCombination(new int[]{KEYCODE_POWER, KEYCODE_VOLUME_DOWN},
- ViewConfiguration.get(mContext).getScreenshotChordKeyTimeout());
- mPhoneWindowManager.assertTakeScreenshotCalled();
- }
-
- /**
- * Power-VolUp to show global actions or mute audio. (Phone default behavior)
- */
- @Test
- public void testPowerVolumeUp() {
- // Show global actions.
- mPhoneWindowManager.overridePowerVolumeUp(POWER_VOLUME_UP_BEHAVIOR_GLOBAL_ACTIONS);
- sendKeyCombination(new int[]{KEYCODE_POWER, KEYCODE_VOLUME_UP}, 0);
- mPhoneWindowManager.assertShowGlobalActionsCalled();
-
- // Mute audio (hold over 100ms).
- mPhoneWindowManager.overridePowerVolumeUp(POWER_VOLUME_UP_BEHAVIOR_MUTE);
- sendKeyCombination(new int[]{KEYCODE_POWER, KEYCODE_VOLUME_UP}, 100);
- mPhoneWindowManager.assertVolumeMute();
- }
-
- /**
- * VolDown-VolUp and hold 3 secs to enable accessibility service.
- */
- @Test
- public void testVolumeDownVolumeUp() {
- sendKeyCombination(new int[]{KEYCODE_VOLUME_DOWN, KEYCODE_VOLUME_UP}, A11Y_KEY_HOLD_MILLIS);
- mPhoneWindowManager.assertAccessibilityKeychordCalled();
- }
-}
diff --git a/services/tests/wmtests/src/com/android/server/policy/KeyGestureEventTests.java b/services/tests/wmtests/src/com/android/server/policy/KeyGestureEventTests.java
index 0a6c57e..a63fe7a 100644
--- a/services/tests/wmtests/src/com/android/server/policy/KeyGestureEventTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/KeyGestureEventTests.java
@@ -28,8 +28,6 @@
import static com.android.server.policy.PhoneWindowManager.LONG_PRESS_HOME_ALL_APPS;
import static com.android.server.policy.PhoneWindowManager.LONG_PRESS_HOME_ASSIST;
import static com.android.server.policy.PhoneWindowManager.LONG_PRESS_HOME_NOTIFICATION_PANEL;
-import static com.android.server.policy.PhoneWindowManager.POWER_VOLUME_UP_BEHAVIOR_GLOBAL_ACTIONS;
-import static com.android.server.policy.PhoneWindowManager.POWER_VOLUME_UP_BEHAVIOR_MUTE;
import android.app.role.RoleManager;
import android.content.ComponentName;
@@ -37,6 +35,8 @@
import android.hardware.input.AppLaunchData;
import android.hardware.input.KeyGestureEvent;
import android.os.RemoteException;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
import android.provider.Settings;
import android.util.SparseArray;
@@ -272,12 +272,15 @@
}
@Test
- public void testKeyGestureBack() {
- mPhoneWindowManager.overrideDelegateBackGestureRemote(true);
+ @EnableFlags(com.android.window.flags.Flags.FLAG_DELEGATE_BACK_GESTURE_TO_SHELL)
+ public void testKeyGestureBack_notHandled() {
sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_BACK);
- mPhoneWindowManager.assertBackEventInjected();
+ mPhoneWindowManager.assertBackEventNotInjected();
+ }
- mPhoneWindowManager.overrideDelegateBackGestureRemote(false);
+ @Test
+ @DisableFlags(com.android.window.flags.Flags.FLAG_DELEGATE_BACK_GESTURE_TO_SHELL)
+ public void testKeyGestureBackHandled() {
sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_BACK);
mPhoneWindowManager.assertBackEventInjected();
}
@@ -362,7 +365,7 @@
@Test
public void testKeyGestureRingerToggleChord() {
- mPhoneWindowManager.overridePowerVolumeUp(POWER_VOLUME_UP_BEHAVIOR_MUTE);
+ mPhoneWindowManager.overrideVolumeHushMode();
sendKeyGestureEventStart(KeyGestureEvent.KEY_GESTURE_TYPE_RINGER_TOGGLE_CHORD);
mPhoneWindowManager.moveTimeForward(500);
sendKeyGestureEventCancel(KeyGestureEvent.KEY_GESTURE_TYPE_RINGER_TOGGLE_CHORD);
@@ -371,7 +374,7 @@
@Test
public void testKeyGestureRingerToggleChordCancelled() {
- mPhoneWindowManager.overridePowerVolumeUp(POWER_VOLUME_UP_BEHAVIOR_MUTE);
+ mPhoneWindowManager.overrideVolumeHushMode();
sendKeyGestureEventStart(KeyGestureEvent.KEY_GESTURE_TYPE_RINGER_TOGGLE_CHORD);
sendKeyGestureEventCancel(KeyGestureEvent.KEY_GESTURE_TYPE_RINGER_TOGGLE_CHORD);
mPhoneWindowManager.assertVolumeNotMuted();
@@ -379,7 +382,6 @@
@Test
public void testKeyGestureGlobalAction() {
- mPhoneWindowManager.overridePowerVolumeUp(POWER_VOLUME_UP_BEHAVIOR_GLOBAL_ACTIONS);
sendKeyGestureEventStart(KeyGestureEvent.KEY_GESTURE_TYPE_GLOBAL_ACTIONS);
mPhoneWindowManager.moveTimeForward(500);
sendKeyGestureEventCancel(KeyGestureEvent.KEY_GESTURE_TYPE_GLOBAL_ACTIONS);
@@ -388,7 +390,6 @@
@Test
public void testKeyGestureGlobalActionCancelled() {
- mPhoneWindowManager.overridePowerVolumeUp(POWER_VOLUME_UP_BEHAVIOR_GLOBAL_ACTIONS);
sendKeyGestureEventStart(KeyGestureEvent.KEY_GESTURE_TYPE_GLOBAL_ACTIONS);
sendKeyGestureEventCancel(KeyGestureEvent.KEY_GESTURE_TYPE_GLOBAL_ACTIONS);
mPhoneWindowManager.assertShowGlobalActionsNotCalled();
@@ -410,12 +411,6 @@
}
@Test
- public void testKeyGestureAccessibilityShortcut() {
- sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT);
- mPhoneWindowManager.assertAccessibilityKeychordCalled();
- }
-
- @Test
public void testKeyGestureCloseAllDialogs() {
sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_CLOSE_ALL_DIALOGS);
mPhoneWindowManager.assertCloseAllDialogs();
diff --git a/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTests.java b/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTests.java
index cde6b03..89d460e 100644
--- a/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTests.java
@@ -52,17 +52,18 @@
import android.app.AppOpsManager;
import android.content.Context;
import android.hardware.input.InputManager;
+import android.hardware.input.KeyGestureEvent;
import android.os.Bundle;
import android.os.IBinder;
import android.os.PowerManager;
import android.os.PowerManagerInternal;
+import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
import android.service.dreams.DreamManagerInternal;
import android.testing.TestableContext;
-import android.view.KeyEvent;
import androidx.test.filters.SmallTest;
@@ -76,6 +77,7 @@
import com.android.server.wm.DisplayPolicy;
import com.android.server.wm.DisplayRotation;
import com.android.server.wm.WindowManagerInternal;
+import com.android.window.flags.Flags;
import org.junit.After;
import org.junit.Before;
@@ -85,6 +87,8 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.List;
+
/**
* Test class for {@link PhoneWindowManager}.
*
@@ -107,6 +111,7 @@
@Mock private IBinder mInputToken;
PhoneWindowManager mPhoneWindowManager;
+
@Mock
private ActivityTaskManagerInternal mAtmInternal;
@Mock
@@ -114,6 +119,8 @@
@Mock
private InputManagerInternal mInputManagerInternal;
@Mock
+ private InputManager mInputManager;
+ @Mock
private PowerManagerInternal mPowerManagerInternal;
@Mock
private StatusBarManagerInternal mStatusBarManagerInternal;
@@ -156,9 +163,8 @@
mock(WindowManagerInternal.class));
mPhoneWindowManager.mKeyguardDelegate = mKeyguardServiceDelegate;
- final InputManager im = mock(InputManager.class);
- doNothing().when(im).registerKeyGestureEventHandler(anyList(), any());
- doReturn(im).when(mContext).getSystemService(eq(Context.INPUT_SERVICE));
+ doNothing().when(mInputManager).registerKeyGestureEventHandler(anyList(), any());
+ doReturn(mInputManager).when(mContext).getSystemService(eq(Context.INPUT_SERVICE));
}
@After
@@ -426,6 +432,32 @@
verify(mDreamManagerInternal).requestDream();
}
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_KEY_GESTURE_HANDLER_FOR_RECENTS)
+ public void testKeyGestureEvents_recentKeyGesturesEventsEnabled_notRegistered() {
+ initPhoneWindowManager();
+
+ ArgumentCaptor<List<Integer>> registeredKeyGestureEvents = ArgumentCaptor.forClass(
+ List.class);
+ verify(mInputManager).registerKeyGestureEventHandler(registeredKeyGestureEvents.capture(),
+ any());
+ assertThat(registeredKeyGestureEvents.getValue()).doesNotContain(
+ KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS);
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_ENABLE_KEY_GESTURE_HANDLER_FOR_RECENTS)
+ public void testKeyGestureEvents_recentKeyGesturesEventsDisabled_registered() {
+ initPhoneWindowManager();
+
+ ArgumentCaptor<List<Integer>> registeredKeyGestureEvents = ArgumentCaptor.forClass(
+ List.class);
+ verify(mInputManager).registerKeyGestureEventHandler(registeredKeyGestureEvents.capture(),
+ any());
+ assertThat(registeredKeyGestureEvents.getValue()).contains(
+ KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS);
+ }
+
private void initPhoneWindowManager() {
mPhoneWindowManager.mDefaultDisplayPolicy = mDisplayPolicy;
mPhoneWindowManager.mDefaultDisplayRotation = mock(DisplayRotation.class);
diff --git a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
index 048f6f6..9692d15 100644
--- a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
+++ b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
@@ -43,7 +43,6 @@
import static com.android.server.policy.PhoneWindowManager.LONG_PRESS_POWER_NOTHING;
import static com.android.server.policy.PhoneWindowManager.LONG_PRESS_POWER_SHUT_OFF;
import static com.android.server.policy.PhoneWindowManager.LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM;
-import static com.android.server.policy.PhoneWindowManager.POWER_VOLUME_UP_BEHAVIOR_MUTE;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -200,7 +199,6 @@
private boolean mIsTalkBackEnabled;
private boolean mIsTalkBackShortcutGestureEnabled;
- private boolean mDelegateBackGestureRemote;
private boolean mIsVoiceAccessEnabled;
private Intent mBrowserIntent;
@@ -502,15 +500,10 @@
/**
* Below functions will override the setting or the policy behavior.
*/
- void overridePowerVolumeUp(int behavior) {
- mPhoneWindowManager.mPowerVolUpBehavior = behavior;
-
- // override mRingerToggleChord as mute so we could trigger the behavior.
- if (behavior == POWER_VOLUME_UP_BEHAVIOR_MUTE) {
- mPhoneWindowManager.mRingerToggleChord = VOLUME_HUSH_MUTE;
- doReturn(mAudioManagerInternal).when(
- () -> LocalServices.getService(eq(AudioManagerInternal.class)));
- }
+ void overrideVolumeHushMode() {
+ mPhoneWindowManager.mRingerToggleChord = VOLUME_HUSH_MUTE;
+ doReturn(mAudioManagerInternal).when(
+ () -> LocalServices.getService(eq(AudioManagerInternal.class)));
}
void overrideShortPressOnPower(int behavior) {
@@ -581,12 +574,6 @@
setPhoneCallIsInProgress();
}
- void overrideDelegateBackGestureRemote(boolean isDelegating) {
- mDelegateBackGestureRemote = isDelegating;
- doReturn(mDelegateBackGestureRemote).when(mActivityTaskManagerInternal)
- .requestBackGesture();
- }
-
void prepareBrightnessDecrease(float currentBrightness) {
doReturn(0.0f).when(mPowerManager).getBrightnessConstraint(
DEFAULT_DISPLAY, PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM);
@@ -668,21 +655,19 @@
}
void assertBackEventInjected() {
- if (mDelegateBackGestureRemote) {
- Mockito.verify(mActivityTaskManagerInternal).requestBackGesture();
- ArgumentCaptor<InputEvent> intentCaptor = ArgumentCaptor.forClass(InputEvent.class);
- verify(mInputManager, never()).injectInputEvent(intentCaptor.capture(), anyInt());
- } else {
- ArgumentCaptor<InputEvent> intentCaptor = ArgumentCaptor.forClass(InputEvent.class);
- verify(mInputManager, times(2)).injectInputEvent(intentCaptor.capture(), anyInt());
- List<InputEvent> inputEvents = intentCaptor.getAllValues();
- Assert.assertEquals(KeyEvent.KEYCODE_BACK,
- ((KeyEvent) inputEvents.get(0)).getKeyCode());
- Assert.assertEquals(KeyEvent.KEYCODE_BACK,
- ((KeyEvent) inputEvents.get(1)).getKeyCode());
- // Reset verifier for next call.
- Mockito.clearInvocations(mContext);
- }
+ ArgumentCaptor<InputEvent> intentCaptor = ArgumentCaptor.forClass(InputEvent.class);
+ verify(mInputManager, times(2)).injectInputEvent(intentCaptor.capture(), anyInt());
+ List<InputEvent> inputEvents = intentCaptor.getAllValues();
+ Assert.assertEquals(KeyEvent.KEYCODE_BACK,
+ ((KeyEvent) inputEvents.get(0)).getKeyCode());
+ Assert.assertEquals(KeyEvent.KEYCODE_BACK,
+ ((KeyEvent) inputEvents.get(1)).getKeyCode());
+ // Reset verifier for next call.
+ Mockito.clearInvocations(mContext);
+ }
+
+ void assertBackEventNotInjected() {
+ verify(mInputManager, never()).injectInputEvent(any(), anyInt());
}
void overrideEnableBugReportTrigger(boolean enable) {
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 773a566..98d3992 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -93,8 +93,6 @@
import static com.android.server.wm.TaskFragment.TASK_FRAGMENT_VISIBILITY_VISIBLE;
import static com.android.server.wm.TaskFragment.TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
-import static com.android.server.wm.WindowStateAnimator.ROOT_TASK_CLIP_AFTER_ANIM;
-import static com.android.server.wm.WindowStateAnimator.ROOT_TASK_CLIP_NONE;
import static com.google.common.truth.Truth.assertThat;
@@ -126,7 +124,6 @@
import android.content.pm.ApplicationInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
-import android.graphics.Point;
import android.graphics.Rect;
import android.os.Binder;
import android.os.Build;
@@ -1274,6 +1271,7 @@
public void testFinishActivityIfPossible_nonVisibleNoAppTransition() {
registerTestTransitionPlayer();
spyOn(mRootWindowContainer.mTransitionController);
+ mWm.mAnimator.ready();
final ActivityRecord bottomActivity = createActivityWithTask();
bottomActivity.setVisibility(false);
bottomActivity.setState(STOPPED, "test");
@@ -1292,7 +1290,13 @@
assertTrue(bottomActivity.isVisible());
verify(mRootWindowContainer.mTransitionController).onVisibleWithoutCollectingTransition(
eq(bottomActivity), any());
- assertTrue(bottomActivity.mLastSurfaceShowing);
+ if (!mWm.mFlags.mEnsureSurfaceVisibility) {
+ assertTrue(bottomActivity.mLastSurfaceShowing);
+ return;
+ }
+ clearInvocations(mTransaction);
+ waitUntilWindowAnimatorIdle();
+ verify(mTransaction).setVisibility(bottomActivity.mSurfaceControl, true);
}
/**
@@ -3123,72 +3127,6 @@
}
@Test
- public void testTransitionAnimationBounds() {
- removeGlobalMinSizeRestriction();
- final Task task = new TaskBuilder(mSupervisor)
- .setCreateParentTask(true).setCreateActivity(true).build();
- final Task rootTask = task.getRootTask();
- final ActivityRecord activity = task.getTopNonFinishingActivity();
- final Rect stackBounds = new Rect(0, 0, 1000, 600);
- final Rect taskBounds = new Rect(100, 400, 600, 800);
- // Set the bounds and windowing mode to window configuration directly, otherwise the
- // testing setups may be discarded by configuration resolving.
- rootTask.getWindowConfiguration().setBounds(stackBounds);
- task.getWindowConfiguration().setBounds(taskBounds);
- activity.getWindowConfiguration().setBounds(taskBounds);
-
- // Check that anim bounds for freeform window match task bounds
- task.getWindowConfiguration().setWindowingMode(WINDOWING_MODE_FREEFORM);
- assertEquals(task.getBounds(), activity.getAnimationBounds(ROOT_TASK_CLIP_NONE));
-
- // ROOT_TASK_CLIP_AFTER_ANIM should use task bounds since they will be clipped by
- // bounds animation layer.
- task.getWindowConfiguration().setWindowingMode(WINDOWING_MODE_FULLSCREEN);
- assertEquals(task.getBounds(), activity.getAnimationBounds(ROOT_TASK_CLIP_AFTER_ANIM));
-
- // Even the activity is smaller than task and it is not aligned to the top-left corner of
- // task, the animation bounds the same as task and position should be zero because in real
- // case the letterbox will fill the remaining area in task.
- final Rect halfBounds = new Rect(taskBounds);
- halfBounds.scale(0.5f);
- activity.getWindowConfiguration().setBounds(halfBounds);
- final Point animationPosition = new Point();
- activity.getAnimationPosition(animationPosition);
-
- assertEquals(taskBounds, activity.getAnimationBounds(ROOT_TASK_CLIP_AFTER_ANIM));
- assertEquals(new Point(0, 0), animationPosition);
- }
-
- @Test
- public void testTransitionAnimationBounds_returnTaskFragment() {
- removeGlobalMinSizeRestriction();
- final Task task = new TaskBuilder(mSupervisor).setCreateParentTask(true).build();
- final Task rootTask = task.getRootTask();
- final TaskFragment taskFragment = createTaskFragmentWithActivity(task);
- final ActivityRecord activity = taskFragment.getTopNonFinishingActivity();
- final Rect stackBounds = new Rect(0, 0, 1000, 600);
- final Rect taskBounds = new Rect(100, 400, 600, 800);
- final Rect taskFragmentBounds = new Rect(100, 400, 300, 800);
- final Rect activityBounds = new Rect(100, 400, 300, 600);
- // Set the bounds and windowing mode to window configuration directly, otherwise the
- // testing setups may be discarded by configuration resolving.
- rootTask.getWindowConfiguration().setBounds(stackBounds);
- task.getWindowConfiguration().setBounds(taskBounds);
- taskFragment.getWindowConfiguration().setBounds(taskFragmentBounds);
- activity.getWindowConfiguration().setBounds(activityBounds);
-
- // Check that anim bounds for freeform window match task fragment bounds
- task.getWindowConfiguration().setWindowingMode(WINDOWING_MODE_FREEFORM);
- assertEquals(taskFragment.getBounds(), activity.getAnimationBounds(ROOT_TASK_CLIP_NONE));
-
- // ROOT_TASK_CLIP_AFTER_ANIM should use task fragment bounds since they will be clipped by
- // bounds animation layer.
- task.getWindowConfiguration().setWindowingMode(WINDOWING_MODE_FULLSCREEN);
- assertEquals(taskFragment.getBounds(),
- activity.getAnimationBounds(ROOT_TASK_CLIP_AFTER_ANIM));
- }
-
- @Test
public void testHasStartingWindow() {
final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build();
final WindowManager.LayoutParams attrs =
@@ -3215,7 +3153,8 @@
activity.mStartingData = new SplashScreenStartingData(mWm, 0, 0);
registerTestTransitionPlayer();
final Transition transition = activity.mTransitionController.requestTransitionIfNeeded(
- WindowManager.TRANSIT_OPEN, 0 /* flags */, null /* trigger */, mDisplayContent);
+ WindowManager.TRANSIT_OPEN, 0 /* flags */, null /* trigger */, mDisplayContent,
+ ActionChain.test());
activity.onStartingWindowDrawn();
assertTrue(activity.mStartingData.mIsDisplayed);
// The transition can be ready by the starting window of a visible-requested activity
@@ -3311,6 +3250,18 @@
assertFalse(activity.isVisible());
assertTrue(activity.isVisibleRequested());
assertTrue(activity.inTransition());
+
+ if (!mWm.mFlags.mEnsureSurfaceVisibility) {
+ return;
+ }
+ final Transition transition = activity.mTransitionController.getCollectingTransition();
+ assertNotNull(transition);
+ mWm.mAnimator.ready();
+ transition.start();
+ mWm.mSyncEngine.abort(transition.getSyncId());
+ transition.finishTransition(ActionChain.testFinish(transition));
+ waitUntilWindowAnimatorIdle();
+ verify(mTransaction).show(activity.mSurfaceControl);
}
@Test
@@ -3342,34 +3293,44 @@
assertFalse(app.mActivityRecord.isVisibleRequested());
assertTrue(app.mActivityRecord.isVisible());
- assertTrue(app.mActivityRecord.isSurfaceShowing());
+ if (!mWm.mFlags.mEnsureSurfaceVisibility) {
+ assertTrue(app.mActivityRecord.isSurfaceShowing());
- // Start transition.
- app.mActivityRecord.prepareSurfaces();
+ // Start transition.
+ app.mActivityRecord.prepareSurfaces();
- // Because the app is waiting for transition, it should not hide the surface.
- assertTrue(app.mActivityRecord.isSurfaceShowing());
+ // Because the app is waiting for transition, it should not hide the surface.
+ assertTrue(app.mActivityRecord.isSurfaceShowing());
+ return;
+ }
+ verify(mTransaction, never()).hide(app.mActivityRecord.mSurfaceControl);
}
@Test
public void testInClosingAnimation_visibilityCommitted_hideSurface() {
- final WindowState app = newWindowBuilder("app", TYPE_APPLICATION).build();
- makeWindowVisibleAndDrawn(app);
- app.mActivityRecord.prepareSurfaces();
+ final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build();
+ if (mWm.mFlags.mEnsureSurfaceVisibility) {
+ mWm.mAnimator.ready();
+ } else {
+ activity.prepareSurfaces();
+ }
- // Commit visibility before start transition.
- app.mActivityRecord.commitVisibility(false, false);
+ // Commit visibility without a transition.
+ activity.commitVisibility(false /* visible */, false /* performLayout */);
- assertFalse(app.mActivityRecord.isVisibleRequested());
- assertFalse(app.mActivityRecord.isVisible());
- assertTrue(app.mActivityRecord.isSurfaceShowing());
+ assertFalse(activity.isVisibleRequested());
+ assertFalse(activity.isVisible());
+ if (!mWm.mFlags.mEnsureSurfaceVisibility) {
+ assertTrue(activity.isSurfaceShowing());
+ activity.prepareSurfaces();
+ // Because the app visibility has been committed before the transition start, it should
+ // hide the surface.
+ assertFalse(activity.isSurfaceShowing());
+ return;
+ }
- // Start transition.
- app.mActivityRecord.prepareSurfaces();
-
- // Because the app visibility has been committed before the transition start, it should hide
- // the surface.
- assertFalse(app.mActivityRecord.isSurfaceShowing());
+ waitUntilWindowAnimatorIdle();
+ verify(mTransaction).setVisibility(activity.mSurfaceControl, false);
}
@Test // b/162542125
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatActivityRobot.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatActivityRobot.java
index cb98b9a..f48a853 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatActivityRobot.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatActivityRobot.java
@@ -212,7 +212,7 @@
}
void setTopActivityInTransition(boolean inTransition) {
- doReturn(inTransition).when(mActivityStack.top()).isInTransition();
+ doReturn(inTransition).when(mActivityStack.top()).inTransition();
}
void setShouldApplyUserMinAspectRatioOverride(boolean enabled) {
@@ -360,11 +360,11 @@
final DisplayRotation r = topActivity.mDisplayContent.getDisplayRotation();
doReturn(isHalfFolded).when(r).isDisplaySeparatingHinge();
doReturn(false).when(r)
- .isDeviceInPosture(any(DeviceStateController.DeviceState.class),
+ .isDeviceInPosture(any(DeviceStateController.DeviceStateEnum.class),
anyBoolean());
if (isHalfFolded) {
doReturn(true).when(r)
- .isDeviceInPosture(DeviceStateController.DeviceState.HALF_FOLDED,
+ .isDeviceInPosture(DeviceStateController.DeviceStateEnum.HALF_FOLDED,
isTabletop);
}
topActivity.recomputeConfiguration();
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatLetterboxUtilsTest.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatLetterboxUtilsTest.java
index ac707d2..59a0545 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatLetterboxUtilsTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatLetterboxUtilsTest.java
@@ -21,6 +21,7 @@
import static com.android.server.wm.AppCompatLetterboxUtils.calculateLetterboxInnerBounds;
import static com.android.server.wm.AppCompatLetterboxUtils.calculateLetterboxOuterBounds;
import static com.android.server.wm.AppCompatLetterboxUtils.calculateLetterboxPosition;
+import static com.android.server.wm.AppCompatLetterboxUtils.fullyContainsOrNotIntersects;
import static org.mockito.Mockito.mock;
@@ -143,6 +144,123 @@
});
}
+ @Test
+ public void testNoBoundsToCheck() {
+ runTestScenario((robot) -> {
+ robot.setWindowFrameArea(/* left */ 0, /* top */ 0, /* right */ 10, /* bottom */ 10);
+ robot.setBoundsToCheck();
+ robot.checkFullyContainsOrNotIntersects(/* expected */ true);
+ });
+ }
+
+ @Test
+ public void testEmptyBoundsToCheck() {
+ runTestScenario((robot) -> {
+ robot.setWindowFrameArea(/* left */ 0, /* top */ 0, /* right */ 10, /* bottom */ 10);
+ robot.setBoundsToCheck(new Rect(10, 10, 20, 20), new Rect(30, 30, 40, 40));
+ robot.checkFullyContainsOrNotIntersects(/* expected */ true);
+ });
+ }
+
+ @Test
+ public void testContainsEmptyRect() {
+ runTestScenario((robot) -> {
+ robot.setWindowFrameArea(/* left */ 0, /* top */ 0, /* right */ 10, /* bottom */ 10);
+ robot.setBoundsToCheck(new Rect(10, 10, 20, 20), new Rect());
+ robot.checkFullyContainsOrNotIntersects(/* expected */ true);
+ });
+ }
+
+ @Test
+ public void testCheckFullyContainsOrNotIntersects_NoIntersection() {
+ runTestScenario((robot) -> {
+ robot.setWindowFrameArea(/* left */ 0, /* top */ 0, /* right */ 10, /* bottom */ 10);
+ robot.setBoundsToCheck(new Rect(10, 10, 20, 20));
+ robot.checkFullyContainsOrNotIntersects(/* expected */ true);
+ });
+ }
+
+ @Test
+ public void testCheckFullyContainsOrNotIntersects_FullyContains() {
+ runTestScenario((robot) -> {
+ robot.setWindowFrameArea(/* left */ 0, /* top */ 0, /* right */ 10, /* bottom */ 10);
+ robot.setBoundsToCheck(new Rect(-5, -5, 15, 15));
+ robot.checkFullyContainsOrNotIntersects(/* expected */ true);
+ });
+ }
+
+ @Test
+ public void testCheckFullyContainsOrNotIntersects_PartiallyIntersects() {
+ runTestScenario((robot) -> {
+ robot.setWindowFrameArea(/* left */ 0, /* top */ 0, /* right */ 10, /* bottom */ 10);
+ robot.setBoundsToCheck(new Rect(5, 5, 15, 15));
+ robot.checkFullyContainsOrNotIntersects(/* expected */ false);
+ });
+ }
+
+ @Test
+ public void testCheckFullyContainsOrNotIntersects_MultipleBoundsNoIntersection() {
+ runTestScenario((robot) -> {
+ robot.setWindowFrameArea(/* left */ 0, /* top */ 0, /* right */ 10, /* bottom */ 10);
+ robot.setBoundsToCheck(new Rect(10, 10, 20, 20), new Rect(-20, -20, -10, -10));
+ robot.checkFullyContainsOrNotIntersects(/* expected */ true);
+ });
+ }
+
+ @Test
+ public void testCheckFullyContainsOrNotIntersects_MultipleBoundsWithOneContaining() {
+ runTestScenario((robot) -> {
+ robot.setWindowFrameArea(/* left */ 0, /* top */ 0, /* right */ 10, /* bottom */ 10);
+ robot.setBoundsToCheck(new Rect(10, 10, 20, 20), new Rect(-5, -5, 15, 15));
+ robot.checkFullyContainsOrNotIntersects(/* expected */ true);
+ });
+ }
+
+ @Test
+ public void testCheckFullyContainsOrNotIntersects_MultipleBoundsWithOneIntersecting() {
+ runTestScenario((robot) -> {
+ robot.setWindowFrameArea(/* left */ 0, /* top */ 0, /* right */ 10, /* bottom */ 10);
+ robot.setBoundsToCheck(new Rect(10, 10, 20, 20), new Rect(5, 5, 15, 15));
+ robot.checkFullyContainsOrNotIntersects(/* expected */ false);
+ });
+ }
+
+ @Test
+ public void testCheckFullyContainsOrNotIntersects_MultipleBoundsWithEmptyAndNoIntersection() {
+ runTestScenario((robot) -> {
+ robot.setWindowFrameArea(/* left */ 0, /* top */ 0, /* right */ 10, /* bottom */ 10);
+ robot.setBoundsToCheck(new Rect(), new Rect(10, 10, 20, 20));
+ robot.checkFullyContainsOrNotIntersects(/* expected */ true);
+ });
+ }
+
+ @Test
+ public void testCheckFullyContainsOrNotIntersects_MultipleBoundsWithEmptyAndContaining() {
+ runTestScenario((robot) -> {
+ robot.setWindowFrameArea(/* left */ 0, /* top */ 0, /* right */ 10, /* bottom */ 10);
+ robot.setBoundsToCheck(new Rect(), new Rect(-5, -5, 15, 15));
+ robot.checkFullyContainsOrNotIntersects(/* expected */ true);
+ });
+ }
+
+ @Test
+ public void testCheckFullyContainsOrNotIntersects_EmptyRectToCheck() {
+ runTestScenario((robot) -> {
+ robot.setWindowFrameArea(new Rect());
+ robot.setBoundsToCheck(new Rect(10, 10, 20, 20), new Rect(-5, -5, 15, 15));
+ robot.checkFullyContainsOrNotIntersects(/* expected */ true);
+ });
+ }
+
+ @Test
+ public void testCheckFullyContainsOrNotIntersects_EmptyRectToCheckAndEmptyBounds() {
+ runTestScenario((robot) -> {
+ robot.setWindowFrameArea(new Rect());
+ robot.setBoundsToCheck(new Rect());
+ robot.checkFullyContainsOrNotIntersects(/* expected */ true);
+ });
+ }
+
/**
* Runs a test scenario providing a Robot.
*/
@@ -157,6 +275,10 @@
private final Rect mInnerBound = new Rect();
private final Rect mOuterBound = new Rect();
+ private final Rect mWindowFrameArea = new Rect();
+
+ private Rect[] mBoundsToCheck;
+
@NonNull
private final WindowState mWindowState;
@@ -188,6 +310,18 @@
doReturn(frame).when(mWindowState).getFrame();
}
+ void setWindowFrameArea(int left, int top, int right, int bottom) {
+ mWindowFrameArea.set(left, top, right, bottom);
+ }
+
+ void setWindowFrameArea(@NonNull Rect windowFrameArea) {
+ mWindowFrameArea.set(windowFrameArea);
+ }
+
+ void setBoundsToCheck(@NonNull Rect... boundsToCheck) {
+ mBoundsToCheck = boundsToCheck;
+ }
+
void getLetterboxPosition() {
calculateLetterboxPosition(activity().top(), mPosition);
}
@@ -235,5 +369,10 @@
Assert.assertEquals(mInnerBound, activity().top().getBounds());
}
+ void checkFullyContainsOrNotIntersects(boolean expected) {
+ Assert.assertEquals(expected,
+ fullyContainsOrNotIntersects(mWindowFrameArea, mBoundsToCheck));
+ }
+
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
index dd3e9fc..a8dcbfd 100644
--- a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
@@ -57,6 +57,7 @@
import android.os.Looper;
import android.os.RemoteCallback;
import android.os.RemoteException;
+import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
import android.view.WindowManager;
import android.window.BackAnimationAdapter;
@@ -72,6 +73,7 @@
import android.window.WindowOnBackInvokedDispatcher;
import com.android.server.LocalServices;
+import com.android.window.flags.Flags;
import org.junit.Before;
import org.junit.Test;
@@ -671,6 +673,28 @@
assertThat(mostRecentUsedWindow).isEqualTo(secondaryWindow);
}
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_INDEPENDENT_BACK_IN_PROJECTED)
+ public void testBackIsInProjectedMode_returnsWindowOnUnfocusedDisplay() {
+ final DisplayContent secondDc = createNewDisplay();
+ doReturn(true).when(mBackNavigationController).isInProjectedMode(secondDc.mDisplayId);
+
+ final Task taskOnSecondDisplay = createTopTaskWithActivity(secondDc);
+ final Task taskOnDefaultDisplay = createTopTaskWithActivity();
+ withSystemCallback(taskOnSecondDisplay);
+ withSystemCallback(taskOnDefaultDisplay);
+ mBackAnimationAdapter.mOriginDisplayId = secondDc.mDisplayId;
+
+ // Top focused task on top focused display is on default display.
+ assertEquals(taskOnDefaultDisplay.getTopVisibleAppMainWindow(),
+ mWm.getFocusedWindowLocked());
+
+ final BackNavigationInfo backNavigationInfo = startBackNavigation();
+ assertWithMessage("BackNavigationInfo").that(backNavigationInfo).isNotNull();
+ // Returns task on unfocused display (second display) if projected mode.
+ assertEquals(taskOnSecondDisplay.mTaskId, backNavigationInfo.getFocusedTaskId());
+ }
+
/**
* Test with
* config_predictShowStartingSurface = true
@@ -859,7 +883,12 @@
@NonNull
private Task createTopTaskWithActivity() {
- Task task = createTask(mDefaultDisplay);
+ return createTopTaskWithActivity(mDefaultDisplay);
+ }
+
+ @NonNull
+ private Task createTopTaskWithActivity(@NonNull DisplayContent dc) {
+ Task task = createTask(dc);
ActivityRecord record = createActivityRecord(task);
// enable OnBackInvokedCallbacks
record.info.applicationInfo.privateFlagsExt |=
diff --git a/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerTests.java
index fe9a6e7..2d22f65 100644
--- a/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerTests.java
@@ -20,7 +20,6 @@
import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_PERMISSION;
import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_VISIBLE_WINDOW;
import static com.android.server.wm.BackgroundActivityStartController.BAL_BLOCK;
-import static com.android.window.flags.Flags.balImprovedMetrics;
import static com.google.common.truth.Truth.assertThat;
@@ -259,12 +258,8 @@
// assertions
assertThat(verdict.getCode()).isEqualTo(BackgroundActivityStartController.BAL_BLOCK);
- if (balImprovedMetrics()) {
- assertThat(mBalAllowedLogs).containsExactly(
- new BalAllowedLog("package.app3/someClass", BAL_BLOCK));
- } else {
- assertThat(mBalAllowedLogs).isEmpty(); // not allowed
- }
+ assertThat(mBalAllowedLogs).containsExactly(
+ new BalAllowedLog("package.app3/someClass", BAL_BLOCK));
}
// Tests for BackgroundActivityStartController.checkBackgroundActivityStart
@@ -294,12 +289,8 @@
// assertions
assertThat(verdict).isEqualTo(BalVerdict.BLOCK);
- if (balImprovedMetrics()) {
- assertThat(mBalAllowedLogs).containsExactly(
- new BalAllowedLog("package.app3/someClass", BAL_BLOCK));
- } else {
- assertThat(mBalAllowedLogs).isEmpty(); // not allowed
- }
+ assertThat(mBalAllowedLogs).containsExactly(
+ new BalAllowedLog("package.app3/someClass", BAL_BLOCK));
}
@Test
@@ -329,12 +320,8 @@
// assertions
assertThat(verdict).isEqualTo(callerVerdict);
- if (balImprovedMetrics()) {
- assertThat(mBalAllowedLogs).containsExactly(
- new BalAllowedLog("package.app3/someClass", callerVerdict.getCode()));
- } else {
- assertThat(mBalAllowedLogs).isEmpty(); // non-critical exception
- }
+ assertThat(mBalAllowedLogs).containsExactly(
+ new BalAllowedLog("package.app3/someClass", callerVerdict.getCode()));
}
@Test
@@ -398,13 +385,8 @@
// assertions
assertThat(verdict).isEqualTo(callerVerdict);
- if (balImprovedMetrics()) {
- assertThat(mBalAllowedLogs).containsExactly(
- new BalAllowedLog("package.app3/someClass", callerVerdict.getCode()));
- } else {
- assertThat(mBalAllowedLogs).containsExactly(
- new BalAllowedLog("", callerVerdict.getCode()));
- }
+ assertThat(mBalAllowedLogs).containsExactly(
+ new BalAllowedLog("package.app3/someClass", callerVerdict.getCode()));
}
@Test
@@ -440,12 +422,8 @@
// assertions
assertThat(verdict).isEqualTo(BalVerdict.BLOCK);
- if (balImprovedMetrics()) {
- assertThat(mBalAllowedLogs).containsExactly(
- new BalAllowedLog("package.app3/someClass", BAL_BLOCK));
- } else {
- assertThat(mBalAllowedLogs).isEmpty();
- }
+ assertThat(mBalAllowedLogs).containsExactly(
+ new BalAllowedLog("package.app3/someClass", BAL_BLOCK));
}
@Test
@@ -477,12 +455,8 @@
// assertions
assertThat(verdict).isEqualTo(callerVerdict);
- if (balImprovedMetrics()) {
- assertThat(mBalAllowedLogs).containsExactly(
- new BalAllowedLog("package.app3/someClass", callerVerdict.getCode()));
- } else {
- assertThat(mBalAllowedLogs).isEmpty();
- }
+ assertThat(mBalAllowedLogs).containsExactly(
+ new BalAllowedLog("package.app3/someClass", callerVerdict.getCode()));
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/BackgroundLaunchProcessControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BackgroundLaunchProcessControllerTests.java
index 747b09c..06d11f3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/BackgroundLaunchProcessControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/BackgroundLaunchProcessControllerTests.java
@@ -24,7 +24,6 @@
import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_GRACE_PERIOD;
import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_PERMISSION;
import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_TOKEN;
-import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_VISIBLE_WINDOW;
import static com.android.server.wm.BackgroundActivityStartController.BAL_BLOCK;
import static com.google.common.truth.Truth.assertThat;
@@ -33,15 +32,12 @@
import android.content.Context;
import android.os.Binder;
import android.os.IBinder;
-import android.platform.test.annotations.DisableFlags;
-import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
import android.platform.test.flag.junit.SetFlagsRule;
import androidx.test.filters.SmallTest;
import com.android.server.wm.BackgroundActivityStartController.BalVerdict;
-import com.android.window.flags.Flags;
import org.junit.ClassRule;
import org.junit.Rule;
@@ -132,25 +128,6 @@
}
@Test
- @DisableFlags(Flags.FLAG_BAL_IMPROVED_METRICS)
- public void testAllowedByTokenNoCallbackOld() {
- mController = new BackgroundLaunchProcessController(mHasActiveVisibleWindow::contains,
- null);
- Binder token = new Binder();
- mActivityStartAllowed.add(token);
- mController.addOrUpdateAllowBackgroundStartPrivileges(token,
- BackgroundStartPrivileges.ALLOW_BAL);
- BalVerdict balVerdict = mController.areBackgroundActivityStartsAllowed(
- mPid, mUid, mPackageName,
- mAppSwitchState, mBalCheckConfiguration,
- mHasActivityInVisibleTask, mHasBackgroundActivityStartPrivileges,
- mLastStopAppSwitchesTime, mLastActivityLaunchTime,
- mLastActivityFinishTime);
- assertThat(balVerdict.getCode()).isEqualTo(BAL_ALLOW_PERMISSION);
- }
-
- @Test
- @EnableFlags(Flags.FLAG_BAL_IMPROVED_METRICS)
public void testAllowedByTokenNoCallback() {
mController = new BackgroundLaunchProcessController(mHasActiveVisibleWindow::contains,
null);
@@ -168,23 +145,6 @@
}
@Test
- @DisableFlags(Flags.FLAG_BAL_IMPROVED_METRICS)
- public void testAllowedByTokenOld() {
- Binder token = new Binder();
- mActivityStartAllowed.add(token);
- mController.addOrUpdateAllowBackgroundStartPrivileges(token,
- BackgroundStartPrivileges.ALLOW_BAL);
- BalVerdict balVerdict = mController.areBackgroundActivityStartsAllowed(
- mPid, mUid, mPackageName,
- mAppSwitchState, mBalCheckConfiguration,
- mHasActivityInVisibleTask, mHasBackgroundActivityStartPrivileges,
- mLastStopAppSwitchesTime, mLastActivityLaunchTime,
- mLastActivityFinishTime);
- assertThat(balVerdict.getCode()).isEqualTo(BAL_ALLOW_PERMISSION);
- }
-
- @Test
- @EnableFlags(Flags.FLAG_BAL_IMPROVED_METRICS)
public void testAllowedByToken() {
Binder token = new Binder();
mActivityStartAllowed.add(token);
@@ -200,22 +160,6 @@
}
@Test
- @DisableFlags(Flags.FLAG_BAL_IMPROVED_METRICS)
- public void testBoundByForegroundOld() {
- mAppSwitchState = APP_SWITCH_ALLOW;
- mController.addBoundClientUid(999, "visible.package", Context.BIND_ALLOW_ACTIVITY_STARTS);
- mHasActiveVisibleWindow.add(999);
- BalVerdict balVerdict = mController.areBackgroundActivityStartsAllowed(
- mPid, mUid, mPackageName,
- mAppSwitchState, mBalCheckConfiguration,
- mHasActivityInVisibleTask, mHasBackgroundActivityStartPrivileges,
- mLastStopAppSwitchesTime, mLastActivityLaunchTime,
- mLastActivityFinishTime);
- assertThat(balVerdict.getCode()).isEqualTo(BAL_ALLOW_VISIBLE_WINDOW);
- }
-
- @Test
- @EnableFlags(Flags.FLAG_BAL_IMPROVED_METRICS)
public void testBoundByForeground() {
mAppSwitchState = APP_SWITCH_ALLOW;
mController.addBoundClientUid(999, "visible.package", Context.BIND_ALLOW_ACTIVITY_STARTS);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java b/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java
index ad76662..c7b358c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java
@@ -52,8 +52,10 @@
import android.graphics.Rect;
import android.media.projection.StopReason;
import android.os.IBinder;
+import android.os.Process;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.annotations.RequiresFlagsEnabled;
import android.view.ContentRecordingSession;
import android.view.Display;
import android.view.DisplayInfo;
@@ -62,6 +64,7 @@
import androidx.test.filters.SmallTest;
+import com.android.media.projection.flags.Flags;
import com.android.server.wm.ContentRecorder.MediaProjectionManagerWrapper;
import org.junit.Before;
@@ -88,6 +91,8 @@
ContentRecordingSession.createDisplaySession(DEFAULT_DISPLAY);
private final ContentRecordingSession mWaitingDisplaySession =
ContentRecordingSession.createDisplaySession(DEFAULT_DISPLAY);
+ private final ContentRecordingSession mOverlaySession =
+ ContentRecordingSession.createOverlaySession(DEFAULT_DISPLAY, 1234);
private ContentRecordingSession mTaskSession;
private Point mSurfaceSize;
private ContentRecorder mContentRecorder;
@@ -146,6 +151,10 @@
mTaskSession = ContentRecordingSession.createTaskSession(sTaskWindowContainerToken);
mTaskSession.setVirtualDisplayId(displayId);
+ // GIVEN MediaProjection is recording as an overlay
+ mOverlaySession.setVirtualDisplayId(displayId);
+ mOverlaySession.setDisplayToRecord(mDefaultDisplay.mDisplayId);
+
// GIVEN a session is waiting for the user to review consent.
mWaitingDisplaySession.setVirtualDisplayId(displayId);
mWaitingDisplaySession.setWaitingForConsent(true);
@@ -518,6 +527,24 @@
mDisplaySession.getTargetUid(), mRootWindowContainer.getWindowingMode());
}
+ @EnableFlags(Flags.FLAG_RECORDING_OVERLAY)
+ @RequiresFlagsEnabled(com.android.graphics.surfaceflinger.flags.Flags.FLAG_STOP_LAYER)
+ @Test
+ public void testStartRecording_notifiesCallback_overlaySession() {
+ defaultInit();
+ // WHEN a recording is ongoing.
+ mContentRecorder.setContentRecordingSession(mOverlaySession);
+ mContentRecorder.updateRecording();
+ assertThat(mContentRecorder.isCurrentlyRecording()).isTrue();
+
+ // THEN the visibility change & windowing mode change callbacks are notified.
+ verify(mMediaProjectionManagerWrapper)
+ .notifyActiveProjectionCapturedContentVisibilityChanged(true);
+ verify(mMediaProjectionManagerWrapper)
+ .notifyWindowingModeChanged(mOverlaySession.getContentToRecord(),
+ mOverlaySession.getTargetUid(), mRootWindowContainer.getWindowingMode());
+ }
+
@Test
public void testStartRecording_taskInPIP_recordingNotStarted() {
defaultInit();
@@ -535,7 +562,7 @@
@Test
public void testStartRecording_taskInSplit_recordingStarted() {
defaultInit();
- // GIVEN a task is in PIP.
+ // GIVEN a task is in split screen.
mContentRecorder.setContentRecordingSession(mTaskSession);
mTask.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
@@ -549,14 +576,29 @@
@Test
public void testStartRecording_taskInFullscreen_recordingStarted() {
defaultInit();
- // GIVEN a task is in PIP.
+ // GIVEN a task is in FULLSCREEN.
mContentRecorder.setContentRecordingSession(mTaskSession);
mTask.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
// WHEN a recording tries to start.
mContentRecorder.updateRecording();
- // THEN recording does not start.
+ // THEN recording does start.
+ assertThat(mContentRecorder.isCurrentlyRecording()).isTrue();
+ }
+
+ @EnableFlags(Flags.FLAG_RECORDING_OVERLAY)
+ @RequiresFlagsEnabled(com.android.graphics.surfaceflinger.flags.Flags.FLAG_STOP_LAYER)
+ @Test
+ public void testStartRecording_overlayRecording_recordingStarted() {
+ defaultInit();
+ // GIVEN an overlay recording session
+ mContentRecorder.setContentRecordingSession(mOverlaySession);
+
+ // WHEN a recording tries to start.
+ mContentRecorder.updateRecording();
+
+ // THEN recording does start.
assertThat(mContentRecorder.isCurrentlyRecording()).isTrue();
}
@@ -927,6 +969,7 @@
.setCallsite("mirrorSurface")
.build();
doReturn(mirroredSurface).when(() -> SurfaceControl.mirrorSurface(any()));
+ doReturn(mirroredSurface).when(() -> SurfaceControl.mirrorSurface(any(), any()));
doReturn(surfaceSize).when(mWm.mDisplayManagerInternal).getDisplaySurfaceDefaultSize(
anyInt());
return mirroredSurface;
diff --git a/services/tests/wmtests/src/com/android/server/wm/DeviceStateAutoRotateSettingControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/DeviceStateAutoRotateSettingControllerTests.java
index 0ddce72..375977d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DeviceStateAutoRotateSettingControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DeviceStateAutoRotateSettingControllerTests.java
@@ -36,6 +36,7 @@
import com.android.server.testutils.OffsettableClock;
import com.android.server.testutils.TestHandler;
+import com.android.server.wm.utils.DeviceStateTestUtils;
import com.android.window.flags.Flags;
import org.junit.Before;
@@ -85,8 +86,7 @@
@Test
@EnableFlags(Flags.FLAG_ENABLE_DEVICE_STATE_AUTO_ROTATE_SETTING_LOGGING)
public void loggingFlagEnabled_onDeviceStateChanged_loggerNotified() {
- mDeviceStateAutoRotateSettingController.onDeviceStateChange(
- DeviceStateController.DeviceState.FOLDED);
+ mDeviceStateAutoRotateSettingController.onDeviceStateChange(DeviceStateTestUtils.FOLDED);
verify(mMockLogger, times(1)).onDeviceStateChange();
}
@@ -94,8 +94,7 @@
@Test
@DisableFlags(Flags.FLAG_ENABLE_DEVICE_STATE_AUTO_ROTATE_SETTING_LOGGING)
public void loggingFlagDisabled_onDeviceStateChanged_loggerNotNotified() {
- mDeviceStateAutoRotateSettingController.onDeviceStateChange(
- DeviceStateController.DeviceState.FOLDED);
+ mDeviceStateAutoRotateSettingController.onDeviceStateChange(DeviceStateTestUtils.FOLDED);
verify(mMockLogger, never()).onDeviceStateChange();
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DeviceStateControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/DeviceStateControllerTests.java
index 36861fa..c2597b1 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DeviceStateControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DeviceStateControllerTests.java
@@ -53,7 +53,6 @@
import java.util.HashSet;
import java.util.List;
import java.util.concurrent.Executor;
-import java.util.function.Consumer;
/**
* Test class for {@link DeviceStateController}.
@@ -61,6 +60,7 @@
* Build/Install/Run:
* atest WmTests:DeviceStateControllerTests
*/
+@SuppressWarnings("GuardedBy")
@SmallTest
@Presubmit
public class DeviceStateControllerTests {
@@ -70,23 +70,23 @@
private Context mMockContext;
private DeviceStateManager mMockDeviceStateManager;
- private DeviceStateController.DeviceState mCurrentState =
- DeviceStateController.DeviceState.UNKNOWN;
- private Consumer<DeviceStateController.DeviceState> mDelegate;
+ private DeviceStateController.DeviceStateEnum mDeviceStateEnum =
+ DeviceStateController.DeviceStateEnum.UNKNOWN;
+ private DeviceStateController.DeviceStateListener mDeviceStateListener;
private Executor mExecutor = MoreExecutors.directExecutor();
@Before
public void setUp() {
mBuilder = new DeviceStateControllerBuilder();
- mCurrentState = DeviceStateController.DeviceState.UNKNOWN;
+ mDeviceStateEnum = DeviceStateController.DeviceStateEnum.UNKNOWN;
}
private void initialize(boolean supportFold, boolean supportHalfFold) {
mBuilder.setSupportFold(supportFold, supportHalfFold);
- mDelegate = (newFoldState) -> {
- mCurrentState = newFoldState;
+ mDeviceStateListener = (deviceStateEnum, deviceState) -> {
+ mDeviceStateEnum = deviceStateEnum;
};
- mBuilder.setDelegate(mDelegate);
+ mBuilder.setDeviceStateListener(mDeviceStateListener);
mBuilder.build();
}
@@ -94,7 +94,7 @@
public void testInitialization() {
initialize(true /* supportFold */, true /* supportHalfFolded */);
mTarget.onDeviceStateReceivedByDisplayManager(mOpenDeviceStates.get(0).getIdentifier());
- assertEquals(DeviceStateController.DeviceState.OPEN, mCurrentState);
+ assertEquals(DeviceStateController.DeviceStateEnum.OPEN, mDeviceStateEnum);
}
@Test
@@ -102,7 +102,7 @@
initialize(false /* supportFold */, false /* supportHalfFolded */);
mTarget.onDeviceStateReceivedByDisplayManager(mFoldedStates.get(0).getIdentifier());
// Note that the folded state is ignored.
- assertEquals(DeviceStateController.DeviceState.UNKNOWN, mCurrentState);
+ assertEquals(DeviceStateController.DeviceStateEnum.UNKNOWN, mDeviceStateEnum);
}
@Test
@@ -110,11 +110,11 @@
public void testWithFoldSupported_withOverlayConfigValues() {
initialize(true /* supportFold */, false /* supportHalfFolded */);
mTarget.onDeviceStateReceivedByDisplayManager(mOpenDeviceStates.get(0).getIdentifier());
- assertEquals(DeviceStateController.DeviceState.OPEN, mCurrentState);
+ assertEquals(DeviceStateController.DeviceStateEnum.OPEN, mDeviceStateEnum);
mTarget.onDeviceStateReceivedByDisplayManager(mFoldedStates.get(0).getIdentifier());
- assertEquals(DeviceStateController.DeviceState.FOLDED, mCurrentState);
+ assertEquals(DeviceStateController.DeviceStateEnum.FOLDED, mDeviceStateEnum);
mTarget.onDeviceStateReceivedByDisplayManager(mHalfFoldedStates.get(0).getIdentifier());
- assertEquals(DeviceStateController.DeviceState.UNKNOWN, mCurrentState); // Ignored
+ assertEquals(DeviceStateController.DeviceStateEnum.UNKNOWN, mDeviceStateEnum); // Ignored
}
@Test
@@ -122,11 +122,11 @@
public void testWithFoldSupported_withDeviceStateManagerPropertyAPI() {
initialize(true /* supportFold */, false /* supportHalfFolded */);
mTarget.onDeviceStateReceivedByDisplayManager(mOpenDeviceStates.get(0).getIdentifier());
- assertEquals(DeviceStateController.DeviceState.OPEN, mCurrentState);
+ assertEquals(DeviceStateController.DeviceStateEnum.OPEN, mDeviceStateEnum);
mTarget.onDeviceStateReceivedByDisplayManager(mFoldedStates.get(0).getIdentifier());
- assertEquals(DeviceStateController.DeviceState.FOLDED, mCurrentState);
+ assertEquals(DeviceStateController.DeviceStateEnum.FOLDED, mDeviceStateEnum);
mTarget.onDeviceStateReceivedByDisplayManager(mHalfFoldedStates.get(0).getIdentifier());
- assertEquals(DeviceStateController.DeviceState.UNKNOWN, mCurrentState); // Ignored
+ assertEquals(DeviceStateController.DeviceStateEnum.UNKNOWN, mDeviceStateEnum); // Ignored
}
@Test
@@ -134,13 +134,13 @@
public void testWithHalfFoldSupported_withOverlayConfigValue() {
initialize(true /* supportFold */, true /* supportHalfFolded */);
mTarget.onDeviceStateReceivedByDisplayManager(mOpenDeviceStates.get(0).getIdentifier());
- assertEquals(DeviceStateController.DeviceState.OPEN, mCurrentState);
+ assertEquals(DeviceStateController.DeviceStateEnum.OPEN, mDeviceStateEnum);
mTarget.onDeviceStateReceivedByDisplayManager(mFoldedStates.get(0).getIdentifier());
- assertEquals(DeviceStateController.DeviceState.FOLDED, mCurrentState);
+ assertEquals(DeviceStateController.DeviceStateEnum.FOLDED, mDeviceStateEnum);
mTarget.onDeviceStateReceivedByDisplayManager(mHalfFoldedStates.get(0).getIdentifier());
- assertEquals(DeviceStateController.DeviceState.HALF_FOLDED, mCurrentState);
+ assertEquals(DeviceStateController.DeviceStateEnum.HALF_FOLDED, mDeviceStateEnum);
mTarget.onDeviceStateReceivedByDisplayManager(mConcurrentDisplayState.getIdentifier());
- assertEquals(DeviceStateController.DeviceState.CONCURRENT, mCurrentState);
+ assertEquals(DeviceStateController.DeviceStateEnum.CONCURRENT, mDeviceStateEnum);
}
@Test
@@ -148,50 +148,50 @@
public void testWithHalfFoldSupported_withDeviceStateManagerPropertyApi() {
initialize(true /* supportFold */, true /* supportHalfFolded */);
mTarget.onDeviceStateReceivedByDisplayManager(mOpenDeviceStates.get(0).getIdentifier());
- assertEquals(DeviceStateController.DeviceState.OPEN, mCurrentState);
+ assertEquals(DeviceStateController.DeviceStateEnum.OPEN, mDeviceStateEnum);
mTarget.onDeviceStateReceivedByDisplayManager(mFoldedStates.get(0).getIdentifier());
- assertEquals(DeviceStateController.DeviceState.FOLDED, mCurrentState);
+ assertEquals(DeviceStateController.DeviceStateEnum.FOLDED, mDeviceStateEnum);
mTarget.onDeviceStateReceivedByDisplayManager(mHalfFoldedStates.get(0).getIdentifier());
- assertEquals(DeviceStateController.DeviceState.HALF_FOLDED, mCurrentState);
+ assertEquals(DeviceStateController.DeviceStateEnum.HALF_FOLDED, mDeviceStateEnum);
mTarget.onDeviceStateReceivedByDisplayManager(mConcurrentDisplayState.getIdentifier());
- assertEquals(DeviceStateController.DeviceState.CONCURRENT, mCurrentState);
+ assertEquals(DeviceStateController.DeviceStateEnum.CONCURRENT, mDeviceStateEnum);
}
@Test
public void testUnregisterDeviceStateCallback() {
initialize(true /* supportFold */, true /* supportHalfFolded */);
assertEquals(1, mTarget.mDeviceStateCallbacks.size());
- assertTrue(mTarget.mDeviceStateCallbacks.containsKey(mDelegate));
+ assertTrue(mTarget.mDeviceStateCallbacks.containsKey(mDeviceStateListener));
mTarget.onDeviceStateReceivedByDisplayManager(mOpenDeviceStates.get(0).getIdentifier());
- assertEquals(DeviceStateController.DeviceState.OPEN, mCurrentState);
+ assertEquals(DeviceStateController.DeviceStateEnum.OPEN, mDeviceStateEnum);
mTarget.onDeviceStateReceivedByDisplayManager(mFoldedStates.get(0).getIdentifier());
- assertEquals(DeviceStateController.DeviceState.FOLDED, mCurrentState);
+ assertEquals(DeviceStateController.DeviceStateEnum.FOLDED, mDeviceStateEnum);
// The callback should not receive state change when it is unregistered.
- mTarget.unregisterDeviceStateCallback(mDelegate);
+ mTarget.unregisterDeviceStateCallback(mDeviceStateListener);
assertTrue(mTarget.mDeviceStateCallbacks.isEmpty());
mTarget.onDeviceStateReceivedByDisplayManager(mOpenDeviceStates.get(0).getIdentifier());
- assertEquals(DeviceStateController.DeviceState.FOLDED /* unchanged */,
- mCurrentState);
+ assertEquals(DeviceStateController.DeviceStateEnum.FOLDED /* unchanged */,
+ mDeviceStateEnum);
}
@Test
public void testCopyDeviceStateCallbacks() {
initialize(true /* supportFold */, true /* supportHalfFolded */);
assertEquals(1, mTarget.mDeviceStateCallbacks.size());
- assertTrue(mTarget.mDeviceStateCallbacks.containsKey(mDelegate));
+ assertTrue(mTarget.mDeviceStateCallbacks.containsKey(mDeviceStateListener));
- List<Pair<Consumer<DeviceStateController.DeviceState>, Executor>> entries =
+ List<Pair<DeviceStateController.DeviceStateListener, Executor>> entries =
mTarget.copyDeviceStateCallbacks();
- mTarget.unregisterDeviceStateCallback(mDelegate);
+ mTarget.unregisterDeviceStateCallback(mDeviceStateListener);
// In contrast to List<Map.Entry> where the entries are tied to changes in the backing map,
// List<Pair> should still contain non-null callbacks and executors even though they were
// removed from the backing map via the unregister method above.
assertEquals(1, entries.size());
- assertEquals(mDelegate, entries.get(0).first);
+ assertEquals(mDeviceStateListener, entries.get(0).first);
assertEquals(mExecutor, entries.get(0).second);
}
@@ -237,7 +237,7 @@
private boolean mSupportFold = false;
private boolean mSupportHalfFold = false;
- private Consumer<DeviceStateController.DeviceState> mDelegate;
+ private DeviceStateController.DeviceStateListener mDeviceStateListener;
private final List<DeviceState> mDeviceStateList = new ArrayList<>();
DeviceStateControllerBuilder setSupportFold(
@@ -247,9 +247,9 @@
return this;
}
- DeviceStateControllerBuilder setDelegate(
- Consumer<DeviceStateController.DeviceState> delegate) {
- mDelegate = delegate;
+ DeviceStateControllerBuilder setDeviceStateListener(
+ DeviceStateController.DeviceStateListener deviceStateListener) {
+ mDeviceStateListener = deviceStateListener;
return this;
}
@@ -299,7 +299,7 @@
when(mMockContext.getResources()).thenReturn((mockRes));
mockFold(mSupportFold, mSupportHalfFold);
mTarget = new DeviceStateController(mMockContext, new WindowManagerGlobalLock());
- mTarget.registerDeviceStateCallback(mDelegate, mExecutor);
+ mTarget.registerDeviceStateCallback(mDeviceStateListener, mExecutor);
}
private int[] mapDeviceStateListToIdentifierArray(List<DeviceState> deviceStates) {
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 42905f1..e095557 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -1752,7 +1752,6 @@
assertFalse(mDisplayContent.hasTopFixedRotationLaunchingApp());
}
- @EnableFlags(com.android.window.flags.Flags.FLAG_RESPECT_NON_TOP_VISIBLE_FIXED_ORIENTATION)
@Test
public void testRespectNonTopVisibleFixedOrientation() {
spyOn(mWm.mAppCompatConfiguration);
@@ -1766,7 +1765,8 @@
.setTask(nonTopVisible.getTask()).setVisible(false)
.setActivityTheme(android.R.style.Theme_Translucent).build();
final TestTransitionPlayer player = registerTestTransitionPlayer();
- mDisplayContent.requestTransitionAndLegacyPrepare(WindowManager.TRANSIT_OPEN, 0, null);
+ mDisplayContent.requestTransitionAndLegacyPrepare(WindowManager.TRANSIT_OPEN, 0, null,
+ ActionChain.test());
translucentTop.setVisibility(true);
mDisplayContent.updateOrientation();
assertEquals("Non-top visible activity must be portrait",
@@ -2265,12 +2265,13 @@
spyOn(mWm.mPolicy);
doReturn(true).when(mWm.mPolicy).isScreenOn();
- // Preparation: Simulate snapshot IME surface.
+ // Preparation: Simulate IME screenshot surface.
spyOn(mWm.mTaskSnapshotController);
ScreenCapture.ScreenshotHardwareBuffer mockHwBuffer = mock(
ScreenCapture.ScreenshotHardwareBuffer.class);
doReturn(mock(HardwareBuffer.class)).when(mockHwBuffer).getHardwareBuffer();
- doReturn(mockHwBuffer).when(mWm.mTaskSnapshotController).snapshotImeFromAttachedTask(any());
+ doReturn(mockHwBuffer).when(mWm.mTaskSnapshotController)
+ .screenshotImeFromAttachedTask(any());
// Preparation: Simulate snapshot Task.
ActivityRecord act1 = createActivityRecord(mDisplayContent);
@@ -2298,22 +2299,22 @@
TYPE_BASE_APPLICATION).setWindowToken(act2).build();
appWin2.setHasSurface(true);
assertTrue(appWin2.canBeImeLayeringTarget());
- doReturn(true).when(appWin1).inTransitionSelfOrParent();
+ doReturn(true).when(appWin1).inTransition();
- // Test step 3: Verify appWin2 will be the next IME layering target and the IME snapshot
+ // Test step 3: Verify appWin2 will be the next IME layering target and the IME screenshot
// surface will be attached and shown on the display at this time.
mDisplayContent.computeImeLayeringTarget(true /* update */);
assertEquals(appWin2, mDisplayContent.getImeLayeringTarget());
assertTrue(mDisplayContent.shouldImeAttachedToApp());
verify(mDisplayContent, atLeast(1)).showImeScreenshot();
- verify(mWm.mTaskSnapshotController).snapshotImeFromAttachedTask(appWin1.getTask());
+ verify(mWm.mTaskSnapshotController).screenshotImeFromAttachedTask(appWin1.getTask());
assertNotNull(mDisplayContent.mImeScreenshot);
}
@SetupWindows(addWindows = W_INPUT_METHOD)
@Test
- public void testShowImeScreenshot_removeCurSnapshotBeforeCreateNext() {
+ public void testShowImeScreenshot_removeCurScreenshotBeforeCreateNext() {
final Task rootTask = createTask(mDisplayContent);
final Task task = createTaskInRootTask(rootTask, 0 /* userId */);
final ActivityRecord activity = createActivityRecord(mDisplayContent, task);
@@ -2327,15 +2328,15 @@
doReturn(true).when(mDisplayContent.mInputMethodWindow).isVisible();
mDisplayContent.getInsetsStateController().getImeSourceProvider().setImeShowing(true);
- // Verify when the timing of 2 showImeScreenshot invocations are very close, will first
- // detach the current snapshot then create the next one.
+ // Verify that when the timing of 2 showImeScreenshot invocations are very close, it will
+ // first remove the current screenshot and then create the next one.
mDisplayContent.showImeScreenshot();
- DisplayContent.ImeScreenshot curSnapshot = mDisplayContent.mImeScreenshot;
- spyOn(curSnapshot);
+ DisplayContent.ImeScreenshot curScreenshot = mDisplayContent.mImeScreenshot;
+ spyOn(curScreenshot);
mDisplayContent.showImeScreenshot();
- verify(curSnapshot).detach(any());
+ verify(curScreenshot).removeSurface(any());
assertNotNull(mDisplayContent.mImeScreenshot);
- assertNotEquals(curSnapshot, mDisplayContent.mImeScreenshot);
+ assertNotEquals(curScreenshot, mDisplayContent.mImeScreenshot);
}
@UseTestDisplay(addWindows = {W_INPUT_METHOD})
@@ -2354,8 +2355,8 @@
mDisplayContent.showImeScreenshot();
assertNotNull(mDisplayContent.mImeScreenshot);
- // Expect IME snapshot will be removed when the win is IME layering target and invoked
- // removeImeSurfaceByTarget.
+ // Expect IME screenshot will be removed when the win is IME layering target and invoked
+ // removeImeScreenshotByTarget.
win.removeImmediately();
assertNull(mDisplayContent.mImeScreenshot);
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
index 19cdb13..3b4fa6e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
@@ -85,6 +85,7 @@
import com.android.server.testutils.OffsettableClock;
import com.android.server.testutils.TestHandler;
import com.android.server.wm.DisplayContent.FixedRotationTransitionListener;
+import com.android.server.wm.utils.DeviceStateTestUtils;
import org.junit.AfterClass;
import org.junit.Before;
@@ -918,7 +919,8 @@
enableOrientationSensor();
- mTarget.foldStateChanged(DeviceStateController.DeviceState.OPEN);
+ mTarget.foldStateChanged(DeviceStateController.DeviceStateEnum.OPEN,
+ DeviceStateTestUtils.OPEN);
freezeRotation(Surface.ROTATION_270);
mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_0));
@@ -929,7 +931,8 @@
clearInvocations(sMockWm);
// ... until half-fold
- mTarget.foldStateChanged(DeviceStateController.DeviceState.HALF_FOLDED);
+ mTarget.foldStateChanged(DeviceStateController.DeviceStateEnum.HALF_FOLDED,
+ DeviceStateTestUtils.HALF_FOLDED);
assertTrue(waitForUiHandler());
verify(sMockWm).updateRotation(false, false);
assertTrue(waitForUiHandler());
@@ -937,7 +940,8 @@
SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0));
// ... then transition back to flat
- mTarget.foldStateChanged(DeviceStateController.DeviceState.OPEN);
+ mTarget.foldStateChanged(DeviceStateController.DeviceStateEnum.OPEN,
+ DeviceStateTestUtils.OPEN);
assertTrue(waitForUiHandler());
verify(sMockWm, atLeast(1)).updateRotation(false, false);
assertTrue(waitForUiHandler());
@@ -954,7 +958,8 @@
enableOrientationSensor();
- mTarget.foldStateChanged(DeviceStateController.DeviceState.OPEN);
+ mTarget.foldStateChanged(DeviceStateController.DeviceStateEnum.OPEN,
+ DeviceStateTestUtils.OPEN);
freezeRotation(Surface.ROTATION_270);
mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_0));
@@ -965,7 +970,8 @@
clearInvocations(sMockWm);
// ... half-fold -> still no rotation
- mTarget.foldStateChanged(DeviceStateController.DeviceState.HALF_FOLDED);
+ mTarget.foldStateChanged(DeviceStateController.DeviceStateEnum.HALF_FOLDED,
+ DeviceStateTestUtils.HALF_FOLDED);
assertTrue(waitForUiHandler());
verify(sMockWm).updateRotation(false, false);
assertTrue(waitForUiHandler());
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java
index 9ab20d1..c4d72dd 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java
@@ -508,7 +508,9 @@
public void testShouldShowImeOnDisplayWithinForceDesktopMode() {
try {
// Presume display enabled force desktop mode from developer options.
- final DisplayContent dc = createMockSimulatedDisplay();
+ final SettingsEntry settingsEntry = new SettingsEntry();
+ settingsEntry.mShouldShowSystemDecors = true;
+ final DisplayContent dc = createMockSimulatedDisplay(settingsEntry);
mWm.setForceDesktopModeOnExternalDisplays(true);
final WindowManagerInternal wmInternal = LocalServices.getService(
WindowManagerInternal.class);
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
index 58b78b8..de567b4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
@@ -509,6 +509,7 @@
final InsetsSourceProvider imeSourceProvider =
getController().getOrCreateSourceProvider(ID_IME, ime());
imeSourceProvider.setWindowContainer(ime, null, null);
+ imeSourceProvider.setServerVisible(true);
waitUntilHandlersIdle();
clearInvocations(mDisplayContent);
diff --git a/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java
index 0101c3c..9fe9f87 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java
@@ -20,8 +20,6 @@
import static android.view.SurfaceControl.RefreshRateRange.FLOAT_TOLERANCE;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
-import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
-
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -34,6 +32,7 @@
import android.view.Display.Mode;
import android.view.Surface;
import android.view.WindowInsets;
+import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
import androidx.test.filters.SmallTest;
@@ -328,9 +327,8 @@
assertEquals(LOW_REFRESH_RATE,
mPolicy.getPreferredMaxRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE);
- cameraUsingWindow.mActivityRecord.mSurfaceAnimator.startAnimation(
- cameraUsingWindow.getPendingTransaction(), mock(AnimationAdapter.class),
- false /* hidden */, ANIMATION_TYPE_APP_TRANSITION);
+ requestTransition(cameraUsingWindow.mActivityRecord, WindowManager.TRANSIT_OPEN);
+ mDisplayContent.mTransitionController.collect(cameraUsingWindow.mActivityRecord);
assertEquals(0, mPolicy.getPreferredModeId(cameraUsingWindow));
assertFalse(mPolicy.updateFrameRateVote(cameraUsingWindow));
assertEquals(FRAME_RATE_VOTE_NONE, cameraUsingWindow.mFrameRateVote);
@@ -349,9 +347,8 @@
assertEquals(0, mPolicy.getPreferredMinRefreshRate(window), FLOAT_TOLERANCE);
assertEquals(LOW_REFRESH_RATE, mPolicy.getPreferredMaxRefreshRate(window), FLOAT_TOLERANCE);
- window.mActivityRecord.mSurfaceAnimator.startAnimation(
- window.getPendingTransaction(), mock(AnimationAdapter.class),
- false /* hidden */, ANIMATION_TYPE_APP_TRANSITION);
+ requestTransition(window.mActivityRecord, WindowManager.TRANSIT_OPEN);
+ mDisplayContent.mTransitionController.collect(window.mActivityRecord);
assertEquals(0, mPolicy.getPreferredModeId(window));
assertFalse(mPolicy.updateFrameRateVote(window));
assertEquals(FRAME_RATE_VOTE_NONE, window.mFrameRateVote);
@@ -370,9 +367,8 @@
assertEquals(LOW_REFRESH_RATE, mPolicy.getPreferredMinRefreshRate(window), FLOAT_TOLERANCE);
assertEquals(0, mPolicy.getPreferredMaxRefreshRate(window), FLOAT_TOLERANCE);
- window.mActivityRecord.mSurfaceAnimator.startAnimation(
- window.getPendingTransaction(), mock(AnimationAdapter.class),
- false /* hidden */, ANIMATION_TYPE_APP_TRANSITION);
+ requestTransition(window.mActivityRecord, WindowManager.TRANSIT_OPEN);
+ mDisplayContent.mTransitionController.collect(window.mActivityRecord);
assertEquals(0, mPolicy.getPreferredModeId(window));
assertFalse(mPolicy.updateFrameRateVote(window));
assertEquals(FRAME_RATE_VOTE_NONE, window.mFrameRateVote);
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java b/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java
index d965125..75c4b09 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java
@@ -832,102 +832,6 @@
}
@Test
- public void testMoveHomeRootTaskBehindBottomMostVisible_NoMoveHomeBehindFullscreen() {
- final Task homeRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
- WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
- final Task fullscreenRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
- WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-
- doReturn(false).when(homeRootTask).isTranslucent(any());
- doReturn(false).when(fullscreenRootTask).isTranslucent(any());
-
- // Ensure that we don't move the home root task if it is already behind the top fullscreen
- // root task.
- int homeRootTaskIndex = getTaskIndexOf(mDefaultTaskDisplayArea, homeRootTask);
- assertEquals(fullscreenRootTask, getRootTaskAbove(homeRootTask));
- mDefaultTaskDisplayArea.moveRootTaskBehindBottomMostVisibleRootTask(homeRootTask);
- assertEquals(homeRootTaskIndex, getTaskIndexOf(mDefaultTaskDisplayArea, homeRootTask));
- }
-
- @Test
- public void testMoveHomeRootTaskBehindBottomMostVisible_NoMoveHomeBehindTranslucent() {
- final Task homeRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
- WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
- final Task fullscreenRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
- WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-
- doReturn(false).when(homeRootTask).isTranslucent(any());
- doReturn(true).when(fullscreenRootTask).isTranslucent(any());
-
- // Ensure that we don't move the home root task if it is already behind the top fullscreen
- // root task.
- int homeRootTaskIndex = getTaskIndexOf(mDefaultTaskDisplayArea, homeRootTask);
- assertEquals(fullscreenRootTask, getRootTaskAbove(homeRootTask));
- mDefaultTaskDisplayArea.moveRootTaskBehindBottomMostVisibleRootTask(homeRootTask);
- assertEquals(homeRootTaskIndex, getTaskIndexOf(mDefaultTaskDisplayArea, homeRootTask));
- }
-
- @Test
- public void testMoveHomeRootTaskBehindBottomMostVisible_NoMoveHomeOnTop() {
- final Task fullscreenRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
- WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
- final Task homeRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
- WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
-
- doReturn(false).when(homeRootTask).isTranslucent(any());
- doReturn(false).when(fullscreenRootTask).isTranslucent(any());
-
- // Ensure we don't move the home root task if it is already on top
- int homeRootTaskIndex = getTaskIndexOf(mDefaultTaskDisplayArea, homeRootTask);
- assertNull(getRootTaskAbove(homeRootTask));
- mDefaultTaskDisplayArea.moveRootTaskBehindBottomMostVisibleRootTask(homeRootTask);
- assertEquals(homeRootTaskIndex, getTaskIndexOf(mDefaultTaskDisplayArea, homeRootTask));
- }
-
- @Test
- public void testMoveHomeRootTaskBehindBottomMostVisible_MoveHomeBehindFullscreen() {
- final Task homeRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
- WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
- final Task fullscreenRootTask1 = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
- WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
- final Task fullscreenRootTask2 = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
- WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
- final Task pinnedRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
- WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-
- doReturn(false).when(homeRootTask).isTranslucent(any());
- doReturn(false).when(fullscreenRootTask1).isTranslucent(any());
- doReturn(false).when(fullscreenRootTask2).isTranslucent(any());
-
- // Ensure that we move the home root task behind the bottom most fullscreen root task,
- // ignoring the pinned root task.
- assertEquals(fullscreenRootTask1, getRootTaskAbove(homeRootTask));
- mDefaultTaskDisplayArea.moveRootTaskBehindBottomMostVisibleRootTask(homeRootTask);
- assertEquals(fullscreenRootTask2, getRootTaskAbove(homeRootTask));
- }
-
- @Test
- public void
- testMoveHomeRootTaskBehindBottomMostVisible_MoveHomeBehindFullscreenAndTranslucent() {
- final Task homeRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
- WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
- final Task fullscreenRootTask1 = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
- WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
- final Task fullscreenRootTask2 = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
- WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-
- doReturn(false).when(homeRootTask).isTranslucent(any());
- doReturn(false).when(fullscreenRootTask1).isTranslucent(any());
- doReturn(true).when(fullscreenRootTask2).isTranslucent(any());
-
- // Ensure that we move the home root task behind the bottom most non-translucent fullscreen
- // root task.
- assertEquals(fullscreenRootTask1, getRootTaskAbove(homeRootTask));
- mDefaultTaskDisplayArea.moveRootTaskBehindBottomMostVisibleRootTask(homeRootTask);
- assertEquals(fullscreenRootTask1, getRootTaskAbove(homeRootTask));
- }
-
- @Test
public void testMoveHomeRootTaskBehindRootTask_BehindHomeRootTask() {
final Task fullscreenRootTask1 = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
index 34e9bed..56f4bb6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
@@ -292,8 +292,6 @@
@Test
public void testTaskLayerRankFreeform() {
- mSetFlagsRule.enableFlags(com.android.window.flags.Flags
- .FLAG_PROCESS_PRIORITY_POLICY_FOR_MULTI_WINDOW_MODE);
final Task[] freeformTasks = new Task[3];
final WindowProcessController[] processes = new WindowProcessController[3];
for (int i = 0; i < freeformTasks.length; i++) {
@@ -435,7 +433,7 @@
ensureTaskPlacement(fullscreenTask, firstActivity, secondActivity);
// Move first activity to pinned root task.
- mRootWindowContainer.moveActivityToPinnedRootTask(firstActivity, "initialMove");
+ mRootWindowContainer.moveActivityToPinnedRootTaskForTest(firstActivity, "initialMove");
final TaskDisplayArea taskDisplayArea = fullscreenTask.getDisplayArea();
Task pinnedRootTask = taskDisplayArea.getRootPinnedTask();
@@ -444,7 +442,7 @@
ensureTaskPlacement(fullscreenTask, secondActivity);
// Move second activity to pinned root task.
- mRootWindowContainer.moveActivityToPinnedRootTask(secondActivity, "secondMove");
+ mRootWindowContainer.moveActivityToPinnedRootTaskForTest(secondActivity, "secondMove");
// Need to get root tasks again as a new instance might have been created.
pinnedRootTask = taskDisplayArea.getRootPinnedTask();
@@ -470,7 +468,7 @@
transientActivity.setState(RESUMED, "test");
transientActivity.getTask().moveToFront("test");
- mRootWindowContainer.moveActivityToPinnedRootTask(activity2, "test");
+ mRootWindowContainer.moveActivityToPinnedRootTaskForTest(activity2, "test");
assertEquals("Created PiP task must not change focus", transientActivity.getTask(),
mRootWindowContainer.getTopDisplayFocusedRootTask());
final Task newPipTask = activity2.getTask();
@@ -495,7 +493,7 @@
final Task task = activity.getTask();
// Move activity to pinned root task.
- mRootWindowContainer.moveActivityToPinnedRootTask(activity, "test");
+ mRootWindowContainer.moveActivityToPinnedRootTaskForTest(activity, "test");
// Ensure a task has moved over.
ensureTaskPlacement(task, activity);
@@ -533,7 +531,7 @@
final Task task = activity.getTask();
// Move activity to pinned root task.
- mRootWindowContainer.moveActivityToPinnedRootTask(activity, "test");
+ mRootWindowContainer.moveActivityToPinnedRootTaskForTest(activity, "test");
// Ensure a task has moved over.
ensureTaskPlacement(task, activity);
@@ -557,7 +555,7 @@
final ActivityRecord secondActivity = taskFragment.getBottomMostActivity();
// Move first activity to pinned root task.
- mRootWindowContainer.moveActivityToPinnedRootTask(firstActivity, "test");
+ mRootWindowContainer.moveActivityToPinnedRootTaskForTest(firstActivity, "test");
final TaskDisplayArea taskDisplayArea = fullscreenTask.getDisplayArea();
final Task pinnedRootTask = taskDisplayArea.getRootPinnedTask();
@@ -588,7 +586,7 @@
final ActivityRecord topActivity = taskFragment.getTopMostActivity();
// Move the top activity to pinned root task.
- mRootWindowContainer.moveActivityToPinnedRootTask(topActivity, "test");
+ mRootWindowContainer.moveActivityToPinnedRootTaskForTest(topActivity, "test");
final Task pinnedRootTask = task.getDisplayArea().getRootPinnedTask();
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index 142cb47..d8bd266 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -123,7 +123,7 @@
import com.android.internal.policy.SystemBarUtils;
import com.android.internal.statusbar.LetterboxDetails;
import com.android.server.statusbar.StatusBarManagerInternal;
-import com.android.server.wm.DeviceStateController.DeviceState;
+import com.android.server.wm.DeviceStateController.DeviceStateEnum;
import com.android.window.flags.Flags;
import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges;
@@ -4469,10 +4469,10 @@
boolean isTabletop) {
final DisplayRotation r = activity.mDisplayContent.getDisplayRotation();
doReturn(isHalfFolded).when(r).isDisplaySeparatingHinge();
- doReturn(false).when(r).isDeviceInPosture(any(DeviceState.class), anyBoolean());
+ doReturn(false).when(r).isDeviceInPosture(any(DeviceStateEnum.class), anyBoolean());
if (isHalfFolded) {
doReturn(true).when(r)
- .isDeviceInPosture(DeviceState.HALF_FOLDED, isTabletop);
+ .isDeviceInPosture(DeviceStateEnum.HALF_FOLDED, isTabletop);
}
activity.recomputeConfiguration();
}
@@ -5431,12 +5431,18 @@
public void testUniversalResizeableByDefault() {
mSetFlagsRule.enableFlags(Flags.FLAG_UNIVERSAL_RESIZABLE_BY_DEFAULT);
mDisplayContent.setIgnoreOrientationRequest(false);
- setUpApp(mDisplayContent);
+ setUpApp(mDisplayContent, new ActivityBuilder(mAtm)
+ .setResizeMode(RESIZE_MODE_UNRESIZEABLE)
+ .setScreenOrientation(SCREEN_ORIENTATION_LANDSCAPE)
+ .setMinAspectRatio(OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE));
assertFalse(mActivity.isUniversalResizeable());
+ mActivity.mAppCompatController.getSizeCompatModePolicy().updateAppCompatDisplayInsets();
+ assertNotNull(mActivity.getAppCompatDisplayInsets());
mDisplayContent.setIgnoreOrientationRequest(true);
makeDisplayLargeScreen(mDisplayContent);
assertTrue(mActivity.isUniversalResizeable());
+ assertFitted();
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
index 7c1d7fec..bdd00e6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
@@ -20,6 +20,7 @@
import static android.Manifest.permission.MANAGE_ACTIVITY_TASKS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
@@ -67,6 +68,7 @@
import android.graphics.Color;
import android.graphics.Rect;
import android.os.Binder;
+import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
import android.view.SurfaceControl;
import android.view.View;
@@ -326,6 +328,177 @@
}
@Test
+ public void testVisibility_behindEmptyTaskThatFillsParentBounds_visible() {
+ // A fullscreen task with an opaque activity.
+ final Task bottomTask = createTask(mDisplayContent.getDefaultTaskDisplayArea(),
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+ createActivityRecord(bottomTask);
+ // Above it, an empty fullscreen task.
+ createTask(mDisplayContent.getDefaultTaskDisplayArea(),
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+
+ assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE,
+ bottomTask.getVisibility(null /* starting */));
+ }
+
+ @Test
+ public void testVisibility_behindOpaqueTaskFillingParentBounds_invisible() {
+ // A fullscreen task with an opaque activity.
+ final Task bottomTask = createTask(mDisplayContent.getDefaultTaskDisplayArea(),
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+ createActivityRecord(bottomTask);
+ // Above it, an opaque fullscreen task.
+ final Task topTask = createTask(mDisplayContent.getDefaultTaskDisplayArea(),
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+ final ActivityRecord topActivity = createActivityRecord(topTask);
+ topActivity.setOccludesParent(true);
+
+ assertEquals(TASK_FRAGMENT_VISIBILITY_INVISIBLE,
+ bottomTask.getVisibility(topActivity /* starting */));
+ }
+
+ @Test
+ public void testVisibility_behindTranslucentTaskFillingParentBounds_visibleBehindTranslucent() {
+ // A fullscreen task with an opaque activity.
+ final Task bottomTask = createTask(mDisplayContent.getDefaultTaskDisplayArea(),
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+ createActivityRecord(bottomTask);
+ // Above it, a translucent fullscreen task.
+ final Task topTask = createTask(mDisplayContent.getDefaultTaskDisplayArea(),
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+ final ActivityRecord topActivity = createActivityRecord(topTask);
+ topActivity.setOccludesParent(false);
+
+ assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
+ bottomTask.getVisibility(topActivity /* starting */));
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_SEE_THROUGH_TASK_FRAGMENTS)
+ public void testVisibility_behindOpaqueNestedFreeformTasksNotFillingParenBounds_visible() {
+ // A fullscreen task with an opaque activity.
+ final Task bottomTask = createTask(mDisplayContent.getDefaultTaskDisplayArea(),
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+ createActivityRecord(bottomTask);
+ // Above it, a freeform root task with a freeform child task with an opaque activity.
+ final Task topTask = createTask(mDisplayContent.getDefaultTaskDisplayArea(),
+ WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD);
+ final Task topTaskChild = createTaskInRootTask(topTask, 0 /* userId */);
+ topTaskChild.setWindowingMode(WINDOWING_MODE_FREEFORM);
+ topTaskChild.setBounds(1, 1, 2, 2); // It does not fill its parent.
+ final ActivityRecord topActivity = createActivityRecord(topTaskChild);
+ topActivity.setOccludesParent(true);
+
+ // The freeform root should not affect the bottom's visibility because it does not fill
+ // its parent.
+ assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE,
+ bottomTask.getVisibility(topActivity /* starting */));
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_SEE_THROUGH_TASK_FRAGMENTS)
+ public void testVisibility_behindOpaqueNestedFreeformTasksThatFillParenBounds_invisible() {
+ // A fullscreen task with an opaque activity.
+ final Task bottomTask = createTask(mDisplayContent.getDefaultTaskDisplayArea(),
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+ createActivityRecord(bottomTask);
+ // Above it, a freeform root task with a freeform child task with an opaque activity.
+ final Task topTask = createTask(mDisplayContent.getDefaultTaskDisplayArea(),
+ WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD);
+ final Task topTaskChild = createTaskInRootTask(topTask, 0 /* userId */);
+ topTaskChild.setWindowingMode(WINDOWING_MODE_FREEFORM);
+ topTaskChild.setBounds(null); // Fills parent.
+ final ActivityRecord topActivity = createActivityRecord(topTaskChild);
+ topActivity.setOccludesParent(true);
+
+ // The freeform root should not affect the bottom's visibility because it does not fill
+ // its parent.
+ assertEquals(TASK_FRAGMENT_VISIBILITY_INVISIBLE,
+ bottomTask.getVisibility(topActivity /* starting */));
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_SEE_THROUGH_TASK_FRAGMENTS)
+ public void testVisibility_behindTranslucentNestedFreeformFillingBounds_visBehindTranslucent() {
+ // A fullscreen task with an opaque activity.
+ final Task bottomTask = createTask(mDisplayContent.getDefaultTaskDisplayArea(),
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+ createActivityRecord(bottomTask);
+ // Above it, a freeform root task with a freeform child task with a translucent
+ // activity.
+ final Task topTask = createTask(mDisplayContent.getDefaultTaskDisplayArea(),
+ WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD);
+ final Task topTaskChild = createTaskInRootTask(topTask, 0 /* userId */);
+ topTaskChild.setWindowingMode(WINDOWING_MODE_FREEFORM);
+ topTaskChild.setBounds(null);
+ final ActivityRecord topActivity = createActivityRecord(topTaskChild);
+ topActivity.setOccludesParent(false);
+
+ // The freeform root should not affect the bottom's visibility because it does not fill
+ // its parent.
+ assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
+ bottomTask.getVisibility(topActivity /* starting */));
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_SEE_THROUGH_TASK_FRAGMENTS)
+ public void testVisibility_behindAtLeastOneNonFillingAdjacentTaskFragments_visible() {
+ // A fullscreen task with an opaque activity.
+ final Task bottomTask = createTask(mDisplayContent.getDefaultTaskDisplayArea(),
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+ createActivityRecord(bottomTask);
+ // Above it, two adjacent task fragments but one is non-filling.
+ final Task topTask = createTask(mDisplayContent.getDefaultTaskDisplayArea(),
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+ final Rect top = new Rect();
+ final Rect bottom = new Rect();
+ topTask.getBounds().splitVertically(top, bottom);
+ final TaskFragment topAdjacentTaskFragment1 = createTaskFragmentWithActivity(topTask);
+ topAdjacentTaskFragment1.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+ topAdjacentTaskFragment1.setBounds(top);
+ topAdjacentTaskFragment1.getTopMostActivity().setBounds(new Rect(0, 0, 1, 1));
+ final TaskFragment topAdjacentTaskFragment2 = createTaskFragmentWithActivity(topTask);
+ topAdjacentTaskFragment2.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+ topAdjacentTaskFragment2.setBounds(bottom);
+ topAdjacentTaskFragment2.getTopMostActivity().setBounds(null);
+ topAdjacentTaskFragment2.setAdjacentTaskFragments(
+ new TaskFragment.AdjacentSet(topAdjacentTaskFragment2, topAdjacentTaskFragment1));
+
+ assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE,
+ bottomTask.getVisibility(null /* starting */));
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_SEE_THROUGH_TASK_FRAGMENTS)
+ public void testVisibility_behindFillingAdjacentTaskFragments_invisible() {
+ // A fullscreen task with an opaque activity.
+ final Task bottomTask = createTask(mDisplayContent.getDefaultTaskDisplayArea(),
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+ createActivityRecord(bottomTask);
+ // Above it, two adjacent task fragments that are filling.
+ final Task topTask = createTask(mDisplayContent.getDefaultTaskDisplayArea(),
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+ final Rect top = new Rect();
+ final Rect bottom = new Rect();
+ topTask.getBounds().splitVertically(top, bottom);
+ final TaskFragment topAdjacentTaskFragment1 = createTaskFragmentWithActivity(topTask);
+ topAdjacentTaskFragment1.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+ topAdjacentTaskFragment1.setBounds(top);
+ topAdjacentTaskFragment1.getTopMostActivity().setVisible(true);
+ topAdjacentTaskFragment1.getTopMostActivity().visibleIgnoringKeyguard = true;
+ final TaskFragment topAdjacentTaskFragment2 = createTaskFragmentWithActivity(topTask);
+ topAdjacentTaskFragment2.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+ topAdjacentTaskFragment2.setBounds(bottom);
+ topAdjacentTaskFragment2.getTopMostActivity().setVisible(true);
+ topAdjacentTaskFragment2.getTopMostActivity().visibleIgnoringKeyguard = true;
+ topAdjacentTaskFragment2.setAdjacentTaskFragments(
+ new TaskFragment.AdjacentSet(topAdjacentTaskFragment2, topAdjacentTaskFragment1));
+
+ assertEquals(TASK_FRAGMENT_VISIBILITY_INVISIBLE,
+ bottomTask.getVisibility(null /* starting */));
+ }
+
+ @Test
public void testFindTopNonFinishingActivity_ignoresLaunchedFromBubbleActivities() {
final ActivityOptions opts = ActivityOptions.makeBasic();
opts.setTaskAlwaysOnTop(true);
@@ -403,7 +576,7 @@
assertNotEquals(TaskFragmentAnimationParams.DEFAULT, taskFragment0.getAnimationParams());
// Move activity to pinned root task.
- mRootWindowContainer.moveActivityToPinnedRootTask(activity, "test");
+ mRootWindowContainer.moveActivityToPinnedRootTaskForTest(activity, "test");
// Ensure taskFragment requested config is reset.
assertEquals(taskFragment0, activity.getOrganizedTaskFragment());
@@ -433,7 +606,7 @@
spyOn(mAtm.mTaskFragmentOrganizerController);
// Move activity to pinned.
- mRootWindowContainer.moveActivityToPinnedRootTask(activity0, "test");
+ mRootWindowContainer.moveActivityToPinnedRootTaskForTest(activity0, "test");
// Ensure taskFragment requested config is reset.
assertTrue(taskFragment0.mClearedTaskFragmentForPip);
@@ -467,7 +640,7 @@
.createActivityCount(1)
.build();
final ActivityRecord activity = taskFragment.getTopMostActivity();
- mRootWindowContainer.moveActivityToPinnedRootTask(activity, "test");
+ mRootWindowContainer.moveActivityToPinnedRootTaskForTest(activity, "test");
spyOn(mAtm.mTaskFragmentOrganizerController);
assertEquals(mIOrganizer, activity.mLastTaskFragmentOrganizerBeforePip);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java
index 1bca53a..dee94ab 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java
@@ -46,17 +46,11 @@
import android.graphics.Point;
import android.graphics.Rect;
import android.hardware.HardwareBuffer;
-import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
-import android.util.ArraySet;
import android.window.TaskSnapshot;
import androidx.test.filters.SmallTest;
-import com.android.window.flags.Flags;
-
-import com.google.android.collect.Sets;
-
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
@@ -73,65 +67,6 @@
public class TaskSnapshotControllerTest extends WindowTestsBase {
@Test
- public void testGetClosingApps_closing() {
- final WindowState closingWindow = newWindowBuilder("closingWindow",
- FIRST_APPLICATION_WINDOW).build();
- closingWindow.mActivityRecord.commitVisibility(
- false /* visible */, true /* performLayout */);
- final ArraySet<ActivityRecord> closingApps = new ArraySet<>();
- closingApps.add(closingWindow.mActivityRecord);
- final ArraySet<Task> closingTasks = new ArraySet<>();
- getClosingTasks(closingApps, closingTasks);
- assertEquals(1, closingTasks.size());
- assertEquals(closingWindow.mActivityRecord.getTask(), closingTasks.valueAt(0));
- }
-
- @Test
- public void testGetClosingApps_notClosing() {
- final WindowState closingWindow = newWindowBuilder("closingWindow",
- FIRST_APPLICATION_WINDOW).build();
- final WindowState openingWindow = createAppWindow(closingWindow.getTask(),
- FIRST_APPLICATION_WINDOW, "openingWindow");
- closingWindow.mActivityRecord.commitVisibility(
- false /* visible */, true /* performLayout */);
- openingWindow.mActivityRecord.commitVisibility(
- true /* visible */, true /* performLayout */);
- final ArraySet<ActivityRecord> closingApps = new ArraySet<>();
- closingApps.add(closingWindow.mActivityRecord);
- final ArraySet<Task> closingTasks = new ArraySet<>();
- getClosingTasks(closingApps, closingTasks);
- assertEquals(0, closingTasks.size());
- }
-
- @Test
- public void testGetClosingApps_skipClosingAppsSnapshotTasks() {
- final WindowState closingWindow = newWindowBuilder("closingWindow",
- FIRST_APPLICATION_WINDOW).build();
- closingWindow.mActivityRecord.commitVisibility(
- false /* visible */, true /* performLayout */);
- final ArraySet<ActivityRecord> closingApps = new ArraySet<>();
- closingApps.add(closingWindow.mActivityRecord);
- final ArraySet<Task> closingTasks = new ArraySet<>();
- mWm.mTaskSnapshotController.addSkipClosingAppSnapshotTasks(
- Sets.newArraySet(closingWindow.mActivityRecord.getTask()));
- getClosingTasks(closingApps, closingTasks);
- assertEquals(0, closingTasks.size());
- }
-
- /** Retrieves all closing tasks based on the list of closing apps during an app transition. */
- private void getClosingTasks(ArraySet<ActivityRecord> closingApps,
- ArraySet<Task> outClosingTasks) {
- outClosingTasks.clear();
- for (int i = closingApps.size() - 1; i >= 0; i--) {
- final ActivityRecord activity = closingApps.valueAt(i);
- final Task task = activity.getTask();
- if (task == null) continue;
-
- mWm.mTaskSnapshotController.getClosingTasksInner(task, outClosingTasks);
- }
- }
-
- @Test
public void testGetSnapshotMode() {
final WindowState disabledWindow = newWindowBuilder("disabledWindow",
FIRST_APPLICATION_WINDOW).setDisplay(mDisplayContent).build();
@@ -291,7 +226,6 @@
}
@Test
- @EnableFlags(Flags.FLAG_EXCLUDE_DRAWING_APP_THEME_SNAPSHOT_FROM_LOCK)
public void testRecordTaskSnapshot() {
spyOn(mWm.mTaskSnapshotController.mCache);
spyOn(mWm.mTaskSnapshotController);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
index e57f114..b0c7e06 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
@@ -241,7 +241,7 @@
final Task originalTask = activityMain.getTask();
final ActivityRecord activityPip = new ActivityBuilder(mAtm).setTask(originalTask).build();
activityPip.setState(RESUMED, "test");
- mAtm.mRootWindowContainer.moveActivityToPinnedRootTask(activityPip, "test");
+ mAtm.mRootWindowContainer.moveActivityToPinnedRootTaskForTest(activityPip, "test");
final Task pinnedActivityTask = activityPip.getTask();
// Simulate pinnedActivityTask unintentionally added to recent during top activity resume.
diff --git a/services/tests/wmtests/src/com/android/server/TransitionSubject.java b/services/tests/wmtests/src/com/android/server/wm/TransitionSubject.java
similarity index 100%
rename from services/tests/wmtests/src/com/android/server/TransitionSubject.java
rename to services/tests/wmtests/src/com/android/server/wm/TransitionSubject.java
diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
index 5699c29..209e26f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -1128,7 +1128,8 @@
mDisplayContent.getDisplayRotation().setRotation(mDisplayContent.getRotation() + 1);
mDisplayContent.setLastHasContent();
- mDisplayContent.requestChangeTransition(1 /* any changes */, null /* displayChange */);
+ mDisplayContent.requestChangeTransition(1 /* any changes */, null /* displayChange */,
+ ActionChain.test());
assertEquals(WindowContainer.SYNC_STATE_NONE, statusBar.mSyncState);
assertEquals(WindowContainer.SYNC_STATE_NONE, navBar.mSyncState);
assertEquals(WindowContainer.SYNC_STATE_NONE, screenDecor.mSyncState);
@@ -1265,7 +1266,8 @@
// so the previous async rotation controller should still exist.
mDisplayContent.getDisplayRotation().setRotation(mDisplayContent.getRotation() + 1);
mDisplayContent.setLastHasContent();
- mDisplayContent.requestChangeTransition(1 /* changes */, null /* displayChange */);
+ mDisplayContent.requestChangeTransition(1 /* changes */, null /* displayChange */,
+ ActionChain.test());
assertTrue(mDisplayContent.hasTopFixedRotationLaunchingApp());
assertNotNull(mDisplayContent.getAsyncRotationController());
@@ -1316,7 +1318,8 @@
mDisplayContent.setFixedRotationLaunchingAppUnchecked(app);
registerTestTransitionPlayer();
mDisplayContent.setLastHasContent();
- mDisplayContent.requestChangeTransition(1 /* changes */, null /* displayChange */);
+ mDisplayContent.requestChangeTransition(1 /* changes */, null /* displayChange */,
+ ActionChain.test());
assertNotNull(mDisplayContent.getAsyncRotationController());
mDisplayContent.setFixedRotationLaunchingAppUnchecked(null);
assertNull("Clear rotation controller if rotation is not changed",
diff --git a/services/tests/wmtests/src/com/android/server/wm/TransparentPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/TransparentPolicyTest.java
index 9cd302e..21a12fc 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransparentPolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransparentPolicyTest.java
@@ -27,7 +27,6 @@
import static org.mockito.Mockito.clearInvocations;
-import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
import androidx.annotation.NonNull;
@@ -230,7 +229,6 @@
});
}
- @EnableFlags(com.android.window.flags.Flags.FLAG_RESPECT_NON_TOP_VISIBLE_FIXED_ORIENTATION)
@Test
public void testNotRunStrategyToTranslucentActivitiesIfRespectOrientation() {
runTestScenario(robot -> robot.transparentActivity(ta -> ta.applyOnActivity((a) -> {
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTestSupport.kt b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTestSupport.kt
index a165d20..74d8691 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTestSupport.kt
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTestSupport.kt
@@ -24,7 +24,6 @@
import com.android.server.LocalServices
import com.android.server.input.InputManagerService
import com.android.server.policy.WindowManagerPolicy
-import com.android.window.flags.Flags
/**
* Provides support for tests that require a [WindowManagerService].
@@ -102,11 +101,8 @@
LocalServices.removeServiceForTest(WindowManagerPolicy::class.java)
LocalServices.removeServiceForTest(WindowManagerInternal::class.java)
LocalServices.removeServiceForTest(ImeTargetVisibilityPolicy::class.java)
-
- if (Flags.condenseConfigurationChangeForSimpleMode()) {
- LocalServices.removeServiceForTest(
- ConfigurationChangeSetting.ConfigurationChangeSettingInternal::class.java,
- )
- }
+ LocalServices.removeServiceForTest(
+ ConfigurationChangeSetting.ConfigurationChangeSettingInternal::class.java
+ )
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
index 5fda0c8..c0241f4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
@@ -1147,8 +1147,7 @@
argThat(h -> (h.inputConfig & InputConfig.SPY) == 0));
assertThrows(IllegalArgumentException.class, () ->
- mWm.updateInputChannel(inputChannel.getToken(), null /* hostInputToken */,
- DEFAULT_DISPLAY, surfaceControl,
+ mWm.updateInputChannel(inputChannel.getToken(), DEFAULT_DISPLAY, surfaceControl,
FLAG_NOT_FOCUSABLE, PRIVATE_FLAG_TRUSTED_OVERLAY, INPUT_FEATURE_SPY,
null /* region */));
}
@@ -1171,8 +1170,7 @@
eq(surfaceControl),
argThat(h -> (h.inputConfig & InputConfig.SPY) == 0));
- mWm.updateInputChannel(inputChannel.getToken(), null /* hostInputToken */,
- DEFAULT_DISPLAY, surfaceControl,
+ mWm.updateInputChannel(inputChannel.getToken(), DEFAULT_DISPLAY, surfaceControl,
FLAG_NOT_FOCUSABLE, PRIVATE_FLAG_TRUSTED_OVERLAY, INPUT_FEATURE_SPY,
null /* region */);
verify(mTransaction).setInputWindowInfo(
@@ -1199,8 +1197,7 @@
eq(surfaceControl),
argThat(h -> (h.inputConfig & InputConfig.SENSITIVE_FOR_PRIVACY) == 0));
- mWm.updateInputChannel(inputChannel.getToken(), null /* hostInputToken */,
- DEFAULT_DISPLAY, surfaceControl,
+ mWm.updateInputChannel(inputChannel.getToken(), DEFAULT_DISPLAY, surfaceControl,
FLAG_NOT_FOCUSABLE, PRIVATE_FLAG_TRUSTED_OVERLAY,
INPUT_FEATURE_SENSITIVE_FOR_PRIVACY,
null /* region */);
@@ -1545,7 +1542,6 @@
}
@Test
- @EnableFlags(Flags.FLAG_CONDENSE_CONFIGURATION_CHANGE_FOR_SIMPLE_MODE)
public void createImplFromParcel_invalidSettingType_throwsException() {
final Parcelable.Creator<ConfigurationChangeSetting> creator =
new ConfigurationChangeSetting.CreatorImpl(true /* isSystem */);
@@ -1563,7 +1559,6 @@
}
@Test
- @EnableFlags(Flags.FLAG_CONDENSE_CONFIGURATION_CHANGE_FOR_SIMPLE_MODE)
public void setConfigurationChangeSettingsForUser_createsFromParcel_callsSettingImpl()
throws Settings.SettingNotFoundException {
final int currentUserId = ActivityManager.getCurrentUser();
@@ -1587,16 +1582,6 @@
}
@Test
- @DisableFlags(Flags.FLAG_CONDENSE_CONFIGURATION_CHANGE_FOR_SIMPLE_MODE)
- public void setConfigurationChangeSettingsForUser_flagDisabled_throwsException() {
- final List<ConfigurationChangeSetting> settings = List.of();
-
- assertThrows(IllegalStateException.class, () -> {
- mWm.setConfigurationChangeSettingsForUser(settings, UserHandle.USER_CURRENT);
- });
- }
-
- @Test
@EnableFlags(Flags.FLAG_ENABLE_PERSISTING_DISPLAY_SIZE_FOR_CONNECTED_DISPLAYS)
public void setForcedDisplayDensityRatio_forExternalDisplay_setsRatio() {
final DisplayInfo displayInfo = new DisplayInfo(mDisplayInfo);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index 420f015..66c2ff8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -31,6 +31,7 @@
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
import static android.view.WindowManager.LayoutParams.FLAG_SPLIT_TOUCH;
import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
+import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ABOVE_SUB_PANEL;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
@@ -41,7 +42,11 @@
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+import static android.view.WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
+import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
+import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE;
import static android.view.WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
@@ -277,6 +282,99 @@
}
@Test
+ @DisableFlags(com.android.server.accessibility
+ .Flags.FLAG_ENABLE_MAGNIFICATION_MAGNIFY_NAV_BAR_AND_IME)
+ public void testMagnifyNavAndIme_flagOffAndSettingsEnabled_typeIsIme_shouldNotMagnify() {
+ final ContentResolver cr = useFakeSettingsProvider();
+ Settings.Secure.putInt(cr,
+ Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MAGNIFY_NAV_AND_IME, 1);
+ mWm.mSettingsObserver.onChange(false /* selfChange */,
+ Settings.Secure.getUriFor(
+ Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MAGNIFY_NAV_AND_IME));
+ final WindowState imeWindow = newWindowBuilder("imeWindow", TYPE_INPUT_METHOD).build();
+ final WindowState imeDialogWindow =
+ newWindowBuilder("imeDialogWindow", TYPE_INPUT_METHOD_DIALOG).build();
+ final WindowState navWindow = newWindowBuilder("navWindow", TYPE_NAVIGATION_BAR).build();
+
+ imeWindow.setHasSurface(true);
+ imeDialogWindow.setHasSurface(true);
+ navWindow.setHasSurface(true);
+
+ assertFalse(mWm.isMagnifyNavAndImeEnabled());
+ assertFalse(imeWindow.shouldMagnify());
+ assertFalse(imeDialogWindow.shouldMagnify());
+ assertFalse(navWindow.shouldMagnify());
+ }
+
+ @Test
+ @EnableFlags(com.android.server.accessibility
+ .Flags.FLAG_ENABLE_MAGNIFICATION_MAGNIFY_NAV_BAR_AND_IME)
+ public void testMagnifyNavAndIme_flagOnAndSettingsDisabled_typeIsIme_shouldNotMagnify() {
+ final ContentResolver cr = useFakeSettingsProvider();
+ Settings.Secure.putInt(cr,
+ Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MAGNIFY_NAV_AND_IME, 0);
+ mWm.mSettingsObserver.onChange(false /* selfChange */,
+ Settings.Secure.getUriFor(
+ Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MAGNIFY_NAV_AND_IME));
+ final WindowState imeWindow = newWindowBuilder("imeWindow", TYPE_INPUT_METHOD).build();
+ final WindowState imeDialogWindow =
+ newWindowBuilder("imeDialogWindow", TYPE_INPUT_METHOD_DIALOG).build();
+ final WindowState navWindow = newWindowBuilder("navWindow", TYPE_NAVIGATION_BAR).build();
+
+ imeWindow.setHasSurface(true);
+ imeDialogWindow.setHasSurface(true);
+ navWindow.setHasSurface(true);
+
+ assertFalse(mWm.isMagnifyNavAndImeEnabled());
+ assertFalse(imeWindow.shouldMagnify());
+ assertFalse(imeDialogWindow.shouldMagnify());
+ assertFalse(navWindow.shouldMagnify());
+ }
+
+ @Test
+ public void testMagnifyNavAndIme_typeIsMagnification_shouldNotMagnify() {
+ final WindowState a11yMagWindow = newWindowBuilder("a11yMagWindow",
+ TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY).build();
+ final WindowState magWindow = newWindowBuilder("magWindow",
+ TYPE_MAGNIFICATION_OVERLAY).build();
+ final WindowState navPanelWindow = newWindowBuilder("navPanelWindow",
+ TYPE_NAVIGATION_BAR_PANEL).build();
+
+ a11yMagWindow.setHasSurface(true);
+ magWindow.setHasSurface(true);
+ navPanelWindow.setHasSurface(true);
+
+ assertFalse(a11yMagWindow.shouldMagnify());
+ assertFalse(magWindow.shouldMagnify());
+ assertFalse(navPanelWindow.shouldMagnify());
+ }
+
+ @Test
+ @EnableFlags(com.android.server.accessibility
+ .Flags.FLAG_ENABLE_MAGNIFICATION_MAGNIFY_NAV_BAR_AND_IME)
+ public void testMagnifyNavAndIme_flagOnAndSettingsEnabled_typeIsIme_shouldMagnify() {
+ final ContentResolver cr = useFakeSettingsProvider();
+ Settings.Secure.putInt(cr,
+ Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MAGNIFY_NAV_AND_IME, 1);
+ mWm.mSettingsObserver.onChange(false /* selfChange */,
+ Settings.Secure.getUriFor(
+ Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MAGNIFY_NAV_AND_IME));
+ final WindowState imeWindow = newWindowBuilder("imeWindow", TYPE_INPUT_METHOD).build();
+ final WindowState imeDialogWindow =
+ newWindowBuilder("imeDialogWindow", TYPE_INPUT_METHOD_DIALOG).build();
+ final WindowState navWindow = newWindowBuilder("navWindow", TYPE_NAVIGATION_BAR).build();
+
+ imeWindow.setHasSurface(true);
+ imeDialogWindow.setHasSurface(true);
+ navWindow.setHasSurface(true);
+
+ assertTrue(mWm.isMagnifyNavAndImeEnabled());
+ assertTrue(imeWindow.shouldMagnify());
+ assertTrue(imeDialogWindow.shouldMagnify());
+ assertTrue(navWindow.shouldMagnify());
+ }
+
+ @Test
public void testCanBeImeLayeringTarget() {
final WindowState appWindow = newWindowBuilder("appWindow", TYPE_APPLICATION).build();
final WindowState imeWindow = newWindowBuilder("imeWindow", TYPE_INPUT_METHOD).build();
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 f05d519..34a43aa 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -953,7 +953,7 @@
registerTestTransitionPlayer();
}
controller.requestTransitionIfNeeded(transit, 0 /* flags */, null /* trigger */,
- wc.mDisplayContent);
+ wc.mDisplayContent, ActionChain.test());
}
/** Overrides the behavior of config_reverseDefaultRotation for the given display. */
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTracingPerfettoTest.java b/services/tests/wmtests/src/com/android/server/wm/WindowTracingPerfettoTest.java
index e9eb12f1..7bbd489 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTracingPerfettoTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTracingPerfettoTest.java
@@ -30,6 +30,7 @@
import static org.junit.Assume.assumeTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.timeout;
import static java.io.File.createTempFile;
import static java.nio.file.Files.createTempDirectory;
@@ -67,6 +68,11 @@
public class WindowTracingPerfettoTest {
private static final String TEST_DATA_SOURCE_NAME = "android.windowmanager.test";
+ // The initial snapshot is written to perfetto asynchronously (not by the perfetto onStart
+ // thread). The timeout is used after the perfetto onStart returns (i.e. after the tracing
+ // session has started) to wait till the initial snapshot is actually written.
+ private static final int INITIAL_SNAPSHOT_TIMEOUT_MS = 5000;
+
private static WindowManagerService sWmMock;
private static Choreographer sChoreographerMock;
@Captor
@@ -141,12 +147,16 @@
@Test
public void trace_writesInitialStateSnapshot_whenTracingStarts() {
startTracing(LogFrequency.LOG_FREQUENCY_TRANSACTION);
- verify(sWmMock, times(1)).dumpDebugLocked(any(), eq(WindowTracingLogLevel.ALL));
+ verify(sWmMock, timeout(INITIAL_SNAPSHOT_TIMEOUT_MS)
+ .times(1)).dumpDebugLocked(any(), eq(WindowTracingLogLevel.ALL));
}
@Test
public void trace_writesStateSnapshot_onLogStateCall() {
startTracing(LogFrequency.LOG_FREQUENCY_TRANSACTION);
+ verify(sWmMock, timeout(INITIAL_SNAPSHOT_TIMEOUT_MS)
+ .times(1)).dumpDebugLocked(any(), eq(WindowTracingLogLevel.ALL));
+
sWindowTracing.logState("where");
verify(sWmMock, times(2)).dumpDebugLocked(any(), eq(WindowTracingLogLevel.ALL));
}
@@ -182,7 +192,8 @@
public void dump_writesOneSingleStateSnapshot() {
startTracing(LogFrequency.LOG_FREQUENCY_SINGLE_DUMP);
sWindowTracing.logState("where");
- verify(sWmMock, times(1)).dumpDebugLocked(any(), eq(WindowTracingLogLevel.ALL));
+ verify(sWmMock, timeout(INITIAL_SNAPSHOT_TIMEOUT_MS)
+ .times(1)).dumpDebugLocked(any(), eq(WindowTracingLogLevel.ALL));
}
private void startTracing(LogFrequency logFrequency) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
index 936eadc..b9caebc 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
@@ -522,14 +522,13 @@
spyOn(mDisplayContent.mWmService.mTaskSnapshotController);
doReturn(imeBuffer).when(mDisplayContent.mWmService.mTaskSnapshotController)
- .snapshotImeFromAttachedTask(task);
+ .screenshotImeFromAttachedTask(task);
mDisplayContent.showImeScreenshot(imeAppTarget);
assertEquals(imeAppTarget, mDisplayContent.mImeScreenshot.getImeTarget());
assertNotNull(mDisplayContent.mImeScreenshot);
- assertZOrderGreaterThan(mTransaction,
- mDisplayContent.mImeScreenshot.getImeScreenshotSurface(),
+ assertZOrderGreaterThan(mTransaction, mDisplayContent.mImeScreenshot.getSurface(),
imeAppTarget.mSurfaceControl);
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/utils/DeviceStateTestUtils.java b/services/tests/wmtests/src/com/android/server/wm/utils/DeviceStateTestUtils.java
new file mode 100644
index 0000000..99e040a
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/utils/DeviceStateTestUtils.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.utils;
+
+import android.hardware.devicestate.DeviceState;
+
+/**
+ * Utility class for creating {@link DeviceState} objects for testing.
+ */
+public final class DeviceStateTestUtils {
+ private DeviceStateTestUtils() {
+ }
+
+ // TODO: Update these with the correct properties
+ public static final DeviceState UNKNOWN = new DeviceState(
+ new DeviceState.Configuration.Builder(0, "UNKNOWN").build());
+ public static final DeviceState OPEN = new DeviceState(
+ new DeviceState.Configuration.Builder(1, "OPEN").build());
+ public static final DeviceState FOLDED = new DeviceState(
+ new DeviceState.Configuration.Builder(2, "FOLDED").build());
+ public static final DeviceState HALF_FOLDED = new DeviceState(
+ new DeviceState.Configuration.Builder(3, "HALF_FOLDED").build());
+ public static final DeviceState REAR = new DeviceState(
+ new DeviceState.Configuration.Builder(4, "REAR").build());
+ public static final DeviceState CONCURRENT = new DeviceState(
+ new DeviceState.Configuration.Builder(5, "CONCURRENT").build());
+}
diff --git a/services/usb/java/com/android/server/usb/UsbAlsaJackDetector.java b/services/usb/java/com/android/server/usb/UsbAlsaJackDetector.java
index d4f0b59..3b822ca 100644
--- a/services/usb/java/com/android/server/usb/UsbAlsaJackDetector.java
+++ b/services/usb/java/com/android/server/usb/UsbAlsaJackDetector.java
@@ -16,6 +16,8 @@
package com.android.server.usb;
+import java.util.concurrent.atomic.AtomicBoolean;
+
/**
* Detects and reports ALSA jack state and events.
*/
@@ -27,7 +29,7 @@
private native boolean nativeOutputJackConnected(int card);
private native boolean nativeInputJackConnected(int card);
- private boolean mStopJackDetect = false;
+ private AtomicBoolean mStopJackDetect = new AtomicBoolean(false);
private UsbAlsaDevice mAlsaDevice;
/* use startJackDetect to create a UsbAlsaJackDetector */
@@ -67,9 +69,7 @@
* locking issues), but will cause the thread to exit at the next safe opportunity.
*/
public void pleaseStop() {
- synchronized (this) {
- mStopJackDetect = true;
- }
+ mStopJackDetect.set(true);
}
/**
@@ -78,7 +78,7 @@
*/
public boolean jackDetectCallback() {
synchronized (this) {
- if (mStopJackDetect) {
+ if (mStopJackDetect.get()) {
return false;
}
mAlsaDevice.updateOutputWiredDeviceConnectionState(true);
diff --git a/services/usb/java/com/android/server/usb/UsbAlsaManager.java b/services/usb/java/com/android/server/usb/UsbAlsaManager.java
index d976f5ba..c7a386d4 100644
--- a/services/usb/java/com/android/server/usb/UsbAlsaManager.java
+++ b/services/usb/java/com/android/server/usb/UsbAlsaManager.java
@@ -35,6 +35,7 @@
import com.android.internal.alsa.AlsaCardsParser;
import com.android.internal.util.dump.DualDumpOutputStream;
import com.android.server.usb.descriptors.UsbDescriptorParser;
+import com.android.server.usb.flags.Flags;
import libcore.io.IoUtils;
@@ -55,9 +56,15 @@
// Flag to turn on/off multi-peripheral select mode
// Set to true to have multi-devices mode
- private static final boolean IS_MULTI_MODE = SystemProperties.getBoolean(
+ private static final boolean IS_MULTI_DEV_SINGLE_CONN_MODE = SystemProperties.getBoolean(
"ro.audio.multi_usb_mode", false /*def*/);
+ // Set to true to allow multiple usb audio connections simultaneously.
+ // Note `IS_MULTI_DEV_SINGLE_CONN_MODE` allows multiple devices under ONE active connection.
+ // This will override `IS_MULTI_DEV_SINGLE_CONN_MODE` if set to true.
+ private static final boolean IS_MULTI_DEV_MULTI_CONN_MODE = SystemProperties.getBoolean(
+ "ro.audio.same_type_multi_device_allowed", false /*def*/);
+
private static final String ALSA_DIRECTORY = "/dev/snd/";
private static final int ALSA_DEVICE_TYPE_UNKNOWN = 0;
@@ -294,23 +301,41 @@
UsbDescriptorParser parser) {
if (DEBUG) {
Slog.d(TAG, "usbDeviceAdded(): " + usbDevice.getManufacturerName()
- + " nm:" + usbDevice.getProductName());
+ + " nm:" + usbDevice.getProductName()
+ + " hasAudioInterface: " + parser.hasAudioInterface());
}
- // Scan the Alsa File Space
- mCardsParser.scan();
+ AlsaCardsParser.AlsaCardRecord cardRec = null;
- // Find the ALSA spec for this device address
- AlsaCardsParser.AlsaCardRecord cardRec =
- mCardsParser.findCardNumFor(deviceAddress);
- if (cardRec == null) {
- if (parser.hasAudioInterface()) {
- Slog.e(TAG, "usbDeviceAdded(): cannot find sound card for " + deviceAddress);
+ if (Flags.waitForAlsaScanResultsIfHasAudioInterface()) {
+ if (!parser.hasAudioInterface()) {
+ return;
}
- return;
- }
- waitForAlsaDevice(cardRec.getCardNum(), true /*isAdded*/);
+ waitForAlsaDeviceAddress(deviceAddress, true /*isAdded*/);
+
+ // Find the ALSA spec for this device address
+ cardRec = mCardsParser.findCardNumFor(deviceAddress);
+
+ if (cardRec == null) {
+ Slog.e(TAG, "usbDeviceAdded(): cannot find sound card for " + deviceAddress);
+ return;
+ }
+ } else {
+ // Scan the Alsa File Space
+ mCardsParser.scan();
+
+ // Find the ALSA spec for this device address
+ cardRec = mCardsParser.findCardNumFor(deviceAddress);
+ if (cardRec == null) {
+ if (parser.hasAudioInterface()) {
+ Slog.e(TAG, "usbDeviceAdded(): cannot find sound card for " + deviceAddress);
+ }
+ return;
+ }
+
+ waitForAlsaDevice(cardRec.getCardNum(), true /*isAdded*/);
+ }
// Add it to the devices list
boolean hasInput = parser.hasInput()
@@ -338,15 +363,21 @@
isInputHeadset, isOutputHeadset, isDock);
alsaDevice.setDeviceNameAndDescription(
usbDevice.getProductName(), cardRec.getCardDescription());
- if (IS_MULTI_MODE) {
- deselectCurrentDevice(alsaDevice.getInputDeviceType());
- deselectCurrentDevice(alsaDevice.getOutputDeviceType());
- } else {
- // At single mode, the first device is the selected device.
- if (!mAlsaDevices.isEmpty()) {
- deselectAlsaDevice(mAlsaDevices.get(0));
+
+ // Deselect the current active audio connection to allow the new
+ // device to kick in, unless multiple connections is supported.
+ if (!IS_MULTI_DEV_MULTI_CONN_MODE) {
+ if (IS_MULTI_DEV_SINGLE_CONN_MODE) {
+ deselectCurrentDevice(alsaDevice.getInputDeviceType());
+ deselectCurrentDevice(alsaDevice.getOutputDeviceType());
+ } else {
+ // At single mode, the first device is the selected device.
+ if (!mAlsaDevices.isEmpty()) {
+ deselectAlsaDevice(mAlsaDevices.get(0));
+ }
}
}
+
addAlsaDevice(alsaDevice);
selectAlsaDevice(alsaDevice);
}
@@ -417,15 +448,23 @@
UsbAlsaDevice alsaDevice = removeAlsaDevice(deviceAddress);
Slog.i(TAG, "USB Audio Device Removed: " + alsaDevice);
if (alsaDevice != null) {
- waitForAlsaDevice(alsaDevice.getCardNum(), false /*isAdded*/);
- deselectAlsaDevice(alsaDevice);
- if (IS_MULTI_MODE) {
- selectDefaultDevice(alsaDevice.getOutputDeviceType());
- selectDefaultDevice(alsaDevice.getInputDeviceType());
+ if (Flags.waitForAlsaScanResultsIfHasAudioInterface()) {
+ waitForAlsaDeviceAddress(deviceAddress, false /*isAdded*/);
} else {
- // If there are any external devices left, select the latest attached one
- if (!mAlsaDevices.isEmpty() && mAlsaDevices.get(0) != null) {
- selectAlsaDevice(mAlsaDevices.get(0));
+ waitForAlsaDevice(alsaDevice.getCardNum(), false /*isAdded*/);
+ }
+ deselectAlsaDevice(alsaDevice);
+ // Update the "single" active audio connection when multiple connections
+ // is not supported, with the new default device.
+ if (!IS_MULTI_DEV_MULTI_CONN_MODE) {
+ if (IS_MULTI_DEV_SINGLE_CONN_MODE) {
+ selectDefaultDevice(alsaDevice.getOutputDeviceType());
+ selectDefaultDevice(alsaDevice.getInputDeviceType());
+ } else {
+ // If there are any external devices left, select the latest attached one
+ if (!mAlsaDevices.isEmpty() && mAlsaDevices.get(0) != null) {
+ selectAlsaDevice(mAlsaDevices.get(0));
+ }
}
}
}
@@ -464,6 +503,50 @@
}
}
+ private boolean waitForAlsaDeviceAddress(String addr, boolean isAdded) {
+ if (DEBUG) {
+ Slog.e(TAG, "waitForAlsaDeviceAddress(" + addr + ")");
+ }
+
+ // This value was empirically determined.
+ final int kWaitTimeMs = 2500;
+
+ synchronized (mAlsaCards) {
+ final long timeoutMs = SystemClock.elapsedRealtime() + kWaitTimeMs;
+
+ do {
+ mCardsParser.scan();
+
+ AlsaCardsParser.AlsaCardRecord cardRec =
+ mCardsParser.findCardNumFor(addr);
+
+ if (isAdded && (cardRec != null)) {
+ if (mAlsaCards.contains(cardRec.getCardNum())) {
+ return true;
+ }
+ }
+
+ // Consider removed if can't be probed by the parser as
+ // it's not as time sensitive for the removal case.
+ if (!isAdded && (cardRec == null)) {
+ return true;
+ }
+
+ long waitTimeMs = timeoutMs - SystemClock.elapsedRealtime();
+ if (waitTimeMs <= 0) {
+ Slog.e(TAG, "waitForAlsaDeviceAddress(" + addr + ") timeout");
+ return false;
+ }
+
+ try {
+ mAlsaCards.wait(waitTimeMs);
+ } catch (InterruptedException e) {
+ Slog.d(TAG, "usb: InterruptedException while waiting for ALSA file.");
+ }
+ } while (true);
+ }
+ }
+
private boolean waitForAlsaDevice(int card, boolean isAdded) {
if (DEBUG) {
Slog.e(TAG, "waitForAlsaDevice(c:" + card + ")");
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index f92b788..9cff272 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -2507,19 +2507,6 @@
return;
}
try {
- if ((config & UsbManager.FUNCTION_ADB) != 0) {
- /**
- * Start adbd if ADB function is included in the configuration.
- */
- LocalServices.getService(AdbManagerInternal.class)
- .startAdbdForTransport(AdbTransportType.USB);
- } else {
- /**
- * Stop adbd otherwise
- */
- LocalServices.getService(AdbManagerInternal.class)
- .stopAdbdForTransport(AdbTransportType.USB);
- }
mUsbGadgetHal.setCurrentUsbFunctions(mCurrentRequest,
config, chargingFunctions,
SET_FUNCTIONS_TIMEOUT_MS - SET_FUNCTIONS_LEEWAY_MS, operationId);
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index cd1c485..4f2425f 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -110,6 +110,7 @@
import com.android.server.FgThread;
import com.android.server.LocalServices;
import com.android.server.SoundTriggerInternal;
+import com.android.server.SystemServerInitThreadPool;
import com.android.server.SystemService;
import com.android.server.UiThread;
import com.android.server.pm.UserManagerInternal;
@@ -127,8 +128,9 @@
import java.util.List;
import java.util.Locale;
import java.util.Objects;
-import java.util.Set;
+import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
+import java.util.concurrent.Future;
/**
* SystemService that publishes an IVoiceInteractionManagerService.
@@ -346,6 +348,13 @@
public void onPreCreatedUserConversion(int userId) {
Slogf.d(TAG, "onPreCreatedUserConversion(%d): calling onRoleHoldersChanged() again",
userId);
+ if (mServiceStub.mRoleObserver == null) {
+ try {
+ mServiceStub.mRoleObserver = mServiceStub.mRoleObserverFuture.get();
+ } catch (ExecutionException | InterruptedException e) {
+ Slogf.wtf(TAG, "Unable to get role observer for user %d", userId);
+ }
+ }
mServiceStub.mRoleObserver.onRoleHoldersChanged(RoleManager.ROLE_ASSISTANT,
UserHandle.of(userId));
}
@@ -416,11 +425,23 @@
private final boolean mEnableService;
// TODO(b/226201975): remove reference once RoleService supports pre-created users
- private final RoleObserver mRoleObserver;
+ private final Future<RoleObserver> mRoleObserverFuture;
+ private RoleObserver mRoleObserver;
VoiceInteractionManagerServiceStub() {
mEnableService = shouldEnableService(mContext);
- mRoleObserver = new RoleObserver(mContext.getMainExecutor());
+
+ // If this flag is enabled, initialize in SystemServerInitThreadPool. This is intended
+ // to avoid blocking system_server start on loading resources.
+ if (android.server.Flags.voiceinteractionmanagerserviceGetResourcesInInitThread()) {
+ mRoleObserver = null;
+ mRoleObserverFuture = SystemServerInitThreadPool.submit(() -> {
+ return new RoleObserver(mContext.getMainExecutor());
+ }, "RoleObserver");
+ } else {
+ mRoleObserver = new RoleObserver(mContext.getMainExecutor());
+ mRoleObserverFuture = null;
+ }
}
void handleUserStop(String packageName, int userHandle) {
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 68216b2..b6f03bd 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -949,6 +949,15 @@
public static final String EVENT_MERGE_COMPLETE = "android.telecom.event.MERGE_COMPLETE";
/**
+ * Connection event used to inform Telecom when a disconnect request fails during a call merge.
+ * <p>
+ * Sent via {@link #sendConnectionEvent(String, Bundle)}. The {@link Bundle} parameter is
+ * expected to be null when this connection event is used.
+ */
+ @FlaggedApi(Flags.FLAG_REVERT_DISCONNECTING_DURING_MERGE)
+ public static final String EVENT_DISCONNECT_FAILED = "android.telecom.event.DISCONNECT_FAILED";
+
+ /**
* Connection event used to inform {@link InCallService}s when a call has been put on hold by
* the remote party.
* <p>
diff --git a/tests/BinaryTransparencyHostTest/test-app/AndroidManifest.xml b/tests/BinaryTransparencyHostTest/test-app/AndroidManifest.xml
index 42e616e..54e3a30 100644
--- a/tests/BinaryTransparencyHostTest/test-app/AndroidManifest.xml
+++ b/tests/BinaryTransparencyHostTest/test-app/AndroidManifest.xml
@@ -17,6 +17,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="android.transparency.test.app">
+ <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS"/>
+
<application>
<uses-library android:name="android.test.runner" />
</application>
diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTest.kt
index 48ca36f..11a889c 100644
--- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTest.kt
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTest.kt
@@ -63,31 +63,31 @@
@Presubmit
@Test
- fun imeLayerAndImeSnapshotVisibleOnScreen() {
+ fun imeLayerAndImeScreenshotVisibleOnScreen() {
flicker.assertLayers {
this.isVisible(ComponentNameMatcher.IME)
.then()
- .isVisible(ComponentNameMatcher.IME_SNAPSHOT, isOptional = true)
+ .isVisible(ComponentNameMatcher.IME_SCREENSHOT, isOptional = true)
.then()
- .isInvisible(ComponentNameMatcher.IME_SNAPSHOT, isOptional = true)
+ .isInvisible(ComponentNameMatcher.IME_SCREENSHOT, isOptional = true)
.isInvisible(ComponentNameMatcher.IME)
}
}
@Presubmit
@Test
- fun imeSnapshotAssociatedOnAppVisibleRegion() {
+ fun imeScreenshotAssociatedOnAppVisibleRegion() {
flicker.assertLayers {
- this.invoke("imeSnapshotAssociatedOnAppVisibleRegion") {
- val imeSnapshotLayers =
+ this.invoke("imeScreenshotAssociatedOnAppVisibleRegion") {
+ val imeScreenshotLayers =
it.subjects.filter { subject ->
- subject.name.contains(ComponentNameMatcher.IME_SNAPSHOT.toLayerName()) &&
+ subject.name.contains(ComponentNameMatcher.IME_SCREENSHOT.toLayerName()) &&
subject.isVisible
}
- if (imeSnapshotLayers.isNotEmpty()) {
+ if (imeScreenshotLayers.isNotEmpty()) {
val visibleAreas =
- imeSnapshotLayers.mapNotNull { imeSnapshotLayer ->
- imeSnapshotLayer.layer.visibleRegion
+ imeScreenshotLayers.mapNotNull { imeScreenshotLayer ->
+ imeScreenshotLayer.layer.visibleRegion
}
val imeVisibleRegion = RegionSubject(visibleAreas, timestamp)
val appVisibleRegion = it.visibleRegion(imeTestApp)
diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/OpenImeWindowToFixedPortraitAppTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/OpenImeWindowToFixedPortraitAppTest.kt
index 9b8d86d..50e586a 100644
--- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/OpenImeWindowToFixedPortraitAppTest.kt
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/OpenImeWindowToFixedPortraitAppTest.kt
@@ -29,6 +29,8 @@
import com.android.server.wm.flicker.BaseTest
import com.android.server.wm.flicker.helpers.ImeShownOnAppStartHelper
import com.android.server.wm.flicker.testapp.ActivityOptions.Ime.Default.ACTION_TOGGLE_ORIENTATION
+import com.android.window.flags.Flags
+import org.junit.Assume
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -93,6 +95,8 @@
@Presubmit
@Test
fun appWindowWithLetterboxCoversExactlyOnScreen() {
+ // TODO(b/409043134): Remove assumption when Shell handles setRequestedOrientation().
+ Assume.assumeFalse(Flags.appCompatRefactoring())
val displayBounds = WindowUtils.getDisplayBounds(flicker.scenario.startRotation)
flicker.assertLayersEnd {
this.visibleRegion(testApp.or(ComponentNameMatcher.LETTERBOX))
diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest.kt
index ad083fa..caeb73c 100644
--- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest.kt
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest.kt
@@ -84,8 +84,8 @@
fun imeLayerAlphaOneAfterSnapshotStartingWindowRemoval() {
val layerTrace = flicker.reader.readLayersTrace() ?: error("Unable to read layers trace")
- // Find the entries immediately after the IME snapshot has disappeared
- val imeSnapshotRemovedEntries =
+ // Find the entries immediately after the snapshot starting window was removed
+ val afterSnapshotStartingWindowRemovedEntries =
layerTrace.entries
.asSequence()
.zipWithNext { prev, next ->
@@ -101,7 +101,7 @@
.filterNotNull()
// If we find it, make sure the IME is visible and fully animated in.
- imeSnapshotRemovedEntries.forEach { entry ->
+ afterSnapshotStartingWindowRemovedEntries.forEach { entry ->
val entrySubject = LayerTraceEntrySubject(entry)
val imeLayerSubjects =
entrySubject.subjects.filter {
diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnUnlockScreenTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnUnlockScreenTest.kt
index d47e7ad..920f91e 100644
--- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnUnlockScreenTest.kt
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnUnlockScreenTest.kt
@@ -43,7 +43,7 @@
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
class ShowImeOnUnlockScreenTest(flicker: LegacyFlickerTest) : BaseTest(flicker) {
private val testApp = ImeAppHelper(instrumentation)
- private val imeOrSnapshot = ComponentNameMatcher.IME.or(ComponentNameMatcher.IME_SNAPSHOT)
+ private val imeOrScreenshot = ComponentNameMatcher.IME.or(ComponentNameMatcher.IME_SCREENSHOT)
/** {@inheritDoc} */
override val transition: FlickerBuilder.() -> Unit = {
@@ -66,13 +66,13 @@
fun imeAndAppAnimateTogetherWhenLockingAndUnlocking() {
flicker.assertLayers {
this.isVisible(testApp)
- .isVisible(imeOrSnapshot)
+ .isVisible(imeOrScreenshot)
.then()
.isInvisible(testApp)
- .isInvisible(imeOrSnapshot)
+ .isInvisible(imeOrScreenshot)
.then()
.isVisible(testApp)
- .isVisible(imeOrSnapshot)
+ .isVisible(imeOrScreenshot)
}
}
diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTest.kt
index e3118b4..25741c4 100644
--- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTest.kt
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTest.kt
@@ -40,7 +40,9 @@
import org.junit.runners.Parameterized
/**
- * Test IME snapshot mechanism won't apply when transitioning from non-IME focused dialog activity.
+ * Test IME screenshot mechanism won't apply when transitioning from non-IME focused dialog
+ * activity.
+ *
* To run this test: `atest FlickerTestsIme:ShowImeWhileDismissingThemedPopupDialogTest`
*/
@RunWith(Parameterized::class)
@@ -80,11 +82,11 @@
flicker.assertLayersEnd { this.isVisible(ComponentNameMatcher.IME) }
}
- /** Checks that [ComponentNameMatcher.IME_SNAPSHOT] layer is invisible always. */
+ /** Checks that [ComponentNameMatcher.IME_SCREENSHOT] layer is invisible always. */
@Presubmit
@Test
- fun imeSnapshotNotVisible() {
- flicker.assertLayers { this.isInvisible(ComponentNameMatcher.IME_SNAPSHOT) }
+ fun imeScreenshotNotVisible() {
+ flicker.assertLayers { this.isInvisible(ComponentNameMatcher.IME_SCREENSHOT) }
}
companion object {
diff --git a/tests/Input/AndroidTest.xml b/tests/Input/AndroidTest.xml
index bc9322f..d78bf13 100644
--- a/tests/Input/AndroidTest.xml
+++ b/tests/Input/AndroidTest.xml
@@ -37,7 +37,7 @@
<!-- Pull files created by tests, like the output of screenshot tests -->
<option name="directory-keys" value="/sdcard/Download/InputTests" />
<!-- Pull perfetto traces from DefaultUITraceListener -->
- <option name="pull-pattern-keys" value="perfetto_file_path*" />
+ <option name="pull-pattern-keys" value="perfetto_file_path"/>
<option name="collect-on-run-ended-only" value="false" />
</metrics_collector>
</configuration>
diff --git a/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt b/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt
index 1858b1d..6b92456 100644
--- a/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt
+++ b/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt
@@ -185,8 +185,7 @@
val inputManager = InputManager(context)
whenever(context.getSystemService(InputManager::class.java)).thenReturn(inputManager)
whenever(context.getSystemService(Context.INPUT_SERVICE)).thenReturn(inputManager)
- whenever(context.checkCallingOrSelfPermission(Manifest.permission.MANAGE_KEY_GESTURES))
- .thenReturn(PackageManager.PERMISSION_GRANTED)
+ fakePermissionEnforcer.grant(Manifest.permission.MANAGE_KEY_GESTURES)
ExtendedMockito.doReturn(windowManagerInternal).`when` {
LocalServices.getService(eq(WindowManagerInternal::class.java))
diff --git a/services/tests/wmtests/src/com/android/server/policy/KeyCombinationManagerTests.java b/tests/Input/src/com/android/server/input/KeyCombinationManagerTests.java
similarity index 99%
rename from services/tests/wmtests/src/com/android/server/policy/KeyCombinationManagerTests.java
rename to tests/Input/src/com/android/server/input/KeyCombinationManagerTests.java
index a912c17..90816a7 100644
--- a/services/tests/wmtests/src/com/android/server/policy/KeyCombinationManagerTests.java
+++ b/tests/Input/src/com/android/server/input/KeyCombinationManagerTests.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.policy;
+package com.android.server.input;
import static android.view.KeyEvent.ACTION_DOWN;
import static android.view.KeyEvent.ACTION_UP;
@@ -255,4 +255,4 @@
pressKeys(eventTime, KEYCODE_POWER, eventTime, KEYCODE_VOLUME_DOWN);
assertTrue(mAction1Triggered.await(SCHEDULE_TIME, TimeUnit.MILLISECONDS));
}
-}
\ No newline at end of file
+}
diff --git a/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt b/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt
index 93593e0..c32827f2 100644
--- a/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt
+++ b/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt
@@ -503,18 +503,6 @@
intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
),
TestData(
- "CTRL + ALT + Z -> Accessibility Shortcut",
- intArrayOf(
- KeyEvent.KEYCODE_CTRL_LEFT,
- KeyEvent.KEYCODE_ALT_LEFT,
- KeyEvent.KEYCODE_Z,
- ),
- KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT,
- intArrayOf(KeyEvent.KEYCODE_Z),
- KeyEvent.META_CTRL_ON or KeyEvent.META_ALT_ON,
- intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
- ),
- TestData(
"META + B -> Launch Default Browser",
intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_B),
KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION,
@@ -1278,9 +1266,6 @@
@Test
@Parameters(method = "systemGesturesTestArguments_forKeyCombinations")
- @EnableFlags(
- com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER_MULTI_KEY_GESTURES
- )
fun testKeyCombinationGestures(test: TestData) {
setupKeyGestureController()
testKeyGestureInternal(test)
@@ -1611,6 +1596,20 @@
}
@Test
+ fun testAccessibilityShortcutPressed() {
+ setupKeyGestureController()
+
+ sendKeys(
+ intArrayOf(
+ KeyEvent.KEYCODE_CTRL_LEFT,
+ KeyEvent.KEYCODE_ALT_LEFT,
+ KeyEvent.KEYCODE_Z
+ )
+ )
+ Mockito.verify(accessibilityShortcutController, times(1)).performAccessibilityShortcut()
+ }
+
+ @Test
fun testAccessibilityShortcutChordPressed() {
setupKeyGestureController()
diff --git a/tests/Internal/src/com/android/internal/os/ApplicationSharedMemoryTest.java b/tests/Internal/src/com/android/internal/os/ApplicationSharedMemoryTest.java
index 5ce0ede..68e6169 100644
--- a/tests/Internal/src/com/android/internal/os/ApplicationSharedMemoryTest.java
+++ b/tests/Internal/src/com/android/internal/os/ApplicationSharedMemoryTest.java
@@ -41,12 +41,6 @@
@RunWith(JUnit4.class)
public class ApplicationSharedMemoryTest {
- @Before
- public void setUp() {
- // Skip tests if the feature under test is disabled.
- assumeTrue(Flags.applicationSharedMemoryEnabled());
- }
-
/**
* Every application process, including ours, should have had an instance installed at this
* point.
diff --git a/tests/Tracing/src/android/tracing/perfetto/DataSourceTest.java b/tests/Tracing/src/android/tracing/perfetto/DataSourceTest.java
index d3f444b..02adba8 100644
--- a/tests/Tracing/src/android/tracing/perfetto/DataSourceTest.java
+++ b/tests/Tracing/src/android/tracing/perfetto/DataSourceTest.java
@@ -610,6 +610,55 @@
}
@Test
+ public void canUseDataSourceInstanceInTrace() throws InvalidProtocolBufferException {
+ final Object testObject = new Object();
+
+ sInstanceProvider = (ds, idx, configStream) -> {
+ final TestDataSource.TestDataSourceInstance dsInstance =
+ new TestDataSource.TestDataSourceInstance(ds, idx);
+ dsInstance.testObject = testObject;
+ return dsInstance;
+ };
+
+ final TraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder()
+ .enableCustomTrace(PerfettoConfig.DataSourceConfig.newBuilder()
+ .setName(sTestDataSource.name).build()).build();
+
+ try {
+ traceMonitor.start();
+ sTestDataSource.trace((ctx) -> {
+ try (TestDataSource.TestDataSourceInstance instance =
+ ctx.getDataSourceInstanceLocked()) {
+ if (instance != null) {
+ final ProtoOutputStream protoOutputStream = ctx.newTracePacket();
+ long forTestingToken = protoOutputStream.start(FOR_TESTING);
+ long payloadToken = protoOutputStream.start(PAYLOAD);
+ protoOutputStream.write(SINGLE_INT, instance.testObject.hashCode());
+ protoOutputStream.end(payloadToken);
+ protoOutputStream.end(forTestingToken);
+ }
+ }
+ });
+ } finally {
+ traceMonitor.stop(mWriter);
+ }
+
+ final ResultReader reader = new ResultReader(mWriter.write(), mTraceConfig);
+ final byte[] rawProtoFromFile = reader.readBytes(TraceType.PERFETTO, Tag.ALL);
+ assert rawProtoFromFile != null;
+ final perfetto.protos.TraceOuterClass.Trace trace = perfetto.protos.TraceOuterClass.Trace
+ .parseFrom(rawProtoFromFile);
+
+ Truth.assertThat(trace.getPacketCount()).isGreaterThan(0);
+ final List<TracePacketOuterClass.TracePacket> tracePackets = trace.getPacketList()
+ .stream().filter(TracePacketOuterClass.TracePacket::hasForTesting).toList();
+ final List<TracePacketOuterClass.TracePacket> matchingPackets = tracePackets.stream()
+ .filter(it -> it.getForTesting().getPayload().getSingleInt()
+ == testObject.hashCode()).toList();
+ Truth.assertThat(matchingPackets).hasSize(1);
+ }
+
+ @Test
public void canUseDataSourceInstanceToCreateTlsState() throws InvalidProtocolBufferException {
final Object testObject = new Object();
diff --git a/tests/graphics/SilkFX/src/com/android/test/silkfx/materials/BackgroundBlurActivity.kt b/tests/graphics/SilkFX/src/com/android/test/silkfx/materials/BackgroundBlurActivity.kt
index 4d38660..1013a07 100644
--- a/tests/graphics/SilkFX/src/com/android/test/silkfx/materials/BackgroundBlurActivity.kt
+++ b/tests/graphics/SilkFX/src/com/android/test/silkfx/materials/BackgroundBlurActivity.kt
@@ -98,16 +98,15 @@
backgroundAlphaSeekBar = requireViewById(R.id.set_background_alpha)
blurBehindSeekBar = requireViewById(R.id.set_blur_behind)
dimAmountSeekBar = requireViewById(R.id.set_dim_amount)
-
- arrayOf(blurBackgroundSeekBar, backgroundAlphaSeekBar, blurBehindSeekBar, dimAmountSeekBar)
- .forEach {
- it.setOnSeekBarChangeListener(this)
- }
}
override fun onAttachedToWindow() {
super.onAttachedToWindow()
getWindowManager().addCrossWindowBlurEnabledListener(blurEnabledListener)
+ arrayOf(blurBackgroundSeekBar, backgroundAlphaSeekBar, blurBehindSeekBar, dimAmountSeekBar)
+ .forEach {
+ it.setOnSeekBarChangeListener(this)
+ }
}
override fun onDetachedFromWindow() {
diff --git a/tools/aapt2/cmd/Util_test.cpp b/tools/aapt2/cmd/Util_test.cpp
index 7818340..80dd260 100644
--- a/tools/aapt2/cmd/Util_test.cpp
+++ b/tools/aapt2/cmd/Util_test.cpp
@@ -412,26 +412,33 @@
std::string path;
test_constraints.push_back({});
- ASSERT_TRUE(ParseSplitParameter(CREATE_PATH("v7"),
- diagnostics, &path, &test_constraints.back()));
+ ASSERT_TRUE(ParseSplitParameter(CREATE_PATH("v7"), diagnostics, &path, &test_constraints.back()));
test_constraints.push_back({});
- ASSERT_TRUE(ParseSplitParameter(CREATE_PATH("xhdpi"),
- diagnostics, &path, &test_constraints.back()));
- EXPECT_EQ(test_constraints.size(), 2);
+ ASSERT_TRUE(
+ ParseSplitParameter(CREATE_PATH("xhdpi"), diagnostics, &path, &test_constraints.back()));
+ test_constraints.push_back({});
+ ASSERT_TRUE(
+ ParseSplitParameter(CREATE_PATH("v38.9"), diagnostics, &path, &test_constraints.back()));
+ EXPECT_EQ(test_constraints.size(), 3);
EXPECT_EQ(test_constraints[0].name, "v7");
EXPECT_EQ(test_constraints[0].configs.size(), 1);
EXPECT_NE(*test_constraints[0].configs.begin(), ConfigDescription::DefaultConfig());
EXPECT_EQ(test_constraints[1].name, "xhdpi");
EXPECT_EQ(test_constraints[1].configs.size(), 1);
- EXPECT_NE(*test_constraints[0].configs.begin(), ConfigDescription::DefaultConfig());
+ EXPECT_NE(*test_constraints[1].configs.begin(), ConfigDescription::DefaultConfig());
+ EXPECT_EQ(test_constraints[2].name, "v38.9");
+ EXPECT_EQ(test_constraints[2].configs.size(), 1);
+ EXPECT_NE(*test_constraints[2].configs.begin(), ConfigDescription::DefaultConfig());
- auto adjusted_contraints = AdjustSplitConstraintsForMinSdk(26, test_constraints);
- EXPECT_EQ(adjusted_contraints.size(), 2);
+ auto adjusted_contraints = AdjustSplitConstraintsForMinSdk(40, test_constraints);
+ EXPECT_EQ(adjusted_contraints.size(), 3);
EXPECT_EQ(adjusted_contraints[0].name, "v7");
EXPECT_EQ(adjusted_contraints[0].configs.size(), 0);
EXPECT_EQ(adjusted_contraints[1].name, "xhdpi");
EXPECT_EQ(adjusted_contraints[1].configs.size(), 1);
EXPECT_NE(*adjusted_contraints[1].configs.begin(), ConfigDescription::DefaultConfig());
+ EXPECT_EQ(adjusted_contraints[2].name, "v38.9");
+ EXPECT_EQ(adjusted_contraints[2].configs.size(), 0);
}
TEST (UtilTest, RegularExperssionsSimple) {
diff --git a/tools/aapt2/format/binary/BinaryResourceParser.cpp b/tools/aapt2/format/binary/BinaryResourceParser.cpp
index bac871b..0312244 100644
--- a/tools/aapt2/format/binary/BinaryResourceParser.cpp
+++ b/tools/aapt2/format/binary/BinaryResourceParser.cpp
@@ -451,11 +451,9 @@
return false;
}
- const KeyedVector<String16, uint8_t>& entries = dynamic_ref_table.entries();
- const size_t count = entries.size();
- for (size_t i = 0; i < count; i++) {
- table_->included_packages_[entries.valueAt(i)] =
- android::util::Utf16ToUtf8(StringPiece16(entries.keyAt(i).c_str()));
+ const auto& entries = dynamic_ref_table.entries();
+ for (auto [name, id] : entries) {
+ table_->included_packages_[id] = name;
}
return true;
}
diff --git a/tools/aapt2/format/binary/TableFlattener_test.cpp b/tools/aapt2/format/binary/TableFlattener_test.cpp
index 0e8aae1..1137212 100644
--- a/tools/aapt2/format/binary/TableFlattener_test.cpp
+++ b/tools/aapt2/format/binary/TableFlattener_test.cpp
@@ -602,15 +602,15 @@
const DynamicRefTable* dynamic_ref_table = result.getDynamicRefTableForCookie(1);
ASSERT_THAT(dynamic_ref_table, NotNull());
- const KeyedVector<String16, uint8_t>& entries = dynamic_ref_table->entries();
+ const auto& entries = dynamic_ref_table->entries();
- ssize_t idx = entries.indexOfKey(android::String16("lib_one"));
- ASSERT_GE(idx, 0);
- EXPECT_EQ(0x02u, entries.valueAt(idx));
+ auto it = entries.find("lib_one");
+ ASSERT_NE(it, entries.end());
+ EXPECT_EQ(0x02u, it->second);
- idx = entries.indexOfKey(android::String16("lib_two"));
- ASSERT_GE(idx, 0);
- EXPECT_EQ(0x03u, entries.valueAt(idx));
+ it = entries.find("lib_two");
+ ASSERT_NE(it, entries.end());
+ EXPECT_EQ(0x03u, it->second);
}
TEST_F(TableFlattenerTest, PackageWithNonStandardIdHasDynamicRefTable) {
@@ -626,10 +626,10 @@
const DynamicRefTable* dynamic_ref_table = result.getDynamicRefTableForCookie(1);
ASSERT_THAT(dynamic_ref_table, NotNull());
- const KeyedVector<String16, uint8_t>& entries = dynamic_ref_table->entries();
- ssize_t idx = entries.indexOfKey(android::String16("app"));
- ASSERT_GE(idx, 0);
- EXPECT_EQ(0x80u, entries.valueAt(idx));
+ const auto& entries = dynamic_ref_table->entries();
+ auto it = entries.find("app");
+ ASSERT_NE(it, entries.end());
+ EXPECT_EQ(0x80u, it->second);
}
TEST_F(TableFlattenerTest, LongPackageNameIsTruncated) {
diff --git a/tools/processors/property_cache/src/java/android/processor/property_cache/CacheConfig.java b/tools/processors/property_cache/src/java/android/processor/property_cache/CacheConfig.java
index 326a7a9..65f3e49 100644
--- a/tools/processors/property_cache/src/java/android/processor/property_cache/CacheConfig.java
+++ b/tools/processors/property_cache/src/java/android/processor/property_cache/CacheConfig.java
@@ -37,6 +37,7 @@
private int mNumberOfParams = 0;
private String mInputType = Constants.JAVA_LANG_VOID;
private String mResultType;
+ private final boolean mCacheNulls;
public CacheConfig(TypeElement classElement, ExecutableElement method) {
CachedPropertyDefaults classAnnotation = classElement.getAnnotation(
@@ -61,6 +62,7 @@
}
mResultType = primitiveTypeToObjectEquivalent(method.getReturnType().toString());
mGeneratedClassName = classAnnotation.name().isEmpty() ? mClassName + "Cache" : mClassName;
+ mCacheNulls = methodAnnotation.cacheNulls();
}
public CacheModifiers getModifiers() {
@@ -113,6 +115,10 @@
}
}
+ public boolean getCacheNulls() {
+ return mCacheNulls;
+ }
+
public int getNumberOfParams() {
return mNumberOfParams;
}
diff --git a/tools/processors/property_cache/src/java/android/processor/property_cache/IpcDataCacheComposer.java b/tools/processors/property_cache/src/java/android/processor/property_cache/IpcDataCacheComposer.java
index f0b857b..8a96e93 100644
--- a/tools/processors/property_cache/src/java/android/processor/property_cache/IpcDataCacheComposer.java
+++ b/tools/processors/property_cache/src/java/android/processor/property_cache/IpcDataCacheComposer.java
@@ -124,8 +124,10 @@
mCacheConfig.getPropertyVariable(), queryCall);
result += "\n synchronized (" + lockObject + " ) {";
result += "\n if (" + mCacheConfig.getPropertyVariable() + " == null) {";
+ result += "\n IpcDataCache.Config config = " + generateCreateIpcConfig() + ";";
+ result += "\n config = config.cacheNulls(" + mCacheConfig.getCacheNulls() + ");";
result += "\n " + mCacheConfig.getPropertyVariable() + " = new IpcDataCache" + "("
- + generateCreateIpcConfig() + ", " + binderParam.getName()
+ + "config" + ", " + binderParam.getName()
+ bypassParam.getNextName() + ");\n";
result += "\n }";
result += "\n }";
diff --git a/tools/processors/property_cache/test/java/android/processor/property_cache/shadows/IpcDataCache.java b/tools/processors/property_cache/test/java/android/processor/property_cache/shadows/IpcDataCache.java
index e5ef48c..8bf68ad 100644
--- a/tools/processors/property_cache/test/java/android/processor/property_cache/shadows/IpcDataCache.java
+++ b/tools/processors/property_cache/test/java/android/processor/property_cache/shadows/IpcDataCache.java
@@ -21,6 +21,13 @@
public static class Config {
public Config(int max, String module, String api, String name) {
}
+
+ /**
+ * Set the cacheNull behavior.
+ */
+ public Config cacheNulls(boolean enable) {
+ return this;
+ }
}
/** Shadow method for generated code compilation tests purposes only.
diff --git a/tools/processors/property_cache/test/resources/Custom.java b/tools/processors/property_cache/test/resources/Custom.java
index 2af6ebb..7bbed12 100644
--- a/tools/processors/property_cache/test/resources/Custom.java
+++ b/tools/processors/property_cache/test/resources/Custom.java
@@ -32,12 +32,12 @@
}
/**
- * Testing custom class values to generate static IpcDataCache
+ * Testing custom class values to generate static IpcDataCache and caching nulls
*
* @param userId - user Id
* @return birthday date of given user Id
*/
- @CachedProperty()
+ @CachedProperty(cacheNulls = true)
public Date getBirthday(int userId) {
return TestCache.getBirthday(mService::getBirthday, userId);
}
diff --git a/tools/processors/property_cache/test/resources/DefaultCache.java b/tools/processors/property_cache/test/resources/DefaultCache.java
index 9531118..f34f052 100644
--- a/tools/processors/property_cache/test/resources/DefaultCache.java
+++ b/tools/processors/property_cache/test/resources/DefaultCache.java
@@ -45,11 +45,10 @@
}
synchronized (sBirthdayLock) {
if (sBirthday == null) {
- sBirthday = new IpcDataCache(
- new IpcDataCache.Config(32, "system_server",
- "default_birthday", "Birthday"),
- binderCall, bypassPredicate);
-
+ IpcDataCache.Config config = new IpcDataCache.Config(32, "system_server",
+ "default_birthday", "Birthday");
+ config = config.cacheNulls(false);
+ sBirthday = new IpcDataCache(config, binderCall, bypassPredicate);
}
}
return sBirthday.query(query);
@@ -73,11 +72,10 @@
}
synchronized (sBirthdayLock) {
if (sBirthday == null) {
- sBirthday = new IpcDataCache(
- new IpcDataCache.Config(32, "system_server",
- "default_birthday", "Birthday"),
- binderCall);
-
+ IpcDataCache.Config config = new IpcDataCache.Config(32, "system_server",
+ "default_birthday", "Birthday");
+ config = config.cacheNulls(false);
+ sBirthday = new IpcDataCache(config, binderCall);
}
}
return sBirthday.query(query);
@@ -116,10 +114,10 @@
}
synchronized (sDaysTillBirthdayLock) {
if (sDaysTillBirthday == null) {
- sDaysTillBirthday = new IpcDataCache(
- new IpcDataCache.Config(32, "system_server", "default_days_till_birthday",
- "DaysTillBirthday"), binderCall, bypassPredicate);
-
+ IpcDataCache.Config config = new IpcDataCache.Config(32, "system_server",
+ "default_days_till_birthday", "DaysTillBirthday");
+ config = config.cacheNulls(false);
+ sDaysTillBirthday = new IpcDataCache(config, binderCall, bypassPredicate);
}
}
return sDaysTillBirthday.query(query);
@@ -144,10 +142,10 @@
}
synchronized (sDaysTillBirthdayLock) {
if (sDaysTillBirthday == null) {
- sDaysTillBirthday = new IpcDataCache(
- new IpcDataCache.Config(32, "system_server", "default_days_till_birthday",
- "DaysTillBirthday"), binderCall);
-
+ IpcDataCache.Config config = new IpcDataCache.Config(32, "system_server",
+ "default_days_till_birthday", "DaysTillBirthday");
+ config = config.cacheNulls(false);
+ sDaysTillBirthday = new IpcDataCache(config, binderCall);
}
}
return sDaysTillBirthday.query(query);
@@ -186,10 +184,10 @@
}
synchronized (mDaysSinceBirthdayLock) {
if (mDaysSinceBirthday == null) {
- mDaysSinceBirthday = new IpcDataCache(
- new IpcDataCache.Config(32, "system_server", "default_days_since_birthday",
- "DaysSinceBirthday"), binderCall, bypassPredicate);
-
+ IpcDataCache.Config config = new IpcDataCache.Config(32, "system_server",
+ "default_days_since_birthday", "DaysSinceBirthday");
+ config = config.cacheNulls(false);
+ mDaysSinceBirthday = new IpcDataCache(config, binderCall, bypassPredicate);
}
}
return mDaysSinceBirthday.query(query);
@@ -214,10 +212,10 @@
}
synchronized (mDaysSinceBirthdayLock) {
if (mDaysSinceBirthday == null) {
- mDaysSinceBirthday = new IpcDataCache(
- new IpcDataCache.Config(32, "system_server", "default_days_since_birthday",
- "DaysSinceBirthday"), binderCall);
-
+ IpcDataCache.Config config = new IpcDataCache.Config(32, "system_server",
+ "default_days_since_birthday", "DaysSinceBirthday");
+ config = config.cacheNulls(false);
+ mDaysSinceBirthday = new IpcDataCache(config, binderCall);
}
}
return mDaysSinceBirthday.query(query);
@@ -253,10 +251,10 @@
}
synchronized (sDaysTillMyBirthdayLock) {
if (sDaysTillMyBirthday == null) {
- sDaysTillMyBirthday = new IpcDataCache(
- new IpcDataCache.Config(1, "system_server", "default_days_till_my_birthday",
- "DaysTillMyBirthday"), binderCall);
-
+ IpcDataCache.Config config = new IpcDataCache.Config(1, "system_server",
+ "default_days_till_my_birthday", "DaysTillMyBirthday");
+ config = config.cacheNulls(false);
+ sDaysTillMyBirthday = new IpcDataCache(config, binderCall);
}
}
return sDaysTillMyBirthday.query(null);
@@ -292,10 +290,10 @@
}
synchronized (mDaysSinceMyBirthdayLock) {
if (mDaysSinceMyBirthday == null) {
- mDaysSinceMyBirthday = new IpcDataCache(
- new IpcDataCache.Config(1, "system_server", "my_unique_key",
- "DaysSinceMyBirthday"), binderCall);
-
+ IpcDataCache.Config config = new IpcDataCache.Config(1, "system_server",
+ "my_unique_key", "DaysSinceMyBirthday");
+ config = config.cacheNulls(false);
+ mDaysSinceMyBirthday = new IpcDataCache(config, binderCall);
}
}
return mDaysSinceMyBirthday.query(null);
@@ -336,11 +334,11 @@
}
synchronized (sBirthdayWishesFromUserLock) {
if (sBirthdayWishesFromUser == null) {
- sBirthdayWishesFromUser = new IpcDataCache(
- new IpcDataCache.Config(32, "telephony",
- "default_birthday_wishes_from_user",
- "BirthdayWishesFromUser"), binderCall, bypassPredicate);
-
+ IpcDataCache.Config config = new IpcDataCache.Config(32, "telephony",
+ "default_birthday_wishes_from_user",
+ "BirthdayWishesFromUser");
+ config = config.cacheNulls(false);
+ sBirthdayWishesFromUser = new IpcDataCache(config, binderCall, bypassPredicate);
}
}
return sBirthdayWishesFromUser.query(query);
@@ -365,11 +363,11 @@
}
synchronized (sBirthdayWishesFromUserLock) {
if (sBirthdayWishesFromUser == null) {
- sBirthdayWishesFromUser = new IpcDataCache(
- new IpcDataCache.Config(32, "telephony",
- "default_birthday_wishes_from_user",
- "BirthdayWishesFromUser"), binderCall);
-
+ IpcDataCache.Config config = new IpcDataCache.Config(32, "telephony",
+ "default_birthday_wishes_from_user",
+ "BirthdayWishesFromUser");
+ config = config.cacheNulls(false);
+ sBirthdayWishesFromUser = new IpcDataCache(config, binderCall);
}
}
return sBirthdayWishesFromUser.query(query);
diff --git a/tools/processors/property_cache/test/resources/TestCache.java b/tools/processors/property_cache/test/resources/TestCache.java
index b09344a..6ba4d55 100644
--- a/tools/processors/property_cache/test/resources/TestCache.java
+++ b/tools/processors/property_cache/test/resources/TestCache.java
@@ -44,10 +44,10 @@
}
synchronized (sBirthdayLock) {
if (sBirthday == null) {
- sBirthday = new IpcDataCache(
- new IpcDataCache.Config(4, "bluetooth", "test_cache_birthday", "Birthday"),
- binderCall, bypassPredicate);
-
+ IpcDataCache.Config config = new IpcDataCache.Config(4, "bluetooth",
+ "test_cache_birthday", "Birthday");
+ config = config.cacheNulls(true);
+ sBirthday = new IpcDataCache(config, binderCall, bypassPredicate);
}
}
return sBirthday.query(query);
@@ -69,9 +69,10 @@
}
synchronized (sBirthdayLock) {
if (sBirthday == null) {
- sBirthday = new IpcDataCache(
- new IpcDataCache.Config(4, "bluetooth", "test_cache_birthday", "Birthday"),
- binderCall);
+ IpcDataCache.Config config = new IpcDataCache.Config(4, "bluetooth",
+ "test_cache_birthday", "Birthday");
+ config = config.cacheNulls(true);
+ sBirthday = new IpcDataCache(config, binderCall);
}
}
return sBirthday.query(query);
@@ -109,10 +110,10 @@
}
synchronized (sDaysTillBirthdayLock) {
if (sDaysTillBirthday == null) {
- sDaysTillBirthday = new IpcDataCache(
- new IpcDataCache.Config(4, "bluetooth", "test_cache_days_till_birthday",
- "DaysTillBirthday"), binderCall, bypassPredicate);
-
+ IpcDataCache.Config config = new IpcDataCache.Config(4, "bluetooth",
+ "test_cache_days_till_birthday", "DaysTillBirthday");
+ config = config.cacheNulls(false);
+ sDaysTillBirthday = new IpcDataCache(config, binderCall, bypassPredicate);
}
}
return sDaysTillBirthday.query(query);
@@ -135,10 +136,10 @@
}
synchronized (sDaysTillBirthdayLock) {
if (sDaysTillBirthday == null) {
- sDaysTillBirthday = new IpcDataCache(
- new IpcDataCache.Config(4, "bluetooth", "test_cache_days_till_birthday",
- "DaysTillBirthday"), binderCall);
-
+ IpcDataCache.Config config = new IpcDataCache.Config(4, "bluetooth",
+ "test_cache_days_till_birthday", "DaysTillBirthday");
+ config = config.cacheNulls(false);
+ sDaysTillBirthday = new IpcDataCache(config, binderCall);
}
}
return sDaysTillBirthday.query(query);
@@ -176,10 +177,10 @@
}
synchronized (mDaysSinceBirthdayLock) {
if (mDaysSinceBirthday == null) {
- mDaysSinceBirthday = new IpcDataCache(
- new IpcDataCache.Config(4, "bluetooth", "test_cache_days_since_birthday",
- "DaysSinceBirthday"), binderCall, bypassPredicate);
-
+ IpcDataCache.Config config = new IpcDataCache.Config(4, "bluetooth",
+ "test_cache_days_since_birthday", "DaysSinceBirthday");
+ config = config.cacheNulls(false);
+ mDaysSinceBirthday = new IpcDataCache(config, binderCall, bypassPredicate);
}
}
return mDaysSinceBirthday.query(query);
@@ -203,10 +204,10 @@
}
synchronized (mDaysSinceBirthdayLock) {
if (mDaysSinceBirthday == null) {
- mDaysSinceBirthday = new IpcDataCache(
- new IpcDataCache.Config(4, "bluetooth", "test_cache_days_since_birthday",
- "DaysSinceBirthday"), binderCall);
-
+ IpcDataCache.Config config = new IpcDataCache.Config(4, "bluetooth",
+ "test_cache_days_since_birthday", "DaysSinceBirthday");
+ config = config.cacheNulls(false);
+ mDaysSinceBirthday = new IpcDataCache(config, binderCall);
}
}
return mDaysSinceBirthday.query(query);
@@ -241,10 +242,10 @@
}
synchronized (sDaysTillMyBirthdayLock) {
if (sDaysTillMyBirthday == null) {
- sDaysTillMyBirthday = new IpcDataCache(
- new IpcDataCache.Config(1, "bluetooth", "test_cache_days_till_my_birthday",
- "DaysTillMyBirthday"), binderCall);
-
+ IpcDataCache.Config config = new IpcDataCache.Config(1, "bluetooth",
+ "test_cache_days_till_my_birthday", "DaysTillMyBirthday");
+ config = config.cacheNulls(false);
+ sDaysTillMyBirthday = new IpcDataCache(config, binderCall);
}
}
return sDaysTillMyBirthday.query(null);
@@ -279,10 +280,10 @@
}
synchronized (mDaysSinceMyBirthdayLock) {
if (mDaysSinceMyBirthday == null) {
- mDaysSinceMyBirthday = new IpcDataCache(
- new IpcDataCache.Config(1, "bluetooth", "my_unique_key",
- "DaysSinceMyBirthday"), binderCall);
-
+ IpcDataCache.Config config = new IpcDataCache.Config(1, "bluetooth",
+ "my_unique_key", "DaysSinceMyBirthday");
+ config = config.cacheNulls(false);
+ mDaysSinceMyBirthday = new IpcDataCache(config, binderCall);
}
}
return mDaysSinceMyBirthday.query(null);
@@ -321,11 +322,10 @@
}
synchronized (sBirthdayWishesFromUserLock) {
if (sBirthdayWishesFromUser == null) {
- sBirthdayWishesFromUser = new IpcDataCache(
- new IpcDataCache.Config(4, "telephony",
- "test_cache_birthday_wishes_from_user", "BirthdayWishesFromUser"),
- binderCall, bypassPredicate);
-
+ IpcDataCache.Config config = new IpcDataCache.Config(4, "telephony",
+ "test_cache_birthday_wishes_from_user", "BirthdayWishesFromUser");
+ config = config.cacheNulls(false);
+ sBirthdayWishesFromUser = new IpcDataCache(config, binderCall, bypassPredicate);
}
}
return sBirthdayWishesFromUser.query(query);
@@ -349,11 +349,10 @@
}
synchronized (sBirthdayWishesFromUserLock) {
if (sBirthdayWishesFromUser == null) {
- sBirthdayWishesFromUser = new IpcDataCache(
- new IpcDataCache.Config(4, "telephony",
- "test_cache_birthday_wishes_from_user", "BirthdayWishesFromUser"),
- binderCall);
-
+ IpcDataCache.Config config = new IpcDataCache.Config(4, "telephony",
+ "test_cache_birthday_wishes_from_user", "BirthdayWishesFromUser");
+ config = config.cacheNulls(false);
+ sBirthdayWishesFromUser = new IpcDataCache(config, binderCall);
}
}
return sBirthdayWishesFromUser.query(query);
diff --git a/tools/systemfeatures/Android.bp b/tools/systemfeatures/Android.bp
index 87ea5db..8245bb5 100644
--- a/tools/systemfeatures/Android.bp
+++ b/tools/systemfeatures/Android.bp
@@ -8,12 +8,13 @@
default_team: "trendy_team_system_performance",
}
+// Code generation for compile-time system feature queries in the framework.
java_library_host {
name: "systemfeatures-gen-lib",
srcs: [
- "src/**/*.java",
- "src/**/*.kt",
":framework-metalava-annotations",
+ ":systemfeatures-lookup-srcs",
+ "src/com/android/systemfeatures/SystemFeaturesGenerator.kt",
],
static_libs: [
"guava",
@@ -27,23 +28,77 @@
static_libs: ["systemfeatures-gen-lib"],
}
+// Code generation for system feature name lookups in host tools that cannot
+// depend on the framework.
+java_library_host {
+ name: "systemfeatures-lookup-gen-lib",
+ srcs: ["src/com/android/systemfeatures/SystemFeaturesLookupGenerator.kt"],
+ static_libs: [
+ "javapoet",
+ "metalava-signature-reader",
+ ],
+ visibility: ["//visibility:private"],
+}
+
+java_binary_host {
+ name: "systemfeatures-lookup-gen-tool",
+ main_class: "com.android.systemfeatures.SystemFeaturesLookupGenerator",
+ static_libs: ["systemfeatures-lookup-gen-lib"],
+ visibility: ["//visibility:private"],
+}
+
+genrule {
+ name: "systemfeatures-lookup-srcs",
+ cmd: "$(location systemfeatures-lookup-gen-tool) $(in) > $(out)",
+ out: ["SystemFeaturesLookup.java"],
+ srcs: [
+ ":non-updatable-current.txt",
+ ":non-updatable-module-lib-current.txt",
+ ":non-updatable-system-current.txt",
+ ":non-updatable-test-current.txt",
+ ],
+ tools: ["systemfeatures-lookup-gen-tool"],
+ visibility: ["//visibility:private"],
+}
+
+// Code generation for runtime system feature metadata queries in the framework.
+java_library_host {
+ name: "systemfeatures-metadata-lib",
+ srcs: [
+ ":framework-metalava-annotations",
+ "src/com/android/systemfeatures/SystemFeaturesMetadataProcessor.kt",
+ ],
+ static_libs: [
+ "guava",
+ "javapoet",
+ ],
+}
+
java_plugin {
name: "systemfeatures-metadata-processor",
processor_class: "com.android.systemfeatures.SystemFeaturesMetadataProcessor",
- static_libs: ["systemfeatures-gen-lib"],
+ static_libs: ["systemfeatures-metadata-lib"],
}
genrule {
name: "systemfeatures-gen-tests-srcs",
+ srcs: [
+ "tests/data/features-1.xml",
+ "tests/data/features-2.xml",
+ ],
cmd: "$(location systemfeatures-gen-tool) com.android.systemfeatures.RwNoFeatures --readonly=false > $(location RwNoFeatures.java) && " +
"$(location systemfeatures-gen-tool) com.android.systemfeatures.RoNoFeatures --readonly=true --feature-apis=WATCH > $(location RoNoFeatures.java) && " +
"$(location systemfeatures-gen-tool) com.android.systemfeatures.RwFeatures --readonly=false --feature=WATCH:1 --feature=WIFI:0 --feature=VULKAN:UNAVAILABLE --feature=AUTO: > $(location RwFeatures.java) && " +
- "$(location systemfeatures-gen-tool) com.android.systemfeatures.RoFeatures --readonly=true --feature=WATCH:1 --feature=WIFI:0 --feature=VULKAN:UNAVAILABLE --feature=AUTO: --feature-apis=WATCH,PC > $(location RoFeatures.java)",
+ "$(location systemfeatures-gen-tool) com.android.systemfeatures.RoFeatures --readonly=true --feature=WATCH:1 --feature=WIFI:0 --feature=VULKAN:UNAVAILABLE --feature=AUTO: --feature-apis=WATCH,PC > $(location RoFeatures.java) && " +
+ "$(location systemfeatures-gen-tool) com.android.systemfeatures.RwFeaturesFromXml --readonly=false --feature-xml-files=$(location tests/data/features-1.xml),$(location tests/data/features-2.xml) > $(location RwFeaturesFromXml.java) && " +
+ "$(location systemfeatures-gen-tool) com.android.systemfeatures.RoFeaturesFromXml --readonly=true --feature-xml-files=$(location tests/data/features-1.xml),$(location tests/data/features-2.xml) > $(location RoFeaturesFromXml.java)",
out: [
"RwNoFeatures.java",
"RoNoFeatures.java",
"RwFeatures.java",
"RoFeatures.java",
+ "RwFeaturesFromXml.java",
+ "RoFeaturesFromXml.java",
],
tools: ["systemfeatures-gen-tool"],
}
@@ -56,6 +111,10 @@
"tests/src/**/*.java",
":systemfeatures-gen-tests-srcs",
],
+ data: [
+ "tests/data/features-1.xml",
+ "tests/data/features-2.xml",
+ ],
test_options: {
unit_test: true,
},
@@ -66,6 +125,7 @@
"objenesis",
"mockito",
"systemfeatures-gen-lib",
+ "systemfeatures-lookup-gen-lib",
"truth",
],
plugins: ["systemfeatures-metadata-processor"],
@@ -82,6 +142,8 @@
"tests/gen/RoNoFeatures.java.gen",
"tests/gen/RwFeatures.java.gen",
"tests/gen/RoFeatures.java.gen",
+ "tests/gen/RwFeaturesFromXml.java.gen",
+ "tests/gen/RoFeaturesFromXml.java.gen",
],
}
diff --git a/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesGenerator.kt b/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesGenerator.kt
index 22d364e..661d5f6 100644
--- a/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesGenerator.kt
+++ b/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesGenerator.kt
@@ -22,7 +22,13 @@
import com.squareup.javapoet.MethodSpec
import com.squareup.javapoet.ParameterizedTypeName
import com.squareup.javapoet.TypeSpec
+import java.io.File
import javax.lang.model.element.Modifier
+import javax.xml.parsers.DocumentBuilderFactory
+import javax.xml.xpath.XPathConstants
+import javax.xml.xpath.XPathFactory
+import org.w3c.dom.Element
+import org.w3c.dom.Node
/*
* Simple Java code generator that takes as input a list of defined features and generates an
@@ -64,6 +70,7 @@
*/
object SystemFeaturesGenerator {
private const val FEATURE_ARG = "--feature="
+ private const val FEATURE_XML_FILES_ARG = "--feature-xml-files="
private const val FEATURE_APIS_ARG = "--feature-apis="
private const val READONLY_ARG = "--readonly="
private const val METADATA_ONLY_ARG = "--metadata-only="
@@ -93,6 +100,10 @@
println(" runtime passthrough API will be generated, regardless")
println(" of the `--readonly` flag. This allows decoupling the")
println(" API surface from variations in device feature sets.")
+ println(" --feature-xml-files=\$XML_FILE_1,\$XML_FILE_2")
+ println(" A comma-separated list of XML permission feature files")
+ println(" to parse and add to the generated query APIs. The file")
+ println(" format matches that used by SystemConfig parsing.")
println(" --metadata-only=true|false Whether to simply output metadata about the")
println(" generated API surface.")
}
@@ -139,27 +150,45 @@
}
)
}
+ arg.startsWith(FEATURE_XML_FILES_ARG) -> {
+ featureArgs.addAll(
+ parseFeatureXmlFiles(arg.substring(FEATURE_XML_FILES_ARG.length).split(","))
+ )
+ }
else -> outputClassName = ClassName.bestGuess(arg)
}
}
// First load in all of the feature APIs we want to generate. Explicit feature definitions
- // will then override this set with the appropriate readonly and version value.
- val features = mutableMapOf<String, FeatureInfo>()
+ // will then override this set with the appropriate readonly and version value. Note that we
+ // use a sorted map to ensure stable codegen outputs given identical inputs.
+ val features = sortedMapOf<String, FeatureInfo>()
featureApiArgs.associateByTo(
features,
{ it },
{ FeatureInfo(it, version = null, readonly = false) },
)
- featureArgs.associateByTo(
- features,
- { it.name },
- { FeatureInfo(it.name, it.version, it.readonly && readonly) },
- )
+
+ // Multiple defs for the same feature may be present when aggregating permission files.
+ // To preserve SystemConfig semantics, use the following ordering for insertion priority:
+ // * readonly (build-time overrides runtime)
+ // * unavailable (version == null, overrides available)
+ // * version (higher overrides lower)
+ featureArgs
+ .sortedWith(
+ compareBy<FeatureInfo> { it.readonly }
+ .thenBy { it.version == null }
+ .thenBy { it.version }
+ )
+ .associateByTo(
+ features,
+ { it.name },
+ { FeatureInfo(it.name, it.version, it.readonly && readonly) },
+ )
outputClassName
?: run {
- println("Output class name must be provided.")
+ System.err.println("Output class name must be provided.")
usage()
return
}
@@ -213,15 +242,83 @@
private fun parseFeatureName(name: String): String =
when {
- name.startsWith("android") ->
- throw IllegalArgumentException(
- "Invalid feature name input: \"android\"-namespaced features must be " +
- "provided as PackageManager.FEATURE_* suffixes, not raw feature strings."
- )
+ name.startsWith("android") -> {
+ parseFeatureNameFromValue(name)
+ ?: throw IllegalArgumentException(
+ "Unrecognized Android system feature name: $name"
+ )
+ }
name.startsWith("FEATURE_") -> name
else -> "FEATURE_$name"
}
+ private fun parseFeatureNameFromValue(name: String): String? =
+ SystemFeaturesLookup.getDeclaredFeatureVarNameFromValue(name)
+
+
+ /**
+ * Parses a list of feature permission XML file paths into a list of FeatureInfo definitions.
+ */
+ private fun parseFeatureXmlFiles(filePaths: Collection<String>): Collection<FeatureInfo> =
+ filePaths.flatMap {
+ try {
+ parseFeatureXmlFile(File(it))
+ } catch (e: Exception) {
+ throw IllegalArgumentException("Error parsing feature XML file: $it", e)
+ }
+ }
+
+ /**
+ * Parses a feature permission XML file into a (possibly empty) list of FeatureInfo definitions.
+ */
+ private fun parseFeatureXmlFile(file: File): Collection<FeatureInfo> {
+ val doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(file)
+ doc.documentElement.normalize()
+
+ val xPath = XPathFactory.newInstance().newXPath()
+ val rootElement =
+ xPath.evaluate("/permissions", doc, XPathConstants.NODE) as? Element
+ ?: xPath.evaluate("/config", doc, XPathConstants.NODE) as? Element
+ if (rootElement == null) {
+ System.err.println("Warning: No <permissions>/<config> elements found in ${file.path}")
+ return emptyList()
+ }
+
+ return rootElement.childNodes.let { nodeList ->
+ (0 until nodeList.length)
+ .asSequence()
+ .map { nodeList.item(it) }
+ .filter { it.nodeType == Node.ELEMENT_NODE }
+ .map { it as Element }
+ .mapNotNull { element ->
+ when (element.tagName) {
+ "feature" -> parseFeatureElement(element)
+ "unavailable-feature" -> parseUnavailableFeatureElement(element)
+ else -> null
+ }
+ }
+ .toList()
+ }
+ }
+
+ private fun parseFeatureElement(element: Element): FeatureInfo? {
+ val name = parseFeatureNameFromValue(element.getAttribute("name")) ?: return null
+ return if (element.getAttribute("notLowRam") == "true") {
+ // If a feature is marked as being disabled on low-ram devices (notLowRam==true), we
+ // we cannot finalize the exported feature version or its availability, as we don't
+ // (yet) know whether the target product is low-ram.
+ FeatureInfo(name, version = null, readonly = false)
+ } else {
+ val version = element.getAttribute("version")
+ FeatureInfo(name, version.toIntOrNull() ?: 0, readonly = true)
+ }
+ }
+
+ private fun parseUnavailableFeatureElement(element: Element): FeatureInfo? {
+ val name = parseFeatureNameFromValue(element.getAttribute("name")) ?: return null
+ return FeatureInfo(name, version = null, readonly = true)
+ }
+
/*
* Adds per-feature query methods to the class with the form:
* {@code public static boolean hasFeatureX(Context context)},
diff --git a/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesLookupGenerator.kt b/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesLookupGenerator.kt
new file mode 100644
index 0000000..0cfdf70
--- /dev/null
+++ b/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesLookupGenerator.kt
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemfeatures
+
+import com.android.tools.metalava.model.text.ApiFile
+import com.squareup.javapoet.ClassName
+import com.squareup.javapoet.JavaFile
+import com.squareup.javapoet.MethodSpec
+import com.squareup.javapoet.TypeSpec
+import java.io.File
+import javax.lang.model.element.Modifier
+
+/*
+ * Simple Java code generator that takes as input a list of Metalava API files and generates an
+ * accessory class that maps from PackageManager system feature variable values to their
+ * declared PackageManager variable names. This is needed for host tooling that cannot depend
+ * directly on the base framework lib/srcs.
+ *
+ * <pre>
+ * package com.android.systemfeatures;
+ * public final class SystemFeaturesLookup {
+ * // Gets the declared system feature var name from its string value.
+ * // Example: "android.software.print" -> "FEATURE_PRINTING"
+ * public static String getDeclaredFeatureVarNameFromValue(String featureVarValue);
+ * }
+ * </pre>
+ */
+object SystemFeaturesLookupGenerator {
+
+ /** Main entrypoint for system feature constant lookup codegen. */
+ @JvmStatic
+ fun main(args: Array<String>) {
+ generate(args.asIterable(), System.out)
+ }
+
+ /**
+ * Simple API entrypoint for system feature lookup codegen.
+ *
+ * Given a list of Metalava API files, pipes a generated SystemFeaturesLookup class into output.
+ */
+ @JvmStatic
+ fun generate(apiFilePaths: Iterable<String>, output: Appendable) {
+ val featuresMap = parse(apiFilePaths)
+
+ val stringClassName = ClassName.get(String::class.java)
+
+ val featureLookupMethod =
+ MethodSpec.methodBuilder("getDeclaredFeatureVarNameFromValue")
+ .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
+ .addAnnotation(ClassName.get("android.annotation", "Nullable"))
+ .addJavadoc("Gets the declared system feature var name from its string value.")
+ .addJavadoc("\n\nExample: \"android.software.print\" -> \"FEATURE_PRINTING\"")
+ .addJavadoc("\n\n@hide")
+ .returns(stringClassName)
+ .addParameter(stringClassName, "featureVarValue")
+ .beginControlFlow("switch (featureVarValue)")
+ .apply {
+ featuresMap.forEach { (key, value) ->
+ addStatement("case \$S: return \$S", key, value)
+ }
+ }
+ .addStatement("default: return null")
+ .endControlFlow()
+ .build()
+
+ val outputClassName = ClassName.get("com.android.systemfeatures", "SystemFeaturesLookup")
+ val systemFeaturesApiLookupClass =
+ TypeSpec.classBuilder(outputClassName)
+ .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
+ .addMethod(featureLookupMethod)
+ .addJavadoc("@hide")
+ .build()
+
+ JavaFile.builder(outputClassName.packageName(), systemFeaturesApiLookupClass)
+ .indent(" ")
+ .skipJavaLangImports(true)
+ .addFileComment("This file is auto-generated. DO NOT MODIFY.\n")
+ .build()
+ .writeTo(output)
+ }
+
+ /**
+ * Given a list of Metalava API files, extracts a mapping from all @SdkConstantType.FEATURE
+ * PackageManager values to their declared variable names, e.g.,
+ * - "android.hardware.type.automotive" -> "FEATURE_AUTOMOTIVE"
+ * - "android.software.print" -> "FEATURE_PRINTING"
+ */
+ @JvmStatic
+ fun parse(apiFilePaths: Iterable<String>): Map<String, String> {
+ return ApiFile.parseApi(apiFilePaths.map(::File))
+ .findClass("android.content.pm.PackageManager")
+ ?.fields()
+ ?.filter { field ->
+ field.type().isString() &&
+ field.modifiers.isStatic() &&
+ field.modifiers.isFinal() &&
+ field.name().startsWith("FEATURE_") &&
+ field.legacyInitialValue() != null
+ }
+ ?.associateBy({ it.legacyInitialValue()!!.toString() }, { it.name() }) ?: emptyMap()
+ }
+}
diff --git a/tools/systemfeatures/tests/data/features-1.xml b/tools/systemfeatures/tests/data/features-1.xml
new file mode 100644
index 0000000..12a1609
--- /dev/null
+++ b/tools/systemfeatures/tests/data/features-1.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2025 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<permissions>
+ <!-- Implicit version=0 will be overridden in features-2.xml. -->
+ <feature name="android.hardware.type.embedded" />
+ <!--- Overridden by the pc <unavailable-feature /> def in features-2.xml. -->
+ <feature name="android.hardware.type.pc" />
+ <!-- Ignored as it's not a platform-defined feature. -->
+ <feature name="com.arbitrary.feature" />
+ <!-- Overrides the watch <feature /> def in features-2.xml. -->
+ <unavailable-feature name="android.hardware.type.watch" />
+ <!-- Exposed in the API but not as a build-time constant (readonly) feature,
+ as we don't know if the device is low-ram. -->
+ <feature name="android.hardware.bluetooth" notLowRam="true" />
+</permissions>
\ No newline at end of file
diff --git a/tools/systemfeatures/tests/data/features-2.xml b/tools/systemfeatures/tests/data/features-2.xml
new file mode 100644
index 0000000..9618669
--- /dev/null
+++ b/tools/systemfeatures/tests/data/features-2.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2025 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<permissions>
+ <!-- Overrides the embedded <feature /> version from features-1.xml. -->
+ <feature name="android.hardware.type.embedded" version="1"/>
+ <!-- Overridden by the <unavailable-feature /> def in features-1.xml. -->
+ <feature name="android.hardware.type.watch" version="1" />
+ <!-- Redundant with the wifi <feature /> def in features-1.xml. -->
+ <feature name="android.hardware.wifi" />
+ <!-- Ignored as it's not a platform-defined feature. -->
+ <feature name="com.arbitrary.feature.2" />
+ <!-- Overrides the pc <feature /> def from features-1.xml. -->
+ <unavailable-feature name="android.hardware.type.pc" />
+</permissions>
\ No newline at end of file
diff --git a/tools/systemfeatures/tests/golden/RoFeatures.java.gen b/tools/systemfeatures/tests/golden/RoFeatures.java.gen
index 730dacb..7b33590 100644
--- a/tools/systemfeatures/tests/golden/RoFeatures.java.gen
+++ b/tools/systemfeatures/tests/golden/RoFeatures.java.gen
@@ -22,13 +22,12 @@
*/
public final class RoFeatures {
/**
- * Check for FEATURE_WATCH.
+ * Check for FEATURE_AUTO.
*
* @hide
*/
- @AssumeTrueForR8
- public static boolean hasFeatureWatch(Context context) {
- return true;
+ public static boolean hasFeatureAuto(Context context) {
+ return hasFeatureFallback(context, PackageManager.FEATURE_AUTO);
}
/**
@@ -41,16 +40,6 @@
}
/**
- * Check for FEATURE_WIFI.
- *
- * @hide
- */
- @AssumeTrueForR8
- public static boolean hasFeatureWifi(Context context) {
- return true;
- }
-
- /**
* Check for FEATURE_VULKAN.
*
* @hide
@@ -61,12 +50,23 @@
}
/**
- * Check for FEATURE_AUTO.
+ * Check for FEATURE_WATCH.
*
* @hide
*/
- public static boolean hasFeatureAuto(Context context) {
- return hasFeatureFallback(context, PackageManager.FEATURE_AUTO);
+ @AssumeTrueForR8
+ public static boolean hasFeatureWatch(Context context) {
+ return true;
+ }
+
+ /**
+ * Check for FEATURE_WIFI.
+ *
+ * @hide
+ */
+ @AssumeTrueForR8
+ public static boolean hasFeatureWifi(Context context) {
+ return true;
}
private static boolean hasFeatureFallback(Context context, String featureName) {
@@ -79,9 +79,9 @@
@Nullable
public static Boolean maybeHasFeature(String featureName, int version) {
switch (featureName) {
+ case PackageManager.FEATURE_VULKAN: return false;
case PackageManager.FEATURE_WATCH: return 1 >= version;
case PackageManager.FEATURE_WIFI: return 0 >= version;
- case PackageManager.FEATURE_VULKAN: return false;
default: break;
}
return null;
diff --git a/tools/systemfeatures/tests/golden/RoFeaturesFromXml.java.gen b/tools/systemfeatures/tests/golden/RoFeaturesFromXml.java.gen
new file mode 100644
index 0000000..a2254909
--- /dev/null
+++ b/tools/systemfeatures/tests/golden/RoFeaturesFromXml.java.gen
@@ -0,0 +1,105 @@
+// This file is auto-generated. DO NOT MODIFY.
+// Args: com.android.systemfeatures.RoFeaturesFromXml \
+// --readonly=true \
+// --feature-xml-files=frameworks/base/tools/systemfeatures/tests/data/features-1.xml,frameworks/base/tools/systemfeatures/tests/data/features-2.xml
+package com.android.systemfeatures;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.pm.FeatureInfo;
+import android.content.pm.PackageManager;
+import android.util.ArrayMap;
+import com.android.aconfig.annotations.AssumeFalseForR8;
+import com.android.aconfig.annotations.AssumeTrueForR8;
+
+/**
+ * @hide
+ */
+public final class RoFeaturesFromXml {
+ /**
+ * Check for FEATURE_BLUETOOTH.
+ *
+ * @hide
+ */
+ public static boolean hasFeatureBluetooth(Context context) {
+ return hasFeatureFallback(context, PackageManager.FEATURE_BLUETOOTH);
+ }
+
+ /**
+ * Check for FEATURE_EMBEDDED.
+ *
+ * @hide
+ */
+ @AssumeTrueForR8
+ public static boolean hasFeatureEmbedded(Context context) {
+ return true;
+ }
+
+ /**
+ * Check for FEATURE_PC.
+ *
+ * @hide
+ */
+ @AssumeFalseForR8
+ public static boolean hasFeaturePc(Context context) {
+ return false;
+ }
+
+ /**
+ * Check for FEATURE_WATCH.
+ *
+ * @hide
+ */
+ @AssumeFalseForR8
+ public static boolean hasFeatureWatch(Context context) {
+ return false;
+ }
+
+ /**
+ * Check for FEATURE_WIFI.
+ *
+ * @hide
+ */
+ @AssumeTrueForR8
+ public static boolean hasFeatureWifi(Context context) {
+ return true;
+ }
+
+ private static boolean hasFeatureFallback(Context context, String featureName) {
+ return context.getPackageManager().hasSystemFeature(featureName);
+ }
+
+ /**
+ * @hide
+ */
+ @Nullable
+ public static Boolean maybeHasFeature(String featureName, int version) {
+ switch (featureName) {
+ case PackageManager.FEATURE_EMBEDDED: return 1 >= version;
+ case PackageManager.FEATURE_PC: return false;
+ case PackageManager.FEATURE_WATCH: return false;
+ case PackageManager.FEATURE_WIFI: return 0 >= version;
+ default: break;
+ }
+ return null;
+ }
+
+ /**
+ * Gets features marked as available at compile-time, keyed by name.
+ *
+ * @hide
+ */
+ @NonNull
+ public static ArrayMap<String, FeatureInfo> getReadOnlySystemEnabledFeatures() {
+ ArrayMap<String, FeatureInfo> features = new ArrayMap<>(2);
+ FeatureInfo fi = new FeatureInfo();
+ fi.name = PackageManager.FEATURE_EMBEDDED;
+ fi.version = 1;
+ features.put(fi.name, new FeatureInfo(fi));
+ fi.name = PackageManager.FEATURE_WIFI;
+ fi.version = 0;
+ features.put(fi.name, new FeatureInfo(fi));
+ return features;
+ }
+}
diff --git a/tools/systemfeatures/tests/golden/RwFeatures.java.gen b/tools/systemfeatures/tests/golden/RwFeatures.java.gen
index bcf978d..4d732dc 100644
--- a/tools/systemfeatures/tests/golden/RwFeatures.java.gen
+++ b/tools/systemfeatures/tests/golden/RwFeatures.java.gen
@@ -19,6 +19,24 @@
*/
public final class RwFeatures {
/**
+ * Check for FEATURE_AUTO.
+ *
+ * @hide
+ */
+ public static boolean hasFeatureAuto(Context context) {
+ return hasFeatureFallback(context, PackageManager.FEATURE_AUTO);
+ }
+
+ /**
+ * Check for FEATURE_VULKAN.
+ *
+ * @hide
+ */
+ public static boolean hasFeatureVulkan(Context context) {
+ return hasFeatureFallback(context, PackageManager.FEATURE_VULKAN);
+ }
+
+ /**
* Check for FEATURE_WATCH.
*
* @hide
@@ -36,24 +54,6 @@
return hasFeatureFallback(context, PackageManager.FEATURE_WIFI);
}
- /**
- * Check for FEATURE_VULKAN.
- *
- * @hide
- */
- public static boolean hasFeatureVulkan(Context context) {
- return hasFeatureFallback(context, PackageManager.FEATURE_VULKAN);
- }
-
- /**
- * Check for FEATURE_AUTO.
- *
- * @hide
- */
- public static boolean hasFeatureAuto(Context context) {
- return hasFeatureFallback(context, PackageManager.FEATURE_AUTO);
- }
-
private static boolean hasFeatureFallback(Context context, String featureName) {
return context.getPackageManager().hasSystemFeature(featureName);
}
diff --git a/tools/systemfeatures/tests/golden/RwFeaturesFromXml.java.gen b/tools/systemfeatures/tests/golden/RwFeaturesFromXml.java.gen
new file mode 100644
index 0000000..77dc6c62
--- /dev/null
+++ b/tools/systemfeatures/tests/golden/RwFeaturesFromXml.java.gen
@@ -0,0 +1,85 @@
+// This file is auto-generated. DO NOT MODIFY.
+// Args: com.android.systemfeatures.RwFeaturesFromXml \
+// --readonly=false \
+// --feature-xml-files=frameworks/base/tools/systemfeatures/tests/data/features-1.xml,frameworks/base/tools/systemfeatures/tests/data/features-2.xml
+package com.android.systemfeatures;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.pm.FeatureInfo;
+import android.content.pm.PackageManager;
+import android.util.ArrayMap;
+
+/**
+ * @hide
+ */
+public final class RwFeaturesFromXml {
+ /**
+ * Check for FEATURE_BLUETOOTH.
+ *
+ * @hide
+ */
+ public static boolean hasFeatureBluetooth(Context context) {
+ return hasFeatureFallback(context, PackageManager.FEATURE_BLUETOOTH);
+ }
+
+ /**
+ * Check for FEATURE_EMBEDDED.
+ *
+ * @hide
+ */
+ public static boolean hasFeatureEmbedded(Context context) {
+ return hasFeatureFallback(context, PackageManager.FEATURE_EMBEDDED);
+ }
+
+ /**
+ * Check for FEATURE_PC.
+ *
+ * @hide
+ */
+ public static boolean hasFeaturePc(Context context) {
+ return hasFeatureFallback(context, PackageManager.FEATURE_PC);
+ }
+
+ /**
+ * Check for FEATURE_WATCH.
+ *
+ * @hide
+ */
+ public static boolean hasFeatureWatch(Context context) {
+ return hasFeatureFallback(context, PackageManager.FEATURE_WATCH);
+ }
+
+ /**
+ * Check for FEATURE_WIFI.
+ *
+ * @hide
+ */
+ public static boolean hasFeatureWifi(Context context) {
+ return hasFeatureFallback(context, PackageManager.FEATURE_WIFI);
+ }
+
+ private static boolean hasFeatureFallback(Context context, String featureName) {
+ return context.getPackageManager().hasSystemFeature(featureName);
+ }
+
+ /**
+ * @hide
+ */
+ @Nullable
+ public static Boolean maybeHasFeature(String featureName, int version) {
+ return null;
+ }
+
+ /**
+ * Gets features marked as available at compile-time, keyed by name.
+ *
+ * @hide
+ */
+ @NonNull
+ public static ArrayMap<String, FeatureInfo> getReadOnlySystemEnabledFeatures() {
+ ArrayMap<String, FeatureInfo> features = new ArrayMap<>(0);
+ return features;
+ }
+}
diff --git a/tools/systemfeatures/tests/src/SystemFeaturesGeneratorApiTest.java b/tools/systemfeatures/tests/src/SystemFeaturesGeneratorApiTest.java
index f8c585d..051d5c1 100644
--- a/tools/systemfeatures/tests/src/SystemFeaturesGeneratorApiTest.java
+++ b/tools/systemfeatures/tests/src/SystemFeaturesGeneratorApiTest.java
@@ -78,4 +78,34 @@
SystemFeaturesGenerator.generate(args, mOut);
verify(mOut, never()).append(any());
}
+
+ @Test
+ public void testValidFeatureNameFromAndroidNamespace() throws IOException {
+ final String[] args = new String[] {
+ "com.foo.Features",
+ "--feature=android.hardware.touchscreen:0",
+ };
+ SystemFeaturesGenerator.generate(args, mOut);
+ verify(mOut, atLeastOnce()).append(any());
+ }
+
+ @Test
+ public void testFeatureXmlFiles() throws IOException {
+ final String[] args = new String[] {
+ "com.foo.Features",
+ "--feature-xml-files=tests/data/features-1.xml,tests/data/features-2.xml",
+ };
+ SystemFeaturesGenerator.generate(args, mOut);
+ verify(mOut, atLeastOnce()).append(any());
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testInvalidFeatureXmlFiles() throws IOException {
+ final String[] args = new String[] {
+ "com.foo.Features",
+ "--feature-xml-files=nonexistent-file.xml",
+ };
+ SystemFeaturesGenerator.generate(args, mOut);
+ verify(mOut, never()).append(any());
+ }
}
diff --git a/tools/systemfeatures/tests/src/SystemFeaturesGeneratorTest.java b/tools/systemfeatures/tests/src/SystemFeaturesGeneratorTest.java
index 491b55e..ad37af7 100644
--- a/tools/systemfeatures/tests/src/SystemFeaturesGeneratorTest.java
+++ b/tools/systemfeatures/tests/src/SystemFeaturesGeneratorTest.java
@@ -169,4 +169,87 @@
assertThat(compiledFeatures.get(PackageManager.FEATURE_WATCH).version).isEqualTo(1);
assertThat(compiledFeatures.get(PackageManager.FEATURE_WIFI).version).isEqualTo(0);
}
+
+ @Test
+ public void testReadonlyDisabledWithDefinedFeaturesFromXml() {
+ // Always fall back to the PackageManager for defined, explicit features queries.
+ when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)).thenReturn(true);
+ assertThat(RwFeaturesFromXml.hasFeatureBluetooth(mContext)).isTrue();
+
+ when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_EMBEDDED)).thenReturn(true);
+ assertThat(RwFeaturesFromXml.hasFeatureEmbedded(mContext)).isTrue();
+
+ when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_PC)).thenReturn(false);
+ assertThat(RwFeaturesFromXml.hasFeatureWatch(mContext)).isFalse();
+
+ when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_WATCH)).thenReturn(false);
+ assertThat(RwFeaturesFromXml.hasFeatureWatch(mContext)).isFalse();
+
+ when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_WIFI)).thenReturn(true);
+ assertThat(RwFeaturesFromXml.hasFeatureWifi(mContext)).isTrue();
+
+ // For defined and undefined features, conditional queries should report null (unknown).
+ assertThat(RwFeaturesFromXml.maybeHasFeature(PackageManager.FEATURE_BLUETOOTH, 0)).isNull();
+ assertThat(RwFeaturesFromXml.maybeHasFeature(PackageManager.FEATURE_EMBEDDED, 0)).isNull();
+ assertThat(RwFeaturesFromXml.maybeHasFeature(PackageManager.FEATURE_PC, 0)).isNull();
+ assertThat(RwFeaturesFromXml.maybeHasFeature(PackageManager.FEATURE_WATCH, 0)).isNull();
+ assertThat(RwFeaturesFromXml.maybeHasFeature(PackageManager.FEATURE_WIFI, 0)).isNull();
+ assertThat(RwFeaturesFromXml.maybeHasFeature("com.arbitrary.feature", 0)).isNull();
+ assertThat(RwFeaturesFromXml.getReadOnlySystemEnabledFeatures()).isEmpty();
+ }
+
+ @Test
+ public void testReadonlyWithDefinedFeaturesFromXml() {
+ // Always use the build-time feature version for defined, explicit feature queries, never
+ // falling back to the runtime query.
+ assertThat(RoFeaturesFromXml.hasFeatureEmbedded(mContext)).isTrue();
+ assertThat(RoFeaturesFromXml.hasFeaturePc(mContext)).isFalse();
+ assertThat(RoFeaturesFromXml.hasFeatureWatch(mContext)).isFalse();
+ assertThat(RoFeaturesFromXml.hasFeatureWifi(mContext)).isTrue();
+ verify(mPackageManager, never()).hasSystemFeature(anyString(), anyInt());
+
+ // For defined feature types from XML, conditional queries should reflect either:
+ // * Disabled if the feature was *ever* declared w/ <unavailable-feature />
+ // * Enabled if the feature was otherwise declared w/ <feature />
+ // * Unknown for features conditionally enabled on non-low-ram devices (notLowRam="true").
+ // * Unknown for non platform-defined (custom) features.
+
+ // <feature version="1" />
+ assertThat(RoFeaturesFromXml.maybeHasFeature(PackageManager.FEATURE_EMBEDDED, -1)).isTrue();
+ assertThat(RoFeaturesFromXml.maybeHasFeature(PackageManager.FEATURE_EMBEDDED, 0)).isTrue();
+ assertThat(RoFeaturesFromXml.maybeHasFeature(PackageManager.FEATURE_EMBEDDED, 2)).isFalse();
+
+ // <feature />
+ assertThat(RoFeaturesFromXml.maybeHasFeature(PackageManager.FEATURE_WIFI, -1)).isTrue();
+ assertThat(RoFeaturesFromXml.maybeHasFeature(PackageManager.FEATURE_WIFI, 0)).isTrue();
+ assertThat(RoFeaturesFromXml.maybeHasFeature(PackageManager.FEATURE_WIFI, 100)).isFalse();
+
+ // <unavailable-feature />
+ assertThat(RoFeaturesFromXml.maybeHasFeature(PackageManager.FEATURE_PC, -1)).isFalse();
+ assertThat(RoFeaturesFromXml.maybeHasFeature(PackageManager.FEATURE_PC, 0)).isFalse();
+ assertThat(RoFeaturesFromXml.maybeHasFeature(PackageManager.FEATURE_PC, 100)).isFalse();
+ assertThat(RoFeaturesFromXml.maybeHasFeature(PackageManager.FEATURE_WATCH, -1)).isFalse();
+ assertThat(RoFeaturesFromXml.maybeHasFeature(PackageManager.FEATURE_WATCH, 0)).isFalse();
+ assertThat(RoFeaturesFromXml.maybeHasFeature(PackageManager.FEATURE_WATCH, 100)).isFalse();
+
+ // <feature notLowRam="true" />
+ assertThat(RoFeaturesFromXml.maybeHasFeature(PackageManager.FEATURE_BLUETOOTH, -1))
+ .isNull();
+ assertThat(RoFeaturesFromXml.maybeHasFeature(PackageManager.FEATURE_BLUETOOTH, 0)).isNull();
+ assertThat(RoFeaturesFromXml.maybeHasFeature(PackageManager.FEATURE_BLUETOOTH, 100))
+ .isNull();
+
+ // For custom/undefined feature types, conditional queries should report null (unknown).
+ assertThat(RoFeaturesFromXml.maybeHasFeature("com.arbitrary.feature", -1)).isNull();
+ assertThat(RoFeaturesFromXml.maybeHasFeature("com.arbitrary.feature", 0)).isNull();
+ assertThat(RoFeaturesFromXml.maybeHasFeature("com.arbitrary.feature", 100)).isNull();
+ assertThat(RoFeaturesFromXml.maybeHasFeature("", 0)).isNull();
+
+ Map<String, FeatureInfo> compiledFeatures =
+ RoFeaturesFromXml.getReadOnlySystemEnabledFeatures();
+ assertThat(compiledFeatures.keySet())
+ .containsExactly(PackageManager.FEATURE_EMBEDDED, PackageManager.FEATURE_WIFI);
+ assertThat(compiledFeatures.get(PackageManager.FEATURE_EMBEDDED).version).isEqualTo(1);
+ assertThat(compiledFeatures.get(PackageManager.FEATURE_WIFI).version).isEqualTo(0);
+ }
}
diff --git a/tools/systemfeatures/tests/src/SystemFeaturesLookupGeneratorTest.java b/tools/systemfeatures/tests/src/SystemFeaturesLookupGeneratorTest.java
new file mode 100644
index 0000000..1e73199
--- /dev/null
+++ b/tools/systemfeatures/tests/src/SystemFeaturesLookupGeneratorTest.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemfeatures;
+
+import static com.android.systemfeatures.SystemFeaturesLookup.getDeclaredFeatureVarNameFromValue;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class SystemFeaturesLookupGeneratorTest {
+
+ @Test
+ public void testDeclaredFeatures() {
+ // Public hardware features
+ assertThat(getDeclaredFeatureVarNameFromValue("android.hardware.bluetooth"))
+ .isEqualTo("FEATURE_BLUETOOTH");
+ assertThat(getDeclaredFeatureVarNameFromValue("android.hardware.type.automotive"))
+ .isEqualTo("FEATURE_AUTOMOTIVE");
+ assertThat(getDeclaredFeatureVarNameFromValue("android.hardware.type.pc"))
+ .isEqualTo("FEATURE_PC");
+ assertThat(getDeclaredFeatureVarNameFromValue("android.hardware.type.television"))
+ .isEqualTo("FEATURE_TELEVISION");
+ assertThat(getDeclaredFeatureVarNameFromValue("android.hardware.type.watch"))
+ .isEqualTo("FEATURE_WATCH");
+
+ // Public software features
+ assertThat(getDeclaredFeatureVarNameFromValue("android.software.print"))
+ .isEqualTo("FEATURE_PRINTING");
+ assertThat(getDeclaredFeatureVarNameFromValue("android.software.webview"))
+ .isEqualTo("FEATURE_WEBVIEW");
+
+ // Public deprecated feature
+ assertThat(getDeclaredFeatureVarNameFromValue("android.software.vr.mode"))
+ .isEqualTo("FEATURE_VR_MODE");
+
+ // TestApi feature
+ assertThat(getDeclaredFeatureVarNameFromValue("android.software.adoptable_storage"))
+ .isEqualTo("FEATURE_ADOPTABLE_STORAGE");
+
+ // SystemApi feature
+ assertThat(getDeclaredFeatureVarNameFromValue("android.software.incremental_delivery"))
+ .isEqualTo("FEATURE_INCREMENTAL_DELIVERY");
+
+ // Flagged feature
+ assertThat(getDeclaredFeatureVarNameFromValue("android.hardware.xr.input.controller"))
+ .isEqualTo("FEATURE_XR_INPUT_CONTROLLER");
+ }
+
+ @Test
+ public void testUndeclaredFeatures() {
+ assertThat(getDeclaredFeatureVarNameFromValue("")).isNull();
+ assertThat(getDeclaredFeatureVarNameFromValue("com.foo.")).isNull();
+ assertThat(getDeclaredFeatureVarNameFromValue("com.my.custom.feature")).isNull();
+ assertThat(getDeclaredFeatureVarNameFromValue("android.")).isNull();
+ assertThat(getDeclaredFeatureVarNameFromValue("android.hardware.")).isNull();
+ assertThat(getDeclaredFeatureVarNameFromValue("android.hardware.nonexistent")).isNull();
+ assertThat(getDeclaredFeatureVarNameFromValue("android.software.")).isNull();
+ assertThat(getDeclaredFeatureVarNameFromValue("android.software.nonexistent")).isNull();
+ }
+}
diff --git a/tools/systemfeatures/tests/src/SystemFeaturesMetadataProcessorTest.java b/tools/systemfeatures/tests/src/SystemFeaturesMetadataProcessorTest.java
index 560454b..963c01c 100644
--- a/tools/systemfeatures/tests/src/SystemFeaturesMetadataProcessorTest.java
+++ b/tools/systemfeatures/tests/src/SystemFeaturesMetadataProcessorTest.java
@@ -36,8 +36,8 @@
@Test
public void testSdkFeatureCount() {
// See the fake PackageManager definition in this directory.
- // It defines 6 annotated features, and any/all other constants should be ignored.
- assertThat(SystemFeaturesMetadata.SDK_FEATURE_COUNT).isEqualTo(6);
+ // It defines 8 annotated features, and any/all other constants should be ignored.
+ assertThat(SystemFeaturesMetadata.SDK_FEATURE_COUNT).isEqualTo(8);
}
@Test
diff --git a/tools/systemfeatures/tests/src/android/content/pm/PackageManager.java b/tools/systemfeatures/tests/src/android/content/pm/PackageManager.java
index 4a9edd6..567a982 100644
--- a/tools/systemfeatures/tests/src/android/content/pm/PackageManager.java
+++ b/tools/systemfeatures/tests/src/android/content/pm/PackageManager.java
@@ -37,6 +37,12 @@
public static final String FEATURE_WIFI = "wifi";
@SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_BLUETOOTH = "bluetooth";
+
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_EMBEDDED = "android.hardware.type.embedded";
+
+ @SdkConstant(SdkConstantType.FEATURE)
public static final String FEATURE_NOT_IN_RO_FEATURE_API = "not_in_ro_feature_api";
@SdkConstant(SdkConstantType.INTENT_CATEGORY)