summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/app/UiAutomation.java2
-rw-r--r--core/java/android/app/jank/JankDataProcessor.java113
-rw-r--r--libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/foldable_inner/light_landscape_dragZones_bubble.pngbin0 -> 20393 bytes
-rw-r--r--libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/foldable_inner/light_landscape_dragZones_bubbleBar.pngbin0 -> 20220 bytes
-rw-r--r--libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/foldable_inner/light_landscape_dragZones_bubble_split_10_90.pngbin0 -> 20319 bytes
-rw-r--r--libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/foldable_inner/light_landscape_dragZones_bubble_split_90_10.pngbin0 -> 20401 bytes
-rw-r--r--libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/foldable_inner/light_landscape_dragZones_expandedView.pngbin0 -> 20635 bytes
-rw-r--r--libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/foldable_inner/light_landscape_dragZones_expandedView_split_10_90.pngbin0 -> 20522 bytes
-rw-r--r--libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/foldable_inner/light_landscape_dragZones_expandedView_split_90_10.pngbin0 -> 20569 bytes
-rw-r--r--libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/foldable_inner/light_portrait_dragZones_bubble.pngbin0 -> 21304 bytes
-rw-r--r--libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/foldable_inner/light_portrait_dragZones_bubbleBar.pngbin0 -> 21159 bytes
-rw-r--r--libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/foldable_inner/light_portrait_dragZones_bubble_split_10_90.pngbin0 -> 21276 bytes
-rw-r--r--libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/foldable_inner/light_portrait_dragZones_bubble_split_90_10.pngbin0 -> 21283 bytes
-rw-r--r--libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/foldable_inner/light_portrait_dragZones_expandedView.pngbin0 -> 21318 bytes
-rw-r--r--libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/tablet/light_landscape_dragZones_bubble.pngbin0 -> 20400 bytes
-rw-r--r--libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/tablet/light_landscape_dragZones_bubbleBar.pngbin0 -> 20082 bytes
-rw-r--r--libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/tablet/light_landscape_dragZones_expandedView.pngbin0 -> 20406 bytes
-rw-r--r--libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/tablet/light_portrait_dragZones_bubble.pngbin0 -> 22270 bytes
-rw-r--r--libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/tablet/light_portrait_dragZones_bubbleBar.pngbin0 -> 21917 bytes
-rw-r--r--libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/tablet/light_portrait_dragZones_expandedView.pngbin0 -> 22462 bytes
-rw-r--r--libs/WindowManager/Shell/multivalentScreenshotTests/src/com/android/wm/shell/shared/bubbles/DragZoneFactoryScreenshotTest.kt170
-rw-r--r--libs/WindowManager/Shell/shared/res/values/dimen.xml1
-rw-r--r--libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/DragZone.kt24
-rw-r--r--libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/DropTargetManager.kt98
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt8
-rw-r--r--libs/WindowManager/Shell/tests/unittest/Android.bp1
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/bubbles/DropTargetManagerTest.kt210
-rw-r--r--packages/SettingsLib/res/drawable/ic_1x_mobiledata_updated.xml24
-rw-r--r--packages/SettingsLib/res/drawable/ic_3g_mobiledata_updated.xml24
-rw-r--r--packages/SettingsLib/res/drawable/ic_4g_lte_mobiledata_updated.xml24
-rw-r--r--packages/SettingsLib/res/drawable/ic_4g_lte_plus_mobiledata_updated.xml27
-rw-r--r--packages/SettingsLib/res/drawable/ic_4g_mobiledata_updated.xml25
-rw-r--r--packages/SettingsLib/res/drawable/ic_4g_plus_mobiledata_updated.xml27
-rw-r--r--packages/SettingsLib/res/drawable/ic_5g_e_mobiledata_updated.xml25
-rw-r--r--packages/SettingsLib/res/drawable/ic_5g_mobiledata_updated.xml24
-rw-r--r--packages/SettingsLib/res/drawable/ic_5g_plus_mobiledata_default_updated.xml27
-rw-r--r--packages/SettingsLib/res/drawable/ic_5g_plus_mobiledata_updated.xml27
-rw-r--r--packages/SettingsLib/res/drawable/ic_carrier_wifi_updated.xml30
-rw-r--r--packages/SettingsLib/res/drawable/ic_e_mobiledata_updated.xml24
-rw-r--r--packages/SettingsLib/res/drawable/ic_g_mobiledata_updated.xml24
-rw-r--r--packages/SettingsLib/res/drawable/ic_h_mobiledata_updated.xml24
-rw-r--r--packages/SettingsLib/res/drawable/ic_h_plus_mobiledata_updated.xml27
-rw-r--r--packages/SettingsLib/res/drawable/ic_lte_mobiledata_updated.xml24
-rw-r--r--packages/SettingsLib/res/drawable/ic_lte_plus_mobiledata_updated.xml27
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/mobile/TelephonyIcons.java58
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt38
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewModel.kt2
-rw-r--r--packages/SystemUI/res/drawable/mobile_network_type_background_updated.xml30
-rw-r--r--packages/SystemUI/res/layout/internet_connectivity_dialog.xml2
-rw-r--r--packages/SystemUI/res/values/dimens.xml25
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalTransitionKeys.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/OngoingActivityChipBinder.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/OngoingActivityChip.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/OngoingActivityChips.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/featurepods/media/ui/viewmodel/MediaControlChipViewModel.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/shared/model/PopupChipModel.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/ui/compose/StatusBarPopup.kt65
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/ui/compose/StatusBarPopupChip.kt16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/ui/compose/StatusBarPopupChipsContainer.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/ui/viewmodel/StatusBarPopupChipsViewModel.kt74
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernStatusBarMobileView.kt34
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt24
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt845
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/featurepods/popups/ui/viewmodel/StatusBarPopupChipsViewModelTest.kt53
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/featurepods/popups/ui/viewmodel/StatusBarPopupChipsViewModelKosmos.kt13
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelKosmos.kt4
-rw-r--r--services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java19
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java152
-rw-r--r--services/core/java/com/android/server/notification/NotificationRecordLogger.java25
-rw-r--r--services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java4
-rw-r--r--telephony/java/android/telephony/TelephonyManager.java6
76 files changed, 1893 insertions, 739 deletions
diff --git a/core/java/android/app/UiAutomation.java b/core/java/android/app/UiAutomation.java
index 464bcc025d92..361532613047 100644
--- a/core/java/android/app/UiAutomation.java
+++ b/core/java/android/app/UiAutomation.java
@@ -122,7 +122,7 @@ public final class UiAutomation {
private static final String LOG_TAG = UiAutomation.class.getSimpleName();
private static final boolean DEBUG = false;
- private static final boolean VERBOSE = Build.IS_DEBUGGABLE;
+ private static final boolean VERBOSE = false;
private static final int CONNECTION_ID_UNDEFINED = -1;
diff --git a/core/java/android/app/jank/JankDataProcessor.java b/core/java/android/app/jank/JankDataProcessor.java
index b4c293eeb695..7718d159896e 100644
--- a/core/java/android/app/jank/JankDataProcessor.java
+++ b/core/java/android/app/jank/JankDataProcessor.java
@@ -34,11 +34,13 @@ import java.util.List;
/**
* This class is responsible for associating frames received from SurfaceFlinger to active widget
* states and logging those states back to the platform.
+ *
* @hide
*/
@FlaggedApi(Flags.FLAG_DETAILED_APP_JANK_METRICS_API)
public class JankDataProcessor {
-
+ private static final String TAG = "JankDataProcessor";
+ private static final boolean DEBUG_LOGGING = false;
private static final int MAX_IN_MEMORY_STATS = 25;
private static final int LOG_BATCH_FREQUENCY = 50;
private int mCurrentBatchCount = 0;
@@ -54,9 +56,10 @@ public class JankDataProcessor {
/**
* Called once per batch of JankData.
- * @param jankData data received from SurfaceFlinger to be processed
+ *
+ * @param jankData data received from SurfaceFlinger to be processed
* @param activityName name of the activity that is tracking jank metrics.
- * @param appUid the uid of the app.
+ * @param appUid the uid of the app.
*/
public void processJankData(List<JankData> jankData, String activityName, int appUid) {
// add all the previous and active states to the pending states list.
@@ -211,8 +214,6 @@ public class JankDataProcessor {
* clear any pending widget states.
*/
public void logMetricCounts() {
- //TODO b/374607503 when api changes are in add enum mapping for category and state.
-
try {
mPendingJankStats.values().forEach(stat -> {
FrameworkStatsLog.write(
@@ -221,15 +222,16 @@ public class JankDataProcessor {
/*activity name*/ stat.getActivityName(),
/*widget id*/ stat.getWidgetId(),
/*refresh rate*/ stat.getRefreshRate(),
- /*widget category*/ 0,
- /*widget state*/ 0,
+ /*widget category*/ widgetCategoryToInt(stat.getWidgetCategory()),
+ /*widget state*/ widgetStateToInt(stat.getWidgetState()),
/*total frames*/ stat.getTotalFrames(),
/*janky frames*/ stat.getJankyFrames(),
- /*histogram*/ stat.mFrameOverrunBuckets);
+ /*histogram*/ stat.getFrameOverrunBuckets());
Log.d(stat.mActivityName, stat.toString());
// return the pending stat to the pool it will be reset the next time its
// used.
mPendingJankStatsPool.release(stat);
+
}
);
// All stats have been recorded and added back to the pool for reuse, clear the pending
@@ -241,6 +243,96 @@ public class JankDataProcessor {
}
}
+ private int widgetCategoryToInt(String widgetCategory) {
+ switch (widgetCategory) {
+ case AppJankStats.WIDGET_CATEGORY_SCROLL -> {
+ return FrameworkStatsLog
+ .JANK_FRAME_COUNT_BY_WIDGET_REPORTED__WIDGET_STATE__SCROLLING;
+ }
+ case AppJankStats.WIDGET_CATEGORY_ANIMATION -> {
+ return FrameworkStatsLog
+ .JANK_FRAME_COUNT_BY_WIDGET_REPORTED__WIDGET_TYPE__ANIMATION;
+ }
+ case AppJankStats.WIDGET_CATEGORY_MEDIA -> {
+ return FrameworkStatsLog
+ .JANK_FRAME_COUNT_BY_WIDGET_REPORTED__WIDGET_TYPE__MEDIA;
+ }
+ case AppJankStats.WIDGET_CATEGORY_NAVIGATION -> {
+ return FrameworkStatsLog
+ .JANK_FRAME_COUNT_BY_WIDGET_REPORTED__WIDGET_TYPE__NAVIGATION;
+ }
+ case AppJankStats.WIDGET_CATEGORY_KEYBOARD -> {
+ return FrameworkStatsLog
+ .JANK_FRAME_COUNT_BY_WIDGET_REPORTED__WIDGET_TYPE__KEYBOARD;
+ }
+ case AppJankStats.WIDGET_CATEGORY_OTHER -> {
+ return FrameworkStatsLog
+ .JANK_FRAME_COUNT_BY_WIDGET_REPORTED__WIDGET_TYPE__OTHER;
+ }
+ default -> {
+ if (DEBUG_LOGGING) {
+ Log.d(TAG, "Default Category Logged: "
+ + AppJankStats.WIDGET_CATEGORY_UNSPECIFIED);
+ }
+ return FrameworkStatsLog
+ .JANK_FRAME_COUNT_BY_WIDGET_REPORTED__WIDGET_TYPE__WIDGET_CATEGORY_UNSPECIFIED;
+ }
+ }
+ }
+
+ private int widgetStateToInt(String widgetState) {
+ switch (widgetState) {
+ case AppJankStats.WIDGET_STATE_NONE -> {
+ return FrameworkStatsLog
+ .JANK_FRAME_COUNT_BY_WIDGET_REPORTED__WIDGET_STATE__NONE;
+ }
+ case AppJankStats.WIDGET_STATE_SCROLLING -> {
+ return FrameworkStatsLog
+ .JANK_FRAME_COUNT_BY_WIDGET_REPORTED__WIDGET_STATE__SCROLLING;
+ }
+ case AppJankStats.WIDGET_STATE_FLINGING -> {
+ return FrameworkStatsLog
+ .JANK_FRAME_COUNT_BY_WIDGET_REPORTED__WIDGET_STATE__FLINGING;
+ }
+ case AppJankStats.WIDGET_STATE_SWIPING -> {
+ return FrameworkStatsLog
+ .JANK_FRAME_COUNT_BY_WIDGET_REPORTED__WIDGET_STATE__SWIPING;
+ }
+ case AppJankStats.WIDGET_STATE_DRAGGING -> {
+ return FrameworkStatsLog
+ .JANK_FRAME_COUNT_BY_WIDGET_REPORTED__WIDGET_STATE__DRAGGING;
+ }
+ case AppJankStats.WIDGET_STATE_ZOOMING -> {
+ return FrameworkStatsLog
+ .JANK_FRAME_COUNT_BY_WIDGET_REPORTED__WIDGET_STATE__ZOOMING;
+ }
+ case AppJankStats.WIDGET_STATE_ANIMATING -> {
+ return FrameworkStatsLog
+ .JANK_FRAME_COUNT_BY_WIDGET_REPORTED__WIDGET_STATE__ANIMATING;
+ }
+ case AppJankStats.WIDGET_STATE_PLAYBACK -> {
+ return FrameworkStatsLog
+ .JANK_FRAME_COUNT_BY_WIDGET_REPORTED__WIDGET_STATE__PLAYBACK;
+ }
+ case AppJankStats.WIDGET_STATE_TAPPING -> {
+ return FrameworkStatsLog
+ .JANK_FRAME_COUNT_BY_WIDGET_REPORTED__WIDGET_STATE__TAPPING;
+ }
+ case AppJankStats.WIDGET_STATE_PREDICTIVE_BACK -> {
+ return FrameworkStatsLog
+ .JANK_FRAME_COUNT_BY_WIDGET_REPORTED__WIDGET_STATE__PREDICTIVE_BACK;
+ }
+ default -> {
+ if (DEBUG_LOGGING) {
+ Log.d(TAG, "Default State Logged: "
+ + AppJankStats.WIDGET_STATE_UNSPECIFIED);
+ }
+ return FrameworkStatsLog
+ .JANK_FRAME_COUNT_BY_WIDGET_REPORTED__WIDGET_STATE__WIDGET_STATE_UNSPECIFIED;
+ }
+ }
+ }
+
public static final class PendingJankStat {
private static final int NANOS_PER_MS = 1000000;
public long processedVsyncId = -1;
@@ -268,7 +360,7 @@ public class JankDataProcessor {
private int mRefreshRate;
- private static final int[] sFrameOverrunHistogramBounds = {
+ private static final int[] sFrameOverrunHistogramBounds = {
Integer.MIN_VALUE, -200, -150, -100, -90, -80, -70, -60, -50, -40, -30, -25, -20,
-18, -16, -14, -12, -10, -8, -6, -4, -2, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 25,
30, 40, 50, 60, 70, 80, 90, 100, 150, 200, 300, 400, 500, 600, 700, 800, 900, 1000,
@@ -279,6 +371,7 @@ public class JankDataProcessor {
// Histogram of frame duration overruns encoded in predetermined buckets.
public PendingJankStat() {
}
+
public long getProcessedVsyncId() {
return processedVsyncId;
}
@@ -422,4 +515,4 @@ public class JankDataProcessor {
}
}
-}
+} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/foldable_inner/light_landscape_dragZones_bubble.png b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/foldable_inner/light_landscape_dragZones_bubble.png
new file mode 100644
index 000000000000..15198748eea5
--- /dev/null
+++ b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/foldable_inner/light_landscape_dragZones_bubble.png
Binary files differ
diff --git a/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/foldable_inner/light_landscape_dragZones_bubbleBar.png b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/foldable_inner/light_landscape_dragZones_bubbleBar.png
new file mode 100644
index 000000000000..99673f6400e9
--- /dev/null
+++ b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/foldable_inner/light_landscape_dragZones_bubbleBar.png
Binary files differ
diff --git a/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/foldable_inner/light_landscape_dragZones_bubble_split_10_90.png b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/foldable_inner/light_landscape_dragZones_bubble_split_10_90.png
new file mode 100644
index 000000000000..ba4ebab75a7e
--- /dev/null
+++ b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/foldable_inner/light_landscape_dragZones_bubble_split_10_90.png
Binary files differ
diff --git a/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/foldable_inner/light_landscape_dragZones_bubble_split_90_10.png b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/foldable_inner/light_landscape_dragZones_bubble_split_90_10.png
new file mode 100644
index 000000000000..b3ff64401a96
--- /dev/null
+++ b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/foldable_inner/light_landscape_dragZones_bubble_split_90_10.png
Binary files differ
diff --git a/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/foldable_inner/light_landscape_dragZones_expandedView.png b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/foldable_inner/light_landscape_dragZones_expandedView.png
new file mode 100644
index 000000000000..534e320a0596
--- /dev/null
+++ b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/foldable_inner/light_landscape_dragZones_expandedView.png
Binary files differ
diff --git a/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/foldable_inner/light_landscape_dragZones_expandedView_split_10_90.png b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/foldable_inner/light_landscape_dragZones_expandedView_split_10_90.png
new file mode 100644
index 000000000000..67c9f49171ba
--- /dev/null
+++ b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/foldable_inner/light_landscape_dragZones_expandedView_split_10_90.png
Binary files differ
diff --git a/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/foldable_inner/light_landscape_dragZones_expandedView_split_90_10.png b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/foldable_inner/light_landscape_dragZones_expandedView_split_90_10.png
new file mode 100644
index 000000000000..a0fb4902a6f3
--- /dev/null
+++ b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/foldable_inner/light_landscape_dragZones_expandedView_split_90_10.png
Binary files differ
diff --git a/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/foldable_inner/light_portrait_dragZones_bubble.png b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/foldable_inner/light_portrait_dragZones_bubble.png
new file mode 100644
index 000000000000..27b35d447868
--- /dev/null
+++ b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/foldable_inner/light_portrait_dragZones_bubble.png
Binary files differ
diff --git a/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/foldable_inner/light_portrait_dragZones_bubbleBar.png b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/foldable_inner/light_portrait_dragZones_bubbleBar.png
new file mode 100644
index 000000000000..11528a028a8f
--- /dev/null
+++ b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/foldable_inner/light_portrait_dragZones_bubbleBar.png
Binary files differ
diff --git a/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/foldable_inner/light_portrait_dragZones_bubble_split_10_90.png b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/foldable_inner/light_portrait_dragZones_bubble_split_10_90.png
new file mode 100644
index 000000000000..ef9937700c08
--- /dev/null
+++ b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/foldable_inner/light_portrait_dragZones_bubble_split_10_90.png
Binary files differ
diff --git a/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/foldable_inner/light_portrait_dragZones_bubble_split_90_10.png b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/foldable_inner/light_portrait_dragZones_bubble_split_90_10.png
new file mode 100644
index 000000000000..f0cf08bfcf4e
--- /dev/null
+++ b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/foldable_inner/light_portrait_dragZones_bubble_split_90_10.png
Binary files differ
diff --git a/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/foldable_inner/light_portrait_dragZones_expandedView.png b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/foldable_inner/light_portrait_dragZones_expandedView.png
new file mode 100644
index 000000000000..bbaafb39845a
--- /dev/null
+++ b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/foldable_inner/light_portrait_dragZones_expandedView.png
Binary files differ
diff --git a/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/tablet/light_landscape_dragZones_bubble.png b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/tablet/light_landscape_dragZones_bubble.png
new file mode 100644
index 000000000000..38ebf3f3201a
--- /dev/null
+++ b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/tablet/light_landscape_dragZones_bubble.png
Binary files differ
diff --git a/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/tablet/light_landscape_dragZones_bubbleBar.png b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/tablet/light_landscape_dragZones_bubbleBar.png
new file mode 100644
index 000000000000..2e4fd51e3932
--- /dev/null
+++ b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/tablet/light_landscape_dragZones_bubbleBar.png
Binary files differ
diff --git a/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/tablet/light_landscape_dragZones_expandedView.png b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/tablet/light_landscape_dragZones_expandedView.png
new file mode 100644
index 000000000000..a1ba9fb50d6a
--- /dev/null
+++ b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/tablet/light_landscape_dragZones_expandedView.png
Binary files differ
diff --git a/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/tablet/light_portrait_dragZones_bubble.png b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/tablet/light_portrait_dragZones_bubble.png
new file mode 100644
index 000000000000..51bb15e10d30
--- /dev/null
+++ b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/tablet/light_portrait_dragZones_bubble.png
Binary files differ
diff --git a/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/tablet/light_portrait_dragZones_bubbleBar.png b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/tablet/light_portrait_dragZones_bubbleBar.png
new file mode 100644
index 000000000000..b643e2a69b2c
--- /dev/null
+++ b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/tablet/light_portrait_dragZones_bubbleBar.png
Binary files differ
diff --git a/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/tablet/light_portrait_dragZones_expandedView.png b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/tablet/light_portrait_dragZones_expandedView.png
new file mode 100644
index 000000000000..e6eeab7129be
--- /dev/null
+++ b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/tablet/light_portrait_dragZones_expandedView.png
Binary files differ
diff --git a/libs/WindowManager/Shell/multivalentScreenshotTests/src/com/android/wm/shell/shared/bubbles/DragZoneFactoryScreenshotTest.kt b/libs/WindowManager/Shell/multivalentScreenshotTests/src/com/android/wm/shell/shared/bubbles/DragZoneFactoryScreenshotTest.kt
new file mode 100644
index 000000000000..24f43d347163
--- /dev/null
+++ b/libs/WindowManager/Shell/multivalentScreenshotTests/src/com/android/wm/shell/shared/bubbles/DragZoneFactoryScreenshotTest.kt
@@ -0,0 +1,170 @@
+/*
+ * 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.shared.bubbles
+
+import android.content.Context
+import android.graphics.Color
+import android.graphics.drawable.Drawable
+import android.graphics.drawable.GradientDrawable
+import android.view.View
+import android.view.WindowManager
+import android.widget.FrameLayout
+import androidx.annotation.ColorInt
+import androidx.core.graphics.blue
+import androidx.core.graphics.green
+import androidx.core.graphics.red
+import androidx.core.graphics.toColorInt
+import androidx.test.core.app.ApplicationProvider.getApplicationContext
+import com.android.wm.shell.shared.bubbles.DragZoneFactory.DesktopWindowModeChecker
+import com.android.wm.shell.shared.bubbles.DragZoneFactory.SplitScreenModeChecker
+import com.android.wm.shell.shared.bubbles.DragZoneFactory.SplitScreenModeChecker.SplitScreenMode
+import com.android.wm.shell.testing.goldenpathmanager.WMShellGoldenPathManager
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
+import platform.test.screenshot.DeviceEmulationSpec
+import platform.test.screenshot.Displays
+import platform.test.screenshot.ViewScreenshotTestRule
+import platform.test.screenshot.ViewScreenshotTestRule.Mode
+import platform.test.screenshot.getEmulatedDevicePathConfig
+
+@RunWith(ParameterizedAndroidJunit4::class)
+class DragZoneFactoryScreenshotTest(private val param: Param) {
+ companion object {
+ @Parameters(name = "{0}")
+ @JvmStatic
+ fun getTestSpecs(): List<Param> {
+ val params = mutableListOf<Param>()
+ val draggedObjects =
+ listOf(
+ DraggedObject.Bubble(BubbleBarLocation.LEFT),
+ DraggedObject.BubbleBar(BubbleBarLocation.LEFT),
+ DraggedObject.ExpandedView(BubbleBarLocation.LEFT),
+ )
+ DeviceEmulationSpec.forDisplays(Displays.Tablet, isDarkTheme = false).forEach { tablet
+ ->
+ draggedObjects.forEach { draggedObject ->
+ params.add(Param(tablet, draggedObject, SplitScreenMode.NONE))
+ }
+ }
+ DeviceEmulationSpec.forDisplays(Displays.FoldableInner, isDarkTheme = false).forEach {
+ foldable ->
+ draggedObjects.forEach { draggedObject ->
+ params.add(Param(foldable, draggedObject, SplitScreenMode.NONE))
+ val isBubble = draggedObject is DraggedObject.Bubble
+ val isExpandedView = draggedObject is DraggedObject.ExpandedView
+ val addMoreSplitModes = isBubble || (isExpandedView && foldable.isLandscape)
+ if (addMoreSplitModes) {
+ params.add(Param(foldable, draggedObject, SplitScreenMode.SPLIT_10_90))
+ params.add(Param(foldable, draggedObject, SplitScreenMode.SPLIT_90_10))
+ }
+ }
+ }
+ return params
+ }
+ }
+
+ class Param(
+ val emulationSpec: DeviceEmulationSpec,
+ val draggedObject: DraggedObject,
+ val splitScreenMode: SplitScreenMode
+ ) {
+ private val draggedObjectName =
+ when (draggedObject) {
+ is DraggedObject.Bubble -> "bubble"
+ is DraggedObject.BubbleBar -> "bubbleBar"
+ is DraggedObject.ExpandedView -> "expandedView"
+ }
+
+ private val splitScreenModeName =
+ when (splitScreenMode) {
+ SplitScreenMode.NONE -> ""
+ SplitScreenMode.SPLIT_50_50 -> "_split_50_50"
+ SplitScreenMode.SPLIT_10_90 -> "_split_10_90"
+ SplitScreenMode.SPLIT_90_10 -> "_split_90_10"
+ }
+
+ val testName = "$draggedObjectName$splitScreenModeName"
+
+ override fun toString() = "${emulationSpec}_$testName"
+ }
+
+ @get:Rule
+ val screenshotRule =
+ ViewScreenshotTestRule(
+ param.emulationSpec,
+ WMShellGoldenPathManager(getEmulatedDevicePathConfig(param.emulationSpec))
+ )
+
+ private val context = getApplicationContext<Context>()
+
+ @Test
+ fun dragZones() {
+ screenshotRule.screenshotTest("dragZones_${param.testName}", mode = Mode.MatchSize) {
+ activity ->
+ activity.actionBar?.hide()
+ val dragZoneFactory = createDragZoneFactory()
+ val dragZones = dragZoneFactory.createSortedDragZones(param.draggedObject)
+ val container = FrameLayout(context)
+ dragZones.forEach { zone -> container.addZoneView(zone) }
+ container
+ }
+ }
+
+ private fun createDragZoneFactory(): DragZoneFactory {
+ val deviceConfig =
+ DeviceConfig.create(context, context.getSystemService(WindowManager::class.java)!!)
+ val splitScreenModeChecker = SplitScreenModeChecker { param.splitScreenMode }
+ val desktopWindowModeChecker = DesktopWindowModeChecker { true }
+ return DragZoneFactory(
+ context,
+ deviceConfig,
+ splitScreenModeChecker,
+ desktopWindowModeChecker
+ )
+ }
+
+ private fun FrameLayout.addZoneView(zone: DragZone) {
+ val view = View(context)
+ this.addView(view, 0)
+ view.layoutParams = FrameLayout.LayoutParams(zone.bounds.width(), zone.bounds.height())
+ view.background = createZoneDrawable(zone.color)
+ view.x = zone.bounds.left.toFloat()
+ view.y = zone.bounds.top.toFloat()
+ }
+
+ private fun createZoneDrawable(@ColorInt color: Int): Drawable {
+ val shape = GradientDrawable()
+ shape.shape = GradientDrawable.RECTANGLE
+ shape.setColor(Color.argb(128, color.red, color.green, color.blue))
+ shape.setStroke(2, color)
+ return shape
+ }
+
+ private val DragZone.color: Int
+ @ColorInt
+ get() =
+ when (this) {
+ is DragZone.Bubble -> "#3F5C8B".toColorInt()
+ is DragZone.Dismiss -> "#8B3F3F".toColorInt()
+ is DragZone.Split -> "#89B675".toColorInt()
+ is DragZone.FullScreen -> "#4ED075".toColorInt()
+ is DragZone.DesktopWindow -> "#EC928E".toColorInt()
+ }
+}
diff --git a/libs/WindowManager/Shell/shared/res/values/dimen.xml b/libs/WindowManager/Shell/shared/res/values/dimen.xml
index 5f013c52d70d..11a6f32d7454 100644
--- a/libs/WindowManager/Shell/shared/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/shared/res/values/dimen.xml
@@ -38,6 +38,7 @@
<dimen name="drag_zone_v_split_from_expanded_view_height_fold_short">100dp</dimen>
<!-- Bubble drop target dimensions -->
+ <dimen name="drop_target_elevation">1dp</dimen>
<dimen name="drop_target_full_screen_padding">20dp</dimen>
<dimen name="drop_target_desktop_window_padding_small">100dp</dimen>
<dimen name="drop_target_desktop_window_padding_large">130dp</dimen>
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/DragZone.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/DragZone.kt
index 5d346c047123..6eff75c9a479 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/DragZone.kt
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/DragZone.kt
@@ -31,29 +31,41 @@ sealed interface DragZone {
/** The bounds of this drag zone. */
val bounds: Rect
+ /** The bounds of the drop target associated with this drag zone. */
+ val dropTarget: Rect?
fun contains(x: Int, y: Int) = bounds.contains(x, y)
/** Represents the bubble drag area on the screen. */
- sealed class Bubble(override val bounds: Rect) : DragZone {
- data class Left(override val bounds: Rect, val dropTarget: Rect) : Bubble(bounds)
- data class Right(override val bounds: Rect, val dropTarget: Rect) : Bubble(bounds)
+ sealed class Bubble(override val bounds: Rect, override val dropTarget: Rect) : DragZone {
+ data class Left(override val bounds: Rect, override val dropTarget: Rect) :
+ Bubble(bounds, dropTarget)
+
+ data class Right(override val bounds: Rect, override val dropTarget: Rect) :
+ Bubble(bounds, dropTarget)
}
/** Represents dragging to Desktop Window. */
- data class DesktopWindow(override val bounds: Rect, val dropTarget: Rect) : DragZone
+ data class DesktopWindow(override val bounds: Rect, override val dropTarget: Rect) : DragZone
/** Represents dragging to Full Screen. */
- data class FullScreen(override val bounds: Rect, val dropTarget: Rect) : DragZone
+ data class FullScreen(override val bounds: Rect, override val dropTarget: Rect) : DragZone
/** Represents dragging to dismiss. */
- data class Dismiss(override val bounds: Rect) : DragZone
+ data class Dismiss(override val bounds: Rect) : DragZone {
+ override val dropTarget: Rect? = null
+ }
/** Represents dragging to enter Split or replace a Split app. */
sealed class Split(override val bounds: Rect) : DragZone {
+ override val dropTarget: Rect? = null
+
data class Left(override val bounds: Rect) : Split(bounds)
+
data class Right(override val bounds: Rect) : Split(bounds)
+
data class Top(override val bounds: Rect) : Split(bounds)
+
data class Bottom(override val bounds: Rect) : Split(bounds)
}
}
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/DropTargetManager.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/DropTargetManager.kt
index 29ce8d90e66f..2dc183f3f707 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/DropTargetManager.kt
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/DropTargetManager.kt
@@ -16,22 +16,54 @@
package com.android.wm.shell.shared.bubbles
+import android.content.Context
+import android.graphics.Rect
+import android.view.View
+import android.widget.FrameLayout
+import androidx.core.animation.Animator
+import androidx.core.animation.AnimatorListenerAdapter
+import androidx.core.animation.ValueAnimator
+
/**
* Manages animating drop targets in response to dragging bubble icons or bubble expanded views
* across different drag zones.
*/
class DropTargetManager(
+ context: Context,
+ private val container: FrameLayout,
private val isLayoutRtl: Boolean,
- private val dragZoneChangedListener: DragZoneChangedListener
+ private val dragZoneChangedListener: DragZoneChangedListener,
) {
private var state: DragState? = null
+ private val dropTargetView = View(context)
+ private var animator: ValueAnimator? = null
+
+ private companion object {
+ const val ANIMATION_DURATION_MS = 250L
+ }
/** Must be called when a drag gesture is starting. */
fun onDragStarted(draggedObject: DraggedObject, dragZones: List<DragZone>) {
val state = DragState(dragZones, draggedObject)
dragZoneChangedListener.onInitialDragZoneSet(state.initialDragZone)
this.state = state
+ animator?.cancel()
+ setupDropTarget()
+ }
+
+ private fun setupDropTarget() {
+ if (dropTargetView.parent != null) container.removeView(dropTargetView)
+ container.addView(dropTargetView, 0)
+ // TODO b/393173014: set elevation and background
+ dropTargetView.alpha = 0f
+ dropTargetView.scaleX = 1f
+ dropTargetView.scaleY = 1f
+ dropTargetView.translationX = 0f
+ dropTargetView.translationY = 0f
+ // the drop target is added with a width and height of 1 pixel. when it gets resized, we use
+ // set its scale to the width and height of the bounds it should have to avoid layout passes
+ dropTargetView.layoutParams = FrameLayout.LayoutParams(/* width= */ 1, /* height= */ 1)
}
/** Called when the user drags to a new location. */
@@ -42,14 +74,67 @@ class DropTargetManager(
state.currentDragZone = newDragZone
if (oldDragZone != newDragZone) {
dragZoneChangedListener.onDragZoneChanged(from = oldDragZone, to = newDragZone)
+ updateDropTarget()
}
}
/** Called when the drag ended. */
fun onDragEnded() {
+ startFadeAnimation(from = dropTargetView.alpha, to = 0f) {
+ container.removeView(dropTargetView)
+ }
state = null
}
+ private fun updateDropTarget() {
+ val currentDragZone = state?.currentDragZone ?: return
+ val dropTargetBounds = currentDragZone.dropTarget
+ when {
+ dropTargetBounds == null -> startFadeAnimation(from = dropTargetView.alpha, to = 0f)
+ dropTargetView.alpha == 0f -> {
+ dropTargetView.translationX = dropTargetBounds.exactCenterX()
+ dropTargetView.translationY = dropTargetBounds.exactCenterY()
+ dropTargetView.scaleX = dropTargetBounds.width().toFloat()
+ dropTargetView.scaleY = dropTargetBounds.height().toFloat()
+ startFadeAnimation(from = 0f, to = 1f)
+ }
+ else -> startMorphAnimation(dropTargetBounds)
+ }
+ }
+
+ private fun startFadeAnimation(from: Float, to: Float, onEnd: (() -> Unit)? = null) {
+ animator?.cancel()
+ val animator = ValueAnimator.ofFloat(from, to).setDuration(ANIMATION_DURATION_MS)
+ animator.addUpdateListener { _ -> dropTargetView.alpha = animator.animatedValue as Float }
+ if (onEnd != null) {
+ animator.doOnEnd(onEnd)
+ }
+ this.animator = animator
+ animator.start()
+ }
+
+ private fun startMorphAnimation(bounds: Rect) {
+ animator?.cancel()
+ val startAlpha = dropTargetView.alpha
+ val startTx = dropTargetView.translationX
+ val startTy = dropTargetView.translationY
+ val startScaleX = dropTargetView.scaleX
+ val startScaleY = dropTargetView.scaleY
+ val animator = ValueAnimator.ofFloat(0f, 1f).setDuration(ANIMATION_DURATION_MS)
+ animator.addUpdateListener { _ ->
+ val fraction = animator.animatedValue as Float
+ dropTargetView.alpha = startAlpha + (1 - startAlpha) * fraction
+ dropTargetView.translationX = startTx + (bounds.exactCenterX() - startTx) * fraction
+ dropTargetView.translationY = startTy + (bounds.exactCenterY() - startTy) * fraction
+ dropTargetView.scaleX =
+ startScaleX + (bounds.width().toFloat() - startScaleX) * fraction
+ dropTargetView.scaleY =
+ startScaleY + (bounds.height().toFloat() - startScaleY) * fraction
+ }
+ this.animator = animator
+ animator.start()
+ }
+
/** Stores the current drag state. */
private inner class DragState(
private val dragZones: List<DragZone>,
@@ -72,7 +157,18 @@ class DropTargetManager(
interface DragZoneChangedListener {
/** An initial drag zone was set. Called when a drag starts. */
fun onInitialDragZoneSet(dragZone: DragZone)
+
/** Called when the object was dragged to a different drag zone. */
fun onDragZoneChanged(from: DragZone, to: DragZone)
}
+
+ private fun Animator.doOnEnd(onEnd: () -> Unit) {
+ addListener(
+ object : AnimatorListenerAdapter() {
+ override fun onAnimationEnd(animation: Animator) {
+ onEnd()
+ }
+ }
+ )
+ }
}
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 d767a0b5bd57..f7fe694be8e2 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
@@ -3442,11 +3442,15 @@ class DesktopTasksController(
}
override fun createDesk(displayId: Int) {
- // TODO: b/362720497 - Implement this API.
+ executeRemoteCallWithTaskPermission(controller, "createDesk") { c ->
+ c.createDesk(displayId)
+ }
}
override fun activateDesk(deskId: Int, remoteTransition: RemoteTransition?) {
- // TODO: b/362720497 - Implement this API.
+ executeRemoteCallWithTaskPermission(controller, "activateDesk") { c ->
+ c.activateDesk(deskId, remoteTransition)
+ }
}
override fun showDesktopApps(displayId: Int, remoteTransition: RemoteTransition?) {
diff --git a/libs/WindowManager/Shell/tests/unittest/Android.bp b/libs/WindowManager/Shell/tests/unittest/Android.bp
index bf5e374c7607..bff12d026b93 100644
--- a/libs/WindowManager/Shell/tests/unittest/Android.bp
+++ b/libs/WindowManager/Shell/tests/unittest/Android.bp
@@ -45,6 +45,7 @@ android_test {
"androidx.test.rules",
"androidx.test.ext.junit",
"androidx.datastore_datastore",
+ "androidx.core_core-animation-testing",
"kotlinx_coroutines_test",
"androidx.dynamicanimation_dynamicanimation",
"dagger2",
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/bubbles/DropTargetManagerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/bubbles/DropTargetManagerTest.kt
index efb91c5fbfda..180a6915b45f 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/bubbles/DropTargetManagerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/bubbles/DropTargetManagerTest.kt
@@ -16,23 +16,33 @@
package com.android.wm.shell.shared.bubbles
+import android.content.Context
import android.graphics.Rect
+import android.view.View
+import android.widget.FrameLayout
+import androidx.core.animation.AnimatorTestRule
+import androidx.test.core.app.ApplicationProvider.getApplicationContext
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import androidx.test.platform.app.InstrumentationRegistry
import com.google.common.truth.Truth.assertThat
+import kotlin.test.assertFails
import org.junit.Before
+import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
-import kotlin.test.assertFails
/** Unit tests for [DropTargetManager]. */
@SmallTest
@RunWith(AndroidJUnit4::class)
class DropTargetManagerTest {
+ @get:Rule val animatorTestRule = AnimatorTestRule()
+
+ private val context = getApplicationContext<Context>()
private lateinit var dropTargetManager: DropTargetManager
private lateinit var dragZoneChangedListener: FakeDragZoneChangedListener
- private val dropTarget = Rect(0, 0, 0, 0)
+ private lateinit var container: FrameLayout
// create 3 drop zones that are horizontally next to each other
// -------------------------------------------------
@@ -43,15 +53,20 @@ class DropTargetManagerTest {
// | | | |
// -------------------------------------------------
private val bubbleLeftDragZone =
- DragZone.Bubble.Left(bounds = Rect(0, 0, 100, 100), dropTarget = dropTarget)
+ DragZone.Bubble.Left(bounds = Rect(0, 0, 100, 100), dropTarget = Rect(0, 0, 50, 200))
private val dismissDragZone = DragZone.Dismiss(bounds = Rect(100, 0, 200, 100))
private val bubbleRightDragZone =
- DragZone.Bubble.Right(bounds = Rect(200, 0, 300, 100), dropTarget = dropTarget)
+ DragZone.Bubble.Right(bounds = Rect(200, 0, 300, 100), dropTarget = Rect(200, 0, 280, 150))
+
+ private val dropTargetView: View
+ get() = container.getChildAt(0)
@Before
fun setUp() {
+ container = FrameLayout(context)
dragZoneChangedListener = FakeDragZoneChangedListener()
- dropTargetManager = DropTargetManager(isLayoutRtl = false, dragZoneChangedListener)
+ dropTargetManager =
+ DropTargetManager(context, container, isLayoutRtl = false, dragZoneChangedListener)
}
@Test
@@ -79,17 +94,21 @@ class DropTargetManagerTest {
DraggedObject.Bubble(BubbleBarLocation.LEFT),
listOf(bubbleLeftDragZone, bubbleRightDragZone, dismissDragZone)
)
- dropTargetManager.onDragUpdated(
- bubbleRightDragZone.bounds.centerX(),
- bubbleRightDragZone.bounds.centerY()
- )
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
+ dropTargetManager.onDragUpdated(
+ bubbleRightDragZone.bounds.centerX(),
+ bubbleRightDragZone.bounds.centerY()
+ )
+ }
assertThat(dragZoneChangedListener.fromDragZone).isEqualTo(bubbleLeftDragZone)
assertThat(dragZoneChangedListener.toDragZone).isEqualTo(bubbleRightDragZone)
- dropTargetManager.onDragUpdated(
- dismissDragZone.bounds.centerX(),
- dismissDragZone.bounds.centerY()
- )
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
+ dropTargetManager.onDragUpdated(
+ dismissDragZone.bounds.centerX(),
+ dismissDragZone.bounds.centerY()
+ )
+ }
assertThat(dragZoneChangedListener.fromDragZone).isEqualTo(bubbleRightDragZone)
assertThat(dragZoneChangedListener.toDragZone).isEqualTo(dismissDragZone)
}
@@ -100,10 +119,12 @@ class DropTargetManagerTest {
DraggedObject.Bubble(BubbleBarLocation.LEFT),
listOf(bubbleLeftDragZone, bubbleRightDragZone, dismissDragZone)
)
- dropTargetManager.onDragUpdated(
- bubbleLeftDragZone.bounds.centerX(),
- bubbleLeftDragZone.bounds.centerY()
- )
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
+ dropTargetManager.onDragUpdated(
+ bubbleLeftDragZone.bounds.centerX(),
+ bubbleLeftDragZone.bounds.centerY()
+ )
+ }
assertThat(dragZoneChangedListener.fromDragZone).isNull()
assertThat(dragZoneChangedListener.toDragZone).isNull()
}
@@ -118,7 +139,9 @@ class DropTargetManagerTest {
val pointY = 200
assertThat(bubbleLeftDragZone.contains(pointX, pointY)).isFalse()
assertThat(bubbleRightDragZone.contains(pointX, pointY)).isFalse()
- dropTargetManager.onDragUpdated(pointX, pointY)
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
+ dropTargetManager.onDragUpdated(pointX, pointY)
+ }
assertThat(dragZoneChangedListener.fromDragZone).isNull()
assertThat(dragZoneChangedListener.toDragZone).isNull()
}
@@ -135,27 +158,30 @@ class DropTargetManagerTest {
// drag to a point that is within both the bubble right zone and split zone
val (pointX, pointY) =
- Pair(
- bubbleRightDragZone.bounds.centerX(),
- bubbleRightDragZone.bounds.centerY()
- )
+ Pair(bubbleRightDragZone.bounds.centerX(), bubbleRightDragZone.bounds.centerY())
assertThat(splitDragZone.contains(pointX, pointY)).isTrue()
- dropTargetManager.onDragUpdated(pointX, pointY)
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
+ dropTargetManager.onDragUpdated(pointX, pointY)
+ }
// verify we dragged to the bubble right zone because that has higher priority than split
assertThat(dragZoneChangedListener.fromDragZone).isEqualTo(bubbleLeftDragZone)
assertThat(dragZoneChangedListener.toDragZone).isEqualTo(bubbleRightDragZone)
- dropTargetManager.onDragUpdated(
- bubbleRightDragZone.bounds.centerX(),
- 150 // below the bubble and dismiss drag zones but within split
- )
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
+ dropTargetManager.onDragUpdated(
+ bubbleRightDragZone.bounds.centerX(),
+ 150 // below the bubble and dismiss drag zones but within split
+ )
+ }
assertThat(dragZoneChangedListener.fromDragZone).isEqualTo(bubbleRightDragZone)
assertThat(dragZoneChangedListener.toDragZone).isEqualTo(splitDragZone)
val (dismissPointX, dismissPointY) =
Pair(dismissDragZone.bounds.centerX(), dismissDragZone.bounds.centerY())
assertThat(splitDragZone.contains(dismissPointX, dismissPointY)).isTrue()
- dropTargetManager.onDragUpdated(dismissPointX, dismissPointY)
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
+ dropTargetManager.onDragUpdated(dismissPointX, dismissPointY)
+ }
assertThat(dragZoneChangedListener.fromDragZone).isEqualTo(splitDragZone)
assertThat(dragZoneChangedListener.toDragZone).isEqualTo(dismissDragZone)
}
@@ -166,7 +192,9 @@ class DropTargetManagerTest {
DraggedObject.Bubble(BubbleBarLocation.LEFT),
listOf(bubbleLeftDragZone, bubbleRightDragZone, dismissDragZone)
)
- dropTargetManager.onDragEnded()
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
+ dropTargetManager.onDragEnded()
+ }
dropTargetManager.onDragUpdated(
bubbleRightDragZone.bounds.centerX(),
bubbleRightDragZone.bounds.centerY()
@@ -175,6 +203,129 @@ class DropTargetManagerTest {
assertThat(dragZoneChangedListener.toDragZone).isNull()
}
+ @Test
+ fun onDragStarted_dropTargetAddedToContainer() {
+ dropTargetManager.onDragStarted(
+ DraggedObject.Bubble(BubbleBarLocation.LEFT),
+ listOf(bubbleLeftDragZone, bubbleRightDragZone)
+ )
+ assertThat(container.childCount).isEqualTo(1)
+ assertThat(dropTargetView.alpha).isEqualTo(0)
+ }
+
+ @Test
+ fun onDragEnded_dropTargetRemovedFromContainer() {
+ dropTargetManager.onDragStarted(
+ DraggedObject.Bubble(BubbleBarLocation.LEFT),
+ listOf(bubbleLeftDragZone, bubbleRightDragZone)
+ )
+ assertThat(container.childCount).isEqualTo(1)
+
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
+ dropTargetManager.onDragEnded()
+ animatorTestRule.advanceTimeBy(250)
+ }
+ assertThat(container.childCount).isEqualTo(0)
+ }
+
+ @Test
+ fun startNewDrag_beforeDropTargetRemoved() {
+ dropTargetManager.onDragStarted(
+ DraggedObject.Bubble(BubbleBarLocation.LEFT),
+ listOf(bubbleLeftDragZone, bubbleRightDragZone)
+ )
+ assertThat(container.childCount).isEqualTo(1)
+
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
+ dropTargetManager.onDragEnded()
+ // advance the timer by 100ms so the animation doesn't complete
+ animatorTestRule.advanceTimeBy(100)
+ }
+ assertThat(container.childCount).isEqualTo(1)
+
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
+ dropTargetManager.onDragStarted(
+ DraggedObject.Bubble(BubbleBarLocation.LEFT),
+ listOf(bubbleLeftDragZone, bubbleRightDragZone)
+ )
+ }
+ assertThat(container.childCount).isEqualTo(1)
+ }
+
+ @Test
+ fun updateDragZone_withDropTarget_dropTargetUpdated() {
+ dropTargetManager.onDragStarted(
+ DraggedObject.Bubble(BubbleBarLocation.LEFT),
+ listOf(dismissDragZone, bubbleLeftDragZone, bubbleRightDragZone)
+ )
+
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
+ dropTargetManager.onDragUpdated(
+ bubbleRightDragZone.bounds.centerX(),
+ bubbleRightDragZone.bounds.centerY()
+ )
+ animatorTestRule.advanceTimeBy(250)
+ }
+
+ assertThat(dropTargetView.alpha).isEqualTo(1)
+ verifyDropTargetPosition(bubbleRightDragZone.dropTarget)
+ }
+
+ @Test
+ fun updateDragZone_withoutDropTarget_dropTargetHidden() {
+ dropTargetManager.onDragStarted(
+ DraggedObject.Bubble(BubbleBarLocation.LEFT),
+ listOf(dismissDragZone, bubbleLeftDragZone, bubbleRightDragZone)
+ )
+
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
+ dropTargetManager.onDragUpdated(
+ dismissDragZone.bounds.centerX(),
+ dismissDragZone.bounds.centerY()
+ )
+ animatorTestRule.advanceTimeBy(250)
+ }
+
+ assertThat(dropTargetView.alpha).isEqualTo(0)
+ }
+
+ @Test
+ fun updateDragZone_betweenZonesWithDropTarget_dropTargetUpdated() {
+ dropTargetManager.onDragStarted(
+ DraggedObject.Bubble(BubbleBarLocation.LEFT),
+ listOf(dismissDragZone, bubbleLeftDragZone, bubbleRightDragZone)
+ )
+
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
+ dropTargetManager.onDragUpdated(
+ bubbleRightDragZone.bounds.centerX(),
+ bubbleRightDragZone.bounds.centerY()
+ )
+ animatorTestRule.advanceTimeBy(250)
+ }
+
+ assertThat(dropTargetView.alpha).isEqualTo(1)
+ verifyDropTargetPosition(bubbleRightDragZone.dropTarget)
+
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
+ dropTargetManager.onDragUpdated(
+ bubbleLeftDragZone.bounds.centerX(),
+ bubbleLeftDragZone.bounds.centerY()
+ )
+ animatorTestRule.advanceTimeBy(250)
+ }
+
+ assertThat(dropTargetView.alpha).isEqualTo(1)
+ verifyDropTargetPosition(bubbleLeftDragZone.dropTarget)
+ }
+
+ private fun verifyDropTargetPosition(rect: Rect) {
+ assertThat(dropTargetView.scaleX).isEqualTo(rect.width())
+ assertThat(dropTargetView.scaleY).isEqualTo(rect.height())
+ assertThat(dropTargetView.translationX).isEqualTo(rect.exactCenterX())
+ assertThat(dropTargetView.translationY).isEqualTo(rect.exactCenterY())
+ }
+
private class FakeDragZoneChangedListener : DropTargetManager.DragZoneChangedListener {
var initialDragZone: DragZone? = null
var fromDragZone: DragZone? = null
@@ -183,6 +334,7 @@ class DropTargetManagerTest {
override fun onInitialDragZoneSet(dragZone: DragZone) {
initialDragZone = dragZone
}
+
override fun onDragZoneChanged(from: DragZone, to: DragZone) {
fromDragZone = from
toDragZone = to
diff --git a/packages/SettingsLib/res/drawable/ic_1x_mobiledata_updated.xml b/packages/SettingsLib/res/drawable/ic_1x_mobiledata_updated.xml
new file mode 100644
index 000000000000..bd2fad2fedc0
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_1x_mobiledata_updated.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
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT 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="17dp"
+ android:height="12.88dp"
+ android:viewportWidth="13.53"
+ android:viewportHeight="10.25">
+ <path
+ android:pathData="M3.738,10.252C3.468,10.252 3.234,10.154 3.038,9.958C2.847,9.757 2.751,9.522 2.751,9.251L2.751,2.587L1.372,3.497C1.167,3.628 0.943,3.672 0.7,3.63C0.462,3.588 0.276,3.467 0.14,3.266C0.01,3.065 -0.032,2.844 0.014,2.601C0.066,2.358 0.192,2.172 0.392,2.041L3.185,0.2C3.265,0.149 3.344,0.104 3.423,0.067C3.507,0.025 3.619,0.004 3.759,0.004C4.03,0.004 4.259,0.102 4.445,0.298C4.637,0.494 4.732,0.734 4.732,1.019L4.732,9.251C4.732,9.522 4.634,9.757 4.438,9.958C4.242,10.154 4.009,10.252 3.738,10.252ZM12.582,10.245C12.391,10.245 12.218,10.194 12.064,10.091C11.915,9.984 11.796,9.848 11.707,9.685L9.803,6.038L9.593,5.961L7.332,1.411C7.136,1.084 7.127,0.769 7.304,0.466C7.482,0.163 7.755,0.011 8.123,0.011C8.301,0.011 8.466,0.065 8.62,0.172C8.779,0.279 8.903,0.415 8.991,0.578L10.783,4.015L11.014,4.05L13.373,8.796C13.569,9.132 13.581,9.459 13.408,9.776C13.236,10.089 12.96,10.245 12.582,10.245ZM7.92,10.238C7.57,10.238 7.309,10.089 7.136,9.79C6.968,9.491 6.978,9.186 7.164,8.873L9.621,4.008L9.817,4.008L11.63,0.557C11.719,0.398 11.836,0.27 11.98,0.172C12.125,0.069 12.288,0.018 12.47,0.018C12.816,0.018 13.075,0.163 13.247,0.452C13.42,0.741 13.411,1.047 13.219,1.369L10.909,5.982L10.762,5.989L8.781,9.713C8.697,9.867 8.578,9.993 8.424,10.091C8.27,10.189 8.102,10.238 7.92,10.238Z"
+ android:fillColor="#000"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_3g_mobiledata_updated.xml b/packages/SettingsLib/res/drawable/ic_3g_mobiledata_updated.xml
new file mode 100644
index 000000000000..8e632e69a56e
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_3g_mobiledata_updated.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
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT 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="17dp"
+ android:height="12.21dp"
+ android:viewportWidth="14.51"
+ android:viewportHeight="10.42">
+ <path
+ android:pathData="M2.827,10.416C2.225,10.416 1.726,10.311 1.329,10.101C0.937,9.891 0.629,9.606 0.405,9.247C0.181,8.888 0.051,8.58 0.013,8.323C-0.019,8.066 0.023,7.849 0.139,7.672C0.256,7.49 0.415,7.371 0.615,7.315C0.816,7.254 1.003,7.259 1.175,7.329C1.353,7.394 1.481,7.495 1.56,7.63C1.644,7.765 1.733,7.922 1.826,8.099C1.92,8.272 2.041,8.416 2.19,8.533C2.34,8.65 2.533,8.708 2.771,8.708C3.089,8.708 3.343,8.615 3.534,8.428C3.726,8.237 3.821,7.959 3.821,7.595L3.821,6.888C3.821,6.529 3.726,6.256 3.534,6.069C3.348,5.882 3.077,5.789 2.722,5.789L2.456,5.789C2.251,5.789 2.071,5.714 1.917,5.565C1.768,5.416 1.693,5.236 1.693,5.026C1.693,4.816 1.768,4.636 1.917,4.487C2.067,4.338 2.244,4.263 2.449,4.263L2.666,4.263C2.984,4.263 3.222,4.177 3.38,4.004C3.544,3.831 3.625,3.558 3.625,3.185L3.625,2.618C3.625,2.315 3.544,2.084 3.38,1.925C3.217,1.766 2.998,1.687 2.722,1.687C2.531,1.687 2.37,1.729 2.239,1.813C2.109,1.892 1.999,2.004 1.91,2.149C1.822,2.294 1.738,2.427 1.658,2.548C1.579,2.665 1.453,2.756 1.28,2.821C1.108,2.882 0.926,2.882 0.734,2.821C0.543,2.756 0.391,2.632 0.279,2.45C0.172,2.268 0.142,2.049 0.188,1.792C0.24,1.531 0.384,1.248 0.622,0.945C0.86,0.642 1.159,0.408 1.518,0.245C1.882,0.082 2.326,0 2.848,0C3.67,0 4.323,0.215 4.808,0.644C5.294,1.069 5.536,1.636 5.536,2.345L5.536,2.8C5.536,3.332 5.417,3.768 5.179,4.109C4.941,4.445 4.598,4.69 4.15,4.844L4.15,4.9C4.678,5.017 5.086,5.259 5.375,5.628C5.669,5.992 5.816,6.484 5.816,7.105L5.816,7.679C5.816,8.514 5.55,9.179 5.018,9.674C4.491,10.169 3.761,10.416 2.827,10.416ZM10.943,10.416C9.819,10.411 8.92,10.031 8.248,9.275C7.581,8.514 7.247,7.406 7.247,5.95L7.247,4.417C7.247,2.975 7.588,1.878 8.269,1.127C8.951,0.371 9.863,-0.005 11.006,0C11.52,0 11.954,0.075 12.308,0.224C12.668,0.369 12.973,0.576 13.225,0.847C13.482,1.113 13.659,1.374 13.757,1.631C13.855,1.883 13.862,2.121 13.778,2.345C13.694,2.569 13.55,2.732 13.344,2.835C13.144,2.938 12.948,2.968 12.756,2.926C12.565,2.884 12.416,2.802 12.308,2.681C12.201,2.555 12.091,2.422 11.979,2.282C11.867,2.142 11.727,2.032 11.559,1.953C11.391,1.869 11.191,1.827 10.957,1.827C10.439,1.822 10.024,2.011 9.711,2.394C9.403,2.777 9.249,3.358 9.249,4.137L9.249,6.293C9.249,7.063 9.408,7.644 9.725,8.036C10.047,8.428 10.467,8.624 10.985,8.624C11.489,8.624 11.884,8.477 12.168,8.183C12.453,7.889 12.607,7.474 12.63,6.937L12.63,6.265L11.657,6.265C11.452,6.265 11.275,6.188 11.125,6.034C10.976,5.875 10.901,5.689 10.901,5.474C10.901,5.255 10.978,5.068 11.132,4.914C11.291,4.755 11.48,4.676 11.699,4.676L13.666,4.676C13.9,4.676 14.098,4.755 14.261,4.914C14.425,5.073 14.509,5.269 14.513,5.502L14.513,6.538C14.513,7.77 14.191,8.724 13.547,9.401C12.908,10.078 12.04,10.416 10.943,10.416Z"
+ android:fillColor="#000"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_4g_lte_mobiledata_updated.xml b/packages/SettingsLib/res/drawable/ic_4g_lte_mobiledata_updated.xml
new file mode 100644
index 000000000000..bba359e8b238
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_4g_lte_mobiledata_updated.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
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT 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="34dp"
+ android:height="14.07dp"
+ android:viewportWidth="27.29"
+ android:viewportHeight="11.29">
+ <path
+ android:pathData="M4.552,11.195C4.286,11.195 4.058,11.099 3.866,10.908C3.68,10.717 3.586,10.488 3.586,10.222L3.586,8.171L3.74,7.891L3.74,2.823L4.72,3.53L3.712,3.53L1.808,7.366L4.37,7.366L4.797,7.275L5.777,7.275C6.015,7.275 6.218,7.359 6.386,7.527C6.554,7.695 6.638,7.898 6.638,8.136C6.638,8.369 6.554,8.57 6.386,8.738C6.218,8.906 6.015,8.99 5.777,8.99L1.192,8.99C0.856,8.99 0.572,8.88 0.338,8.661C0.11,8.442 -0.005,8.169 -0.005,7.842C-0.005,7.683 0.016,7.569 0.058,7.499C0.1,7.424 0.142,7.35 0.184,7.275L3.124,1.633C3.222,1.446 3.374,1.283 3.579,1.143C3.789,1.003 4.013,0.933 4.251,0.933C4.606,0.933 4.905,1.061 5.147,1.318C5.39,1.57 5.511,1.876 5.511,2.235L5.511,10.222C5.511,10.488 5.416,10.717 5.224,10.908C5.038,11.099 4.814,11.195 4.552,11.195ZM11.303,11.286C10.179,11.281 9.28,10.901 8.608,10.145C7.941,9.384 7.607,8.276 7.607,6.82L7.607,5.287C7.607,3.845 7.948,2.748 8.629,1.997C9.311,1.241 10.223,0.865 11.366,0.87C11.88,0.87 12.314,0.945 12.668,1.094C13.028,1.239 13.333,1.446 13.585,1.717C13.842,1.983 14.019,2.244 14.117,2.501C14.215,2.753 14.222,2.991 14.138,3.215C14.054,3.439 13.91,3.602 13.704,3.705C13.504,3.808 13.308,3.838 13.116,3.796C12.925,3.754 12.776,3.672 12.668,3.551C12.561,3.425 12.451,3.292 12.339,3.152C12.227,3.012 12.087,2.902 11.919,2.823C11.751,2.739 11.551,2.697 11.317,2.697C10.799,2.692 10.384,2.881 10.071,3.264C9.763,3.647 9.609,4.228 9.609,5.007L9.609,7.163C9.609,7.933 9.768,8.514 10.085,8.906C10.407,9.298 10.827,9.494 11.345,9.494C11.849,9.494 12.244,9.347 12.528,9.053C12.813,8.759 12.967,8.344 12.99,7.807L12.99,7.135L12.017,7.135C11.812,7.135 11.635,7.058 11.485,6.904C11.336,6.745 11.261,6.559 11.261,6.344C11.261,6.125 11.338,5.938 11.492,5.784C11.651,5.625 11.84,5.546 12.059,5.546L14.026,5.546C14.26,5.546 14.458,5.625 14.621,5.784C14.785,5.943 14.869,6.139 14.873,6.372L14.873,7.408C14.873,8.64 14.551,9.594 13.907,10.271C13.268,10.948 12.4,11.286 11.303,11.286ZM16.641,6.09C16.434,6.09 16.256,6.016 16.108,5.867C15.962,5.719 15.89,5.543 15.89,5.338L15.89,0.689C15.89,0.501 15.957,0.34 16.091,0.206C16.226,0.069 16.385,0 16.57,0C16.758,0 16.919,0.069 17.053,0.206C17.187,0.34 17.255,0.501 17.255,0.689L17.255,4.83L18.54,4.83C18.713,4.83 18.863,4.892 18.989,5.015C19.115,5.138 19.178,5.286 19.178,5.46C19.178,5.631 19.115,5.779 18.989,5.905C18.863,6.028 18.713,6.09 18.54,6.09L16.641,6.09ZM20.979,6.166C20.792,6.166 20.63,6.098 20.496,5.964C20.365,5.827 20.299,5.664 20.299,5.477L20.299,0.731L21.664,0.731L21.664,5.477C21.664,5.664 21.597,5.827 21.462,5.964C21.328,6.098 21.167,6.166 20.979,6.166ZM19.648,1.331C19.474,1.331 19.324,1.27 19.198,1.147C19.075,1.023 19.014,0.876 19.014,0.706C19.014,0.532 19.075,0.384 19.198,0.26C19.324,0.137 19.474,0.076 19.648,0.076L22.306,0.076C22.48,0.076 22.628,0.137 22.751,0.26C22.877,0.384 22.941,0.532 22.941,0.706C22.941,0.876 22.877,1.023 22.751,1.147C22.628,1.27 22.48,1.331 22.306,1.331L19.648,1.331ZM24.553,6.09C24.346,6.09 24.168,6.016 24.019,5.867C23.874,5.719 23.801,5.543 23.801,5.338L23.801,0.823C23.801,0.619 23.874,0.444 24.019,0.298C24.168,0.15 24.346,0.076 24.553,0.076L26.59,0.076C26.763,0.076 26.912,0.137 27.035,0.26C27.161,0.384 27.224,0.531 27.224,0.701C27.224,0.872 27.161,1.019 27.035,1.142C26.912,1.266 26.763,1.327 26.59,1.327L25.158,1.327L25.158,4.838L26.657,4.838C26.831,4.838 26.979,4.9 27.102,5.023C27.225,5.146 27.287,5.293 27.287,5.464C27.287,5.635 27.225,5.782 27.102,5.905C26.979,6.028 26.831,6.09 26.657,6.09L24.553,6.09ZM24.637,3.595L24.637,2.444L26.3,2.444C26.46,2.444 26.595,2.5 26.707,2.612C26.822,2.724 26.88,2.86 26.88,3.02C26.88,3.177 26.822,3.312 26.707,3.427C26.595,3.539 26.46,3.595 26.3,3.595L24.637,3.595Z"
+ android:fillColor="#000"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_4g_lte_plus_mobiledata_updated.xml b/packages/SettingsLib/res/drawable/ic_4g_lte_plus_mobiledata_updated.xml
new file mode 100644
index 000000000000..cb6fd50a07ea
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_4g_lte_plus_mobiledata_updated.xml
@@ -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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="34dp"
+ android:height="11.93dp"
+ android:viewportWidth="32.18"
+ android:viewportHeight="11.29">
+ <path
+ android:pathData="M4.552,11.195C4.286,11.195 4.058,11.099 3.866,10.908C3.68,10.717 3.586,10.488 3.586,10.222L3.586,8.171L3.74,7.891L3.74,2.823L4.72,3.53L3.712,3.53L1.808,7.366L4.37,7.366L4.797,7.275L5.777,7.275C6.015,7.275 6.218,7.359 6.386,7.527C6.554,7.695 6.638,7.898 6.638,8.136C6.638,8.369 6.554,8.57 6.386,8.738C6.218,8.906 6.015,8.99 5.777,8.99L1.192,8.99C0.856,8.99 0.572,8.88 0.338,8.661C0.11,8.442 -0.005,8.169 -0.005,7.842C-0.005,7.683 0.016,7.569 0.058,7.499C0.1,7.424 0.142,7.35 0.184,7.275L3.124,1.633C3.222,1.446 3.374,1.283 3.579,1.143C3.789,1.003 4.013,0.933 4.251,0.933C4.606,0.933 4.905,1.061 5.147,1.318C5.39,1.57 5.511,1.876 5.511,2.235L5.511,10.222C5.511,10.488 5.416,10.717 5.224,10.908C5.038,11.099 4.814,11.195 4.552,11.195ZM11.303,11.286C10.179,11.281 9.28,10.901 8.608,10.145C7.941,9.384 7.607,8.276 7.607,6.82L7.607,5.287C7.607,3.845 7.948,2.748 8.629,1.997C9.311,1.241 10.223,0.865 11.366,0.87C11.88,0.87 12.314,0.945 12.668,1.094C13.028,1.239 13.333,1.446 13.585,1.717C13.842,1.983 14.019,2.244 14.117,2.501C14.215,2.753 14.222,2.991 14.138,3.215C14.054,3.439 13.91,3.602 13.704,3.705C13.504,3.808 13.308,3.838 13.116,3.796C12.925,3.754 12.776,3.672 12.668,3.551C12.561,3.425 12.451,3.292 12.339,3.152C12.227,3.012 12.087,2.902 11.919,2.823C11.751,2.739 11.551,2.697 11.317,2.697C10.799,2.692 10.384,2.881 10.071,3.264C9.763,3.647 9.609,4.228 9.609,5.007L9.609,7.163C9.609,7.933 9.768,8.514 10.085,8.906C10.407,9.298 10.827,9.494 11.345,9.494C11.849,9.494 12.244,9.347 12.528,9.053C12.813,8.759 12.967,8.344 12.99,7.807L12.99,7.135L12.017,7.135C11.812,7.135 11.635,7.058 11.485,6.904C11.336,6.745 11.261,6.559 11.261,6.344C11.261,6.125 11.338,5.938 11.492,5.784C11.651,5.625 11.84,5.546 12.059,5.546L14.026,5.546C14.26,5.546 14.458,5.625 14.621,5.784C14.785,5.943 14.869,6.139 14.873,6.372L14.873,7.408C14.873,8.64 14.551,9.594 13.907,10.271C13.268,10.948 12.4,11.286 11.303,11.286ZM16.641,6.09C16.434,6.09 16.256,6.016 16.108,5.867C15.962,5.719 15.89,5.543 15.89,5.338L15.89,0.689C15.89,0.501 15.957,0.34 16.091,0.206C16.226,0.069 16.385,0 16.57,0C16.758,0 16.919,0.069 17.053,0.206C17.187,0.34 17.255,0.501 17.255,0.689L17.255,4.83L18.54,4.83C18.713,4.83 18.863,4.892 18.989,5.015C19.115,5.138 19.178,5.286 19.178,5.46C19.178,5.631 19.115,5.779 18.989,5.905C18.863,6.028 18.713,6.09 18.54,6.09L16.641,6.09ZM20.979,6.166C20.792,6.166 20.63,6.098 20.496,5.964C20.365,5.827 20.299,5.664 20.299,5.477L20.299,0.731L21.664,0.731L21.664,5.477C21.664,5.664 21.597,5.827 21.462,5.964C21.328,6.098 21.167,6.166 20.979,6.166ZM19.648,1.331C19.474,1.331 19.324,1.27 19.198,1.147C19.075,1.023 19.014,0.876 19.014,0.706C19.014,0.532 19.075,0.384 19.198,0.26C19.324,0.137 19.474,0.076 19.648,0.076L22.306,0.076C22.48,0.076 22.628,0.137 22.751,0.26C22.877,0.384 22.941,0.532 22.941,0.706C22.941,0.876 22.877,1.023 22.751,1.147C22.628,1.27 22.48,1.331 22.306,1.331L19.648,1.331ZM24.553,6.09C24.346,6.09 24.168,6.016 24.019,5.867C23.874,5.719 23.801,5.543 23.801,5.338L23.801,0.823C23.801,0.619 23.874,0.444 24.019,0.298C24.168,0.15 24.346,0.076 24.553,0.076L26.59,0.076C26.763,0.076 26.912,0.137 27.035,0.26C27.161,0.384 27.224,0.531 27.224,0.701C27.224,0.872 27.161,1.019 27.035,1.142C26.912,1.266 26.763,1.327 26.59,1.327L25.158,1.327L25.158,4.838L26.657,4.838C26.831,4.838 26.979,4.9 27.102,5.023C27.225,5.146 27.287,5.293 27.287,5.464C27.287,5.635 27.225,5.782 27.102,5.905C26.979,6.028 26.831,6.09 26.657,6.09L24.553,6.09ZM24.637,3.595L24.637,2.444L26.3,2.444C26.46,2.444 26.595,2.5 26.707,2.612C26.822,2.724 26.88,2.86 26.88,3.02C26.88,3.177 26.822,3.312 26.707,3.427C26.595,3.539 26.46,3.595 26.3,3.595L24.637,3.595Z"
+ android:fillColor="#000"/>
+ <path
+ android:pathData="M30.101,5.346C29.923,5.346 29.768,5.282 29.637,5.154C29.509,5.023 29.445,4.867 29.445,4.686L29.445,1.742C29.445,1.561 29.509,1.406 29.637,1.278C29.768,1.147 29.923,1.082 30.101,1.082C30.28,1.082 30.433,1.147 30.561,1.278C30.689,1.406 30.753,1.561 30.753,1.742L30.753,4.686C30.753,4.867 30.689,5.023 30.561,5.154C30.433,5.282 30.28,5.346 30.101,5.346ZM28.677,3.858C28.499,3.858 28.345,3.795 28.217,3.67C28.089,3.542 28.025,3.39 28.025,3.214C28.025,3.038 28.089,2.887 28.217,2.762C28.345,2.634 28.499,2.57 28.677,2.57L31.525,2.57C31.704,2.57 31.857,2.634 31.985,2.762C32.113,2.887 32.177,3.038 32.177,3.214C32.177,3.39 32.113,3.542 31.985,3.67C31.857,3.795 31.704,3.858 31.525,3.858L28.677,3.858Z"
+ android:fillColor="#000"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_4g_mobiledata_updated.xml b/packages/SettingsLib/res/drawable/ic_4g_mobiledata_updated.xml
new file mode 100644
index 000000000000..562bcaf2fdba
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_4g_mobiledata_updated.xml
@@ -0,0 +1,25 @@
+<!--
+ 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="19dp"
+ android:height="13.31dp"
+ android:viewportWidth="14.88"
+ android:viewportHeight="10.42">
+ <path
+ android:pathData="M4.552,10.325C4.286,10.325 4.058,10.229 3.866,10.038C3.68,9.847 3.586,9.618 3.586,9.352L3.586,7.301L3.74,7.021L3.74,1.953L4.72,2.66L3.712,2.66L1.808,6.496L4.37,6.496L4.797,6.405L5.777,6.405C6.015,6.405 6.218,6.489 6.386,6.657C6.554,6.825 6.638,7.028 6.638,7.266C6.638,7.499 6.554,7.7 6.386,7.868C6.218,8.036 6.015,8.12 5.777,8.12L1.192,8.12C0.856,8.12 0.572,8.01 0.338,7.791C0.11,7.572 -0.005,7.299 -0.005,6.972C-0.005,6.813 0.016,6.699 0.058,6.629C0.1,6.554 0.142,6.48 0.184,6.405L3.124,0.763C3.222,0.576 3.374,0.413 3.579,0.273C3.789,0.133 4.013,0.063 4.251,0.063C4.606,0.063 4.905,0.191 5.147,0.448C5.39,0.7 5.511,1.006 5.511,1.365L5.511,9.352C5.511,9.618 5.416,9.847 5.224,10.038C5.038,10.229 4.814,10.325 4.552,10.325ZM11.303,10.416C10.179,10.411 9.28,10.031 8.608,9.275C7.941,8.514 7.607,7.406 7.607,5.95L7.607,4.417C7.607,2.975 7.948,1.878 8.629,1.127C9.311,0.371 10.223,-0.005 11.366,0C11.88,0 12.314,0.075 12.668,0.224C13.028,0.369 13.333,0.576 13.585,0.847C13.842,1.113 14.019,1.374 14.117,1.631C14.215,1.883 14.222,2.121 14.138,2.345C14.054,2.569 13.91,2.732 13.704,2.835C13.504,2.938 13.308,2.968 13.116,2.926C12.925,2.884 12.776,2.802 12.668,2.681C12.561,2.555 12.451,2.422 12.339,2.282C12.227,2.142 12.087,2.032 11.919,1.953C11.751,1.869 11.551,1.827 11.317,1.827C10.799,1.822 10.384,2.011 10.071,2.394C9.763,2.777 9.609,3.358 9.609,4.137L9.609,6.293C9.609,7.063 9.768,7.644 10.085,8.036C10.407,8.428 10.827,8.624 11.345,8.624C11.849,8.624 12.244,8.477 12.528,8.183C12.813,7.889 12.967,7.474 12.99,6.937L12.99,6.265L12.017,6.265C11.812,6.265 11.635,6.188 11.485,6.034C11.336,5.875 11.261,5.689 11.261,5.474C11.261,5.255 11.338,5.068 11.492,4.914C11.651,4.755 11.84,4.676 12.059,4.676L14.026,4.676C14.26,4.676 14.458,4.755 14.621,4.914C14.785,5.073 14.869,5.269 14.873,5.502L14.873,6.538C14.873,7.77 14.551,8.724 13.907,9.401C13.268,10.078 12.4,10.416 11.303,10.416Z"
+ android:fillColor="#000"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_4g_plus_mobiledata_updated.xml b/packages/SettingsLib/res/drawable/ic_4g_plus_mobiledata_updated.xml
new file mode 100644
index 000000000000..73e8994681c2
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_4g_plus_mobiledata_updated.xml
@@ -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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="23dp"
+ android:height="12.5dp"
+ android:viewportWidth="19.17"
+ android:viewportHeight="10.42">
+ <path
+ android:pathData="M4.552,10.325C4.286,10.325 4.058,10.229 3.866,10.038C3.68,9.847 3.586,9.618 3.586,9.352L3.586,7.301L3.74,7.021L3.74,1.953L4.72,2.66L3.712,2.66L1.808,6.496L4.37,6.496L4.797,6.405L5.777,6.405C6.015,6.405 6.218,6.489 6.386,6.657C6.554,6.825 6.638,7.028 6.638,7.266C6.638,7.499 6.554,7.7 6.386,7.868C6.218,8.036 6.015,8.12 5.777,8.12L1.192,8.12C0.856,8.12 0.572,8.01 0.338,7.791C0.11,7.572 -0.005,7.299 -0.005,6.972C-0.005,6.813 0.016,6.699 0.058,6.629C0.1,6.554 0.142,6.48 0.184,6.405L3.124,0.763C3.222,0.576 3.374,0.413 3.579,0.273C3.789,0.133 4.013,0.063 4.251,0.063C4.606,0.063 4.905,0.191 5.147,0.448C5.39,0.7 5.511,1.006 5.511,1.365L5.511,9.352C5.511,9.618 5.416,9.847 5.224,10.038C5.038,10.229 4.814,10.325 4.552,10.325ZM11.303,10.416C10.179,10.411 9.28,10.031 8.608,9.275C7.941,8.514 7.607,7.406 7.607,5.95L7.607,4.417C7.607,2.975 7.948,1.878 8.629,1.127C9.311,0.371 10.223,-0.005 11.366,0C11.88,0 12.314,0.075 12.668,0.224C13.028,0.369 13.333,0.576 13.585,0.847C13.842,1.113 14.019,1.374 14.117,1.631C14.215,1.883 14.222,2.121 14.138,2.345C14.054,2.569 13.91,2.732 13.704,2.835C13.504,2.938 13.308,2.968 13.116,2.926C12.925,2.884 12.776,2.802 12.668,2.681C12.561,2.555 12.451,2.422 12.339,2.282C12.227,2.142 12.087,2.032 11.919,1.953C11.751,1.869 11.551,1.827 11.317,1.827C10.799,1.822 10.384,2.011 10.071,2.394C9.763,2.777 9.609,3.358 9.609,4.137L9.609,6.293C9.609,7.063 9.768,7.644 10.085,8.036C10.407,8.428 10.827,8.624 11.345,8.624C11.849,8.624 12.244,8.477 12.528,8.183C12.813,7.889 12.967,7.474 12.99,6.937L12.99,6.265L12.017,6.265C11.812,6.265 11.635,6.188 11.485,6.034C11.336,5.875 11.261,5.689 11.261,5.474C11.261,5.255 11.338,5.068 11.492,4.914C11.651,4.755 11.84,4.676 12.059,4.676L14.026,4.676C14.26,4.676 14.458,4.755 14.621,4.914C14.785,5.073 14.869,5.269 14.873,5.502L14.873,6.538C14.873,7.77 14.551,8.724 13.907,9.401C13.268,10.078 12.4,10.416 11.303,10.416Z"
+ android:fillColor="#000"/>
+ <path
+ android:pathData="M17.086,4.476C16.907,4.476 16.752,4.412 16.622,4.284C16.494,4.153 16.43,3.997 16.43,3.816L16.43,0.872C16.43,0.691 16.494,0.536 16.622,0.408C16.752,0.277 16.907,0.212 17.086,0.212C17.264,0.212 17.418,0.277 17.546,0.408C17.674,0.536 17.738,0.691 17.738,0.872L17.738,3.816C17.738,3.997 17.674,4.153 17.546,4.284C17.418,4.412 17.264,4.476 17.086,4.476ZM15.662,2.988C15.483,2.988 15.33,2.925 15.202,2.8C15.074,2.672 15.01,2.52 15.01,2.344C15.01,2.168 15.074,2.017 15.202,1.892C15.33,1.764 15.483,1.7 15.662,1.7L18.51,1.7C18.688,1.7 18.842,1.764 18.97,1.892C19.098,2.017 19.162,2.168 19.162,2.344C19.162,2.52 19.098,2.672 18.97,2.8C18.842,2.925 18.688,2.988 18.51,2.988L15.662,2.988Z"
+ android:fillColor="#000"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_5g_e_mobiledata_updated.xml b/packages/SettingsLib/res/drawable/ic_5g_e_mobiledata_updated.xml
new file mode 100644
index 000000000000..c46da66a9183
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_5g_e_mobiledata_updated.xml
@@ -0,0 +1,25 @@
+<!--
+ 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="22.0dp"
+ android:height="12.57dp"
+ android:viewportHeight="11.21"
+ android:viewportWidth="19.62">
+
+ <path
+ android:fillColor="#000"
+ android:pathData="M2.573,11.206C1.99,11.206 1.502,11.094 1.11,10.87C0.718,10.641 0.436,10.364 0.263,10.037C0.091,9.706 0.002,9.4 -0.003,9.12C-0.007,8.84 0.058,8.616 0.193,8.448C0.333,8.275 0.499,8.168 0.69,8.126C0.882,8.079 1.057,8.089 1.215,8.154C1.379,8.219 1.502,8.315 1.586,8.441C1.675,8.567 1.75,8.716 1.81,8.889C1.871,9.062 1.971,9.202 2.111,9.309C2.251,9.412 2.429,9.463 2.643,9.463C2.947,9.463 3.203,9.363 3.413,9.162C3.623,8.961 3.763,8.663 3.833,8.266L4.008,7.272C4.074,6.899 4.029,6.609 3.875,6.404C3.726,6.199 3.5,6.096 3.196,6.096C3,6.096 2.825,6.143 2.671,6.236C2.522,6.325 2.401,6.43 2.307,6.551C2.181,6.672 2.03,6.752 1.852,6.789C1.675,6.826 1.488,6.81 1.292,6.74C1.045,6.651 0.865,6.497 0.753,6.278C0.641,6.054 0.62,5.804 0.69,5.529L1.593,1.98C1.677,1.672 1.831,1.429 2.055,1.252C2.284,1.075 2.557,0.986 2.874,0.986L5.674,0.986C5.964,0.986 6.195,1.089 6.367,1.294C6.54,1.495 6.601,1.733 6.549,2.008C6.517,2.213 6.412,2.388 6.234,2.533C6.062,2.673 5.875,2.743 5.674,2.743L3.056,2.743L2.433,5.13L2.475,5.137C2.676,4.955 2.905,4.815 3.161,4.717C3.418,4.614 3.7,4.563 4.008,4.563C4.727,4.563 5.268,4.836 5.632,5.382C6.001,5.923 6.111,6.628 5.961,7.496L5.814,8.336C5.656,9.274 5.299,9.988 4.743,10.478C4.188,10.963 3.465,11.206 2.573,11.206ZM10.577,11.206C9.392,11.206 8.515,10.795 7.945,9.974C7.381,9.153 7.229,8.009 7.49,6.544L7.749,5.039C8.006,3.606 8.498,2.54 9.226,1.84C9.959,1.14 10.883,0.79 11.998,0.79C12.535,0.79 12.995,0.879 13.377,1.056C13.76,1.229 14.059,1.46 14.273,1.749C14.488,2.038 14.612,2.314 14.644,2.575C14.677,2.832 14.644,3.053 14.546,3.24C14.453,3.427 14.313,3.567 14.126,3.66C13.94,3.749 13.753,3.774 13.566,3.737C13.384,3.695 13.244,3.606 13.146,3.471C13.048,3.336 12.941,3.2 12.824,3.065C12.708,2.925 12.57,2.815 12.411,2.736C12.257,2.657 12.068,2.617 11.844,2.617C11.35,2.617 10.918,2.808 10.549,3.191C10.185,3.569 9.936,4.141 9.8,4.906L9.422,7.048C9.287,7.813 9.348,8.399 9.604,8.805C9.861,9.211 10.248,9.414 10.766,9.414C11.256,9.414 11.665,9.267 11.991,8.973C12.318,8.679 12.54,8.264 12.656,7.727L12.775,7.055L11.893,7.055C11.665,7.055 11.48,6.964 11.34,6.782C11.205,6.6 11.158,6.385 11.2,6.138C11.233,5.951 11.326,5.795 11.48,5.669C11.639,5.538 11.809,5.473 11.991,5.473L13.979,5.473C14.25,5.473 14.462,5.566 14.616,5.753C14.775,5.94 14.831,6.168 14.784,6.439L14.595,7.489C14.376,8.702 13.916,9.626 13.216,10.261C12.521,10.891 11.641,11.206 10.577,11.206ZM16.001,6.01C15.799,6.01 15.637,5.937 15.514,5.792C15.39,5.643 15.346,5.47 15.379,5.271L16.181,0.735C16.218,0.53 16.321,0.357 16.492,0.214C16.666,0.068 16.856,-0.004 17.063,-0.004L18.983,-0.004C19.187,-0.004 19.351,0.068 19.474,0.214C19.597,0.36 19.642,0.529 19.609,0.722C19.581,0.868 19.505,0.992 19.382,1.096C19.259,1.197 19.126,1.247 18.983,1.247L17.462,1.247L16.841,4.758L18.21,4.758C18.414,4.758 18.577,4.831 18.697,4.977C18.82,5.122 18.865,5.292 18.832,5.485C18.806,5.631 18.732,5.755 18.609,5.859C18.486,5.96 18.353,6.01 18.21,6.01L16.001,6.01ZM16.526,3.515L16.732,2.364L18.281,2.364C18.472,2.364 18.623,2.432 18.735,2.566C18.847,2.698 18.888,2.853 18.857,3.032C18.832,3.167 18.762,3.281 18.647,3.377C18.535,3.469 18.413,3.515 18.281,3.515L16.526,3.515Z" />
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_5g_mobiledata_updated.xml b/packages/SettingsLib/res/drawable/ic_5g_mobiledata_updated.xml
new file mode 100644
index 000000000000..66787b0a7594
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_5g_mobiledata_updated.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
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT 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="17dp"
+ android:height="12.28dp"
+ android:viewportWidth="14.42"
+ android:viewportHeight="10.42">
+ <path
+ android:pathData="M2.807,10.416C2.247,10.416 1.774,10.325 1.386,10.143C0.999,9.956 0.684,9.704 0.441,9.387C0.199,9.065 0.056,8.745 0.014,8.428C-0.028,8.111 0.014,7.859 0.14,7.672C0.271,7.485 0.439,7.366 0.644,7.315C0.85,7.259 1.036,7.266 1.204,7.336C1.372,7.406 1.494,7.506 1.568,7.637C1.643,7.763 1.727,7.912 1.82,8.085C1.914,8.253 2.03,8.393 2.17,8.505C2.315,8.617 2.504,8.673 2.737,8.673C3.055,8.673 3.309,8.57 3.5,8.365C3.696,8.16 3.794,7.849 3.794,7.434L3.794,6.461C3.794,6.083 3.701,5.796 3.514,5.6C3.332,5.399 3.092,5.299 2.793,5.299C2.593,5.299 2.427,5.341 2.296,5.425C2.166,5.509 2.054,5.605 1.96,5.712C1.858,5.819 1.715,5.901 1.533,5.957C1.351,6.008 1.148,6.001 0.924,5.936C0.682,5.861 0.49,5.714 0.35,5.495C0.215,5.271 0.159,5.035 0.182,4.788L0.455,1.281C0.483,0.987 0.614,0.733 0.847,0.518C1.081,0.303 1.347,0.196 1.645,0.196L4.515,0.196C4.758,0.196 4.966,0.282 5.138,0.455C5.316,0.628 5.404,0.833 5.404,1.071C5.404,1.314 5.316,1.521 5.138,1.694C4.966,1.867 4.758,1.953 4.515,1.953L2.044,1.953L1.841,4.34L1.897,4.354C2.07,4.172 2.285,4.03 2.541,3.927C2.803,3.824 3.094,3.773 3.416,3.773C4.126,3.773 4.697,4.02 5.131,4.515C5.565,5.005 5.782,5.677 5.782,6.531L5.782,7.378C5.782,8.33 5.516,9.074 4.984,9.611C4.452,10.148 3.727,10.416 2.807,10.416ZM10.853,10.416C9.729,10.411 8.83,10.031 8.158,9.275C7.491,8.514 7.157,7.406 7.157,5.95L7.157,4.417C7.157,2.975 7.498,1.878 8.179,1.127C8.861,0.371 9.773,-0.005 10.916,0C11.43,0 11.864,0.075 12.218,0.224C12.578,0.369 12.883,0.576 13.135,0.847C13.392,1.113 13.569,1.374 13.667,1.631C13.765,1.883 13.772,2.121 13.688,2.345C13.604,2.569 13.46,2.732 13.254,2.835C13.054,2.938 12.858,2.968 12.666,2.926C12.475,2.884 12.326,2.802 12.218,2.681C12.111,2.555 12.001,2.422 11.889,2.282C11.777,2.142 11.637,2.032 11.469,1.953C11.301,1.869 11.101,1.827 10.867,1.827C10.349,1.822 9.934,2.011 9.621,2.394C9.313,2.777 9.159,3.358 9.159,4.137L9.159,6.293C9.159,7.063 9.318,7.644 9.635,8.036C9.957,8.428 10.377,8.624 10.895,8.624C11.399,8.624 11.794,8.477 12.078,8.183C12.363,7.889 12.517,7.474 12.54,6.937L12.54,6.265L11.567,6.265C11.362,6.265 11.185,6.188 11.035,6.034C10.886,5.875 10.811,5.689 10.811,5.474C10.811,5.255 10.888,5.068 11.042,4.914C11.201,4.755 11.39,4.676 11.609,4.676L13.576,4.676C13.81,4.676 14.008,4.755 14.171,4.914C14.335,5.073 14.419,5.269 14.423,5.502L14.423,6.538C14.423,7.77 14.101,8.724 13.457,9.401C12.818,10.078 11.95,10.416 10.853,10.416Z"
+ android:fillColor="#000"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_5g_plus_mobiledata_default_updated.xml b/packages/SettingsLib/res/drawable/ic_5g_plus_mobiledata_default_updated.xml
new file mode 100644
index 000000000000..5ba3c98028a2
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_5g_plus_mobiledata_default_updated.xml
@@ -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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="22dp"
+ android:height="11.63dp"
+ android:viewportWidth="19.71"
+ android:viewportHeight="10.42">
+ <path
+ android:pathData="M2.807,10.416C2.247,10.416 1.774,10.325 1.386,10.143C0.999,9.956 0.684,9.704 0.441,9.387C0.199,9.065 0.056,8.745 0.014,8.428C-0.028,8.111 0.014,7.859 0.14,7.672C0.271,7.485 0.439,7.366 0.644,7.315C0.85,7.259 1.036,7.266 1.204,7.336C1.372,7.406 1.494,7.506 1.568,7.637C1.643,7.763 1.727,7.912 1.82,8.085C1.914,8.253 2.03,8.393 2.17,8.505C2.315,8.617 2.504,8.673 2.737,8.673C3.055,8.673 3.309,8.57 3.5,8.365C3.696,8.16 3.794,7.849 3.794,7.434L3.794,6.461C3.794,6.083 3.701,5.796 3.514,5.6C3.332,5.399 3.092,5.299 2.793,5.299C2.593,5.299 2.427,5.341 2.296,5.425C2.166,5.509 2.054,5.605 1.96,5.712C1.858,5.819 1.715,5.901 1.533,5.957C1.351,6.008 1.148,6.001 0.924,5.936C0.682,5.861 0.49,5.714 0.35,5.495C0.215,5.271 0.159,5.035 0.182,4.788L0.455,1.281C0.483,0.987 0.614,0.733 0.847,0.518C1.081,0.303 1.347,0.196 1.645,0.196L4.515,0.196C4.758,0.196 4.966,0.282 5.138,0.455C5.316,0.628 5.404,0.833 5.404,1.071C5.404,1.314 5.316,1.521 5.138,1.694C4.966,1.867 4.758,1.953 4.515,1.953L2.044,1.953L1.841,4.34L1.897,4.354C2.07,4.172 2.285,4.03 2.541,3.927C2.803,3.824 3.094,3.773 3.416,3.773C4.126,3.773 4.697,4.02 5.131,4.515C5.565,5.005 5.782,5.677 5.782,6.531L5.782,7.378C5.782,8.33 5.516,9.074 4.984,9.611C4.452,10.148 3.727,10.416 2.807,10.416ZM10.853,10.416C9.729,10.411 8.83,10.031 8.158,9.275C7.491,8.514 7.157,7.406 7.157,5.95L7.157,4.417C7.157,2.975 7.498,1.878 8.179,1.127C8.861,0.371 9.773,-0.005 10.916,0C11.43,0 11.864,0.075 12.218,0.224C12.578,0.369 12.883,0.576 13.135,0.847C13.392,1.113 13.569,1.374 13.667,1.631C13.765,1.883 13.772,2.121 13.688,2.345C13.604,2.569 13.46,2.732 13.254,2.835C13.054,2.938 12.858,2.968 12.666,2.926C12.475,2.884 12.326,2.802 12.218,2.681C12.111,2.555 12.001,2.422 11.889,2.282C11.777,2.142 11.637,2.032 11.469,1.953C11.301,1.869 11.101,1.827 10.867,1.827C10.349,1.822 9.934,2.011 9.621,2.394C9.313,2.777 9.159,3.358 9.159,4.137L9.159,6.293C9.159,7.063 9.318,7.644 9.635,8.036C9.957,8.428 10.377,8.624 10.895,8.624C11.399,8.624 11.794,8.477 12.078,8.183C12.363,7.889 12.517,7.474 12.54,6.937L12.54,6.265L11.567,6.265C11.362,6.265 11.185,6.188 11.035,6.034C10.886,5.875 10.811,5.689 10.811,5.474C10.811,5.255 10.888,5.068 11.042,4.914C11.201,4.755 11.39,4.676 11.609,4.676L13.576,4.676C13.81,4.676 14.008,4.755 14.171,4.914C14.335,5.073 14.419,5.269 14.423,5.502L14.423,6.538C14.423,7.77 14.101,8.724 13.457,9.401C12.818,10.078 11.95,10.416 10.853,10.416Z"
+ android:fillColor="#000"/>
+ <path
+ android:pathData="M17.636,4.476C17.457,4.476 17.302,4.412 17.172,4.284C17.044,4.153 16.98,3.997 16.98,3.816L16.98,0.872C16.98,0.691 17.044,0.536 17.172,0.408C17.302,0.277 17.457,0.212 17.636,0.212C17.814,0.212 17.968,0.277 18.096,0.408C18.224,0.536 18.288,0.691 18.288,0.872L18.288,3.816C18.288,3.997 18.224,4.153 18.096,4.284C17.968,4.412 17.814,4.476 17.636,4.476ZM16.212,2.988C16.033,2.988 15.88,2.925 15.752,2.8C15.624,2.672 15.56,2.52 15.56,2.344C15.56,2.168 15.624,2.017 15.752,1.892C15.88,1.764 16.033,1.7 16.212,1.7L19.06,1.7C19.238,1.7 19.392,1.764 19.52,1.892C19.648,2.017 19.712,2.168 19.712,2.344C19.712,2.52 19.648,2.672 19.52,2.8C19.392,2.925 19.238,2.988 19.06,2.988L16.212,2.988Z"
+ android:fillColor="#000"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_5g_plus_mobiledata_updated.xml b/packages/SettingsLib/res/drawable/ic_5g_plus_mobiledata_updated.xml
new file mode 100644
index 000000000000..5ba3c98028a2
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_5g_plus_mobiledata_updated.xml
@@ -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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="22dp"
+ android:height="11.63dp"
+ android:viewportWidth="19.71"
+ android:viewportHeight="10.42">
+ <path
+ android:pathData="M2.807,10.416C2.247,10.416 1.774,10.325 1.386,10.143C0.999,9.956 0.684,9.704 0.441,9.387C0.199,9.065 0.056,8.745 0.014,8.428C-0.028,8.111 0.014,7.859 0.14,7.672C0.271,7.485 0.439,7.366 0.644,7.315C0.85,7.259 1.036,7.266 1.204,7.336C1.372,7.406 1.494,7.506 1.568,7.637C1.643,7.763 1.727,7.912 1.82,8.085C1.914,8.253 2.03,8.393 2.17,8.505C2.315,8.617 2.504,8.673 2.737,8.673C3.055,8.673 3.309,8.57 3.5,8.365C3.696,8.16 3.794,7.849 3.794,7.434L3.794,6.461C3.794,6.083 3.701,5.796 3.514,5.6C3.332,5.399 3.092,5.299 2.793,5.299C2.593,5.299 2.427,5.341 2.296,5.425C2.166,5.509 2.054,5.605 1.96,5.712C1.858,5.819 1.715,5.901 1.533,5.957C1.351,6.008 1.148,6.001 0.924,5.936C0.682,5.861 0.49,5.714 0.35,5.495C0.215,5.271 0.159,5.035 0.182,4.788L0.455,1.281C0.483,0.987 0.614,0.733 0.847,0.518C1.081,0.303 1.347,0.196 1.645,0.196L4.515,0.196C4.758,0.196 4.966,0.282 5.138,0.455C5.316,0.628 5.404,0.833 5.404,1.071C5.404,1.314 5.316,1.521 5.138,1.694C4.966,1.867 4.758,1.953 4.515,1.953L2.044,1.953L1.841,4.34L1.897,4.354C2.07,4.172 2.285,4.03 2.541,3.927C2.803,3.824 3.094,3.773 3.416,3.773C4.126,3.773 4.697,4.02 5.131,4.515C5.565,5.005 5.782,5.677 5.782,6.531L5.782,7.378C5.782,8.33 5.516,9.074 4.984,9.611C4.452,10.148 3.727,10.416 2.807,10.416ZM10.853,10.416C9.729,10.411 8.83,10.031 8.158,9.275C7.491,8.514 7.157,7.406 7.157,5.95L7.157,4.417C7.157,2.975 7.498,1.878 8.179,1.127C8.861,0.371 9.773,-0.005 10.916,0C11.43,0 11.864,0.075 12.218,0.224C12.578,0.369 12.883,0.576 13.135,0.847C13.392,1.113 13.569,1.374 13.667,1.631C13.765,1.883 13.772,2.121 13.688,2.345C13.604,2.569 13.46,2.732 13.254,2.835C13.054,2.938 12.858,2.968 12.666,2.926C12.475,2.884 12.326,2.802 12.218,2.681C12.111,2.555 12.001,2.422 11.889,2.282C11.777,2.142 11.637,2.032 11.469,1.953C11.301,1.869 11.101,1.827 10.867,1.827C10.349,1.822 9.934,2.011 9.621,2.394C9.313,2.777 9.159,3.358 9.159,4.137L9.159,6.293C9.159,7.063 9.318,7.644 9.635,8.036C9.957,8.428 10.377,8.624 10.895,8.624C11.399,8.624 11.794,8.477 12.078,8.183C12.363,7.889 12.517,7.474 12.54,6.937L12.54,6.265L11.567,6.265C11.362,6.265 11.185,6.188 11.035,6.034C10.886,5.875 10.811,5.689 10.811,5.474C10.811,5.255 10.888,5.068 11.042,4.914C11.201,4.755 11.39,4.676 11.609,4.676L13.576,4.676C13.81,4.676 14.008,4.755 14.171,4.914C14.335,5.073 14.419,5.269 14.423,5.502L14.423,6.538C14.423,7.77 14.101,8.724 13.457,9.401C12.818,10.078 11.95,10.416 10.853,10.416Z"
+ android:fillColor="#000"/>
+ <path
+ android:pathData="M17.636,4.476C17.457,4.476 17.302,4.412 17.172,4.284C17.044,4.153 16.98,3.997 16.98,3.816L16.98,0.872C16.98,0.691 17.044,0.536 17.172,0.408C17.302,0.277 17.457,0.212 17.636,0.212C17.814,0.212 17.968,0.277 18.096,0.408C18.224,0.536 18.288,0.691 18.288,0.872L18.288,3.816C18.288,3.997 18.224,4.153 18.096,4.284C17.968,4.412 17.814,4.476 17.636,4.476ZM16.212,2.988C16.033,2.988 15.88,2.925 15.752,2.8C15.624,2.672 15.56,2.52 15.56,2.344C15.56,2.168 15.624,2.017 15.752,1.892C15.88,1.764 16.033,1.7 16.212,1.7L19.06,1.7C19.238,1.7 19.392,1.764 19.52,1.892C19.648,2.017 19.712,2.168 19.712,2.344C19.712,2.52 19.648,2.672 19.52,2.8C19.392,2.925 19.238,2.988 19.06,2.988L16.212,2.988Z"
+ android:fillColor="#000"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_carrier_wifi_updated.xml b/packages/SettingsLib/res/drawable/ic_carrier_wifi_updated.xml
new file mode 100644
index 000000000000..14db82614ad1
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_carrier_wifi_updated.xml
@@ -0,0 +1,30 @@
+<?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="19.0dp"
+ android:height="12.38dp"
+ android:viewportHeight="10.26"
+ android:viewportWidth="15.75">
+
+ <path
+ android:fillColor="#000"
+ android:pathData="M2.864,10.259C2.598,10.259 2.358,10.168 2.143,9.986C1.929,9.804 1.793,9.589 1.737,9.342L0.036,1.208C-0.043,0.877 0.013,0.594 0.204,0.361C0.4,0.128 0.657,0.011 0.974,0.011C1.208,0.011 1.418,0.093 1.604,0.256C1.791,0.419 1.908,0.611 1.954,0.83L2.885,5.828L3.039,6.92L3.088,6.92L3.249,5.828L4.278,0.851C4.325,0.622 4.446,0.424 4.642,0.256C4.843,0.088 5.065,0.004 5.307,0.004C5.55,0.004 5.769,0.088 5.965,0.256C6.166,0.424 6.292,0.625 6.343,0.858L7.358,5.814L7.526,6.913L7.575,6.913L7.729,5.814L8.653,0.781C8.695,0.571 8.805,0.391 8.982,0.242C9.16,0.093 9.356,0.018 9.57,0.018C9.874,0.018 10.114,0.128 10.291,0.347C10.473,0.566 10.529,0.828 10.459,1.131L8.765,9.342C8.709,9.594 8.574,9.811 8.359,9.993C8.145,10.17 7.902,10.259 7.631,10.259C7.37,10.259 7.132,10.17 6.917,9.993C6.703,9.811 6.57,9.594 6.518,9.342L5.44,4.19L5.279,3.133L5.23,3.133L5.069,4.183L3.977,9.342C3.926,9.589 3.793,9.804 3.578,9.986C3.364,10.168 3.126,10.259 2.864,10.259Z" />
+
+ <path
+ android:fillColor="#000"
+ android:pathData="M13.676,4.396C13.497,4.396 13.342,4.332 13.212,4.204C13.084,4.073 13.02,3.917 13.02,3.736L13.02,0.792C13.02,0.611 13.084,0.456 13.212,0.328C13.342,0.197 13.497,0.132 13.676,0.132C13.854,0.132 14.008,0.197 14.136,0.328C14.264,0.456 14.328,0.611 14.328,0.792L14.328,3.736C14.328,3.917 14.264,4.073 14.136,4.204C14.008,4.332 13.854,4.396 13.676,4.396ZM12.252,2.908C12.073,2.908 11.92,2.845 11.792,2.72C11.664,2.592 11.6,2.44 11.6,2.264C11.6,2.088 11.664,1.937 11.792,1.812C11.92,1.684 12.073,1.62 12.252,1.62L15.1,1.62C15.278,1.62 15.432,1.684 15.56,1.812C15.688,1.937 15.752,2.088 15.752,2.264C15.752,2.44 15.688,2.592 15.56,2.72C15.432,2.845 15.278,2.908 15.1,2.908L12.252,2.908Z" />
+
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_e_mobiledata_updated.xml b/packages/SettingsLib/res/drawable/ic_e_mobiledata_updated.xml
new file mode 100644
index 000000000000..febcd87efbc3
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_e_mobiledata_updated.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
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT 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="17dp"
+ android:height="31.08dp"
+ android:viewportWidth="5.48"
+ android:viewportHeight="10.02">
+ <path
+ android:pathData="M1.081,10.02C0.787,10.02 0.533,9.913 0.318,9.698C0.104,9.483 -0.004,9.229 -0.004,8.935L-0.004,1.081C-0.004,0.787 0.104,0.533 0.318,0.318C0.533,0.103 0.787,-0.004 1.081,-0.004L4.455,-0.004C4.707,-0.004 4.922,0.087 5.099,0.269C5.281,0.446 5.372,0.659 5.372,0.906C5.372,1.153 5.281,1.368 5.099,1.55C4.922,1.727 4.707,1.816 4.455,1.816L1.963,1.816L1.963,8.2L4.56,8.2C4.812,8.2 5.027,8.291 5.204,8.473C5.386,8.65 5.477,8.863 5.477,9.11C5.477,9.357 5.386,9.572 5.204,9.754C5.027,9.931 4.812,10.02 4.56,10.02L1.081,10.02ZM1.186,5.736L1.186,4.042L3.951,4.042C4.185,4.042 4.385,4.126 4.553,4.294C4.721,4.457 4.805,4.656 4.805,4.889C4.805,5.118 4.721,5.316 4.553,5.484C4.385,5.652 4.185,5.736 3.951,5.736L1.186,5.736Z"
+ android:fillColor="#000"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_g_mobiledata_updated.xml b/packages/SettingsLib/res/drawable/ic_g_mobiledata_updated.xml
new file mode 100644
index 000000000000..d719f7a05aa0
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_g_mobiledata_updated.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
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT 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="17dp"
+ android:height="24.37dp"
+ android:viewportWidth="7.27"
+ android:viewportHeight="10.42">
+ <path
+ android:pathData="M3.696,10.416C2.571,10.411 1.673,10.031 1.001,9.275C0.333,8.514 -0,7.406 -0,5.95L-0,4.417C-0,2.975 0.34,1.878 1.022,1.127C1.703,0.371 2.615,-0.005 3.759,0C4.272,0 4.706,0.075 5.061,0.224C5.42,0.369 5.726,0.576 5.978,0.847C6.235,1.113 6.412,1.374 6.51,1.631C6.608,1.883 6.615,2.121 6.531,2.345C6.447,2.569 6.302,2.732 6.097,2.835C5.896,2.938 5.7,2.968 5.509,2.926C5.317,2.884 5.168,2.802 5.061,2.681C4.953,2.555 4.844,2.422 4.732,2.282C4.62,2.142 4.48,2.032 4.312,1.953C4.144,1.869 3.943,1.827 3.71,1.827C3.192,1.822 2.776,2.011 2.464,2.394C2.156,2.777 2.002,3.358 2.002,4.137L2.002,6.293C2.002,7.063 2.16,7.644 2.478,8.036C2.8,8.428 3.22,8.624 3.738,8.624C4.242,8.624 4.636,8.477 4.921,8.183C5.205,7.889 5.359,7.474 5.383,6.937L5.383,6.265L4.41,6.265C4.204,6.265 4.027,6.188 3.878,6.034C3.728,5.875 3.654,5.689 3.654,5.474C3.654,5.255 3.731,5.068 3.885,4.914C4.043,4.755 4.232,4.676 4.452,4.676L6.419,4.676C6.652,4.676 6.85,4.755 7.014,4.914C7.177,5.073 7.261,5.269 7.266,5.502L7.266,6.538C7.266,7.77 6.944,8.724 6.3,9.401C5.661,10.078 4.792,10.416 3.696,10.416Z"
+ android:fillColor="#000"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_h_mobiledata_updated.xml b/packages/SettingsLib/res/drawable/ic_h_mobiledata_updated.xml
new file mode 100644
index 000000000000..e60ff8cde396
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_h_mobiledata_updated.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
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT 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="17dp"
+ android:height="26.68dp"
+ android:viewportWidth="6.53"
+ android:viewportHeight="10.25">
+ <path
+ android:pathData="M5.547,10.252C5.277,10.252 5.043,10.154 4.847,9.958C4.656,9.757 4.56,9.522 4.56,9.251L4.56,1.005C4.56,0.73 4.656,0.494 4.847,0.298C5.043,0.102 5.277,0.004 5.547,0.004C5.818,0.004 6.049,0.102 6.24,0.298C6.436,0.494 6.534,0.73 6.534,1.005L6.534,9.251C6.534,9.522 6.436,9.757 6.24,9.958C6.049,10.154 5.818,10.252 5.547,10.252ZM0.99,10.252C0.72,10.252 0.486,10.154 0.29,9.958C0.099,9.757 0.003,9.522 0.003,9.251L0.003,1.005C0.003,0.73 0.099,0.494 0.29,0.298C0.486,0.102 0.72,0.004 0.99,0.004C1.261,0.004 1.492,0.102 1.683,0.298C1.879,0.494 1.977,0.73 1.977,1.005L1.977,9.251C1.977,9.522 1.879,9.757 1.683,9.958C1.492,10.154 1.261,10.252 0.99,10.252ZM1.151,5.933L1.151,4.106L5.484,4.106L5.484,5.933L1.151,5.933Z"
+ android:fillColor="#000"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_h_plus_mobiledata_updated.xml b/packages/SettingsLib/res/drawable/ic_h_plus_mobiledata_updated.xml
new file mode 100644
index 000000000000..e02df563dfdd
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_h_plus_mobiledata_updated.xml
@@ -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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="16dp"
+ android:height="13.52dp"
+ android:viewportWidth="12.13"
+ android:viewportHeight="10.25">
+ <path
+ android:pathData="M5.541,10.252C5.271,10.252 5.037,10.154 4.841,9.958C4.65,9.757 4.554,9.522 4.554,9.251L4.554,1.005C4.554,0.73 4.65,0.494 4.841,0.298C5.037,0.102 5.271,0.004 5.541,0.004C5.812,0.004 6.043,0.102 6.234,0.298C6.43,0.494 6.528,0.73 6.528,1.005L6.528,9.251C6.528,9.522 6.43,9.757 6.234,9.958C6.043,10.154 5.812,10.252 5.541,10.252ZM0.984,10.252C0.714,10.252 0.48,10.154 0.284,9.958C0.093,9.757 -0.003,9.522 -0.003,9.251L-0.003,1.005C-0.003,0.73 0.093,0.494 0.284,0.298C0.48,0.102 0.714,0.004 0.984,0.004C1.255,0.004 1.486,0.102 1.677,0.298C1.873,0.494 1.971,0.73 1.971,1.005L1.971,9.251C1.971,9.522 1.873,9.757 1.677,9.958C1.486,10.154 1.255,10.252 0.984,10.252ZM1.145,5.933L1.145,4.106L5.478,4.106L5.478,5.933L1.145,5.933Z"
+ android:fillColor="#000"/>
+ <path
+ android:pathData="M10.056,4.396C9.877,4.396 9.722,4.332 9.592,4.204C9.464,4.073 9.4,3.917 9.4,3.736L9.4,0.792C9.4,0.611 9.464,0.456 9.592,0.328C9.722,0.197 9.877,0.132 10.056,0.132C10.234,0.132 10.388,0.197 10.516,0.328C10.644,0.456 10.708,0.611 10.708,0.792L10.708,3.736C10.708,3.917 10.644,4.073 10.516,4.204C10.388,4.332 10.234,4.396 10.056,4.396ZM8.632,2.908C8.453,2.908 8.3,2.845 8.172,2.72C8.044,2.592 7.98,2.44 7.98,2.264C7.98,2.088 8.044,1.937 8.172,1.812C8.3,1.684 8.453,1.62 8.632,1.62L11.48,1.62C11.658,1.62 11.812,1.684 11.94,1.812C12.068,1.937 12.132,2.088 12.132,2.264C12.132,2.44 12.068,2.592 11.94,2.72C11.812,2.845 11.658,2.908 11.48,2.908L8.632,2.908Z"
+ android:fillColor="#000"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_lte_mobiledata_updated.xml b/packages/SettingsLib/res/drawable/ic_lte_mobiledata_updated.xml
new file mode 100644
index 000000000000..9d64439cf30b
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_lte_mobiledata_updated.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
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT 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="20dp"
+ android:height="11.92dp"
+ android:viewportWidth="17.2"
+ android:viewportHeight="10.25">
+ <path
+ android:pathData="M1.082,10.14C0.788,10.14 0.534,10.033 0.319,9.818C0.105,9.603 -0.003,9.349 -0.003,9.055L-0.003,1.005C-0.003,0.73 0.093,0.494 0.284,0.298C0.48,0.102 0.714,0.004 0.984,0.004C1.255,0.004 1.486,0.102 1.677,0.298C1.873,0.494 1.971,0.73 1.971,1.005L1.971,8.313L4.19,8.313C4.442,8.313 4.659,8.404 4.841,8.586C5.023,8.763 5.114,8.976 5.114,9.223C5.114,9.475 5.023,9.692 4.841,9.874C4.659,10.051 4.442,10.14 4.19,10.14L1.082,10.14ZM7.59,10.252C7.32,10.252 7.086,10.154 6.89,9.958C6.694,9.757 6.596,9.522 6.596,9.251L6.596,1.068L8.577,1.068L8.577,9.251C8.577,9.522 8.479,9.757 8.283,9.958C8.087,10.154 7.856,10.252 7.59,10.252ZM5.413,1.936C5.161,1.936 4.944,1.847 4.762,1.67C4.585,1.488 4.496,1.273 4.496,1.026C4.496,0.779 4.585,0.566 4.762,0.389C4.944,0.207 5.161,0.116 5.413,0.116L9.753,0.116C10.005,0.116 10.222,0.207 10.404,0.389C10.586,0.566 10.677,0.779 10.677,1.026C10.677,1.273 10.586,1.488 10.404,1.67C10.222,1.847 10.005,1.936 9.753,1.936L5.413,1.936ZM12.799,10.14C12.505,10.14 12.251,10.033 12.036,9.818C11.821,9.603 11.714,9.349 11.714,9.055L11.714,1.201C11.714,0.907 11.821,0.653 12.036,0.438C12.251,0.223 12.505,0.116 12.799,0.116L16.173,0.116C16.425,0.116 16.64,0.207 16.817,0.389C16.999,0.566 17.09,0.779 17.09,1.026C17.09,1.273 16.999,1.488 16.817,1.67C16.64,1.847 16.425,1.936 16.173,1.936L13.681,1.936L13.681,8.32L16.278,8.32C16.53,8.32 16.745,8.411 16.922,8.593C17.104,8.77 17.195,8.983 17.195,9.23C17.195,9.477 17.104,9.692 16.922,9.874C16.745,10.051 16.53,10.14 16.278,10.14L12.799,10.14ZM12.904,5.856L12.904,4.162L15.669,4.162C15.902,4.162 16.103,4.246 16.271,4.414C16.439,4.577 16.523,4.776 16.523,5.009C16.523,5.238 16.439,5.436 16.271,5.604C16.103,5.772 15.902,5.856 15.669,5.856L12.904,5.856Z"
+ android:fillColor="#000"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_lte_plus_mobiledata_updated.xml b/packages/SettingsLib/res/drawable/ic_lte_plus_mobiledata_updated.xml
new file mode 100644
index 000000000000..7075516e6dd3
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_lte_plus_mobiledata_updated.xml
@@ -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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="25dp"
+ android:height="11.85dp"
+ android:viewportWidth="21.63"
+ android:viewportHeight="10.25">
+ <path
+ android:pathData="M1.082,10.14C0.788,10.14 0.534,10.033 0.319,9.818C0.105,9.603 -0.003,9.349 -0.003,9.055L-0.003,1.005C-0.003,0.73 0.093,0.494 0.284,0.298C0.48,0.102 0.714,0.004 0.984,0.004C1.255,0.004 1.486,0.102 1.677,0.298C1.873,0.494 1.971,0.73 1.971,1.005L1.971,8.313L4.19,8.313C4.442,8.313 4.659,8.404 4.841,8.586C5.023,8.763 5.114,8.976 5.114,9.223C5.114,9.475 5.023,9.692 4.841,9.874C4.659,10.051 4.442,10.14 4.19,10.14L1.082,10.14ZM7.59,10.252C7.32,10.252 7.086,10.154 6.89,9.958C6.694,9.757 6.596,9.522 6.596,9.251L6.596,1.068L8.577,1.068L8.577,9.251C8.577,9.522 8.479,9.757 8.283,9.958C8.087,10.154 7.856,10.252 7.59,10.252ZM5.413,1.936C5.161,1.936 4.944,1.847 4.762,1.67C4.585,1.488 4.496,1.273 4.496,1.026C4.496,0.779 4.585,0.566 4.762,0.389C4.944,0.207 5.161,0.116 5.413,0.116L9.753,0.116C10.005,0.116 10.222,0.207 10.404,0.389C10.586,0.566 10.677,0.779 10.677,1.026C10.677,1.273 10.586,1.488 10.404,1.67C10.222,1.847 10.005,1.936 9.753,1.936L5.413,1.936ZM12.799,10.14C12.505,10.14 12.251,10.033 12.036,9.818C11.821,9.603 11.714,9.349 11.714,9.055L11.714,1.201C11.714,0.907 11.821,0.653 12.036,0.438C12.251,0.223 12.505,0.116 12.799,0.116L16.173,0.116C16.425,0.116 16.64,0.207 16.817,0.389C16.999,0.566 17.09,0.779 17.09,1.026C17.09,1.273 16.999,1.488 16.817,1.67C16.64,1.847 16.425,1.936 16.173,1.936L13.681,1.936L13.681,8.32L16.278,8.32C16.53,8.32 16.745,8.411 16.922,8.593C17.104,8.77 17.195,8.983 17.195,9.23C17.195,9.477 17.104,9.692 16.922,9.874C16.745,10.051 16.53,10.14 16.278,10.14L12.799,10.14ZM12.904,5.856L12.904,4.162L15.669,4.162C15.902,4.162 16.103,4.246 16.271,4.414C16.439,4.577 16.523,4.776 16.523,5.009C16.523,5.238 16.439,5.436 16.271,5.604C16.103,5.772 15.902,5.856 15.669,5.856L12.904,5.856Z"
+ android:fillColor="#000"/>
+ <path
+ android:pathData="M19.556,4.396C19.377,4.396 19.222,4.332 19.092,4.204C18.964,4.073 18.9,3.917 18.9,3.736L18.9,0.792C18.9,0.611 18.964,0.456 19.092,0.328C19.222,0.197 19.377,0.132 19.556,0.132C19.734,0.132 19.888,0.197 20.016,0.328C20.144,0.456 20.208,0.611 20.208,0.792L20.208,3.736C20.208,3.917 20.144,4.073 20.016,4.204C19.888,4.332 19.734,4.396 19.556,4.396ZM18.132,2.908C17.953,2.908 17.8,2.845 17.672,2.72C17.544,2.592 17.48,2.44 17.48,2.264C17.48,2.088 17.544,1.937 17.672,1.812C17.8,1.684 17.953,1.62 18.132,1.62L20.98,1.62C21.158,1.62 21.312,1.684 21.44,1.812C21.568,1.937 21.632,2.088 21.632,2.264C21.632,2.44 21.568,2.592 21.44,2.72C21.312,2.845 21.158,2.908 20.98,2.908L18.132,2.908Z"
+ android:fillColor="#000"/>
+</vector>
diff --git a/packages/SettingsLib/src/com/android/settingslib/mobile/TelephonyIcons.java b/packages/SettingsLib/src/com/android/settingslib/mobile/TelephonyIcons.java
index 094567c400a3..9ca46238c2ce 100644
--- a/packages/SettingsLib/src/com/android/settingslib/mobile/TelephonyIcons.java
+++ b/packages/SettingsLib/src/com/android/settingslib/mobile/TelephonyIcons.java
@@ -18,6 +18,7 @@ package com.android.settingslib.mobile;
import com.android.settingslib.R;
import com.android.settingslib.SignalIcon.MobileIconGroup;
+import com.android.settingslib.flags.Flags;
import java.util.HashMap;
import java.util.Map;
@@ -29,22 +30,47 @@ 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 = R.drawable.ic_lte_mobiledata;
- public static final int ICON_LTE_PLUS = R.drawable.ic_lte_plus_mobiledata;
- public static final int ICON_G = R.drawable.ic_g_mobiledata;
- public static final int ICON_E = R.drawable.ic_e_mobiledata;
- public static final int ICON_H = R.drawable.ic_h_mobiledata;
- public static final int ICON_H_PLUS = R.drawable.ic_h_plus_mobiledata;
- public static final int ICON_3G = R.drawable.ic_3g_mobiledata;
- public static final int ICON_4G = R.drawable.ic_4g_mobiledata;
- public static final int ICON_4G_PLUS = R.drawable.ic_4g_plus_mobiledata;
- public static final int ICON_4G_LTE = R.drawable.ic_4g_lte_mobiledata;
- public static final int ICON_4G_LTE_PLUS = R.drawable.ic_4g_lte_plus_mobiledata;
- public static final int ICON_5G_E = R.drawable.ic_5g_e_mobiledata;
- public static final int ICON_1X = R.drawable.ic_1x_mobiledata;
- public static final int ICON_5G = R.drawable.ic_5g_mobiledata;
- public static final int ICON_5G_PLUS = R.drawable.ic_5g_plus_mobiledata;
- public static final int ICON_CWF = R.drawable.ic_carrier_wifi;
+ 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 =
+ flagged(R.drawable.ic_lte_plus_mobiledata, R.drawable.ic_lte_plus_mobiledata_updated);
+ public static final int ICON_G =
+ flagged(R.drawable.ic_g_mobiledata, R.drawable.ic_g_mobiledata_updated);
+ public static final int ICON_E =
+ flagged(R.drawable.ic_e_mobiledata, R.drawable.ic_e_mobiledata_updated);
+ public static final int ICON_H =
+ flagged(R.drawable.ic_h_mobiledata, R.drawable.ic_h_mobiledata_updated);
+ public static final int ICON_H_PLUS =
+ flagged(R.drawable.ic_h_plus_mobiledata, R.drawable.ic_h_plus_mobiledata_updated);
+ public static final int ICON_3G =
+ flagged(R.drawable.ic_3g_mobiledata, R.drawable.ic_3g_mobiledata_updated);
+ public static final int ICON_4G =
+ flagged(R.drawable.ic_4g_mobiledata, R.drawable.ic_4g_mobiledata_updated);
+ public static final int ICON_4G_PLUS =
+ flagged(R.drawable.ic_4g_plus_mobiledata, R.drawable.ic_4g_plus_mobiledata_updated);
+ public static final int ICON_4G_LTE =
+ flagged(R.drawable.ic_4g_lte_mobiledata, R.drawable.ic_4g_lte_mobiledata_updated);
+ public static final int ICON_4G_LTE_PLUS =
+ flagged(R.drawable.ic_4g_lte_plus_mobiledata,
+ R.drawable.ic_4g_lte_plus_mobiledata_updated);
+ public static final int ICON_5G_E =
+ flagged(R.drawable.ic_5g_e_mobiledata, R.drawable.ic_5g_e_mobiledata_updated);
+ public static final int ICON_1X =
+ flagged(R.drawable.ic_1x_mobiledata, R.drawable.ic_1x_mobiledata_updated);
+ public static final int ICON_5G =
+ flagged(R.drawable.ic_5g_mobiledata, R.drawable.ic_5g_mobiledata_updated);
+ public static final int ICON_5G_PLUS =
+ flagged(R.drawable.ic_5g_plus_mobiledata, R.drawable.ic_5g_plus_mobiledata_updated);
+ public static final int ICON_CWF =
+ flagged(R.drawable.ic_carrier_wifi, R.drawable.ic_carrier_wifi_updated);
+
+ /** Make it slightly more obvious which resource we are using */
+ private static int flagged(int oldIcon, int newIcon) {
+ if (Flags.newStatusBarIcons()) {
+ return newIcon;
+ }
+ return oldIcon;
+ }
public static final MobileIconGroup CARRIER_NETWORK_CHANGE = new MobileIconGroup(
"CARRIER_NETWORK_CHANGE",
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 0b17a3f71bda..9b76f15b3cd6 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
@@ -42,6 +42,7 @@ import com.android.compose.animation.scene.MutableSceneTransitionLayoutState
import com.android.compose.animation.scene.SceneKey
import com.android.compose.animation.scene.SceneTransitionLayout
import com.android.compose.animation.scene.Swipe
+import com.android.compose.animation.scene.UserActionResult
import com.android.compose.animation.scene.observableTransitionState
import com.android.compose.animation.scene.rememberMutableSceneTransitionLayoutState
import com.android.compose.animation.scene.transitions
@@ -87,10 +88,26 @@ val sceneTransitionsV2 = transitions {
spec = tween(durationMillis = TransitionDuration.TO_GLANCEABLE_HUB_DURATION_MS)
fade(AllElements)
}
+ to(CommunalScenes.Communal, key = CommunalTransitionKeys.Swipe) {
+ spec = tween(durationMillis = TransitionDuration.TO_GLANCEABLE_HUB_DURATION_MS)
+ translate(Communal.Elements.Grid, Edge.End)
+ timestampRange(startMillis = 167, endMillis = 334) { fade(AllElements) }
+ }
to(CommunalScenes.Blank) {
spec = tween(durationMillis = TO_GONE_DURATION.toInt(DurationUnit.MILLISECONDS))
fade(AllElements)
}
+ to(CommunalScenes.Blank, key = CommunalTransitionKeys.Swipe) {
+ spec = tween(durationMillis = TransitionDuration.TO_GLANCEABLE_HUB_DURATION_MS)
+ translate(Communal.Elements.Grid, Edge.End)
+ timestampRange(endMillis = 167) {
+ fade(Communal.Elements.Grid)
+ fade(Communal.Elements.IndicationArea)
+ fade(Communal.Elements.LockIcon)
+ fade(Communal.Elements.StatusBar)
+ }
+ timestampRange(startMillis = 167, endMillis = 334) { fade(Communal.Elements.Scrim) }
+ }
}
val sceneTransitions = transitions {
@@ -165,6 +182,7 @@ fun CommunalContainer(
viewModel.communalBackground.collectAsStateWithLifecycle(
initialValue = CommunalBackgroundType.ANIMATED
)
+ val swipeToHubEnabled by viewModel.swipeToHubEnabled.collectAsStateWithLifecycle()
val state: MutableSceneTransitionLayoutState =
rememberMutableSceneTransitionLayoutState(
initialScene = currentSceneKey,
@@ -200,15 +218,27 @@ fun CommunalContainer(
scene(
CommunalScenes.Blank,
userActions =
- if (viewModel.swipeToHubEnabled())
- mapOf(Swipe.Start(fromSource = Edge.End) to CommunalScenes.Communal)
- else emptyMap(),
+ if (swipeToHubEnabled) {
+ mapOf(
+ Swipe.Start(fromSource = Edge.End) to
+ UserActionResult(CommunalScenes.Communal, CommunalTransitionKeys.Swipe)
+ )
+ } else {
+ emptyMap()
+ },
) {
// This scene shows nothing only allowing for transitions to the communal scene.
Box(modifier = Modifier.fillMaxSize())
}
- scene(CommunalScenes.Communal, userActions = mapOf(Swipe.End to CommunalScenes.Blank)) {
+ scene(
+ CommunalScenes.Communal,
+ userActions =
+ mapOf(
+ Swipe.End to
+ UserActionResult(CommunalScenes.Blank, CommunalTransitionKeys.Swipe)
+ ),
+ ) {
CommunalScene(
backgroundType = backgroundType,
colors = colors,
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 183cd8f1ae8b..eb961bd5f4ae 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
@@ -57,7 +57,7 @@ class FakeHomeStatusBarViewModel(
override val ongoingActivityChipsLegacy =
MutableStateFlow(MultipleOngoingActivityChipsModelLegacy())
- override val statusBarPopupChips = MutableStateFlow(emptyList<PopupChipModel.Shown>())
+ override val popupChips = emptyList<PopupChipModel.Shown>()
override val mediaProjectionStopDialogDueToCallEndedState =
MutableStateFlow(MediaProjectionStopDialogModel.Hidden)
diff --git a/packages/SystemUI/res/drawable/mobile_network_type_background_updated.xml b/packages/SystemUI/res/drawable/mobile_network_type_background_updated.xml
new file mode 100644
index 000000000000..7b55b3c6fb56
--- /dev/null
+++ b/packages/SystemUI/res/drawable/mobile_network_type_background_updated.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ 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.
+ ~
+ -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle"
+ >
+ <corners
+ android:topLeftRadius="0dp"
+ android:topRightRadius="@dimen/status_bar_mobile_container_corner_radius_updated"
+ android:bottomRightRadius="0dp"
+ android:bottomLeftRadius="@dimen/status_bar_mobile_container_corner_radius_updated"/>
+ <solid android:color="#FFF" />
+ <padding
+ android:left="2sp"
+ android:right="2sp"/>
+</shape>
diff --git a/packages/SystemUI/res/layout/internet_connectivity_dialog.xml b/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
index 5922a7dcdcf0..67e97010ff22 100644
--- a/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
+++ b/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
@@ -131,7 +131,7 @@
style="@style/InternetDialog.Network">
<FrameLayout
- android:layout_width="24dp"
+ android:layout_width="wrap_content"
android:layout_height="24dp"
android:clickable="false"
android:layout_gravity="center_vertical|start">
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 8cd4c1bb3533..7d0c393f53b5 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -204,15 +204,31 @@
match the core/status_bar_system_icon_size and change to sp unit -->
<dimen name="status_bar_mobile_signal_size">15sp</dimen>
<dimen name="status_bar_mobile_signal_size_updated">12sp</dimen>
+
<!-- Size of the view displaying the mobile signal icon in the status bar. This value should
- match the viewport height of mobile signal drawables such as ic_lte_mobiledata -->
+ match the viewport height of mobile signal drawables such as ic_lte_mobiledata
+ Note: can be removed once new_status_bar_icons is rolled out -->
<dimen name="status_bar_mobile_type_size">16sp</dimen>
<!-- Size of the view that contains the network type. Should be equal to
- status_bar_mobile_type_size + 2, to account for 1sp top and bottom padding -->
+ status_bar_mobile_type_size + 2, to account for 1sp top and bottom padding
+ Note: can be removed once new_status_bar_icons is rolled out -->
<dimen name="status_bar_mobile_container_height">18sp</dimen>
<!-- Corner radius for the background of the network type indicator. Should be equal to
- status_bar_mobile_container_height / 2 -->
+ status_bar_mobile_container_height / 2
+ Note: can be removed once new_status_bar_icons is rolled out -->
<dimen name="status_bar_mobile_container_corner_radius">9sp</dimen>
+
+ <!-- Size of the view displaying the mobile signal icon in the status bar. This value should
+ match the viewport height of mobile signal drawables such as ic_lte_mobiledata -->
+ <dimen name="status_bar_mobile_type_size_updated">12sp</dimen>
+ <!-- Size of the view that contains the network type. Should be equal to
+ status_bar_mobile_type_size + 2, to account for 1sp top and bottom padding -->
+ <dimen name="status_bar_mobile_container_height_updated">14sp</dimen>
+ <dimen name="status_bar_mobile_container_margin_end">2sp</dimen>
+ <!-- Corner radius for the background of the network type indicator. Should be equal to
+ status_bar_mobile_container_height / 2 -->
+ <dimen name="status_bar_mobile_container_corner_radius_updated">7sp</dimen>
+
<!-- Size of the view displaying the mobile roam icon in the status bar. This value should
match the viewport size of drawable stat_sys_roaming -->
<dimen name="status_bar_mobile_roam_size">8sp</dimen>
@@ -1806,7 +1822,8 @@
<!-- The activity chip side padding, used with the default phone icon. -->
<dimen name="ongoing_activity_chip_side_padding">12dp</dimen>
<!-- The activity chip side padding, used with an icon that has embedded padding (e.g. if the icon comes from the notification's smallIcon field). If the icon has padding, the chip itself can have less padding. -->
- <dimen name="ongoing_activity_chip_side_padding_for_embedded_padding_icon">6dp</dimen>
+ <dimen name="ongoing_activity_chip_side_padding_for_embedded_padding_icon">2dp</dimen>
+ <dimen name="ongoing_activity_chip_side_padding_for_embedded_padding_icon_legacy">6dp</dimen>
<!-- The icon size, used with the default phone icon. -->
<dimen name="ongoing_activity_chip_icon_size">16dp</dimen>
<!-- The icon size, used with an icon that has embedded padding. (If the icon has embedded padding, we need to make the whole icon larger so the icon itself doesn't look small.) -->
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 ca49de3b1510..a84c45732169 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
@@ -31,4 +31,6 @@ object CommunalTransitionKeys {
val ToEditMode = TransitionKey("ToEditMode")
/** Transition to the glanceable hub after exiting edit mode */
val FromEditMode = TransitionKey("FromEditMode")
+ /** Swipes the glanceable hub in/out of view */
+ val Swipe = TransitionKey("Swipe")
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
index 2169881d11c5..cce1ae1a2947 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
@@ -359,7 +359,13 @@ constructor(
/** See [CommunalSettingsInteractor.isV2FlagEnabled] */
fun v2FlagEnabled(): Boolean = communalSettingsInteractor.isV2FlagEnabled()
- fun swipeToHubEnabled(): Boolean = swipeToHub
+ val swipeToHubEnabled: StateFlow<Boolean> by lazy {
+ if (v2FlagEnabled()) {
+ communalInteractor.shouldShowCommunal
+ } else {
+ MutableStateFlow(swipeToHub)
+ }
+ }
companion object {
const val POPUP_AUTO_HIDE_TIMEOUT_MS = 12000L
diff --git a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
index 305e71e48702..3be2f1b7b957 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
@@ -231,6 +231,9 @@ constructor(
*/
private var isDreaming = false
+ /** True if we should allow swiping open the glanceable hub. */
+ private var swipeToHubEnabled = false
+
/** Observes and logs state when the lifecycle that controls the [touchMonitor] updates. */
private val touchLifecycleLogger: LifecycleObserver = LifecycleEventObserver { _, event ->
logger.d({
@@ -438,6 +441,7 @@ constructor(
},
)
collectFlow(containerView, keyguardInteractor.isDreaming, { isDreaming = it })
+ collectFlow(containerView, communalViewModel.swipeToHubEnabled, { swipeToHubEnabled = it })
communalContainerWrapper = CommunalWrapper(containerView.context)
communalContainerWrapper?.addView(communalContainerView)
@@ -520,10 +524,7 @@ constructor(
val glanceableHubV2 = communalSettingsInteractor.isV2FlagEnabled()
if (
!hubShowing &&
- (touchOnNotifications ||
- touchOnUmo ||
- touchOnSmartspace ||
- !communalViewModel.swipeToHubEnabled())
+ (touchOnNotifications || touchOnUmo || touchOnSmartspace || !swipeToHubEnabled)
) {
logger.d({
"Lockscreen touch ignored: touchOnNotifications: $bool1, touchOnUmo: $bool2, " +
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 5746cef41d6b..131efaac3d6a 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -2201,7 +2201,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
@Override
@Deprecated
public void onStatusBarLongPress(MotionEvent event) {
- mShadeLog.d("Status Bar was long pressed.");
+ Log.i(TAG, "Status Bar was long pressed.");
ShadeExpandsOnStatusBarLongPress.assertInNewMode();
mStatusBarLongPressDowntime = event.getDownTime();
if (isTracking()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/OngoingActivityChipBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/OngoingActivityChipBinder.kt
index d41353b2c176..20dec11577df 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/OngoingActivityChipBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/OngoingActivityChipBinder.kt
@@ -408,11 +408,13 @@ object OngoingActivityChipBinder {
private fun View.setBackgroundPaddingForEmbeddedPaddingIcon() {
val sidePadding =
if (StatusBarNotifChips.isEnabled) {
- 0
- } else {
context.resources.getDimensionPixelSize(
R.dimen.ongoing_activity_chip_side_padding_for_embedded_padding_icon
)
+ } else {
+ context.resources.getDimensionPixelSize(
+ R.dimen.ongoing_activity_chip_side_padding_for_embedded_padding_icon_legacy
+ )
}
setPaddingRelative(sidePadding, paddingTop, sidePadding, paddingBottom)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/OngoingActivityChip.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/OngoingActivityChip.kt
index 4a999d5f5e0e..efd402ead979 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/OngoingActivityChip.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/OngoingActivityChip.kt
@@ -41,7 +41,6 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.semantics
-import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import com.android.compose.animation.Expandable
import com.android.compose.modifiers.thenIf
@@ -160,7 +159,10 @@ private fun ChipBody(
.padding(
horizontal =
if (hasEmbeddedIcon) {
- 0.dp
+ dimensionResource(
+ R.dimen
+ .ongoing_activity_chip_side_padding_for_embedded_padding_icon
+ )
} else {
dimensionResource(id = R.dimen.ongoing_activity_chip_side_padding)
}
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 4017c436151e..3b8c0f48e40e 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
@@ -24,7 +24,8 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.key
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
-import androidx.compose.ui.unit.dp
+import androidx.compose.ui.res.dimensionResource
+import com.android.systemui.res.R
import com.android.systemui.statusbar.chips.ui.model.MultipleOngoingActivityChipsModel
import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerViewBinder
@@ -35,10 +36,13 @@ fun OngoingActivityChips(
modifier: Modifier = Modifier,
) {
Row(
- // TODO(b/372657935): Remove magic numbers for padding and spacing.
- modifier = modifier.fillMaxHeight().padding(horizontal = 6.dp),
+ modifier =
+ modifier
+ .fillMaxHeight()
+ .padding(start = dimensionResource(R.dimen.ongoing_activity_chip_margin_start)),
verticalAlignment = Alignment.CenterVertically,
- horizontalArrangement = Arrangement.spacedBy(8.dp),
+ horizontalArrangement =
+ Arrangement.spacedBy(dimensionResource(R.dimen.ongoing_activity_chip_margin_start)),
) {
chips.active
.filter { !it.isHidden }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/media/ui/viewmodel/MediaControlChipViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/media/ui/viewmodel/MediaControlChipViewModel.kt
index 7f0f6078f391..90f97df295f5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/media/ui/viewmodel/MediaControlChipViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/media/ui/viewmodel/MediaControlChipViewModel.kt
@@ -82,10 +82,6 @@ constructor(
chipId = PopupChipId.MediaControl,
icon = defaultIcon,
chipText = model.songName.toString(),
- isToggled = false,
- // TODO(b/385202114): Show a popup containing the media carousal when the chip is
- // toggled.
- onToggle = {},
hoverBehavior = createHoverBehavior(model),
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/shared/model/PopupChipModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/shared/model/PopupChipModel.kt
index 683b97166f3e..60615536ab67 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/shared/model/PopupChipModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/shared/model/PopupChipModel.kt
@@ -53,10 +53,11 @@ sealed class PopupChipModel {
/** Default icon displayed on the chip */
val icon: Icon,
val chipText: String,
- val isToggled: Boolean = false,
- val onToggle: () -> Unit,
+ val isPopupShown: Boolean = false,
+ val showPopup: () -> Unit = {},
+ val hidePopup: () -> Unit = {},
val hoverBehavior: HoverBehavior = HoverBehavior.None,
) : PopupChipModel() {
- override val logName = "Shown(id=$chipId, toggled=$isToggled)"
+ override val logName = "Shown(id=$chipId, toggled=$isPopupShown)"
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/ui/compose/StatusBarPopup.kt b/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/ui/compose/StatusBarPopup.kt
new file mode 100644
index 000000000000..8a66904ea59b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/ui/compose/StatusBarPopup.kt
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.featurepods.popups.ui.compose
+
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.wrapContentSize
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.res.dimensionResource
+import androidx.compose.ui.unit.Density
+import androidx.compose.ui.unit.IntOffset
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.window.Popup
+import androidx.compose.ui.window.PopupProperties
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.featurepods.popups.shared.model.PopupChipId
+import com.android.systemui.statusbar.featurepods.popups.shared.model.PopupChipModel
+
+/**
+ * Displays a popup in the status bar area. The offset is calculated to draw the popup below the
+ * status bar.
+ */
+@Composable
+fun StatusBarPopup(viewModel: PopupChipModel.Shown) {
+ val density = Density(LocalContext.current)
+ Popup(
+ properties =
+ PopupProperties(
+ focusable = false,
+ dismissOnBackPress = true,
+ dismissOnClickOutside = true,
+ ),
+ offset =
+ IntOffset(
+ x = 0,
+ y = with(density) { dimensionResource(R.dimen.status_bar_height).roundToPx() },
+ ),
+ onDismissRequest = { viewModel.hidePopup() },
+ ) {
+ Box(modifier = Modifier.padding(8.dp).wrapContentSize()) {
+ when (viewModel.chipId) {
+ is PopupChipId.MediaControl -> {
+ // TODO(b/385202114): Populate MediaControlPopup contents.
+ }
+ }
+ // Future popup types will be handled here.
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/ui/compose/StatusBarPopupChip.kt b/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/ui/compose/StatusBarPopupChip.kt
index 34bef9d3ca3a..eb85d2f32f29 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/ui/compose/StatusBarPopupChip.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/ui/compose/StatusBarPopupChip.kt
@@ -52,14 +52,14 @@ import com.android.systemui.statusbar.featurepods.popups.shared.model.PopupChipM
* the chip can show text containing contextual information.
*/
@Composable
-fun StatusBarPopupChip(model: PopupChipModel.Shown, modifier: Modifier = Modifier) {
- val hasHoverBehavior = model.hoverBehavior !is HoverBehavior.None
+fun StatusBarPopupChip(viewModel: PopupChipModel.Shown, modifier: Modifier = Modifier) {
+ val hasHoverBehavior = viewModel.hoverBehavior !is HoverBehavior.None
val hoverInteractionSource = remember { MutableInteractionSource() }
val isHovered by hoverInteractionSource.collectIsHoveredAsState()
- val isToggled = model.isToggled
+ val isPopupShown = viewModel.isPopupShown
val chipBackgroundColor =
- if (isToggled) {
+ if (isPopupShown) {
MaterialTheme.colorScheme.primaryContainer
} else {
MaterialTheme.colorScheme.surfaceContainerHighest
@@ -72,7 +72,7 @@ fun StatusBarPopupChip(model: PopupChipModel.Shown, modifier: Modifier = Modifie
.padding(vertical = 4.dp)
.animateContentSize()
.thenIf(hasHoverBehavior) { Modifier.hoverable(hoverInteractionSource) }
- .clickable { model.onToggle() },
+ .thenIf(!isPopupShown) { Modifier.clickable { viewModel.showPopup() } },
color = chipBackgroundColor,
) {
Row(
@@ -82,14 +82,14 @@ fun StatusBarPopupChip(model: PopupChipModel.Shown, modifier: Modifier = Modifie
) {
val iconColor =
if (isHovered) chipBackgroundColor else contentColorFor(chipBackgroundColor)
- val hoverBehavior = model.hoverBehavior
+ val hoverBehavior = viewModel.hoverBehavior
val iconBackgroundColor = contentColorFor(chipBackgroundColor)
val iconInteractionSource = remember { MutableInteractionSource() }
Icon(
icon =
when {
isHovered && hoverBehavior is HoverBehavior.Button -> hoverBehavior.icon
- else -> model.icon
+ else -> viewModel.icon
},
modifier =
Modifier.thenIf(isHovered) {
@@ -109,7 +109,7 @@ fun StatusBarPopupChip(model: PopupChipModel.Shown, modifier: Modifier = Modifie
)
Text(
- text = model.chipText,
+ text = viewModel.chipText,
style = MaterialTheme.typography.labelLarge,
softWrap = false,
overflow = TextOverflow.Ellipsis,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/ui/compose/StatusBarPopupChipsContainer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/ui/compose/StatusBarPopupChipsContainer.kt
index d35674d8dd9f..16538c93cf35 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/ui/compose/StatusBarPopupChipsContainer.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/ui/compose/StatusBarPopupChipsContainer.kt
@@ -31,10 +31,15 @@ fun StatusBarPopupChipsContainer(chips: List<PopupChipModel.Shown>, modifier: Mo
// TODO(b/385353140): Add padding and spacing for this container according to UX specs.
Box {
Row(
- modifier = Modifier.padding(horizontal = 8.dp),
+ modifier = modifier.padding(horizontal = 8.dp),
verticalAlignment = Alignment.CenterVertically,
) {
- chips.forEach { chip -> StatusBarPopupChip(chip) }
+ chips.forEach { chip ->
+ StatusBarPopupChip(chip)
+ if (chip.isPopupShown) {
+ StatusBarPopup(chip)
+ }
+ }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/ui/viewmodel/StatusBarPopupChipsViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/ui/viewmodel/StatusBarPopupChipsViewModel.kt
index caa8e6cc02c3..33bf90defb48 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/ui/viewmodel/StatusBarPopupChipsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/ui/viewmodel/StatusBarPopupChipsViewModel.kt
@@ -16,49 +16,65 @@
package com.android.systemui.statusbar.featurepods.popups.ui.viewmodel
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Background
+import androidx.compose.runtime.derivedStateOf
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
+import com.android.systemui.lifecycle.ExclusiveActivatable
+import com.android.systemui.lifecycle.Hydrator
import com.android.systemui.statusbar.featurepods.media.ui.viewmodel.MediaControlChipViewModel
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.shared.model.PopupChipModel
-import javax.inject.Inject
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.SharingStarted
-import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.asStateFlow
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.stateIn
/**
* View model deciding which system process chips to show in the status bar. Emits a list of
* PopupChipModels.
*/
-@SysUISingleton
class StatusBarPopupChipsViewModel
-@Inject
-constructor(
- @Background scope: CoroutineScope,
- mediaControlChipViewModel: MediaControlChipViewModel,
-) {
- private data class PopupChipBundle(
- val media: PopupChipModel = PopupChipModel.Hidden(chipId = PopupChipId.MediaControl)
- )
+@AssistedInject
+constructor(mediaControlChip: MediaControlChipViewModel) : ExclusiveActivatable() {
+ private val hydrator: Hydrator = Hydrator("StatusBarPopupChipsViewModel.hydrator")
- private val incomingPopupChipBundle: StateFlow<PopupChipBundle?> =
- mediaControlChipViewModel.chip
- .map { chip -> PopupChipBundle(media = chip) }
- .stateIn(scope, SharingStarted.WhileSubscribed(), PopupChipBundle())
+ /** The ID of the current chip that is showing its popup, or `null` if no chip is shown. */
+ private var currentShownPopupChipId by mutableStateOf<PopupChipId?>(null)
- val shownPopupChips: StateFlow<List<PopupChipModel.Shown>> =
+ private val incomingPopupChipBundle: PopupChipBundle by
+ hydrator.hydratedStateOf(
+ traceName = "incomingPopupChipBundle",
+ initialValue = PopupChipBundle(),
+ source = mediaControlChip.chip.map { chip -> PopupChipBundle(media = chip) },
+ )
+
+ val shownPopupChips: List<PopupChipModel.Shown> by derivedStateOf {
if (StatusBarPopupChips.isEnabled) {
- incomingPopupChipBundle
- .map { bundle ->
- listOfNotNull(bundle?.media).filterIsInstance<PopupChipModel.Shown>()
- }
- .stateIn(scope, SharingStarted.WhileSubscribed(), emptyList())
+ val bundle = incomingPopupChipBundle
+
+ listOfNotNull(bundle.media).filterIsInstance<PopupChipModel.Shown>().map { chip ->
+ chip.copy(
+ isPopupShown = chip.chipId == currentShownPopupChipId,
+ showPopup = { currentShownPopupChipId = chip.chipId },
+ hidePopup = { currentShownPopupChipId = null },
+ )
+ }
} else {
- MutableStateFlow(emptyList<PopupChipModel.Shown>()).asStateFlow()
+ emptyList()
}
+ }
+
+ override suspend fun onActivated(): Nothing {
+ hydrator.activate()
+ }
+
+ private data class PopupChipBundle(
+ val media: PopupChipModel = PopupChipModel.Hidden(chipId = PopupChipId.MediaControl)
+ )
+
+ @AssistedFactory
+ interface Factory {
+ fun create(): StatusBarPopupChipsViewModel
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernStatusBarMobileView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernStatusBarMobileView.kt
index 4458b224913b..7eda87f8418d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernStatusBarMobileView.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernStatusBarMobileView.kt
@@ -19,6 +19,7 @@ package com.android.systemui.statusbar.pipeline.mobile.ui.view
import android.content.Context
import android.util.AttributeSet
import android.view.LayoutInflater
+import android.widget.FrameLayout
import android.widget.ImageView
import com.android.systemui.res.R
import com.android.systemui.statusbar.StatusBarIconView.getVisibleStateString
@@ -61,12 +62,33 @@ class ModernStatusBarMobileView(context: Context, attrs: AttributeSet?) :
.also {
// Flag-specific configuration
if (NewStatusBarIcons.isEnabled) {
- val iconView = it.requireViewById<ImageView>(R.id.mobile_signal)
- val lp = iconView.layoutParams
- lp.height =
- context.resources.getDimensionPixelSize(
- R.dimen.status_bar_mobile_signal_size_updated
- )
+ // triangle
+ it.requireViewById<ImageView>(R.id.mobile_signal).apply {
+ layoutParams.height =
+ context.resources.getDimensionPixelSize(
+ R.dimen.status_bar_mobile_signal_size_updated
+ )
+ }
+
+ // RAT indicator container
+ it.requireViewById<FrameLayout>(R.id.mobile_type_container).apply {
+ (layoutParams as MarginLayoutParams).marginEnd =
+ context.resources.getDimensionPixelSize(
+ R.dimen.status_bar_mobile_container_margin_end
+ )
+ layoutParams.height =
+ context.resources.getDimensionPixelSize(
+ R.dimen.status_bar_mobile_container_height_updated
+ )
+ }
+
+ // RAT indicator
+ it.requireViewById<ImageView>(R.id.mobile_type).apply {
+ layoutParams.height =
+ context.resources.getDimensionPixelSize(
+ R.dimen.status_bar_mobile_type_size_updated
+ )
+ }
}
it.subId = viewModel.subscriptionId
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt
index 171e4f59b0e5..e37c3f10cfcb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt
@@ -23,6 +23,7 @@ import com.android.systemui.flags.FeatureFlagsClassic
import com.android.systemui.flags.Flags.NEW_NETWORK_SLICE_UI
import com.android.systemui.log.table.logDiffsForTable
import com.android.systemui.res.R
+import com.android.systemui.statusbar.core.NewStatusBarIcons
import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconInteractor
import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractor
@@ -278,10 +279,11 @@ private class CellularIconViewModel(
flowOf(null)
} else {
iconInteractor.showSliceAttribution.map {
- if (it) {
- Icon.Resource(R.drawable.mobile_network_type_background, null)
- } else {
- null
+ when {
+ it && NewStatusBarIcons.isEnabled ->
+ Icon.Resource(R.drawable.mobile_network_type_background_updated, null)
+ it -> Icon.Resource(R.drawable.mobile_network_type_background, null)
+ else -> null
}
}
}
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 a961713230c8..39a1b46292b0 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
@@ -255,10 +255,9 @@ fun StatusBarRoot(
)
setContent {
- val chips =
- statusBarViewModel.statusBarPopupChips
- .collectAsStateWithLifecycle()
- StatusBarPopupChipsContainer(chips = chips.value)
+ StatusBarPopupChipsContainer(
+ chips = statusBarViewModel.popupChips
+ )
}
}
endSideContent.addView(composeView, 0)
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 f396cbfc8000..9ae2cb203d91 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
@@ -20,6 +20,7 @@ import android.annotation.ColorInt
import android.graphics.Rect
import android.view.View
import androidx.compose.runtime.getValue
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
@@ -29,6 +30,7 @@ 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.KeyguardState.OCCLUDED
import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.lifecycle.Activatable
import com.android.systemui.lifecycle.ExclusiveActivatable
import com.android.systemui.lifecycle.Hydrator
import com.android.systemui.log.table.TableLogBufferFactory
@@ -49,6 +51,7 @@ import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipsViewModel
import com.android.systemui.statusbar.events.domain.interactor.SystemStatusEventAnimationInteractor
import com.android.systemui.statusbar.events.shared.model.SystemEventAnimationState.Idle
+import com.android.systemui.statusbar.featurepods.popups.StatusBarPopupChips
import com.android.systemui.statusbar.featurepods.popups.shared.model.PopupChipModel
import com.android.systemui.statusbar.featurepods.popups.ui.viewmodel.StatusBarPopupChipsViewModel
import com.android.systemui.statusbar.headsup.shared.StatusBarNoHunBehavior
@@ -71,6 +74,8 @@ import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.awaitCancellation
+import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
@@ -94,7 +99,7 @@ import kotlinx.coroutines.flow.stateIn
* [StatusBarHideIconsForBouncerManager]. We should move those pieces of logic to this class instead
* so that it's all in one place and easily testable outside of the fragment.
*/
-interface HomeStatusBarViewModel {
+interface HomeStatusBarViewModel : Activatable {
/** Factory to create the view model for the battery icon */
val batteryViewModelFactory: BatteryViewModel.Factory
@@ -133,7 +138,7 @@ interface HomeStatusBarViewModel {
val operatorNameViewModel: StatusBarOperatorNameViewModel
/** The popup chips that should be shown on the right-hand side of the status bar. */
- val statusBarPopupChips: StateFlow<List<PopupChipModel.Shown>>
+ val popupChips: List<PopupChipModel.Shown>
/**
* True if the current scene can show the home status bar (aka this status bar), and false if
@@ -208,7 +213,7 @@ constructor(
shadeInteractor: ShadeInteractor,
shareToAppChipViewModel: ShareToAppChipViewModel,
ongoingActivityChipsViewModel: OngoingActivityChipsViewModel,
- statusBarPopupChipsViewModel: StatusBarPopupChipsViewModel,
+ statusBarPopupChipsViewModelFactory: StatusBarPopupChipsViewModel.Factory,
animations: SystemStatusEventAnimationInteractor,
statusBarContentInsetsViewModelStore: StatusBarContentInsetsViewModelStore,
@Background bgScope: CoroutineScope,
@@ -219,6 +224,8 @@ constructor(
val tableLogger = tableLoggerFactory.getOrCreate(tableLogBufferName(thisDisplayId), 200)
+ private val statusBarPopupChips by lazy { statusBarPopupChipsViewModelFactory.create() }
+
override val isTransitioningFromLockscreenToOccluded: StateFlow<Boolean> =
keyguardTransitionInteractor
.isInTransition(Edge.create(from = LOCKSCREEN, to = OCCLUDED))
@@ -246,7 +253,8 @@ constructor(
override val ongoingActivityChipsLegacy = ongoingActivityChipsViewModel.chipsLegacy
- override val statusBarPopupChips = statusBarPopupChipsViewModel.shownPopupChips
+ override val popupChips
+ get() = statusBarPopupChips.shownPopupChips
override val isHomeStatusBarAllowedByScene: StateFlow<Boolean> =
combine(
@@ -495,7 +503,13 @@ constructor(
private fun Boolean.toVisibleOrInvisible(): Int = if (this) View.VISIBLE else View.INVISIBLE
override suspend fun onActivated(): Nothing {
- hydrator.activate()
+ coroutineScope {
+ launch { hydrator.activate() }
+ if (StatusBarPopupChips.isEnabled) {
+ launch { statusBarPopupChips.activate() }
+ }
+ awaitCancellation()
+ }
}
/** Inject this to create the display-dependent view model */
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
index 944604f94ce4..ae8b52aa6553 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
@@ -18,7 +18,7 @@ package com.android.systemui.shade
import android.graphics.Insets
import android.graphics.Rect
-import android.os.PowerManager
+import android.os.powerManager
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.testing.AndroidTestingRunner
@@ -32,7 +32,7 @@ import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.test.filters.SmallTest
import com.android.compose.animation.scene.SceneKey
-import com.android.systemui.Flags
+import com.android.systemui.Flags.FLAG_COMMUNAL_HUB
import com.android.systemui.Flags.FLAG_GLANCEABLE_HUB_V2
import com.android.systemui.Flags.FLAG_HUBMODE_FULLSCREEN_VERTICAL_SWIPE_FIX
import com.android.systemui.SysuiTestCase
@@ -40,7 +40,6 @@ import com.android.systemui.ambient.touch.TouchHandler
import com.android.systemui.ambient.touch.TouchMonitor
import com.android.systemui.ambient.touch.dagger.AmbientTouchComponent
import com.android.systemui.bouncer.data.repository.fakeKeyguardBouncerRepository
-import com.android.systemui.communal.data.repository.FakeCommunalSceneRepository
import com.android.systemui.communal.data.repository.fakeCommunalSceneRepository
import com.android.systemui.communal.domain.interactor.communalInteractor
import com.android.systemui.communal.domain.interactor.communalSettingsInteractor
@@ -58,8 +57,10 @@ 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.kosmos.Kosmos
-import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.collectLastValue
+import com.android.systemui.kosmos.runTest
import com.android.systemui.kosmos.testScope
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
import com.android.systemui.log.logcatLogBuffer
import com.android.systemui.media.controls.controller.keyguardMediaController
import com.android.systemui.res.R
@@ -69,8 +70,8 @@ import com.android.systemui.statusbar.lockscreen.lockscreenSmartspaceController
import com.android.systemui.statusbar.notification.stack.notificationStackScrollLayoutController
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.runTest
import org.junit.After
import org.junit.Assert.assertThrows
@@ -89,33 +90,23 @@ import org.mockito.kotlin.whenever
@RunWith(AndroidTestingRunner::class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@SmallTest
+@EnableFlags(FLAG_COMMUNAL_HUB)
class GlanceableHubContainerControllerTest : SysuiTestCase() {
- private val kosmos: Kosmos =
- testKosmos().apply {
- // UnconfinedTestDispatcher makes testing simpler due to CommunalInteractor flows using
- // SharedFlow
- testDispatcher = UnconfinedTestDispatcher()
- }
+ private val kosmos: Kosmos = testKosmos().useUnconfinedTestDispatcher()
- private var communalViewModel = mock<CommunalViewModel>()
- private var powerManager = mock<PowerManager>()
- private var touchMonitor = mock<TouchMonitor>()
- private var communalColors = mock<CommunalColors>()
- private var communalContent = mock<CommunalContent>()
- private lateinit var ambientTouchComponentFactory: AmbientTouchComponent.Factory
+ private val swipeToHubEnabled = MutableStateFlow(false)
+ private val mockCommunalViewModel =
+ mock<CommunalViewModel> { on { swipeToHubEnabled } doReturn swipeToHubEnabled }
+ private val touchMonitor = mock<TouchMonitor>()
private lateinit var parentView: FrameLayout
private lateinit var containerView: View
- private lateinit var testableLooper: TestableLooper
-
- private lateinit var communalRepository: FakeCommunalSceneRepository
- private lateinit var underTest: GlanceableHubContainerController
- @Before
- fun setUp() {
- communalRepository = kosmos.fakeCommunalSceneRepository
+ private val Kosmos.testableLooper by
+ Kosmos.Fixture { TestableLooper.get(this@GlanceableHubContainerControllerTest) }
- ambientTouchComponentFactory =
+ private val Kosmos.ambientTouchComponentFactory by
+ Kosmos.Fixture {
object : AmbientTouchComponent.Factory {
override fun create(
lifecycleOwner: LifecycleOwner,
@@ -126,50 +117,39 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() {
override fun getTouchMonitor(): TouchMonitor = touchMonitor
}
}
+ }
- with(kosmos) {
- underTest =
- GlanceableHubContainerController(
- communalInteractor,
- communalSettingsInteractor,
- communalViewModel,
- keyguardInteractor,
- keyguardTransitionInteractor,
- shadeInteractor,
- powerManager,
- communalColors,
- ambientTouchComponentFactory,
- communalContent,
- sceneDataSourceDelegator,
- notificationStackScrollLayoutController,
- keyguardMediaController,
- lockscreenSmartspaceController,
- logcatLogBuffer("GlanceableHubContainerControllerTest"),
- )
-
- // Make below last notification true by default or else touches will be ignored by
- // default when the hub is not showing.
- whenever(notificationStackScrollLayoutController.isBelowLastNotification(any(), any()))
- .thenReturn(true)
+ private val Kosmos.underTest by
+ Kosmos.Fixture {
+ GlanceableHubContainerController(
+ communalInteractor,
+ communalSettingsInteractor,
+ mockCommunalViewModel,
+ keyguardInteractor,
+ keyguardTransitionInteractor,
+ shadeInteractor,
+ powerManager,
+ mock<CommunalColors>(),
+ ambientTouchComponentFactory,
+ mock<CommunalContent>(),
+ sceneDataSourceDelegator,
+ notificationStackScrollLayoutController,
+ keyguardMediaController,
+ lockscreenSmartspaceController,
+ logcatLogBuffer("GlanceableHubContainerControllerTest"),
+ )
}
- testableLooper = TestableLooper.get(this)
+ @Before
+ fun setUp() {
overrideResource(R.dimen.communal_right_edge_swipe_region_width, RIGHT_SWIPE_REGION_WIDTH)
overrideResource(R.dimen.communal_top_edge_swipe_region_height, TOP_SWIPE_REGION_WIDTH)
overrideResource(
R.dimen.communal_bottom_edge_swipe_region_height,
BOTTOM_SWIPE_REGION_WIDTH,
)
-
- // Make communal available so that communalInteractor.desiredScene accurately reflects
- // scene changes instead of just returning Blank.
- mSetFlagsRule.enableFlags(Flags.FLAG_COMMUNAL_HUB)
- with(kosmos.testScope) {
- launch { kosmos.setCommunalAvailable(true) }
- testScheduler.runCurrent()
- }
-
- initAndAttachContainerView()
+ runBlocking { kosmos.setCommunalAvailable(true) }
+ kosmos.initAndAttachContainerView()
}
@After
@@ -179,606 +159,531 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() {
@Test
fun initView_calledTwice_throwsException() =
- with(kosmos) {
- testScope.runTest {
- underTest =
- GlanceableHubContainerController(
- communalInteractor,
- kosmos.communalSettingsInteractor,
- communalViewModel,
- keyguardInteractor,
- kosmos.keyguardTransitionInteractor,
- shadeInteractor,
- powerManager,
- communalColors,
- ambientTouchComponentFactory,
- communalContent,
- kosmos.sceneDataSourceDelegator,
- kosmos.notificationStackScrollLayoutController,
- kosmos.keyguardMediaController,
- kosmos.lockscreenSmartspaceController,
- logcatLogBuffer("GlanceableHubContainerControllerTest"),
- )
+ kosmos.runTest {
+ val controller =
+ GlanceableHubContainerController(
+ communalInteractor,
+ communalSettingsInteractor,
+ mockCommunalViewModel,
+ keyguardInteractor,
+ keyguardTransitionInteractor,
+ shadeInteractor,
+ powerManager,
+ mock<CommunalColors>(),
+ ambientTouchComponentFactory,
+ mock<CommunalContent>(),
+ sceneDataSourceDelegator,
+ notificationStackScrollLayoutController,
+ keyguardMediaController,
+ lockscreenSmartspaceController,
+ logcatLogBuffer("GlanceableHubContainerControllerTest"),
+ )
- // First call succeeds.
- underTest.initView(context)
+ // First call succeeds.
+ controller.initView(context)
- // Second call throws.
- assertThrows(RuntimeException::class.java) { underTest.initView(context) }
- }
+ // Second call throws.
+ assertThrows(RuntimeException::class.java) { controller.initView(context) }
}
@Test
fun lifecycle_initializedAfterConstruction() =
- with(kosmos) {
- val underTest =
+ kosmos.runTest {
+ val controller =
GlanceableHubContainerController(
communalInteractor,
- kosmos.communalSettingsInteractor,
- communalViewModel,
+ communalSettingsInteractor,
+ mockCommunalViewModel,
keyguardInteractor,
- kosmos.keyguardTransitionInteractor,
+ keyguardTransitionInteractor,
shadeInteractor,
powerManager,
- communalColors,
+ mock<CommunalColors>(),
ambientTouchComponentFactory,
- communalContent,
- kosmos.sceneDataSourceDelegator,
- kosmos.notificationStackScrollLayoutController,
- kosmos.keyguardMediaController,
- kosmos.lockscreenSmartspaceController,
+ mock<CommunalContent>(),
+ sceneDataSourceDelegator,
+ notificationStackScrollLayoutController,
+ keyguardMediaController,
+ lockscreenSmartspaceController,
logcatLogBuffer("GlanceableHubContainerControllerTest"),
)
- assertThat(underTest.lifecycle.currentState).isEqualTo(Lifecycle.State.INITIALIZED)
+ assertThat(controller.lifecycle.currentState).isEqualTo(Lifecycle.State.INITIALIZED)
}
@Test
fun lifecycle_createdAfterViewCreated() =
- with(kosmos) {
- val underTest =
+ kosmos.runTest {
+ val controller =
GlanceableHubContainerController(
communalInteractor,
- kosmos.communalSettingsInteractor,
- communalViewModel,
+ communalSettingsInteractor,
+ mockCommunalViewModel,
keyguardInteractor,
- kosmos.keyguardTransitionInteractor,
+ keyguardTransitionInteractor,
shadeInteractor,
powerManager,
- communalColors,
+ mock<CommunalColors>(),
ambientTouchComponentFactory,
- communalContent,
- kosmos.sceneDataSourceDelegator,
- kosmos.notificationStackScrollLayoutController,
- kosmos.keyguardMediaController,
- kosmos.lockscreenSmartspaceController,
+ mock<CommunalContent>(),
+ sceneDataSourceDelegator,
+ notificationStackScrollLayoutController,
+ keyguardMediaController,
+ lockscreenSmartspaceController,
logcatLogBuffer("GlanceableHubContainerControllerTest"),
)
// Only initView without attaching a view as we don't want the flows to start collecting
// yet.
- underTest.initView(View(context))
+ controller.initView(View(context))
- assertThat(underTest.lifecycle.currentState).isEqualTo(Lifecycle.State.CREATED)
+ assertThat(controller.lifecycle.currentState).isEqualTo(Lifecycle.State.CREATED)
}
@Test
- fun lifecycle_startedAfterFlowsUpdate() {
- // Flows start collecting due to test setup, causing the state to advance to STARTED.
- assertThat(underTest.lifecycle.currentState).isEqualTo(Lifecycle.State.STARTED)
- }
+ fun lifecycle_startedAfterFlowsUpdate() =
+ kosmos.runTest {
+ // Flows start collecting due to test setup, causing the state to advance to STARTED.
+ assertThat(underTest.lifecycle.currentState).isEqualTo(Lifecycle.State.STARTED)
+ }
@Test
fun lifecycle_resumedAfterCommunalShows() =
- with(kosmos) {
- testScope.runTest {
- // Communal is open.
- goToScene(CommunalScenes.Communal)
+ kosmos.runTest {
+ // Communal is open.
+ goToScene(CommunalScenes.Communal)
- assertThat(underTest.lifecycle.currentState).isEqualTo(Lifecycle.State.RESUMED)
- }
+ assertThat(underTest.lifecycle.currentState).isEqualTo(Lifecycle.State.RESUMED)
}
@Test
fun lifecycle_startedAfterCommunalCloses() =
- with(kosmos) {
- testScope.runTest {
- // Communal is open.
- goToScene(CommunalScenes.Communal)
+ kosmos.runTest {
+ // Communal is open.
+ goToScene(CommunalScenes.Communal)
- assertThat(underTest.lifecycle.currentState).isEqualTo(Lifecycle.State.RESUMED)
+ assertThat(underTest.lifecycle.currentState).isEqualTo(Lifecycle.State.RESUMED)
- // Communal closes.
- goToScene(CommunalScenes.Blank)
+ // Communal closes.
+ goToScene(CommunalScenes.Blank)
- assertThat(underTest.lifecycle.currentState).isEqualTo(Lifecycle.State.STARTED)
- }
+ assertThat(underTest.lifecycle.currentState).isEqualTo(Lifecycle.State.STARTED)
}
@Test
fun lifecycle_startedAfterPrimaryBouncerShows() =
- with(kosmos) {
- testScope.runTest {
- // Communal is open.
- goToScene(CommunalScenes.Communal)
+ kosmos.runTest {
+ // Communal is open.
+ goToScene(CommunalScenes.Communal)
- // Bouncer is visible.
- fakeKeyguardBouncerRepository.setPrimaryShow(true)
- testableLooper.processAllMessages()
+ // Bouncer is visible.
+ fakeKeyguardBouncerRepository.setPrimaryShow(true)
+ testableLooper.processAllMessages()
- assertThat(underTest.lifecycle.currentState).isEqualTo(Lifecycle.State.STARTED)
- }
+ assertThat(underTest.lifecycle.currentState).isEqualTo(Lifecycle.State.STARTED)
}
@Test
fun lifecycle_startedAfterAlternateBouncerShows() =
- with(kosmos) {
- testScope.runTest {
- // Communal is open.
- goToScene(CommunalScenes.Communal)
+ kosmos.runTest {
+ // Communal is open.
+ goToScene(CommunalScenes.Communal)
- // Bouncer is visible.
- fakeKeyguardBouncerRepository.setAlternateVisible(true)
- testableLooper.processAllMessages()
+ // Bouncer is visible.
+ fakeKeyguardBouncerRepository.setAlternateVisible(true)
+ testableLooper.processAllMessages()
- assertThat(underTest.lifecycle.currentState).isEqualTo(Lifecycle.State.STARTED)
- }
+ assertThat(underTest.lifecycle.currentState).isEqualTo(Lifecycle.State.STARTED)
}
@Test
fun lifecycle_startedWhenEditActivityShowing() =
- with(kosmos) {
- testScope.runTest {
- // Communal is open.
- goToScene(CommunalScenes.Communal)
+ kosmos.runTest {
+ // Communal is open.
+ goToScene(CommunalScenes.Communal)
- // Edit activity is showing.
- communalInteractor.setEditActivityShowing(true)
- testableLooper.processAllMessages()
+ // Edit activity is showing.
+ communalInteractor.setEditActivityShowing(true)
+ testableLooper.processAllMessages()
- assertThat(underTest.lifecycle.currentState).isEqualTo(Lifecycle.State.STARTED)
- }
+ assertThat(underTest.lifecycle.currentState).isEqualTo(Lifecycle.State.STARTED)
}
@Test
fun lifecycle_startedWhenEditModeTransitionStarted() =
- with(kosmos) {
- testScope.runTest {
- // Communal is open.
- goToScene(CommunalScenes.Communal)
-
- // Leaving edit mode to return to the hub.
- fakeKeyguardTransitionRepository.sendTransitionStep(
- TransitionStep(
- from = KeyguardState.GONE,
- to = KeyguardState.GLANCEABLE_HUB,
- value = 1.0f,
- transitionState = TransitionState.RUNNING,
- )
+ kosmos.runTest {
+ // Communal is open.
+ goToScene(CommunalScenes.Communal)
+
+ // Leaving edit mode to return to the hub.
+ fakeKeyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.GONE,
+ to = KeyguardState.GLANCEABLE_HUB,
+ value = 1.0f,
+ transitionState = TransitionState.RUNNING,
)
- testableLooper.processAllMessages()
+ )
+ testableLooper.processAllMessages()
- assertThat(underTest.lifecycle.currentState).isEqualTo(Lifecycle.State.STARTED)
- }
+ assertThat(underTest.lifecycle.currentState).isEqualTo(Lifecycle.State.STARTED)
}
@Test
- fun lifecycle_createdAfterDisposeView() {
- // Container view disposed.
- underTest.disposeView()
+ fun lifecycle_createdAfterDisposeView() =
+ kosmos.runTest {
+ // Container view disposed.
+ underTest.disposeView()
- assertThat(underTest.lifecycle.currentState).isEqualTo(Lifecycle.State.CREATED)
- }
+ assertThat(underTest.lifecycle.currentState).isEqualTo(Lifecycle.State.CREATED)
+ }
@Test
fun lifecycle_startedAfterShadeShows() =
- with(kosmos) {
- testScope.runTest {
- // Communal is open.
- goToScene(CommunalScenes.Communal)
+ kosmos.runTest {
+ // Communal is open.
+ goToScene(CommunalScenes.Communal)
- // Shade shows up.
- shadeTestUtil.setQsExpansion(1.0f)
- testableLooper.processAllMessages()
+ // Shade shows up.
+ shadeTestUtil.setQsExpansion(1.0f)
+ testableLooper.processAllMessages()
- assertThat(underTest.lifecycle.currentState).isEqualTo(Lifecycle.State.STARTED)
- }
+ assertThat(underTest.lifecycle.currentState).isEqualTo(Lifecycle.State.STARTED)
}
@Test
fun lifecycle_doesNotResumeOnUserInteractivityOnceExpanded() =
- with(kosmos) {
- testScope.runTest {
- // Communal is open.
- goToScene(CommunalScenes.Communal)
-
- // Shade shows up.
- shadeTestUtil.setShadeExpansion(1.0f)
- testableLooper.processAllMessages()
- underTest.onTouchEvent(DOWN_EVENT)
- testableLooper.processAllMessages()
-
- assertThat(underTest.lifecycle.currentState).isEqualTo(Lifecycle.State.STARTED)
-
- // Shade starts collapsing.
- shadeTestUtil.setShadeExpansion(.5f)
- testableLooper.processAllMessages()
- underTest.onTouchEvent(DOWN_EVENT)
- testableLooper.processAllMessages()
-
- assertThat(underTest.lifecycle.currentState).isEqualTo(Lifecycle.State.STARTED)
-
- // Shade fully collpase, and then expand should with touch interaction should now
- // be resumed.
- shadeTestUtil.setShadeExpansion(0f)
- testableLooper.processAllMessages()
- shadeTestUtil.setShadeExpansion(.5f)
- testableLooper.processAllMessages()
- underTest.onTouchEvent(DOWN_EVENT)
- testableLooper.processAllMessages()
-
- assertThat(underTest.lifecycle.currentState).isEqualTo(Lifecycle.State.RESUMED)
- }
+ kosmos.runTest {
+ // Communal is open.
+ goToScene(CommunalScenes.Communal)
+
+ // Shade shows up.
+ shadeTestUtil.setShadeExpansion(1.0f)
+ testableLooper.processAllMessages()
+ underTest.onTouchEvent(DOWN_EVENT)
+ testableLooper.processAllMessages()
+
+ assertThat(underTest.lifecycle.currentState).isEqualTo(Lifecycle.State.STARTED)
+
+ // Shade starts collapsing.
+ shadeTestUtil.setShadeExpansion(.5f)
+ testableLooper.processAllMessages()
+ underTest.onTouchEvent(DOWN_EVENT)
+ testableLooper.processAllMessages()
+
+ assertThat(underTest.lifecycle.currentState).isEqualTo(Lifecycle.State.STARTED)
+
+ // Shade fully collpase, and then expand should with touch interaction should now
+ // be resumed.
+ shadeTestUtil.setShadeExpansion(0f)
+ testableLooper.processAllMessages()
+ shadeTestUtil.setShadeExpansion(.5f)
+ testableLooper.processAllMessages()
+ underTest.onTouchEvent(DOWN_EVENT)
+ testableLooper.processAllMessages()
+
+ assertThat(underTest.lifecycle.currentState).isEqualTo(Lifecycle.State.RESUMED)
}
@Test
fun touchHandling_moveEventProcessedAfterCancel() =
- with(kosmos) {
- testScope.runTest {
- // Communal is open.
- goToScene(CommunalScenes.Communal)
-
- // Touch starts and ends.
- assertThat(underTest.onTouchEvent(DOWN_EVENT)).isTrue()
- assertThat(underTest.onTouchEvent(CANCEL_EVENT)).isTrue()
-
- // Up event is no longer processed
- assertThat(underTest.onTouchEvent(UP_EVENT)).isFalse()
-
- // Move event can still be processed
- assertThat(underTest.onTouchEvent(MOVE_EVENT)).isTrue()
- assertThat(underTest.onTouchEvent(MOVE_EVENT)).isTrue()
- assertThat(underTest.onTouchEvent(UP_EVENT)).isTrue()
- }
+ kosmos.runTest {
+ // Communal is open.
+ goToScene(CommunalScenes.Communal)
+
+ // Touch starts and ends.
+ assertThat(underTest.onTouchEvent(DOWN_EVENT)).isTrue()
+ assertThat(underTest.onTouchEvent(CANCEL_EVENT)).isTrue()
+
+ // Up event is no longer processed
+ assertThat(underTest.onTouchEvent(UP_EVENT)).isFalse()
+
+ // Move event can still be processed
+ assertThat(underTest.onTouchEvent(MOVE_EVENT)).isTrue()
+ assertThat(underTest.onTouchEvent(MOVE_EVENT)).isTrue()
+ assertThat(underTest.onTouchEvent(UP_EVENT)).isTrue()
}
@Test
fun editMode_communalAvailable() =
- with(kosmos) {
- testScope.runTest {
- val available by collectLastValue(underTest.communalAvailable())
- setCommunalAvailable(false)
-
- assertThat(available).isFalse()
- communalInteractor.setEditModeOpen(true)
- assertThat(available).isTrue()
- }
+ kosmos.runTest {
+ val available by collectLastValue(underTest.communalAvailable())
+ setCommunalAvailable(false)
+
+ assertThat(available).isFalse()
+ communalInteractor.setEditModeOpen(true)
+ assertThat(available).isTrue()
}
@Test
@DisableFlags(FLAG_HUBMODE_FULLSCREEN_VERTICAL_SWIPE_FIX)
fun gestureExclusionZone_setAfterInit() =
- with(kosmos) {
- testScope.runTest {
- goToScene(CommunalScenes.Communal)
-
- assertThat(containerView.systemGestureExclusionRects)
- .containsExactly(
- Rect(
- /* left= */ 0,
- /* top= */ TOP_SWIPE_REGION_WIDTH,
- /* right= */ CONTAINER_WIDTH,
- /* bottom= */ CONTAINER_HEIGHT - BOTTOM_SWIPE_REGION_WIDTH,
- )
+ kosmos.runTest {
+ goToScene(CommunalScenes.Communal)
+
+ assertThat(containerView.systemGestureExclusionRects)
+ .containsExactly(
+ Rect(
+ /* left= */ 0,
+ /* top= */ TOP_SWIPE_REGION_WIDTH,
+ /* right= */ CONTAINER_WIDTH,
+ /* bottom= */ CONTAINER_HEIGHT - BOTTOM_SWIPE_REGION_WIDTH,
)
- }
+ )
}
@Test
@EnableFlags(FLAG_HUBMODE_FULLSCREEN_VERTICAL_SWIPE_FIX)
fun gestureExclusionZone_setAfterInit_fullSwipe() =
- with(kosmos) {
- testScope.runTest {
- goToScene(CommunalScenes.Communal)
+ kosmos.runTest {
+ goToScene(CommunalScenes.Communal)
- assertThat(containerView.systemGestureExclusionRects).isEmpty()
- }
+ assertThat(containerView.systemGestureExclusionRects).isEmpty()
}
@Test
@DisableFlags(FLAG_HUBMODE_FULLSCREEN_VERTICAL_SWIPE_FIX)
fun gestureExclusionZone_unsetWhenShadeOpen() =
- with(kosmos) {
- testScope.runTest {
- goToScene(CommunalScenes.Communal)
+ kosmos.runTest {
+ goToScene(CommunalScenes.Communal)
- // Exclusion rect is set.
- assertThat(containerView.systemGestureExclusionRects).isNotEmpty()
+ // Exclusion rect is set.
+ assertThat(containerView.systemGestureExclusionRects).isNotEmpty()
- // Shade shows up.
- shadeTestUtil.setQsExpansion(1.0f)
- testableLooper.processAllMessages()
+ // Shade shows up.
+ shadeTestUtil.setQsExpansion(1.0f)
+ testableLooper.processAllMessages()
- // Exclusion rects are unset.
- assertThat(containerView.systemGestureExclusionRects).isEmpty()
- }
+ // Exclusion rects are unset.
+ assertThat(containerView.systemGestureExclusionRects).isEmpty()
}
@Test
@DisableFlags(FLAG_HUBMODE_FULLSCREEN_VERTICAL_SWIPE_FIX)
fun gestureExclusionZone_unsetWhenBouncerOpen() =
- with(kosmos) {
- testScope.runTest {
- goToScene(CommunalScenes.Communal)
+ kosmos.runTest {
+ goToScene(CommunalScenes.Communal)
- // Exclusion rect is set.
- assertThat(containerView.systemGestureExclusionRects).isNotEmpty()
+ // Exclusion rect is set.
+ assertThat(containerView.systemGestureExclusionRects).isNotEmpty()
- // Bouncer is visible.
- fakeKeyguardBouncerRepository.setPrimaryShow(true)
- testableLooper.processAllMessages()
+ // Bouncer is visible.
+ fakeKeyguardBouncerRepository.setPrimaryShow(true)
+ testableLooper.processAllMessages()
- // Exclusion rects are unset.
- assertThat(containerView.systemGestureExclusionRects).isEmpty()
- }
+ // Exclusion rects are unset.
+ assertThat(containerView.systemGestureExclusionRects).isEmpty()
}
@Test
@DisableFlags(FLAG_HUBMODE_FULLSCREEN_VERTICAL_SWIPE_FIX)
fun gestureExclusionZone_unsetWhenHubClosed() =
- with(kosmos) {
- testScope.runTest {
- goToScene(CommunalScenes.Communal)
+ kosmos.runTest {
+ goToScene(CommunalScenes.Communal)
- // Exclusion rect is set.
- assertThat(containerView.systemGestureExclusionRects).isNotEmpty()
+ // Exclusion rect is set.
+ assertThat(containerView.systemGestureExclusionRects).isNotEmpty()
- // Leave the hub.
- goToScene(CommunalScenes.Blank)
+ // Leave the hub.
+ goToScene(CommunalScenes.Blank)
- // Exclusion rect is unset.
- assertThat(containerView.systemGestureExclusionRects).isEmpty()
- }
+ // Exclusion rect is unset.
+ assertThat(containerView.systemGestureExclusionRects).isEmpty()
}
@Test
fun fullScreenSwipeGesture_doNotProcessTouchesInNotificationStack() =
- with(kosmos) {
- testScope.runTest {
- // Communal is closed.
- goToScene(CommunalScenes.Blank)
- whenever(
- notificationStackScrollLayoutController.isBelowLastNotification(
- any(),
- any(),
- )
- )
- .thenReturn(false)
- assertThat(underTest.onTouchEvent(DOWN_EVENT)).isFalse()
- }
+ kosmos.runTest {
+ // Communal is closed.
+ goToScene(CommunalScenes.Blank)
+ whenever(notificationStackScrollLayoutController.isBelowLastNotification(any(), any()))
+ .thenReturn(false)
+ assertThat(underTest.onTouchEvent(DOWN_EVENT)).isFalse()
}
@Test
fun fullScreenSwipeGesture_doNotProcessTouchesInUmo() =
- with(kosmos) {
- testScope.runTest {
- // Communal is closed.
- goToScene(CommunalScenes.Blank)
- whenever(keyguardMediaController.isWithinMediaViewBounds(any(), any()))
- .thenReturn(true)
- assertThat(underTest.onTouchEvent(DOWN_EVENT)).isFalse()
- }
+ kosmos.runTest {
+ // Communal is closed.
+ goToScene(CommunalScenes.Blank)
+ whenever(keyguardMediaController.isWithinMediaViewBounds(any(), any())).thenReturn(true)
+ assertThat(underTest.onTouchEvent(DOWN_EVENT)).isFalse()
}
@Test
fun fullScreenSwipeGesture_doNotProcessTouchesInSmartspace() =
- with(kosmos) {
- testScope.runTest {
- // Communal is closed.
- goToScene(CommunalScenes.Blank)
- whenever(lockscreenSmartspaceController.isWithinSmartspaceBounds(any(), any()))
- .thenReturn(true)
- assertThat(underTest.onTouchEvent(DOWN_EVENT)).isFalse()
- }
+ kosmos.runTest {
+ // Communal is closed.
+ goToScene(CommunalScenes.Blank)
+ whenever(lockscreenSmartspaceController.isWithinSmartspaceBounds(any(), any()))
+ .thenReturn(true)
+ assertThat(underTest.onTouchEvent(DOWN_EVENT)).isFalse()
}
@Test
fun onTouchEvent_hubOpen_touchesDispatched() =
- with(kosmos) {
- testScope.runTest {
- // Communal is open.
- goToScene(CommunalScenes.Communal)
-
- // Touch event is sent to the container view.
- assertThat(underTest.onTouchEvent(DOWN_EVENT)).isTrue()
- verify(containerView).onTouchEvent(DOWN_EVENT)
- assertThat(underTest.onTouchEvent(UP_EVENT)).isTrue()
- verify(containerView).onTouchEvent(UP_EVENT)
- }
+ kosmos.runTest {
+ // Communal is open.
+ goToScene(CommunalScenes.Communal)
+
+ // Touch event is sent to the container view.
+ assertThat(underTest.onTouchEvent(DOWN_EVENT)).isTrue()
+ verify(containerView).onTouchEvent(DOWN_EVENT)
+ assertThat(underTest.onTouchEvent(UP_EVENT)).isTrue()
+ verify(containerView).onTouchEvent(UP_EVENT)
}
@Test
fun onTouchEvent_editActivityShowing_touchesConsumedButNotDispatched() =
- with(kosmos) {
- testScope.runTest {
- // Communal is open.
- goToScene(CommunalScenes.Communal)
-
- // Transitioning to or from edit mode.
- communalInteractor.setEditActivityShowing(true)
- testableLooper.processAllMessages()
-
- // onTouchEvent returns true to consume the touch, but it is not sent to the
- // container view.
- assertThat(underTest.onTouchEvent(DOWN_EVENT)).isTrue()
- verify(containerView, never()).onTouchEvent(any())
- }
+ kosmos.runTest {
+ // Communal is open.
+ goToScene(CommunalScenes.Communal)
+
+ // Transitioning to or from edit mode.
+ communalInteractor.setEditActivityShowing(true)
+ testableLooper.processAllMessages()
+
+ // onTouchEvent returns true to consume the touch, but it is not sent to the
+ // container view.
+ assertThat(underTest.onTouchEvent(DOWN_EVENT)).isTrue()
+ verify(containerView, never()).onTouchEvent(any())
}
@Test
fun onTouchEvent_editModeTransitionStarted_touchesConsumedButNotDispatched() =
- with(kosmos) {
- testScope.runTest {
- // Communal is open.
- goToScene(CommunalScenes.Communal)
-
- // Leaving edit mode to return to the hub.
- fakeKeyguardTransitionRepository.sendTransitionStep(
- TransitionStep(
- from = KeyguardState.GONE,
- to = KeyguardState.GLANCEABLE_HUB,
- value = 1.0f,
- transitionState = TransitionState.RUNNING,
- )
+ kosmos.runTest {
+ // Communal is open.
+ goToScene(CommunalScenes.Communal)
+
+ // Leaving edit mode to return to the hub.
+ fakeKeyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.GONE,
+ to = KeyguardState.GLANCEABLE_HUB,
+ value = 1.0f,
+ transitionState = TransitionState.RUNNING,
)
- testableLooper.processAllMessages()
+ )
+ testableLooper.processAllMessages()
- // onTouchEvent returns true to consume the touch, but it is not sent to the
- // container view.
- assertThat(underTest.onTouchEvent(DOWN_EVENT)).isTrue()
- verify(containerView, never()).onTouchEvent(any())
- }
+ // onTouchEvent returns true to consume the touch, but it is not sent to the
+ // container view.
+ assertThat(underTest.onTouchEvent(DOWN_EVENT)).isTrue()
+ verify(containerView, never()).onTouchEvent(any())
}
@Test
fun onTouchEvent_shadeInteracting_movesNotDispatched() =
- with(kosmos) {
- testScope.runTest {
- `whenever`(communalViewModel.swipeToHubEnabled()).thenReturn(true)
- // On lockscreen.
- goToScene(CommunalScenes.Blank)
- whenever(
- notificationStackScrollLayoutController.isBelowLastNotification(
- any(),
- any(),
- )
- )
- .thenReturn(true)
+ kosmos.runTest {
+ swipeToHubEnabled.value = true
- // Touches not consumed by default but are received by containerView.
- assertThat(underTest.onTouchEvent(DOWN_EVENT)).isFalse()
- verify(containerView).onTouchEvent(DOWN_EVENT)
+ // On lockscreen.
+ goToScene(CommunalScenes.Blank)
+ whenever(notificationStackScrollLayoutController.isBelowLastNotification(any(), any()))
+ .thenReturn(true)
- // User is interacting with shade on lockscreen.
- shadeTestUtil.setLockscreenShadeTracking(true)
- testableLooper.processAllMessages()
+ // Touches not consumed by default but are received by containerView.
+ assertThat(underTest.onTouchEvent(DOWN_EVENT)).isFalse()
+ verify(containerView).onTouchEvent(DOWN_EVENT)
- // A move event is ignored while the user is already interacting.
- assertThat(underTest.onTouchEvent(MOVE_EVENT)).isFalse()
- verify(containerView, never()).onTouchEvent(MOVE_EVENT)
+ // User is interacting with shade on lockscreen.
+ shadeTestUtil.setLockscreenShadeTracking(true)
+ testableLooper.processAllMessages()
- // An up event is still delivered.
- assertThat(underTest.onTouchEvent(UP_EVENT)).isFalse()
- verify(containerView).onTouchEvent(UP_EVENT)
- }
+ // A move event is ignored while the user is already interacting.
+ assertThat(underTest.onTouchEvent(MOVE_EVENT)).isFalse()
+ verify(containerView, never()).onTouchEvent(MOVE_EVENT)
+
+ // An up event is still delivered.
+ assertThat(underTest.onTouchEvent(UP_EVENT)).isFalse()
+ verify(containerView).onTouchEvent(UP_EVENT)
}
@Test
fun onTouchEvent_shadeExpanding_touchesNotDispatched() =
- with(kosmos) {
- testScope.runTest {
- // On lockscreen.
- goToScene(CommunalScenes.Blank)
- whenever(
- notificationStackScrollLayoutController.isBelowLastNotification(
- any(),
- any(),
- )
- )
- .thenReturn(true)
+ kosmos.runTest {
+ // On lockscreen.
+ goToScene(CommunalScenes.Blank)
+ whenever(notificationStackScrollLayoutController.isBelowLastNotification(any(), any()))
+ .thenReturn(true)
- // Shade is open slightly.
- shadeTestUtil.setShadeExpansion(0.01f)
- testableLooper.processAllMessages()
+ // Shade is open slightly.
+ shadeTestUtil.setShadeExpansion(0.01f)
+ testableLooper.processAllMessages()
- // Touches are not consumed.
- assertThat(underTest.onTouchEvent(DOWN_EVENT)).isFalse()
- verify(containerView, never()).onTouchEvent(DOWN_EVENT)
- }
+ // Touches are not consumed.
+ assertThat(underTest.onTouchEvent(DOWN_EVENT)).isFalse()
+ verify(containerView, never()).onTouchEvent(DOWN_EVENT)
}
@Test
fun onTouchEvent_qsExpanding_touchesNotDispatched() =
- with(kosmos) {
- testScope.runTest {
- // On lockscreen.
- goToScene(CommunalScenes.Blank)
- whenever(
- notificationStackScrollLayoutController.isBelowLastNotification(
- any(),
- any(),
- )
- )
- .thenReturn(true)
+ kosmos.runTest {
+ // On lockscreen.
+ goToScene(CommunalScenes.Blank)
+ whenever(notificationStackScrollLayoutController.isBelowLastNotification(any(), any()))
+ .thenReturn(true)
- // Shade is open slightly.
- shadeTestUtil.setQsExpansion(0.01f)
- testableLooper.processAllMessages()
+ // Shade is open slightly.
+ shadeTestUtil.setQsExpansion(0.01f)
+ testableLooper.processAllMessages()
- // Touches are not consumed.
- assertThat(underTest.onTouchEvent(DOWN_EVENT)).isFalse()
- verify(containerView, never()).onTouchEvent(DOWN_EVENT)
- }
+ // Touches are not consumed.
+ assertThat(underTest.onTouchEvent(DOWN_EVENT)).isFalse()
+ verify(containerView, never()).onTouchEvent(DOWN_EVENT)
}
@Test
fun onTouchEvent_bouncerInteracting_movesNotDispatched() =
- with(kosmos) {
- testScope.runTest {
- `whenever`(communalViewModel.swipeToHubEnabled()).thenReturn(true)
- // On lockscreen.
- goToScene(CommunalScenes.Blank)
- whenever(
- notificationStackScrollLayoutController.isBelowLastNotification(
- any(),
- any(),
- )
- )
- .thenReturn(true)
+ kosmos.runTest {
+ swipeToHubEnabled.value = true
+ // On lockscreen.
+ goToScene(CommunalScenes.Blank)
+ whenever(notificationStackScrollLayoutController.isBelowLastNotification(any(), any()))
+ .thenReturn(true)
- // Touches not consumed by default but are received by containerView.
- assertThat(underTest.onTouchEvent(DOWN_EVENT)).isFalse()
- verify(containerView).onTouchEvent(DOWN_EVENT)
+ // Touches not consumed by default but are received by containerView.
+ assertThat(underTest.onTouchEvent(DOWN_EVENT)).isFalse()
+ verify(containerView).onTouchEvent(DOWN_EVENT)
- // User is interacting with bouncer on lockscreen.
- fakeKeyguardBouncerRepository.setPrimaryShow(true)
- testableLooper.processAllMessages()
+ // User is interacting with bouncer on lockscreen.
+ fakeKeyguardBouncerRepository.setPrimaryShow(true)
+ testableLooper.processAllMessages()
- // A move event is ignored while the user is already interacting.
- assertThat(underTest.onTouchEvent(MOVE_EVENT)).isFalse()
- verify(containerView, never()).onTouchEvent(MOVE_EVENT)
+ // A move event is ignored while the user is already interacting.
+ assertThat(underTest.onTouchEvent(MOVE_EVENT)).isFalse()
+ verify(containerView, never()).onTouchEvent(MOVE_EVENT)
- // An up event is still delivered.
- assertThat(underTest.onTouchEvent(UP_EVENT)).isFalse()
- verify(containerView).onTouchEvent(UP_EVENT)
- }
+ // An up event is still delivered.
+ assertThat(underTest.onTouchEvent(UP_EVENT)).isFalse()
+ verify(containerView).onTouchEvent(UP_EVENT)
}
@EnableFlags(FLAG_GLANCEABLE_HUB_V2)
@Test
fun onTouchEvent_onLockscreenAndGlanceableHubV2_touchIgnored() =
- with(kosmos) {
- testScope.runTest {
- kosmos.setCommunalV2ConfigEnabled(true)
+ kosmos.runTest {
+ swipeToHubEnabled.value = false
+ setCommunalV2ConfigEnabled(true)
- // On lockscreen.
- goToScene(CommunalScenes.Blank)
+ // On lockscreen.
+ goToScene(CommunalScenes.Blank)
- assertThat(underTest.onTouchEvent(DOWN_EVENT)).isFalse()
- verify(containerView, never()).onTouchEvent(DOWN_EVENT)
- }
+ assertThat(underTest.onTouchEvent(DOWN_EVENT)).isFalse()
+ verify(containerView, never()).onTouchEvent(DOWN_EVENT)
}
@Test
- fun disposeView_destroysTouchMonitor() {
- clearInvocations(touchMonitor)
+ fun disposeView_destroysTouchMonitor() =
+ kosmos.runTest {
+ clearInvocations(touchMonitor)
- underTest.disposeView()
+ underTest.disposeView()
- verify(touchMonitor).destroy()
- }
+ verify(touchMonitor).destroy()
+ }
- private fun initAndAttachContainerView() {
+ private fun Kosmos.initAndAttachContainerView() {
val mockInsets =
mock<WindowInsets> {
on { getInsets(WindowInsets.Type.systemGestures()) } doReturn FAKE_INSETS
@@ -802,8 +707,8 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() {
testableLooper.processAllMessages()
}
- private suspend fun goToScene(scene: SceneKey) {
- communalRepository.changeScene(scene)
+ private suspend fun Kosmos.goToScene(scene: SceneKey) {
+ fakeCommunalSceneRepository.changeScene(scene)
if (scene == CommunalScenes.Communal) {
kosmos.fakeKeyguardTransitionRepository.sendTransitionSteps(
from = KeyguardState.LOCKSCREEN,
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 fcbf0fe9a37a..510d6fe2b776 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
@@ -17,19 +17,23 @@
package com.android.systemui.statusbar.featurepods.popups.ui.viewmodel
import android.platform.test.annotations.EnableFlags
+import androidx.compose.runtime.snapshots.Snapshot
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.runTest
import com.android.systemui.kosmos.testScope
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
+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.shared.model.PopupChipId
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
@@ -37,22 +41,41 @@ import org.junit.runner.RunWith
@EnableFlags(StatusBarPopupChips.FLAG_NAME)
@RunWith(AndroidJUnit4::class)
class StatusBarPopupChipsViewModelTest : SysuiTestCase() {
- private val kosmos = testKosmos()
- private val testScope = kosmos.testScope
- private val mediaFilterRepository = kosmos.mediaFilterRepository
- private val underTest = kosmos.statusBarPopupChipsViewModel
+ private val kosmos = testKosmos().useUnconfinedTestDispatcher()
+ private val underTest = kosmos.statusBarPopupChipsViewModelFactory.create()
+
+ @Before
+ fun setUp() {
+ underTest.activateIn(kosmos.testScope)
+ }
@Test
fun shownPopupChips_allHidden_empty() =
- testScope.runTest {
- val shownPopupChips by collectLastValue(underTest.shownPopupChips)
+ kosmos.runTest {
+ val shownPopupChips = underTest.shownPopupChips
assertThat(shownPopupChips).isEmpty()
}
@Test
fun shownPopupChips_activeMedia_restHidden_mediaControlChipShown() =
- testScope.runTest {
- val shownPopupChips by collectLastValue(underTest.shownPopupChips)
+ 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))
+
+ Snapshot.takeSnapshot {
+ assertThat(shownPopupChips).hasSize(1)
+ assertThat(shownPopupChips.first().chipId).isEqualTo(PopupChipId.MediaControl)
+ }
+ }
+
+ @Test
+ fun shownPopupChips_mediaChipToggled_popupShown() =
+ kosmos.runTest {
+ val shownPopupChips = underTest.shownPopupChips
val userMedia = MediaData(active = true, song = "test")
val instanceId = userMedia.instanceId
@@ -60,7 +83,13 @@ class StatusBarPopupChipsViewModelTest : SysuiTestCase() {
mediaFilterRepository.addSelectedUserMediaEntry(userMedia)
mediaFilterRepository.addMediaDataLoadingState(MediaDataLoadingModel.Loaded(instanceId))
- assertThat(shownPopupChips).hasSize(1)
- assertThat(shownPopupChips!!.first().chipId).isEqualTo(PopupChipId.MediaControl)
+ Snapshot.takeSnapshot {
+ assertThat(shownPopupChips).hasSize(1)
+ val mediaChip = shownPopupChips.first()
+ assertThat(mediaChip.isPopupShown).isFalse()
+
+ mediaChip.showPopup.invoke()
+ assertThat(shownPopupChips.first().isPopupShown).isTrue()
+ }
}
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/featurepods/popups/ui/viewmodel/StatusBarPopupChipsViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/featurepods/popups/ui/viewmodel/StatusBarPopupChipsViewModelKosmos.kt
index 93502f365202..b876095fefe5 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/featurepods/popups/ui/viewmodel/StatusBarPopupChipsViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/featurepods/popups/ui/viewmodel/StatusBarPopupChipsViewModelKosmos.kt
@@ -17,13 +17,14 @@
package com.android.systemui.statusbar.featurepods.popups.ui.viewmodel
import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.kosmos.testScope
import com.android.systemui.statusbar.featurepods.media.ui.viewmodel.mediaControlChipViewModel
-val Kosmos.statusBarPopupChipsViewModel: StatusBarPopupChipsViewModel by
+private val Kosmos.statusBarPopupChipsViewModel: StatusBarPopupChipsViewModel by
+ Kosmos.Fixture { StatusBarPopupChipsViewModel(mediaControlChip = mediaControlChipViewModel) }
+
+val Kosmos.statusBarPopupChipsViewModelFactory by
Kosmos.Fixture {
- StatusBarPopupChipsViewModel(
- testScope.backgroundScope,
- mediaControlChipViewModel = mediaControlChipViewModel,
- )
+ object : StatusBarPopupChipsViewModel.Factory {
+ override fun create(): StatusBarPopupChipsViewModel = statusBarPopupChipsViewModel
+ }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelKosmos.kt
index fbada934c9d4..a97c651ba426 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelKosmos.kt
@@ -29,7 +29,7 @@ import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.statusbar.chips.sharetoapp.ui.viewmodel.shareToAppChipViewModel
import com.android.systemui.statusbar.chips.ui.viewmodel.ongoingActivityChipsViewModel
import com.android.systemui.statusbar.events.domain.interactor.systemStatusEventAnimationInteractor
-import com.android.systemui.statusbar.featurepods.popups.ui.viewmodel.statusBarPopupChipsViewModel
+import com.android.systemui.statusbar.featurepods.popups.ui.viewmodel.statusBarPopupChipsViewModelFactory
import com.android.systemui.statusbar.layout.ui.viewmodel.multiDisplayStatusBarContentInsetsViewModelStore
import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor
import com.android.systemui.statusbar.notification.stack.domain.interactor.headsUpNotificationInteractor
@@ -59,7 +59,7 @@ var Kosmos.homeStatusBarViewModel: HomeStatusBarViewModel by
shadeInteractor,
shareToAppChipViewModel,
ongoingActivityChipsViewModel,
- statusBarPopupChipsViewModel,
+ statusBarPopupChipsViewModelFactory,
systemStatusEventAnimationInteractor,
multiDisplayStatusBarContentInsetsViewModelStore,
backgroundScope,
diff --git a/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java b/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java
index 700a1624f7d4..d8e10f842665 100644
--- a/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java
+++ b/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java
@@ -38,7 +38,6 @@ import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.app.ActivityOptions;
import android.app.AppOpsManager;
-import android.app.admin.DevicePolicyManagerInternal;
import android.app.assist.AssistContent;
import android.app.assist.AssistStructure;
import android.app.contextualsearch.CallbackToken;
@@ -67,6 +66,7 @@ import android.os.ResultReceiver;
import android.os.ServiceManager;
import android.os.ShellCallback;
import android.os.SystemClock;
+import android.os.UserManager;
import android.provider.Settings;
import android.util.Log;
import android.util.Slog;
@@ -112,8 +112,8 @@ public class ContextualSearchManagerService extends SystemService {
private final ActivityTaskManagerInternal mAtmInternal;
private final PackageManagerInternal mPackageManager;
private final WindowManagerInternal mWmInternal;
- private final DevicePolicyManagerInternal mDpmInternal;
private final AudioManager mAudioManager;
+ private final UserManager mUserManager;
private final Object mLock = new Object();
private final AssistDataRequester mAssistDataRequester;
@@ -179,9 +179,9 @@ public class ContextualSearchManagerService extends SystemService {
LocalServices.getService(ActivityTaskManagerInternal.class));
mPackageManager = LocalServices.getService(PackageManagerInternal.class);
mAudioManager = context.getSystemService(AudioManager.class);
+ mUserManager = context.getSystemService(UserManager.class);
mWmInternal = Objects.requireNonNull(LocalServices.getService(WindowManagerInternal.class));
- mDpmInternal = LocalServices.getService(DevicePolicyManagerInternal.class);
mAssistDataRequester = new AssistDataRequester(
mContext,
IWindowManager.Stub.asInterface(ServiceManager.getService(Context.WINDOW_SERVICE)),
@@ -308,6 +308,11 @@ public class ContextualSearchManagerService extends SystemService {
}
}
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.MANAGE_USERS,
+ android.Manifest.permission.CREATE_USERS,
+ android.Manifest.permission.QUERY_USERS
+ })
private Intent getContextualSearchIntent(int entrypoint, int userId, CallbackToken mToken) {
final Intent launchIntent = getResolvedLaunchIntent(userId);
if (launchIntent == null) {
@@ -338,8 +343,7 @@ public class ContextualSearchManagerService extends SystemService {
visiblePackageNames.add(record.getComponentName().getPackageName());
activityTokens.add(record.getActivityToken());
}
- if (mDpmInternal != null
- && mDpmInternal.isUserOrganizationManaged(record.getUserId())) {
+ if (mUserManager.isManagedProfile(record.getUserId())) {
isManagedProfileVisible = true;
}
}
@@ -507,7 +511,10 @@ public class ContextualSearchManagerService extends SystemService {
Intent launchIntent = getContextualSearchIntent(entrypoint, callingUserId, mToken);
if (launchIntent != null) {
int result = invokeContextualSearchIntent(launchIntent, callingUserId);
- if (DEBUG) Log.d(TAG, "Launch result: " + result);
+ if (DEBUG) {
+ Log.d(TAG, "Launch intent: " + launchIntent);
+ Log.d(TAG, "Launch result: " + result);
+ }
}
});
}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 057d1274d47d..813e661d6970 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -586,6 +586,9 @@ public class AudioService extends IAudioService.Stub
// protects mRingerMode
private final Object mSettingsLock = new Object();
+ // protects VolumeStreamState / VolumeGroupState operations
+ private final Object mVolumeStateLock = new Object();
+
/** Maximum volume index values for audio streams */
protected static int[] MAX_STREAM_VOLUME = new int[] {
5, // STREAM_VOICE_CALL
@@ -1612,7 +1615,7 @@ public class AudioService extends IAudioService.Stub
private void initVolumeStreamStates() {
int numStreamTypes = AudioSystem.getNumStreamTypes();
- synchronized (VolumeStreamState.class) {
+ synchronized (mVolumeStateLock) {
for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
final VolumeStreamState streamState = getVssForStream(streamType);
if (streamState == null) {
@@ -2420,7 +2423,7 @@ public class AudioService extends IAudioService.Stub
private void checkAllAliasStreamVolumes() {
synchronized (mSettingsLock) {
- synchronized (VolumeStreamState.class) {
+ synchronized (mVolumeStateLock) {
int numStreamTypes = AudioSystem.getNumStreamTypes();
for (int streamType = 0; streamType < numStreamTypes; streamType++) {
int streamAlias = sStreamVolumeAlias.get(streamType, /*valueIfKeyNotFound=*/-1);
@@ -2502,7 +2505,7 @@ public class AudioService extends IAudioService.Stub
private void onUpdateVolumeStatesForAudioDevice(int device, String caller) {
final int numStreamTypes = AudioSystem.getNumStreamTypes();
synchronized (mSettingsLock) {
- synchronized (VolumeStreamState.class) {
+ synchronized (mVolumeStateLock) {
for (int streamType = 0; streamType < numStreamTypes; streamType++) {
updateVolumeStates(device, streamType, caller);
}
@@ -2771,7 +2774,7 @@ public class AudioService extends IAudioService.Stub
updateDefaultVolumes();
synchronized (mSettingsLock) {
- synchronized (VolumeStreamState.class) {
+ synchronized (mVolumeStateLock) {
getVssForStreamOrDefault(AudioSystem.STREAM_DTMF)
.setAllIndexes(getVssForStreamOrDefault(dtmfStreamAlias), caller);
getVssForStreamOrDefault(AudioSystem.STREAM_ACCESSIBILITY).setSettingName(
@@ -3233,8 +3236,10 @@ public class AudioService extends IAudioService.Stub
// Each stream will read its own persisted settings
// Broadcast the sticky intents
- broadcastRingerMode(AudioManager.RINGER_MODE_CHANGED_ACTION, mRingerModeExternal);
- broadcastRingerMode(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION, mRingerMode);
+ synchronized (mSettingsLock) {
+ broadcastRingerMode(AudioManager.RINGER_MODE_CHANGED_ACTION, mRingerModeExternal);
+ broadcastRingerMode(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION, mRingerMode);
+ }
// Broadcast vibrate settings
broadcastVibrateSetting(AudioManager.VIBRATE_TYPE_RINGER);
@@ -4236,7 +4241,7 @@ public class AudioService extends IAudioService.Stub
private void muteAliasStreams(int streamAlias, boolean state) {
// Locking mSettingsLock to avoid inversion when calling doMute -> updateVolumeGroupIndex
synchronized (mSettingsLock) {
- synchronized (VolumeStreamState.class) {
+ synchronized (mVolumeStateLock) {
List<Integer> streamsToMute = new ArrayList<>();
for (int streamIdx = 0; streamIdx < mStreamStates.size(); streamIdx++) {
final VolumeStreamState vss = mStreamStates.valueAt(streamIdx);
@@ -4280,7 +4285,7 @@ public class AudioService extends IAudioService.Stub
// Locking mSettingsLock to avoid inversion when calling vss.mute -> vss.doMute ->
// vss.updateVolumeGroupIndex
synchronized (mSettingsLock) {
- synchronized (VolumeStreamState.class) {
+ synchronized (mVolumeStateLock) {
final VolumeStreamState streamState = getVssForStreamOrDefault(streamAlias);
// if unmuting causes a change, it was muted
wasMuted = streamState.mute(false, "onUnmuteStreamOnSingleVolDevice");
@@ -4456,7 +4461,7 @@ public class AudioService extends IAudioService.Stub
/** @see AudioManager#getVolumeGroupVolumeIndex(int) */
public int getVolumeGroupVolumeIndex(int groupId) {
super.getVolumeGroupVolumeIndex_enforcePermission();
- synchronized (VolumeStreamState.class) {
+ synchronized (mVolumeStateLock) {
if (sVolumeGroupStates.indexOfKey(groupId) < 0) {
Log.e(TAG, "No volume group for id " + groupId);
return 0;
@@ -4473,7 +4478,7 @@ public class AudioService extends IAudioService.Stub
MODIFY_AUDIO_SETTINGS_PRIVILEGED, MODIFY_AUDIO_ROUTING })
public int getVolumeGroupMaxVolumeIndex(int groupId) {
super.getVolumeGroupMaxVolumeIndex_enforcePermission();
- synchronized (VolumeStreamState.class) {
+ synchronized (mVolumeStateLock) {
if (sVolumeGroupStates.indexOfKey(groupId) < 0) {
Log.e(TAG, "No volume group for id " + groupId);
return 0;
@@ -4488,7 +4493,7 @@ public class AudioService extends IAudioService.Stub
MODIFY_AUDIO_SETTINGS_PRIVILEGED, MODIFY_AUDIO_ROUTING })
public int getVolumeGroupMinVolumeIndex(int groupId) {
super.getVolumeGroupMinVolumeIndex_enforcePermission();
- synchronized (VolumeStreamState.class) {
+ synchronized (mVolumeStateLock) {
if (sVolumeGroupStates.indexOfKey(groupId) < 0) {
Log.e(TAG, "No volume group for id " + groupId);
return 0;
@@ -4633,7 +4638,7 @@ public class AudioService extends IAudioService.Stub
@android.annotation.EnforcePermission(QUERY_AUDIO_STATE)
public int getLastAudibleVolumeForVolumeGroup(int groupId) {
super.getLastAudibleVolumeForVolumeGroup_enforcePermission();
- synchronized (VolumeStreamState.class) {
+ synchronized (mVolumeStateLock) {
if (sVolumeGroupStates.indexOfKey(groupId) < 0) {
Log.e(TAG, ": no volume group found for id " + groupId);
return 0;
@@ -4645,7 +4650,7 @@ public class AudioService extends IAudioService.Stub
/** @see AudioManager#isVolumeGroupMuted(int) */
public boolean isVolumeGroupMuted(int groupId) {
- synchronized (VolumeStreamState.class) {
+ synchronized (mVolumeStateLock) {
if (sVolumeGroupStates.indexOfKey(groupId) < 0) {
Log.e(TAG, ": no volume group found for id " + groupId);
return false;
@@ -5473,7 +5478,7 @@ public class AudioService extends IAudioService.Stub
}
streamType = replaceBtScoStreamWithVoiceCall(streamType, "isStreamMute");
- synchronized (VolumeStreamState.class) {
+ synchronized (mVolumeStateLock) {
ensureValidStreamType(streamType);
return getVssForStreamOrDefault(streamType).mIsMuted;
}
@@ -5654,7 +5659,7 @@ public class AudioService extends IAudioService.Stub
}
private int getStreamVolume(int streamType, int device) {
- synchronized (VolumeStreamState.class) {
+ synchronized (mVolumeStateLock) {
final VolumeStreamState vss = getVssForStreamOrDefault(streamType);
int index = vss.getIndex(device);
@@ -5698,7 +5703,7 @@ public class AudioService extends IAudioService.Stub
vib.setMinVolumeIndex((vss.mIndexMin + 5) / 10);
vib.setMaxVolumeIndex((vss.mIndexMax + 5) / 10);
- synchronized (VolumeStreamState.class) {
+ synchronized (mVolumeStateLock) {
final int index;
if (isFixedVolumeDevice(ada.getInternalType())) {
index = (vss.mIndexMax + 5) / 10;
@@ -6266,7 +6271,7 @@ public class AudioService extends IAudioService.Stub
// ring and notifications volume should never be 0 when not silenced
if (sStreamVolumeAlias.get(streamType) == AudioSystem.STREAM_RING
|| sStreamVolumeAlias.get(streamType) == AudioSystem.STREAM_NOTIFICATION) {
- synchronized (VolumeStreamState.class) {
+ synchronized (mVolumeStateLock) {
for (int i = 0; i < vss.mIndexMap.size(); i++) {
int device = vss.mIndexMap.keyAt(i);
int value = vss.mIndexMap.valueAt(i);
@@ -7026,7 +7031,7 @@ public class AudioService extends IAudioService.Stub
}
streamState.readSettings();
- synchronized (VolumeStreamState.class) {
+ synchronized (mVolumeStateLock) {
// unmute stream that was muted but is not affect by mute anymore
if (streamState.mIsMuted && ((!isStreamAffectedByMute(streamType) &&
!isStreamMutedByRingerOrZenMode(streamType)) || mUseFixedVolume)) {
@@ -8059,14 +8064,14 @@ public class AudioService extends IAudioService.Stub
public Set<Integer> getDeviceSetForStream(int stream) {
stream = replaceBtScoStreamWithVoiceCall(stream, "getDeviceSetForStream");
ensureValidStreamType(stream);
- synchronized (VolumeStreamState.class) {
+ synchronized (mVolumeStateLock) {
return getVssForStreamOrDefault(stream).observeDevicesForStream_syncVSS(true);
}
}
private void onObserveDevicesForAllStreams(int skipStream) {
synchronized (mSettingsLock) {
- synchronized (VolumeStreamState.class) {
+ synchronized (mVolumeStateLock) {
for (int stream = 0; stream < mStreamStates.size(); stream++) {
final VolumeStreamState vss = mStreamStates.valueAt(stream);
if (vss != null && vss.getStreamType() != skipStream) {
@@ -8648,7 +8653,7 @@ public class AudioService extends IAudioService.Stub
private void readVolumeGroupsSettings(boolean userSwitch) {
synchronized (mSettingsLock) {
- synchronized (VolumeStreamState.class) {
+ synchronized (mVolumeStateLock) {
if (DEBUG_VOL) {
Log.d(TAG, "readVolumeGroupsSettings userSwitch=" + userSwitch);
}
@@ -8706,7 +8711,7 @@ public class AudioService extends IAudioService.Stub
// NOTE: Locking order for synchronized objects related to volume management:
// 1 mSettingsLock
- // 2 VolumeStreamState.class
+ // 2 mVolumeStateLock
private class VolumeGroupState {
private final AudioVolumeGroup mAudioVolumeGroup;
private final SparseIntArray mIndexMap = new SparseIntArray(8);
@@ -8800,7 +8805,7 @@ public class AudioService extends IAudioService.Stub
* Mute/unmute the volume group
* @param muted the new mute state
*/
- @GuardedBy("AudioService.VolumeStreamState.class")
+ @GuardedBy("AudioService.this.mVolumeStateLock")
public boolean mute(boolean muted) {
if (!isMutable()) {
// Non mutable volume group
@@ -8824,7 +8829,7 @@ public class AudioService extends IAudioService.Stub
public void adjustVolume(int direction, int flags) {
synchronized (mSettingsLock) {
- synchronized (AudioService.VolumeStreamState.class) {
+ synchronized (mVolumeStateLock) {
int device = getDeviceForVolume();
int previousIndex = getIndex(device);
if (isMuteAdjust(direction) && !isMutable()) {
@@ -8878,14 +8883,14 @@ public class AudioService extends IAudioService.Stub
}
public int getVolumeIndex() {
- synchronized (AudioService.VolumeStreamState.class) {
+ synchronized (mVolumeStateLock) {
return getIndex(getDeviceForVolume());
}
}
public void setVolumeIndex(int index, int flags) {
synchronized (mSettingsLock) {
- synchronized (AudioService.VolumeStreamState.class) {
+ synchronized (mVolumeStateLock) {
if (mUseFixedVolume) {
return;
}
@@ -8894,7 +8899,7 @@ public class AudioService extends IAudioService.Stub
}
}
- @GuardedBy("AudioService.VolumeStreamState.class")
+ @GuardedBy("AudioService.this.mVolumeStateLock")
private void setVolumeIndex(int index, int device, int flags) {
// Update cache & persist (muted by volume 0 shall be persisted)
updateVolumeIndex(index, device);
@@ -8907,7 +8912,7 @@ public class AudioService extends IAudioService.Stub
}
}
- @GuardedBy("AudioService.VolumeStreamState.class")
+ @GuardedBy("AudioService.this.mVolumeStateLock")
public void updateVolumeIndex(int index, int device) {
// Filter persistency if already exist and the index has not changed
if (mIndexMap.indexOfKey(device) < 0 || mIndexMap.get(device) != index) {
@@ -8925,7 +8930,7 @@ public class AudioService extends IAudioService.Stub
}
}
- @GuardedBy("AudioService.VolumeStreamState.class")
+ @GuardedBy("AudioService.this.mVolumeStateLock")
private void setVolumeIndexInt(int index, int device, int flags) {
// Reflect mute state of corresponding stream by forcing index to 0 if muted
// Only set audio policy BT SCO stream volume to 0 when the stream is actually muted.
@@ -8958,14 +8963,14 @@ public class AudioService extends IAudioService.Stub
mAudioSystem.setVolumeIndexForAttributes(mAudioAttributes, index, muted, device);
}
- @GuardedBy("AudioService.VolumeStreamState.class")
+ @GuardedBy("AudioService.this.mVolumeStateLock")
private int getIndex(int device) {
int index = mIndexMap.get(device, -1);
// there is always an entry for AudioSystem.DEVICE_OUT_DEFAULT
return (index != -1) ? index : mIndexMap.get(AudioSystem.DEVICE_OUT_DEFAULT);
}
- @GuardedBy("AudioService.VolumeStreamState.class")
+ @GuardedBy("AudioService.this.mVolumeStateLock")
private boolean hasIndexForDevice(int device) {
return (mIndexMap.get(device, -1) != -1);
}
@@ -8992,7 +8997,7 @@ public class AudioService extends IAudioService.Stub
public void applyAllVolumes(boolean userSwitch) {
String caller = "from vgs";
- synchronized (AudioService.VolumeStreamState.class) {
+ synchronized (mVolumeStateLock) {
// apply device specific volumes first
for (int i = 0; i < mIndexMap.size(); i++) {
int device = mIndexMap.keyAt(i);
@@ -9095,25 +9100,27 @@ public class AudioService extends IAudioService.Stub
if (mUseFixedVolume || mHasValidStreamType) {
return;
}
- if (DEBUG_VOL) {
- Log.v(TAG, "persistVolumeGroup: storing index " + getIndex(device) + " for group "
- + mAudioVolumeGroup.name()
- + ", device " + AudioSystem.getOutputDeviceName(device)
- + " and User=" + getCurrentUserId()
- + " mSettingName: " + mSettingName);
- }
+ synchronized (mVolumeStateLock) {
+ if (DEBUG_VOL) {
+ Log.v(TAG, "persistVolumeGroup: storing index " + getIndex(device)
+ + " for group " + mAudioVolumeGroup.name()
+ + ", device " + AudioSystem.getOutputDeviceName(device)
+ + " and User=" + getCurrentUserId()
+ + " mSettingName: " + mSettingName);
+ }
- boolean success = mSettings.putSystemIntForUser(mContentResolver,
- getSettingNameForDevice(device),
- getIndex(device),
- getVolumePersistenceUserId());
- if (!success) {
- Log.e(TAG, "persistVolumeGroup failed for group " + mAudioVolumeGroup.name());
+ boolean success = mSettings.putSystemIntForUser(mContentResolver,
+ getSettingNameForDevice(device),
+ getIndex(device),
+ getVolumePersistenceUserId());
+ if (!success) {
+ Log.e(TAG, "persistVolumeGroup failed for group " + mAudioVolumeGroup.name());
+ }
}
}
public void readSettings() {
- synchronized (AudioService.VolumeStreamState.class) {
+ synchronized (mVolumeStateLock) {
// force maximum volume on all streams if fixed volume property is set
if (mUseFixedVolume) {
mIndexMap.put(AudioSystem.DEVICE_OUT_DEFAULT, mIndexMax);
@@ -9147,7 +9154,7 @@ public class AudioService extends IAudioService.Stub
}
}
- @GuardedBy("AudioService.VolumeStreamState.class")
+ @GuardedBy("AudioService.this.mVolumeStateLock")
private int getValidIndex(int index) {
if (index < mIndexMin) {
return mIndexMin;
@@ -9221,7 +9228,7 @@ public class AudioService extends IAudioService.Stub
// 1 mScoclient OR mSafeMediaVolumeState
// 2 mSetModeLock
// 3 mSettingsLock
- // 4 VolumeStreamState.class
+ // 4 mVolumeStateLock
/*package*/ class VolumeStreamState {
private final int mStreamType;
private VolumeGroupState mVolumeGroupState = null;
@@ -9430,7 +9437,7 @@ public class AudioService extends IAudioService.Stub
*
* This is a reference to the local list, do not modify.
*/
- @GuardedBy("VolumeStreamState.class")
+ @GuardedBy("mVolumeStateLock")
@NonNull
public Set<Integer> observeDevicesForStream_syncVSS(
boolean checkOthers) {
@@ -9498,7 +9505,7 @@ public class AudioService extends IAudioService.Stub
public void readSettings() {
synchronized (mSettingsLock) {
- synchronized (VolumeStreamState.class) {
+ synchronized (mVolumeStateLock) {
// force maximum volume on all streams if fixed volume property is set
if (mUseFixedVolume) {
mIndexMap.put(AudioSystem.DEVICE_OUT_DEFAULT, mIndexMax);
@@ -9518,7 +9525,7 @@ public class AudioService extends IAudioService.Stub
}
}
}
- synchronized (VolumeStreamState.class) {
+ synchronized (mVolumeStateLock) {
for (int device : AudioSystem.DEVICE_OUT_ALL_SET) {
// retrieve current volume for device
@@ -9551,7 +9558,7 @@ public class AudioService extends IAudioService.Stub
* will send the non-zero index together with muted state. Otherwise, index 0 will be sent
* to native for signalising a muted stream.
**/
- @GuardedBy("VolumeStreamState.class")
+ @GuardedBy("mVolumeStateLock")
private void setStreamVolumeIndex(int index, int device) {
// Only set audio policy BT SCO stream volume to 0 when the stream is actually muted.
// This allows RX path muting by the audio HAL only when explicitly muted but not when
@@ -9573,8 +9580,8 @@ public class AudioService extends IAudioService.Stub
mAudioSystem.setStreamVolumeIndexAS(mStreamType, index, muted, device);
}
- // must be called while synchronized VolumeStreamState.class
- @GuardedBy("VolumeStreamState.class")
+ // must be called while synchronized mVolumeStateLock
+ @GuardedBy("mVolumeStateLock")
/*package*/ void applyDeviceVolume_syncVSS(int device) {
int index;
if (isFullyMuted() && !ringMyCar()) {
@@ -9600,7 +9607,7 @@ public class AudioService extends IAudioService.Stub
}
public void applyAllVolumes() {
- synchronized (VolumeStreamState.class) {
+ synchronized (mVolumeStateLock) {
// apply device specific volumes first
int index;
boolean isAbsoluteVolume = false;
@@ -9662,7 +9669,7 @@ public class AudioService extends IAudioService.Stub
final boolean isCurrentDevice;
final StringBuilder aliasStreamIndexes = new StringBuilder();
synchronized (mSettingsLock) {
- synchronized (VolumeStreamState.class) {
+ synchronized (mVolumeStateLock) {
oldIndex = getIndex(device);
index = getValidIndex(index, hasModifyAudioSettings);
// for STREAM_SYSTEM_ENFORCED, do not sync aliased streams on the enforced index
@@ -9780,7 +9787,7 @@ public class AudioService extends IAudioService.Stub
}
public int getIndex(int device) {
- synchronized (VolumeStreamState.class) {
+ synchronized (mVolumeStateLock) {
int index = mIndexMap.get(device, -1);
if (index == -1) {
// there is always an entry for AudioSystem.DEVICE_OUT_DEFAULT
@@ -9791,7 +9798,7 @@ public class AudioService extends IAudioService.Stub
}
public @NonNull VolumeInfo getVolumeInfo(int device) {
- synchronized (VolumeStreamState.class) {
+ synchronized (mVolumeStateLock) {
int index = mIndexMap.get(device, -1);
if (index == -1) {
// there is always an entry for AudioSystem.DEVICE_OUT_DEFAULT
@@ -9808,7 +9815,7 @@ public class AudioService extends IAudioService.Stub
}
public boolean hasIndexForDevice(int device) {
- synchronized (VolumeStreamState.class) {
+ synchronized (mVolumeStateLock) {
return (mIndexMap.get(device, -1) != -1);
}
}
@@ -9840,8 +9847,8 @@ public class AudioService extends IAudioService.Stub
* @param srcStream
* @param caller
*/
- // must be sync'd on mSettingsLock before VolumeStreamState.class
- @GuardedBy("VolumeStreamState.class")
+ // must be sync'd on mSettingsLock before mVolumeStateLock
+ @GuardedBy("mVolumeStateLock")
public void setAllIndexes(VolumeStreamState srcStream, String caller) {
if (srcStream == null || mStreamType == srcStream.mStreamType) {
return;
@@ -9865,8 +9872,8 @@ public class AudioService extends IAudioService.Stub
}
}
- // must be sync'd on mSettingsLock before VolumeStreamState.class
- @GuardedBy("VolumeStreamState.class")
+ // must be sync'd on mSettingsLock before mVolumeStateLock
+ @GuardedBy("mVolumeStateLock")
public void setAllIndexesToMax() {
for (int i = 0; i < mIndexMap.size(); i++) {
mIndexMap.put(mIndexMap.keyAt(i), mIndexMax);
@@ -9879,7 +9886,7 @@ public class AudioService extends IAudioService.Stub
// vss.setIndex which grabs this lock after VSS.class. Locking order needs to be
// preserved
synchronized (mSettingsLock) {
- synchronized (VolumeStreamState.class) {
+ synchronized (mVolumeStateLock) {
if (mVolumeGroupState != null) {
int groupIndex = (getIndex(device) + 5) / 10;
if (DEBUG_VOL) {
@@ -9911,7 +9918,7 @@ public class AudioService extends IAudioService.Stub
*/
public boolean mute(boolean state, String source) {
boolean changed = false;
- synchronized (VolumeStreamState.class) {
+ synchronized (mVolumeStateLock) {
changed = mute(state, true, source);
}
if (changed) {
@@ -9927,7 +9934,7 @@ public class AudioService extends IAudioService.Stub
*/
public boolean muteInternally(boolean state) {
boolean changed = false;
- synchronized (VolumeStreamState.class) {
+ synchronized (mVolumeStateLock) {
if (state != mIsMutedInternally) {
changed = true;
mIsMutedInternally = state;
@@ -9942,7 +9949,7 @@ public class AudioService extends IAudioService.Stub
return changed;
}
- @GuardedBy("VolumeStreamState.class")
+ @GuardedBy("mVolumeStateLock")
public boolean isFullyMuted() {
return mIsMuted || mIsMutedInternally;
}
@@ -9963,7 +9970,7 @@ public class AudioService extends IAudioService.Stub
*/
public boolean mute(boolean state, boolean apply, String src) {
boolean changed;
- synchronized (VolumeStreamState.class) {
+ synchronized (mVolumeStateLock) {
changed = state != mIsMuted;
if (changed) {
sMuteLogger.enqueue(
@@ -9997,7 +10004,7 @@ public class AudioService extends IAudioService.Stub
}
public void doMute() {
- synchronized (VolumeStreamState.class) {
+ synchronized (mVolumeStateLock) {
// If associated to volume group, update group cache
updateVolumeGroupIndex(getDeviceForStream(mStreamType), /* forceMuteState= */true);
@@ -10018,7 +10025,7 @@ public class AudioService extends IAudioService.Stub
}
public void checkFixedVolumeDevices() {
- synchronized (VolumeStreamState.class) {
+ synchronized (mVolumeStateLock) {
// ignore settings for fixed volume devices: volume should always be at max or 0
if (sStreamVolumeAlias.get(mStreamType) == AudioSystem.STREAM_MUSIC) {
for (int i = 0; i < mIndexMap.size(); i++) {
@@ -10189,8 +10196,7 @@ public class AudioService extends IAudioService.Stub
}
/*package*/ void setDeviceVolume(VolumeStreamState streamState, int device) {
-
- synchronized (VolumeStreamState.class) {
+ synchronized (mVolumeStateLock) {
sendMsg(mAudioHandler, SoundDoseHelper.MSG_CSD_UPDATE_ATTENUATION, SENDMSG_QUEUE,
device, (isAbsoluteVolumeDevice(device) || isA2dpAbsoluteVolumeDevice(device)
|| AudioSystem.isLeAudioDeviceType(device) ? 1 : 0),
@@ -12194,7 +12200,7 @@ public class AudioService extends IAudioService.Stub
mCameraSoundForced = cameraSoundForced;
if (cameraSoundForcedChanged) {
if (!mIsSingleVolume) {
- synchronized (VolumeStreamState.class) {
+ synchronized (mVolumeStateLock) {
final VolumeStreamState s = getVssForStreamOrDefault(
AudioSystem.STREAM_SYSTEM_ENFORCED);
if (cameraSoundForced) {
diff --git a/services/core/java/com/android/server/notification/NotificationRecordLogger.java b/services/core/java/com/android/server/notification/NotificationRecordLogger.java
index 6c0035b82a86..f680ce722e81 100644
--- a/services/core/java/com/android/server/notification/NotificationRecordLogger.java
+++ b/services/core/java/com/android/server/notification/NotificationRecordLogger.java
@@ -514,11 +514,14 @@ interface NotificationRecordLogger {
final int fsi_state;
final boolean is_locked;
final int age_in_minutes;
+ final boolean is_promoted_ongoing;
+ final boolean has_promotable_characteristics;
@DurationMillisLong long post_duration_millis; // Not final; calculated at the end.
NotificationReported(NotificationRecordPair p,
NotificationReportedEvent eventType, int position, int buzzBeepBlink,
InstanceId groupId) {
+ final Notification notification = p.r.getSbn().getNotification();
this.event_id = eventType.getId();
this.uid = p.r.getUid();
this.package_name = p.r.getSbn().getPackageName();
@@ -527,8 +530,8 @@ interface NotificationRecordLogger {
this.channel_id_hash = p.getChannelIdHash();
this.group_id_hash = p.getGroupIdHash();
this.group_instance_id = (groupId == null) ? 0 : groupId.getId();
- this.is_group_summary = p.r.getSbn().getNotification().isGroupSummary();
- this.category = p.r.getSbn().getNotification().category;
+ this.is_group_summary = notification.isGroupSummary();
+ this.category = notification.category;
this.style = p.getStyle();
this.num_people = p.getNumPeople();
this.position = position;
@@ -542,22 +545,18 @@ interface NotificationRecordLogger {
this.assistant_ranking_score = p.r.getRankingScore();
this.is_ongoing = p.r.getSbn().isOngoing();
this.is_foreground_service = NotificationRecordLogger.isForegroundService(p.r);
- this.timeout_millis = p.r.getSbn().getNotification().getTimeoutAfter();
+ this.timeout_millis = notification.getTimeoutAfter();
this.is_non_dismissible = NotificationRecordLogger.isNonDismissible(p.r);
-
- final boolean hasFullScreenIntent =
- p.r.getSbn().getNotification().fullScreenIntent != null;
-
- final boolean hasFsiRequestedButDeniedFlag = (p.r.getSbn().getNotification().flags
- & Notification.FLAG_FSI_REQUESTED_BUT_DENIED) != 0;
-
+ final boolean hasFullScreenIntent = notification.fullScreenIntent != null;
+ final boolean hasFsiRequestedButDeniedFlag =
+ (notification.flags & Notification.FLAG_FSI_REQUESTED_BUT_DENIED) != 0;
this.fsi_state = NotificationRecordLogger.getFsiState(
hasFullScreenIntent, hasFsiRequestedButDeniedFlag, eventType);
-
this.is_locked = p.r.isLocked();
-
this.age_in_minutes = NotificationRecordLogger.getAgeInMinutes(
- p.r.getSbn().getPostTime(), p.r.getSbn().getNotification().getWhen());
+ p.r.getSbn().getPostTime(), notification.getWhen());
+ this.is_promoted_ongoing = notification.isPromotedOngoing();
+ this.has_promotable_characteristics = notification.hasPromotableCharacteristics();
}
}
diff --git a/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java b/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java
index fc0a7764963e..e0e3fbaf49f1 100644
--- a/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java
+++ b/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java
@@ -78,7 +78,9 @@ class NotificationRecordLoggerImpl implements NotificationRecordLogger {
notificationReported.post_duration_millis,
notificationReported.fsi_state,
notificationReported.is_locked,
- notificationReported.age_in_minutes);
+ notificationReported.age_in_minutes,
+ notificationReported.is_promoted_ongoing,
+ notificationReported.has_promotable_characteristics);
}
@Override
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 504605d0a1a2..41569deeddb5 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -7080,7 +7080,8 @@ public class TelephonyManager {
*/
@Deprecated
public boolean isVoiceCapable() {
- return hasCapability(PackageManager.FEATURE_TELEPHONY_CALLING,
+ if (mContext == null) return true;
+ return mContext.getResources().getBoolean(
com.android.internal.R.bool.config_voice_capable);
}
@@ -7104,7 +7105,8 @@ public class TelephonyManager {
* @see SubscriptionInfo#getServiceCapabilities()
*/
public boolean isDeviceVoiceCapable() {
- return isVoiceCapable();
+ return hasCapability(PackageManager.FEATURE_TELEPHONY_CALLING,
+ com.android.internal.R.bool.config_voice_capable);
}
/**