diff options
149 files changed, 1523 insertions, 1031 deletions
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index cc72d8f1ad8c..8d4925d8182d 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -3266,7 +3266,6 @@ public class Notification implements Parcelable final Class<? extends Style> notificationStyle = getNotificationStyle(); return notificationStyle == null - || BigPictureStyle.class.equals(notificationStyle) || BigTextStyle.class.equals(notificationStyle) || CallStyle.class.equals(notificationStyle) || ProgressStyle.class.equals(notificationStyle); diff --git a/core/java/android/app/admin/DeviceAdminInfo.java b/core/java/android/app/admin/DeviceAdminInfo.java index cb2b8adae0f9..8a8877f441a9 100644 --- a/core/java/android/app/admin/DeviceAdminInfo.java +++ b/core/java/android/app/admin/DeviceAdminInfo.java @@ -558,6 +558,11 @@ public final class DeviceAdminInfo implements Parcelable { } public void dump(Printer pw, String prefix) { + pw.println("mVisible: " + mVisible); + pw.println("mUsesPolicies: " + mUsesPolicies); + pw.println("mSupportsTransferOwnership: " + mSupportsTransferOwnership); + pw.println("mHeadlessDeviceOwnerMode: " + mHeadlessDeviceOwnerMode); + pw.println(prefix + "Receiver:"); mActivityInfo.dump(pw, prefix + " "); } diff --git a/core/java/android/companion/virtual/flags/flags.aconfig b/core/java/android/companion/virtual/flags/flags.aconfig index 0085e4f42397..4fb3982c3754 100644 --- a/core/java/android/companion/virtual/flags/flags.aconfig +++ b/core/java/android/companion/virtual/flags/flags.aconfig @@ -150,3 +150,11 @@ flag { description: "Settings override for virtual devices" bug: "371801645" } + +flag { + namespace: "virtual_devices" + name: "viewconfiguration_apis" + description: "APIs for settings ViewConfiguration attributes on virtual devices" + bug: "370720522" + is_exported: true +} diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java index 343e4b5668c0..0c8a9ed56ae8 100644 --- a/core/java/android/hardware/display/DisplayManagerInternal.java +++ b/core/java/android/hardware/display/DisplayManagerInternal.java @@ -476,6 +476,16 @@ public abstract class DisplayManagerInternal { public abstract boolean isDisplayReadyForMirroring(int displayId); /** + * Called by {@link com.android.server.wm.WindowManagerService} to notify whether a display + * should be in the topology. + * @param displayId The logical ID of the display + * @param inTopology Whether the display should be in the topology. This being true does not + * guarantee that the display will be in the topology - Display Manager might + * also check other parameters. + */ + public abstract void onDisplayBelongToTopologyChanged(int displayId, boolean inTopology); + + /** * Called by {@link com.android.server.display.DisplayBackupHelper} when backup files were * restored and are ready to be reloaded. */ diff --git a/core/java/android/window/DesktopModeFlags.java b/core/java/android/window/DesktopModeFlags.java index 888d7e7c895d..78769248c013 100644 --- a/core/java/android/window/DesktopModeFlags.java +++ b/core/java/android/window/DesktopModeFlags.java @@ -94,6 +94,7 @@ public enum DesktopModeFlags { ENABLE_DESKTOP_WINDOWING_MULTI_INSTANCE_FEATURES( Flags::enableDesktopWindowingMultiInstanceFeatures, true), ENABLE_DESKTOP_WINDOWING_PERSISTENCE(Flags::enableDesktopWindowingPersistence, true), + ENABLE_DESKTOP_WINDOWING_PIP(Flags::enableDesktopWindowingPip, false), ENABLE_DESKTOP_WINDOWING_QUICK_SWITCH(Flags::enableDesktopWindowingQuickSwitch, true), ENABLE_DESKTOP_WINDOWING_SCVH_CACHE(Flags::enableDesktopWindowingScvhCacheBugFix, true), ENABLE_DESKTOP_WINDOWING_SIZE_CONSTRAINTS(Flags::enableDesktopWindowingSizeConstraints, true), diff --git a/core/java/com/android/internal/widget/ConversationLayout.java b/core/java/com/android/internal/widget/ConversationLayout.java index 2cca3dbc4f2f..3da19220248b 100644 --- a/core/java/com/android/internal/widget/ConversationLayout.java +++ b/core/java/com/android/internal/widget/ConversationLayout.java @@ -1178,6 +1178,7 @@ public class ConversationLayout extends FrameLayout } newGroup.setShowingAvatar(!mIsOneToOne && !mIsCollapsed); newGroup.setSingleLine(mIsCollapsed && TextUtils.isEmpty(mSummarizedContent)); + newGroup.setIsCollapsed(mIsCollapsed); newGroup.setSender(sender, nameOverride); newGroup.setSending(groupIndex == (groups.size() - 1) && showSpinner); mGroups.add(newGroup); diff --git a/core/java/com/android/internal/widget/MessagingGroup.java b/core/java/com/android/internal/widget/MessagingGroup.java index b31a200218ee..21bb7a9468bd 100644 --- a/core/java/com/android/internal/widget/MessagingGroup.java +++ b/core/java/com/android/internal/widget/MessagingGroup.java @@ -103,6 +103,7 @@ public class MessagingGroup extends NotificationOptimizedLinearLayout implements private boolean mShowingAvatar = true; private CharSequence mSenderName; private boolean mSingleLine = false; + private boolean mIsCollapsed = false; private LinearLayout mContentContainer; private int mRequestedMaxDisplayedLines = Integer.MAX_VALUE; private int mSenderTextPaddingSingleLine; @@ -451,7 +452,7 @@ public class MessagingGroup extends NotificationOptimizedLinearLayout implements private void updateIconVisibility() { if (Flags.notificationsRedesignTemplates()) { // We don't show any icon (other than the app or person icon) in the collapsed form. - mMessagingIconContainer.setVisibility(mSingleLine ? GONE : VISIBLE); + mMessagingIconContainer.setVisibility(mIsCollapsed ? GONE : VISIBLE); } } @@ -714,10 +715,18 @@ public class MessagingGroup extends NotificationOptimizedLinearLayout implements updateMaxDisplayedLines(); updateClipRect(); updateSenderVisibility(); - updateIconVisibility(); } } + /** + * Sets whether this is in a collapsed layout or not. Certain elements like icons are not shown + * when the notification is collapsed. + */ + public void setIsCollapsed(boolean isCollapsed) { + mIsCollapsed = isCollapsed; + updateIconVisibility(); + } + public boolean isSingleLine() { return mSingleLine; } diff --git a/core/java/com/android/internal/widget/MessagingLayout.java b/core/java/com/android/internal/widget/MessagingLayout.java index 9fe2de82adee..4cc4b38f95a5 100644 --- a/core/java/com/android/internal/widget/MessagingLayout.java +++ b/core/java/com/android/internal/widget/MessagingLayout.java @@ -551,6 +551,7 @@ public class MessagingLayout extends FrameLayout } newGroup.setSingleLine(mIsCollapsed && TextUtils.isEmpty(mSummarizedContent)); newGroup.setShowingAvatar(!mIsCollapsed); + newGroup.setIsCollapsed(mIsCollapsed); newGroup.setSender(sender, nameOverride); newGroup.setSending(groupIndex == (groups.size() - 1) && showSpinner); mGroups.add(newGroup); diff --git a/core/java/com/android/internal/widget/PeopleHelper.java b/core/java/com/android/internal/widget/PeopleHelper.java index 3f5b4a0d61fe..3aa4c8498747 100644 --- a/core/java/com/android/internal/widget/PeopleHelper.java +++ b/core/java/com/android/internal/widget/PeopleHelper.java @@ -110,7 +110,7 @@ public class PeopleHelper { @NonNull public Icon createAvatarSymbol(@NonNull CharSequence name, @NonNull String symbol, @ColorInt int layoutColor) { - if (symbol.isEmpty() || TextUtils.isDigitsOnly(symbol) + if (symbol == null || symbol.isEmpty() || TextUtils.isDigitsOnly(symbol) || SPECIAL_CHAR_PATTERN.matcher(symbol).find()) { Icon avatarIcon = Icon.createWithResource(mContext, R.drawable.messaging_user); avatarIcon.setTint(findColor(name, layoutColor)); diff --git a/core/tests/coretests/src/android/app/NotificationTest.java b/core/tests/coretests/src/android/app/NotificationTest.java index f89e4416ce78..157c74abc5de 100644 --- a/core/tests/coretests/src/android/app/NotificationTest.java +++ b/core/tests/coretests/src/android/app/NotificationTest.java @@ -371,22 +371,22 @@ public class NotificationTest { @Test @EnableFlags(Flags.FLAG_UI_RICH_ONGOING) - public void testHasPromotableStyle_bigPicture() { + public void testHasPromotableStyle_bigText() { Notification n = new Notification.Builder(mContext, "test") .setSmallIcon(android.R.drawable.sym_def_app_icon) - .setStyle(new Notification.BigPictureStyle()) + .setStyle(new Notification.BigTextStyle()) .build(); assertThat(n.hasPromotableStyle()).isTrue(); } @Test @EnableFlags(Flags.FLAG_UI_RICH_ONGOING) - public void testHasPromotableStyle_bigText() { + public void testHasPromotableStyle_no_bigPictureStyle() { Notification n = new Notification.Builder(mContext, "test") .setSmallIcon(android.R.drawable.sym_def_app_icon) - .setStyle(new Notification.BigTextStyle()) + .setStyle(new Notification.BigPictureStyle()) .build(); - assertThat(n.hasPromotableStyle()).isTrue(); + assertThat(n.hasPromotableStyle()).isFalse(); } @Test diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml index a75453578f16..16e098b39004 100644 --- a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml +++ b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml @@ -142,6 +142,7 @@ android:contentDescription="@string/screenshot_text" android:text="@string/screenshot_text" android:src="@drawable/desktop_mode_ic_handle_menu_screenshot" + android:importantForAccessibility="no" style="@style/DesktopModeHandleMenuActionButton"/> <com.android.wm.shell.windowdecor.HandleMenuActionButton @@ -149,6 +150,7 @@ android:contentDescription="@string/new_window_text" android:text="@string/new_window_text" android:src="@drawable/desktop_mode_ic_handle_menu_new_window" + android:importantForAccessibility="no" style="@style/DesktopModeHandleMenuActionButton"/> <com.android.wm.shell.windowdecor.HandleMenuActionButton @@ -156,6 +158,7 @@ android:contentDescription="@string/manage_windows_text" android:text="@string/manage_windows_text" android:src="@drawable/desktop_mode_ic_handle_menu_manage_windows" + android:importantForAccessibility="no" style="@style/DesktopModeHandleMenuActionButton"/> <com.android.wm.shell.windowdecor.HandleMenuActionButton @@ -163,6 +166,7 @@ android:contentDescription="@string/change_aspect_ratio_text" android:text="@string/change_aspect_ratio_text" android:src="@drawable/desktop_mode_ic_handle_menu_change_aspect_ratio" + android:importantForAccessibility="no" style="@style/DesktopModeHandleMenuActionButton"/> </LinearLayout> @@ -182,6 +186,7 @@ android:text="@string/open_in_browser_text" android:src="@drawable/desktop_mode_ic_handle_menu_open_in_browser" style="@style/DesktopModeHandleMenuActionButton" + android:importantForAccessibility="no" android:layout_width="0dp" android:layout_weight="1"/> diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_action_button.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_action_button.xml index 0163c018479f..de38e6fc2330 100644 --- a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_action_button.xml +++ b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_action_button.xml @@ -34,5 +34,6 @@ <com.android.wm.shell.windowdecor.MarqueedTextView android:id="@+id/label" + android:importantForAccessibility="no" style="@style/DesktopModeHandleMenuActionButtonTextView"/> </LinearLayout> diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java index d1c7f7d7dcad..80b65d025399 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java @@ -28,6 +28,7 @@ import static android.window.TransitionInfo.FLAG_MOVED_TO_TOP; import static android.window.TransitionInfo.FLAG_SHOW_WALLPAPER; import static com.android.internal.jank.InteractionJankMonitor.CUJ_PREDICTIVE_BACK_HOME; +import static com.android.systemui.Flags.predictiveBackDelayTransition; import static com.android.window.flags.Flags.unifyBackNavigationTransition; import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_BACK_PREVIEW; @@ -431,6 +432,11 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont @VisibleForTesting public void onThresholdCrossed() { mThresholdCrossed = true; + BackTouchTracker activeTracker = getActiveTracker(); + if (predictiveBackDelayTransition() && activeTracker != null && mActiveCallback == null + && mBackGestureStarted) { + startBackNavigation(activeTracker); + } // There was no focus window when calling startBackNavigation, still pilfer pointers so // the next focus window won't receive motion events. if (mBackNavigationInfo == null && mReceivedNullNavigationInfo) { @@ -488,9 +494,14 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont if (swipeEdge == EDGE_NONE) { // start animation immediately for non-gestural sources (without ACTION_MOVE // events) - mThresholdCrossed = true; + if (!predictiveBackDelayTransition()) { + mThresholdCrossed = true; + } mPointersPilfered = true; onGestureStarted(touchX, touchY, swipeEdge); + if (predictiveBackDelayTransition()) { + onThresholdCrossed(); + } mShouldStartOnNextMoveEvent = false; } else { mShouldStartOnNextMoveEvent = true; @@ -544,14 +555,17 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont mPostCommitAnimationInProgress = false; mShellExecutor.removeCallbacks(mAnimationTimeoutRunnable); startSystemAnimation(); - } else if (touchTracker == mCurrentTracker) { - // Only start the back navigation if no other gesture is being processed. Otherwise, - // the back navigation will fall back to legacy back event injection. - startBackNavigation(mCurrentTracker); + } else if (!predictiveBackDelayTransition()) { + startBackNavigation(touchTracker); } } private void startBackNavigation(@NonNull BackTouchTracker touchTracker) { + if (touchTracker != mCurrentTracker) { + // Only start the back navigation if no other gesture is being processed. Otherwise, + // the back navigation will fall back to legacy back event injection. + return; + } try { startLatencyTracking(); if (mBackAnimationAdapter != null diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipDesktopState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipDesktopState.java index 01fd3442f604..453ca167557a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipDesktopState.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipDesktopState.java @@ -20,9 +20,10 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import android.window.DesktopExperienceFlags; +import android.window.DesktopModeFlags; import android.window.DisplayAreaInfo; -import com.android.window.flags.Flags; +import com.android.wm.shell.Flags; import com.android.wm.shell.RootTaskDisplayAreaOrganizer; import com.android.wm.shell.desktopmode.DesktopUserRepositories; import com.android.wm.shell.desktopmode.DragToDesktopTransitionHandler; @@ -48,18 +49,23 @@ public class PipDesktopState { /** * Returns whether PiP in Desktop Windowing is enabled by checking the following: - * - Desktop Windowing in PiP flag is enabled + * - PiP in Desktop Windowing flag is enabled * - DesktopUserRepositories is injected * - DragToDesktopTransitionHandler is injected */ public boolean isDesktopWindowingPipEnabled() { - return Flags.enableDesktopWindowingPip() && mDesktopUserRepositoriesOptional.isPresent() + return DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PIP.isTrue() + && mDesktopUserRepositoriesOptional.isPresent() && mDragToDesktopTransitionHandlerOptional.isPresent(); } - /** Returns whether PiP in Connected Displays is enabled by checking the flag. */ + /** + * Returns whether PiP in Connected Displays is enabled by checking the following: + * - PiP in Connected Displays flag is enabled + * - PiP2 flag is enabled + */ public boolean isConnectedDisplaysPipEnabled() { - return DesktopExperienceFlags.ENABLE_CONNECTED_DISPLAYS_PIP.isTrue(); + return DesktopExperienceFlags.ENABLE_CONNECTED_DISPLAYS_PIP.isTrue() && Flags.enablePip2(); } /** Returns whether the display with the PiP task is in freeform windowing mode. */ diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt index 8c2748742595..bbb300ea42da 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt @@ -733,7 +733,8 @@ class DesktopRepository( desktopData.getActiveDesk(displayId)?.topTransparentFullscreenTaskId = null } - private fun notifyVisibleTaskListeners(displayId: Int, visibleTasksCount: Int) { + @VisibleForTesting + public fun notifyVisibleTaskListeners(displayId: Int, visibleTasksCount: Int) { visibleTasksListeners.forEach { (listener, executor) -> executor.execute { listener.onTasksVisibilityChanged(displayId, visibleTasksCount) } } @@ -915,6 +916,7 @@ class DesktopRepository( val wasActive = desktopData.getActiveDesk(desk.displayId)?.deskId == desk.deskId val activeTasks = ArraySet(desk.activeTasks) desktopData.remove(desk.deskId) + notifyVisibleTaskListeners(desk.displayId, getVisibleTaskCount(displayId = desk.displayId)) deskChangeListeners.forEach { (listener, executor) -> executor.execute { if (wasActive) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt index 26a5d5b52358..bd9c30e2a495 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt @@ -32,7 +32,6 @@ import android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVI import android.window.TransitionInfo import android.window.WindowContainerTransaction import com.android.internal.protolog.ProtoLog -import com.android.window.flags.Flags import com.android.wm.shell.ShellTaskOrganizer import com.android.wm.shell.back.BackAnimationController import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.isExitDesktopModeTransition @@ -107,7 +106,7 @@ class DesktopTasksTransitionObserver( info.changes.forEach { change -> change.taskInfo?.let { taskInfo -> if ( - Flags.enableDesktopWindowingPip() && + DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PIP.isTrue && desktopRepository.isTaskMinimizedPipInDisplay( taskInfo.displayId, taskInfo.taskId, diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java index 04f03361258e..0966110d2f41 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java @@ -70,6 +70,7 @@ import android.view.Choreographer; import android.view.Display; import android.view.Surface; import android.view.SurfaceControl; +import android.window.DesktopModeFlags; import android.window.DisplayAreaInfo; import android.window.TaskOrganizer; import android.window.TaskSnapshot; @@ -78,7 +79,6 @@ import android.window.WindowContainerTransaction; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.protolog.ProtoLog; -import com.android.window.flags.Flags; import com.android.wm.shell.R; import com.android.wm.shell.RootTaskDisplayAreaOrganizer; import com.android.wm.shell.ShellTaskOrganizer; @@ -781,7 +781,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, // TODO(b/377581840): Update this check to include non-minimized cases, e.g. split to PiP etc. private boolean isPipExitingToDesktopMode() { DesktopRepository currentRepo = getCurrentRepo(); - return Flags.enableDesktopWindowingPip() && currentRepo != null + return DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PIP.isTrue() && currentRepo != null && (currentRepo.isAnyDeskActive(mTaskInfo.displayId) || isDisplayInFreeform()); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java index 242f7fa5f1b1..5706f19f177a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java @@ -39,11 +39,11 @@ import android.view.InputEventReceiver; import android.view.InputMonitor; import android.view.MotionEvent; import android.view.ViewConfiguration; +import android.window.DesktopModeFlags; import androidx.annotation.VisibleForTesting; import com.android.internal.policy.TaskResizingAlgorithm; -import com.android.window.flags.Flags; import com.android.wm.shell.R; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.pip.PipBoundsAlgorithm; @@ -183,7 +183,7 @@ public class PipResizeGestureHandler { private void reloadResources() { final Resources res = mContext.getResources(); mDelta = res.getDimensionPixelSize(R.dimen.pip_resize_edge_size); - mEnableDragCornerResize = Flags.enableDesktopWindowingPip(); + mEnableDragCornerResize = DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PIP.isTrue(); mTouchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop(); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragDetector.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragDetector.java index d36fc1247dad..a9df4bd1860d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragDetector.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragDetector.java @@ -28,6 +28,7 @@ import static android.view.MotionEvent.ACTION_UP; import android.annotation.NonNull; import android.graphics.PointF; +import android.util.Log; import android.view.MotionEvent; import android.view.View; @@ -43,6 +44,8 @@ import androidx.annotation.Nullable; * All touch events must be passed through this class to track a drag event. */ public class DragDetector { + private static final String TAG = "DragDetector"; + private final MotionEventHandler mEventHandler; private final PointF mInputDownPoint = new PointF(); @@ -109,8 +112,12 @@ public class DragDetector { return mResultOfDownAction; } final int dragPointerIndex = ev.findPointerIndex(mDragPointerId); + // TODO(b/400635953): Separate the app header and its buttons' + // touch listeners so they're not handled by the same DragDetector. if (dragPointerIndex == -1) { - throw new IllegalStateException("Failed to find primary pointer!"); + Log.w(TAG, "Invalid pointer index on ACTION_MOVE. Drag" + + " pointer id: " + mDragPointerId); + return mResultOfDownAction; } if (!mIsDragEvent) { float dx = ev.getRawX(dragPointerIndex) - mInputDownPoint.x; diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt index 2e74d4391c63..de92d391645a 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt @@ -88,7 +88,7 @@ class DesktopRepositoryTest(flags: FlagsParameterization) : ShellTestCase() { datastoreScope = CoroutineScope(Dispatchers.Unconfined + SupervisorJob()) shellInit = spy(ShellInit(testExecutor)) - repo = DesktopRepository(persistentRepository, datastoreScope, DEFAULT_USER_ID) + repo = spy(DesktopRepository(persistentRepository, datastoreScope, DEFAULT_USER_ID)) whenever(runBlocking { persistentRepository.readDesktop(any(), any()) }) .thenReturn(Desktop.getDefaultInstance()) shellInit.init() @@ -1171,6 +1171,7 @@ class DesktopRepositoryTest(flags: FlagsParameterization) : ShellTestCase() { val tasksBeforeRemoval = repo.removeDesk(deskId = DEFAULT_DISPLAY) + verify(repo, times(1)).notifyVisibleTaskListeners(DEFAULT_DISPLAY, visibleTasksCount = 0) assertThat(tasksBeforeRemoval).containsExactly(1, 2, 3).inOrder() assertThat(repo.getActiveTasks(displayId = DEFAULT_DISPLAY)).isEmpty() } @@ -1184,6 +1185,7 @@ class DesktopRepositoryTest(flags: FlagsParameterization) : ShellTestCase() { repo.removeDesk(deskId = 3) + verify(repo, times(1)).notifyVisibleTaskListeners(DEFAULT_DISPLAY, visibleTasksCount = 0) assertThat(repo.getDeskIds(displayId = DEFAULT_DISPLAY)).doesNotContain(3) } @@ -1196,6 +1198,7 @@ class DesktopRepositoryTest(flags: FlagsParameterization) : ShellTestCase() { repo.removeDesk(deskId = 2) + verify(repo, times(1)).notifyVisibleTaskListeners(DEFAULT_DISPLAY, visibleTasksCount = 0) assertThat(repo.getDeskIds(displayId = DEFAULT_DISPLAY)).doesNotContain(2) } @@ -1424,6 +1427,7 @@ class DesktopRepositoryTest(flags: FlagsParameterization) : ShellTestCase() { repo.removeDesk(deskId = 1) executor.flushAll() + verify(repo, times(1)).notifyVisibleTaskListeners(DEFAULT_DISPLAY, visibleTasksCount = 0) val lastRemoval = assertNotNull(listener.lastRemoval) assertThat(lastRemoval.displayId).isEqualTo(0) assertThat(lastRemoval.deskId).isEqualTo(1) @@ -1440,6 +1444,7 @@ class DesktopRepositoryTest(flags: FlagsParameterization) : ShellTestCase() { repo.removeDesk(deskId = 2) executor.flushAll() + verify(repo, times(0)).notifyVisibleTaskListeners(DEFAULT_DISPLAY, visibleTasksCount = 0) assertThat(listener.lastRemoval).isNull() } @@ -1455,6 +1460,7 @@ class DesktopRepositoryTest(flags: FlagsParameterization) : ShellTestCase() { repo.removeDesk(deskId = 1) executor.flushAll() + verify(repo, times(1)).notifyVisibleTaskListeners(DEFAULT_DISPLAY, visibleTasksCount = 0) val lastActivationChange = assertNotNull(listener.lastActivationChange) assertThat(lastActivationChange.displayId).isEqualTo(0) assertThat(lastActivationChange.oldActive).isEqualTo(1) diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt index 9588a5c73385..0871d38ceb46 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt @@ -15,7 +15,6 @@ import android.platform.test.annotations.DisableFlags import android.platform.test.annotations.EnableFlags import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper -import android.view.Display.DEFAULT_DISPLAY import android.view.SurfaceControl import android.view.WindowManager.TRANSIT_OPEN import android.window.TransitionInfo @@ -785,7 +784,7 @@ class DragToDesktopTransitionHandlerTest : ShellTestCase() { startTransaction: SurfaceControl.Transaction = mock(), finishTransaction: SurfaceControl.Transaction = mock(), homeChange: TransitionInfo.Change? = createHomeChange(), - transitionRootLeash: SurfaceControl? = null, + transitionRootLeash: SurfaceControl = mock(), ): IBinder { whenever(dragAnimator.position).thenReturn(PointF()) // Simulate transition is started and is ready to animate. @@ -887,7 +886,7 @@ class DragToDesktopTransitionHandlerTest : ShellTestCase() { type: Int, draggedTask: RunningTaskInfo, homeChange: TransitionInfo.Change? = createHomeChange(), - rootLeash: SurfaceControl? = null, + rootLeash: SurfaceControl = mock(), ) = TransitionInfo(type, /* flags= */ 0).apply { homeChange?.let { addChange(it) } @@ -904,9 +903,7 @@ class DragToDesktopTransitionHandlerTest : ShellTestCase() { flags = flags or FLAG_IS_WALLPAPER } ) - if (rootLeash != null) { - addRootLeash(DEFAULT_DISPLAY, rootLeash, /* offsetLeft= */ 0, /* offsetTop= */ 0) - } + addRootLeash(draggedTask.displayId, rootLeash, /* offsetLeft= */ 0, /* offsetTop= */ 0) } private fun createHomeChange() = diff --git a/packages/SettingsLib/aconfig/settingslib.aconfig b/packages/SettingsLib/aconfig/settingslib.aconfig index a029f56cf1d7..349d13a29b05 100644 --- a/packages/SettingsLib/aconfig/settingslib.aconfig +++ b/packages/SettingsLib/aconfig/settingslib.aconfig @@ -37,13 +37,6 @@ flag { } flag { - name: "enable_hide_exclusively_managed_bluetooth_device" - namespace: "dck_framework" - description: "Hide exclusively managed Bluetooth devices in BT settings menu." - bug: "324475542" -} - -flag { name: "enable_set_preferred_transport_for_le_audio_device" namespace: "bluetooth" description: "Enable setting preferred transport for Le Audio device" @@ -249,3 +242,13 @@ flag { purpose: PURPOSE_BUGFIX } } + +flag { + name: "audio_stream_media_service_by_receive_state" + namespace: "cross_device_experiences" + description: "Start or update audio stream media service by receive state" + bug: "398700619" + metadata { + purpose: PURPOSE_BUGFIX + } +} diff --git a/packages/SettingsLib/res/drawable/ic_news.xml b/packages/SettingsLib/res/drawable/ic_news.xml new file mode 100644 index 000000000000..90615ec1b8fd --- /dev/null +++ b/packages/SettingsLib/res/drawable/ic_news.xml @@ -0,0 +1,19 @@ +<!-- + ~ 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:height="14.933333dp" android:viewportHeight="14" android:viewportWidth="15" android:width="16dp"> + <path android:fillColor="#ffffff" android:pathData="M3,10.8H11V9.6H3V10.8ZM3,8.4H5.4V4.8H3V8.4ZM12.2,13.6H1.8C1.467,13.6 1.183,13.483 0.95,13.25C0.717,13.017 0.6,12.733 0.6,12.4V3.6C0.6,3.267 0.717,2.983 0.95,2.75C1.183,2.517 1.467,2.4 1.8,2.4H6.683C6.528,2.711 6.406,3.033 6.317,3.367C6.239,3.7 6.2,4.044 6.2,4.4C6.2,4.933 6.283,5.439 6.45,5.917C6.628,6.383 6.878,6.811 7.2,7.2H6.6V8.4H8.767C9.056,8.522 9.35,8.622 9.65,8.7C9.961,8.767 10.278,8.8 10.6,8.8C11.111,8.8 11.606,8.717 12.083,8.55C12.572,8.372 13.011,8.122 13.4,7.8V12.4C13.4,12.733 13.283,13.017 13.05,13.25C12.817,13.483 12.533,13.6 12.2,13.6ZM10.6,8C10.6,7 10.25,6.15 9.55,5.45C8.85,4.75 8,4.4 7,4.4C8,4.4 8.85,4.05 9.55,3.35C10.25,2.65 10.6,1.8 10.6,0.8C10.6,1.8 10.95,2.65 11.65,3.35C12.35,4.05 13.2,4.4 14.2,4.4C13.2,4.4 12.35,4.75 11.65,5.45C10.95,6.15 10.6,7 10.6,8Z"/> +</vector> diff --git a/packages/SettingsLib/res/drawable/ic_promotions.xml b/packages/SettingsLib/res/drawable/ic_promotions.xml new file mode 100644 index 000000000000..a597ecebd967 --- /dev/null +++ b/packages/SettingsLib/res/drawable/ic_promotions.xml @@ -0,0 +1,19 @@ +<!-- + ~ 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:height="16dp" android:viewportHeight="15" android:viewportWidth="13" android:width="13.866667dp"> + <path android:fillColor="#ffffff" android:pathData="M1.4,13.4C1.067,13.4 0.783,13.283 0.55,13.05C0.317,12.817 0.2,12.533 0.2,12.2V4.6C0.2,4.267 0.317,3.983 0.55,3.75C0.783,3.517 1.067,3.4 1.4,3.4H2.6V3.133C2.6,2.467 2.822,1.878 3.267,1.367C3.722,0.856 4.3,0.6 5,0.6C5.667,0.6 6.233,0.833 6.7,1.3C7.167,1.767 7.4,2.333 7.4,3V3.4H8.6C8.933,3.4 9.217,3.517 9.45,3.75C9.683,3.983 9.8,4.267 9.8,4.6V6.367C9.611,6.311 9.417,6.272 9.217,6.25C9.017,6.217 8.811,6.2 8.6,6.2C7.378,6.2 6.339,6.628 5.483,7.483C4.628,8.339 4.2,9.378 4.2,10.6C4.2,11.111 4.283,11.611 4.45,12.1C4.628,12.578 4.878,13.011 5.2,13.4H1.4ZM8.6,14.2C8.6,13.2 8.25,12.35 7.55,11.65C6.85,10.95 6,10.6 5,10.6C6,10.6 6.85,10.25 7.55,9.55C8.25,8.85 8.6,8 8.6,7C8.6,8 8.95,8.85 9.65,9.55C10.35,10.25 11.2,10.6 12.2,10.6C11.2,10.6 10.35,10.95 9.65,11.65C8.95,12.35 8.6,13.2 8.6,14.2ZM3.8,3.4H6.2V3C6.2,2.667 6.083,2.383 5.85,2.15C5.617,1.917 5.333,1.8 5,1.8C4.667,1.8 4.383,1.917 4.15,2.15C3.917,2.383 3.8,2.667 3.8,3V3.4ZM3.2,5.8C3.367,5.8 3.506,5.744 3.617,5.633C3.739,5.511 3.8,5.367 3.8,5.2V4.6H2.6V5.2C2.6,5.367 2.656,5.511 2.767,5.633C2.889,5.744 3.033,5.8 3.2,5.8ZM6.8,5.8C6.967,5.8 7.106,5.744 7.217,5.633C7.339,5.511 7.4,5.367 7.4,5.2V4.6H6.2V5.2C6.2,5.367 6.256,5.511 6.367,5.633C6.489,5.744 6.633,5.8 6.8,5.8Z"/> +</vector> diff --git a/packages/SettingsLib/res/drawable/ic_recs.xml b/packages/SettingsLib/res/drawable/ic_recs.xml new file mode 100644 index 000000000000..034ff9e221b0 --- /dev/null +++ b/packages/SettingsLib/res/drawable/ic_recs.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="14dp" + android:height="16dp" + android:viewportWidth="14" + android:viewportHeight="16"> + <path + android:pathData="M6,15.2L3.6,12.8H1.6C1.278,12.8 0.994,12.683 0.75,12.45C0.517,12.206 0.4,11.922 0.4,11.6V2.8C0.4,2.478 0.517,2.2 0.75,1.967C0.994,1.722 1.278,1.6 1.6,1.6H6.2C5.878,1.989 5.628,2.428 5.45,2.917C5.283,3.394 5.2,3.889 5.2,4.4C5.2,5.622 5.628,6.661 6.483,7.517C7.339,8.372 8.378,8.8 9.6,8.8C9.956,8.8 10.3,8.761 10.633,8.683C10.967,8.594 11.289,8.472 11.6,8.317V11.6C11.6,11.922 11.483,12.206 11.25,12.45C11.017,12.683 10.733,12.8 10.4,12.8H8.4L6,15.2ZM9.6,8C9.6,7 9.25,6.15 8.55,5.45C7.85,4.75 7,4.4 6,4.4C7,4.4 7.85,4.05 8.55,3.35C9.25,2.65 9.6,1.8 9.6,0.8C9.6,1.8 9.95,2.65 10.65,3.35C11.35,4.05 12.2,4.4 13.2,4.4C12.2,4.4 11.35,4.75 10.65,5.45C9.95,6.15 9.6,7 9.6,8Z" + android:fillColor="#ffffff"/> +</vector> diff --git a/packages/SettingsLib/res/drawable/ic_social.xml b/packages/SettingsLib/res/drawable/ic_social.xml new file mode 100644 index 000000000000..01974319b441 --- /dev/null +++ b/packages/SettingsLib/res/drawable/ic_social.xml @@ -0,0 +1,19 @@ +<!-- + ~ 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:height="16dp" android:viewportHeight="15" android:viewportWidth="15" android:width="16dp"> + <path android:fillColor="#ffffff" android:pathData="M0.6,14.4V12.733C0.6,12.411 0.678,12.117 0.833,11.85C0.989,11.583 1.211,11.378 1.5,11.233C2.056,10.956 2.628,10.75 3.217,10.617C3.817,10.472 4.428,10.4 5.05,10.4C5.661,10.4 6.256,10.472 6.833,10.617C7.422,10.75 7.989,10.956 8.533,11.233C8.811,11.378 9.028,11.583 9.183,11.85C9.339,12.117 9.417,12.411 9.417,12.733V14.4H0.6ZM10.6,14.4V12.617C10.6,12.183 10.494,11.778 10.283,11.4C10.072,11.022 9.772,10.733 9.383,10.533C9.772,10.6 10.144,10.7 10.5,10.833C10.856,10.956 11.206,11.106 11.55,11.283C11.828,11.439 12.072,11.628 12.283,11.85C12.494,12.061 12.6,12.317 12.6,12.617V14.4H10.6ZM5.2,9.6C4.589,9.6 4.067,9.389 3.633,8.967C3.211,8.533 3,8.011 3,7.4C3,6.789 3.211,6.272 3.633,5.85C4.067,5.417 4.589,5.2 5.2,5.2C5.811,5.2 6.328,5.417 6.75,5.85C7.183,6.272 7.4,6.789 7.4,7.4C7.4,8.011 7.183,8.533 6.75,8.967C6.328,9.389 5.811,9.6 5.2,9.6ZM10.6,7.4C10.6,8.011 10.383,8.533 9.95,8.967C9.528,9.389 9.011,9.6 8.4,9.6C8.311,9.6 8.217,9.594 8.117,9.583C8.017,9.572 7.922,9.55 7.833,9.517C8.089,9.206 8.278,8.872 8.4,8.517C8.533,8.161 8.6,7.789 8.6,7.4C8.6,7.011 8.533,6.639 8.4,6.283C8.278,5.928 8.089,5.594 7.833,5.283C7.922,5.25 8.017,5.228 8.117,5.217C8.217,5.206 8.311,5.2 8.4,5.2C9.011,5.2 9.528,5.417 9.95,5.85C10.383,6.272 10.6,6.789 10.6,7.4ZM11.4,6.4C11.4,5.622 11.128,4.961 10.583,4.417C10.039,3.872 9.378,3.6 8.6,3.6C9.378,3.6 10.039,3.328 10.583,2.783C11.128,2.239 11.4,1.578 11.4,0.8C11.4,1.578 11.672,2.239 12.217,2.783C12.761,3.328 13.422,3.6 14.2,3.6C13.422,3.6 12.761,3.872 12.217,4.417C11.672,4.961 11.4,5.622 11.4,6.4Z"/> +</vector> diff --git a/packages/SystemUI/aconfig/predictive_back.aconfig b/packages/SystemUI/aconfig/predictive_back.aconfig index ee918c275b7b..89a0d895a17c 100644 --- a/packages/SystemUI/aconfig/predictive_back.aconfig +++ b/packages/SystemUI/aconfig/predictive_back.aconfig @@ -7,3 +7,10 @@ flag { description: "Enable Shade Animations" bug: "327732946" } + +flag { + name: "predictive_back_delay_transition" + namespace: "systemui" + description: "Slightly delays the back transition start" + bug: "301195601" +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PagerDots.kt b/packages/SystemUI/compose/features/src/com/android/systemui/common/ui/compose/PagerDots.kt index 91f1477d5325..172d88af4cc8 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PagerDots.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/common/ui/compose/PagerDots.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.qs.panels.ui.compose +package com.android.systemui.common.ui.compose import androidx.compose.animation.core.animateDpAsState import androidx.compose.foundation.Canvas @@ -43,9 +43,9 @@ import androidx.compose.ui.semantics.stateDescription import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.LayoutDirection import androidx.compose.ui.unit.dp +import com.android.app.tracing.coroutines.launchTraced as launch import kotlin.math.absoluteValue import kotlinx.coroutines.CoroutineScope -import com.android.app.tracing.coroutines.launchTraced as launch import platform.test.motion.compose.values.MotionTestValueKey import platform.test.motion.compose.values.motionTestValues 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 4e1aab58ac24..3150e94908cd 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 @@ -182,7 +182,7 @@ fun CommunalContainer( viewModel.communalBackground.collectAsStateWithLifecycle( initialValue = CommunalBackgroundType.ANIMATED ) - val swipeToHubEnabled by viewModel.swipeToHubEnabled.collectAsStateWithLifecycle() + val swipeToHubEnabled by viewModel.swipeToHubEnabled.collectAsStateWithLifecycle(false) val state: MutableSceneTransitionLayoutState = rememberMutableSceneTransitionLayoutState( initialScene = currentSceneKey, diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt index 9bb3bac824e9..365567b17ec0 100644 --- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt +++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt @@ -17,7 +17,6 @@ import android.content.Context import android.content.res.Resources import android.graphics.Color import android.graphics.Rect -import android.graphics.RectF import android.icu.text.NumberFormat import android.util.TypedValue import android.view.LayoutInflater @@ -29,6 +28,7 @@ import com.android.systemui.plugins.clocks.AlarmData import com.android.systemui.plugins.clocks.ClockAnimations import com.android.systemui.plugins.clocks.ClockConfig import com.android.systemui.plugins.clocks.ClockController +import com.android.systemui.plugins.clocks.ClockEventListener import com.android.systemui.plugins.clocks.ClockEvents import com.android.systemui.plugins.clocks.ClockFaceConfig import com.android.systemui.plugins.clocks.ClockFaceController @@ -102,7 +102,7 @@ class DefaultClockController( isDarkTheme: Boolean, dozeFraction: Float, foldFraction: Float, - onBoundsChanged: (RectF) -> Unit, + clockListener: ClockEventListener?, ) { largeClock.recomputePadding(null) diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockController.kt index 6dfd2268005f..5acd4468fe92 100644 --- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockController.kt +++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockController.kt @@ -16,13 +16,13 @@ package com.android.systemui.shared.clocks -import android.graphics.RectF import com.android.systemui.animation.GSFAxes import com.android.systemui.customization.R import com.android.systemui.plugins.clocks.AlarmData import com.android.systemui.plugins.clocks.AxisType import com.android.systemui.plugins.clocks.ClockConfig import com.android.systemui.plugins.clocks.ClockController +import com.android.systemui.plugins.clocks.ClockEventListener import com.android.systemui.plugins.clocks.ClockEvents import com.android.systemui.plugins.clocks.ClockFontAxis import com.android.systemui.plugins.clocks.ClockFontAxis.Companion.merge @@ -107,11 +107,11 @@ class FlexClockController(private val clockCtx: ClockContext) : ClockController isDarkTheme: Boolean, dozeFraction: Float, foldFraction: Float, - onBoundsChanged: (RectF) -> Unit, + clockListener: ClockEventListener?, ) { events.onFontAxesChanged(clockCtx.settings.axes) smallClock.run { - layerController.onViewBoundsChanged = onBoundsChanged + layerController.onViewBoundsChanged = { clockListener?.onBoundsChanged(it) } events.onThemeChanged(theme.copy(isDarkTheme = isDarkTheme)) animations.doze(dozeFraction) animations.fold(foldFraction) @@ -119,7 +119,7 @@ class FlexClockController(private val clockCtx: ClockContext) : ClockController } largeClock.run { - layerController.onViewBoundsChanged = onBoundsChanged + layerController.onViewBoundsChanged = { clockListener?.onBoundsChanged(it) } events.onThemeChanged(theme.copy(isDarkTheme = isDarkTheme)) animations.doze(dozeFraction) animations.fold(foldFraction) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/ActionIntentCreatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/ActionIntentCreatorTest.kt new file mode 100644 index 000000000000..239e02640908 --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/ActionIntentCreatorTest.kt @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.clipboardoverlay + +import android.content.ClipData +import android.content.ComponentName +import android.content.Intent +import android.net.Uri +import android.text.SpannableString +import androidx.test.filters.SmallTest +import androidx.test.runner.AndroidJUnit4 +import com.android.systemui.SysuiTestCase +import com.android.systemui.res.R +import org.junit.Assert.assertEquals +import org.junit.Assert.assertTrue +import org.junit.Test +import org.junit.runner.RunWith + +@SmallTest +@RunWith(AndroidJUnit4::class) +class ActionIntentCreatorTest : SysuiTestCase() { + val creator = ActionIntentCreator() + + @Test + fun test_getTextEditorIntent() { + val intent = creator.getTextEditorIntent(context) + assertEquals(ComponentName(context, EditTextActivity::class.java), intent.component) + assertFlags(intent, Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK) + } + + @Test + fun test_getRemoteCopyIntent() { + context.getOrCreateTestableResources().addOverride(R.string.config_remoteCopyPackage, "") + + val clipData = ClipData.newPlainText("Test", "Test Item") + var intent = creator.getRemoteCopyIntent(clipData, context) + + assertEquals(null, intent.component) + assertFlags(intent, EXTERNAL_INTENT_FLAGS) + assertEquals(clipData, intent.clipData) + + // Try again with a remote copy component + val fakeComponent = + ComponentName("com.android.remotecopy", "com.android.remotecopy.RemoteCopyActivity") + context + .getOrCreateTestableResources() + .addOverride(R.string.config_remoteCopyPackage, fakeComponent.flattenToString()) + + intent = creator.getRemoteCopyIntent(clipData, context) + assertEquals(fakeComponent, intent.component) + } + + @Test + fun test_getImageEditIntent() { + context.getOrCreateTestableResources().addOverride(R.string.config_screenshotEditor, "") + val fakeUri = Uri.parse("content://foo") + var intent = creator.getImageEditIntent(fakeUri, context) + + assertEquals(Intent.ACTION_EDIT, intent.action) + assertEquals("image/*", intent.type) + assertEquals(null, intent.component) + assertEquals("clipboard", intent.getStringExtra("edit_source")) + assertFlags(intent, EXTERNAL_INTENT_FLAGS) + + // try again with an editor component + val fakeComponent = + ComponentName("com.android.remotecopy", "com.android.remotecopy.RemoteCopyActivity") + context + .getOrCreateTestableResources() + .addOverride(R.string.config_screenshotEditor, fakeComponent.flattenToString()) + intent = creator.getImageEditIntent(fakeUri, context) + assertEquals(fakeComponent, intent.component) + } + + @Test + fun test_getShareIntent_plaintext() { + val clipData = ClipData.newPlainText("Test", "Test Item") + val intent = creator.getShareIntent(clipData, context) + + assertEquals(Intent.ACTION_CHOOSER, intent.action) + assertFlags(intent, EXTERNAL_INTENT_FLAGS) + val target = intent.getParcelableExtra(Intent.EXTRA_INTENT, Intent::class.java) + assertEquals("Test Item", target?.getStringExtra(Intent.EXTRA_TEXT)) + assertEquals("text/plain", target?.type) + } + + @Test + fun test_getShareIntent_html() { + val clipData = ClipData.newHtmlText("Test", "Some HTML", "<b>Some HTML</b>") + val intent = creator.getShareIntent(clipData, getContext()) + + assertEquals(Intent.ACTION_CHOOSER, intent.action) + assertFlags(intent, EXTERNAL_INTENT_FLAGS) + val target = intent.getParcelableExtra(Intent.EXTRA_INTENT, Intent::class.java) + assertEquals("Some HTML", target?.getStringExtra(Intent.EXTRA_TEXT)) + assertEquals("text/plain", target?.type) + } + + @Test + fun test_getShareIntent_image() { + val uri = Uri.parse("content://something") + val clipData = ClipData("Test", arrayOf("image/png"), ClipData.Item(uri)) + val intent = creator.getShareIntent(clipData, context) + + assertEquals(Intent.ACTION_CHOOSER, intent.action) + assertFlags(intent, EXTERNAL_INTENT_FLAGS) + val target = intent.getParcelableExtra(Intent.EXTRA_INTENT, Intent::class.java) + assertEquals(uri, target?.getParcelableExtra(Intent.EXTRA_STREAM, Uri::class.java)) + assertEquals(uri, target?.clipData?.getItemAt(0)?.uri) + assertEquals("image/png", target?.type) + } + + @Test + fun test_getShareIntent_spannableText() { + val clipData = ClipData.newPlainText("Test", SpannableString("Test Item")) + val intent = creator.getShareIntent(clipData, context) + + assertEquals(Intent.ACTION_CHOOSER, intent.action) + assertFlags(intent, EXTERNAL_INTENT_FLAGS) + val target = intent.getParcelableExtra(Intent.EXTRA_INTENT, Intent::class.java) + assertEquals("Test Item", target?.getStringExtra(Intent.EXTRA_TEXT)) + assertEquals("text/plain", target?.type) + } + + // Assert that the given flags are set + private fun assertFlags(intent: Intent, flags: Int) { + assertTrue((intent.flags and flags) == flags) + } + + companion object { + private const val EXTERNAL_INTENT_FLAGS: Int = + (Intent.FLAG_ACTIVITY_NEW_TASK or + Intent.FLAG_ACTIVITY_CLEAR_TASK or + Intent.FLAG_GRANT_READ_URI_PERMISSION) + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/IntentCreatorTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/DefaultIntentCreatorTest.java index ea6cb3b6d178..126b3fa9e7ca 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/IntentCreatorTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/DefaultIntentCreatorTest.java @@ -36,13 +36,15 @@ import org.junit.runner.RunWith; @SmallTest @RunWith(AndroidJUnit4.class) -public class IntentCreatorTest extends SysuiTestCase { +public class DefaultIntentCreatorTest extends SysuiTestCase { private static final int EXTERNAL_INTENT_FLAGS = Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_GRANT_READ_URI_PERMISSION; + private final DefaultIntentCreator mIntentCreator = new DefaultIntentCreator(); + @Test public void test_getTextEditorIntent() { - Intent intent = IntentCreator.getTextEditorIntent(getContext()); + Intent intent = mIntentCreator.getTextEditorIntent(getContext()); assertEquals(new ComponentName(getContext(), EditTextActivity.class), intent.getComponent()); assertFlags(intent, Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); @@ -54,7 +56,7 @@ public class IntentCreatorTest extends SysuiTestCase { ""); ClipData clipData = ClipData.newPlainText("Test", "Test Item"); - Intent intent = IntentCreator.getRemoteCopyIntent(clipData, getContext()); + Intent intent = mIntentCreator.getRemoteCopyIntent(clipData, getContext()); assertEquals(null, intent.getComponent()); assertFlags(intent, EXTERNAL_INTENT_FLAGS); @@ -66,7 +68,7 @@ public class IntentCreatorTest extends SysuiTestCase { getContext().getOrCreateTestableResources().addOverride(R.string.config_remoteCopyPackage, fakeComponent.flattenToString()); - intent = IntentCreator.getRemoteCopyIntent(clipData, getContext()); + intent = mIntentCreator.getRemoteCopyIntent(clipData, getContext()); assertEquals(fakeComponent, intent.getComponent()); } @@ -75,7 +77,7 @@ public class IntentCreatorTest extends SysuiTestCase { getContext().getOrCreateTestableResources().addOverride(R.string.config_screenshotEditor, ""); Uri fakeUri = Uri.parse("content://foo"); - Intent intent = IntentCreator.getImageEditIntent(fakeUri, getContext()); + Intent intent = mIntentCreator.getImageEditIntent(fakeUri, getContext()); assertEquals(Intent.ACTION_EDIT, intent.getAction()); assertEquals("image/*", intent.getType()); @@ -88,14 +90,14 @@ public class IntentCreatorTest extends SysuiTestCase { "com.android.remotecopy.RemoteCopyActivity"); getContext().getOrCreateTestableResources().addOverride(R.string.config_screenshotEditor, fakeComponent.flattenToString()); - intent = IntentCreator.getImageEditIntent(fakeUri, getContext()); + intent = mIntentCreator.getImageEditIntent(fakeUri, getContext()); assertEquals(fakeComponent, intent.getComponent()); } @Test public void test_getShareIntent_plaintext() { ClipData clipData = ClipData.newPlainText("Test", "Test Item"); - Intent intent = IntentCreator.getShareIntent(clipData, getContext()); + Intent intent = mIntentCreator.getShareIntent(clipData, getContext()); assertEquals(Intent.ACTION_CHOOSER, intent.getAction()); assertFlags(intent, EXTERNAL_INTENT_FLAGS); @@ -108,7 +110,7 @@ public class IntentCreatorTest extends SysuiTestCase { public void test_getShareIntent_html() { ClipData clipData = ClipData.newHtmlText("Test", "Some HTML", "<b>Some HTML</b>"); - Intent intent = IntentCreator.getShareIntent(clipData, getContext()); + Intent intent = mIntentCreator.getShareIntent(clipData, getContext()); assertEquals(Intent.ACTION_CHOOSER, intent.getAction()); assertFlags(intent, EXTERNAL_INTENT_FLAGS); @@ -122,7 +124,7 @@ public class IntentCreatorTest extends SysuiTestCase { Uri uri = Uri.parse("content://something"); ClipData clipData = new ClipData("Test", new String[]{"image/png"}, new ClipData.Item(uri)); - Intent intent = IntentCreator.getShareIntent(clipData, getContext()); + Intent intent = mIntentCreator.getShareIntent(clipData, getContext()); assertEquals(Intent.ACTION_CHOOSER, intent.getAction()); assertFlags(intent, EXTERNAL_INTENT_FLAGS); @@ -135,7 +137,7 @@ public class IntentCreatorTest extends SysuiTestCase { @Test public void test_getShareIntent_spannableText() { ClipData clipData = ClipData.newPlainText("Test", new SpannableString("Test Item")); - Intent intent = IntentCreator.getShareIntent(clipData, getContext()); + Intent intent = mIntentCreator.getShareIntent(clipData, getContext()); assertEquals(Intent.ACTION_CHOOSER, intent.getAction()); assertFlags(intent, EXTERNAL_INTENT_FLAGS); diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt index f47aa6b3dc03..8dc7a331dc2d 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt @@ -32,6 +32,8 @@ import com.android.systemui.Flags.FLAG_GLANCEABLE_HUB_DIRECT_EDIT_MODE import com.android.systemui.Flags.FLAG_GLANCEABLE_HUB_V2 import com.android.systemui.SysuiTestCase import com.android.systemui.bouncer.data.repository.fakeKeyguardBouncerRepository +import com.android.systemui.common.data.repository.batteryRepository +import com.android.systemui.common.data.repository.fake import com.android.systemui.communal.data.model.CommunalSmartspaceTimer import com.android.systemui.communal.data.repository.FakeCommunalMediaRepository import com.android.systemui.communal.data.repository.FakeCommunalSceneRepository @@ -48,6 +50,7 @@ import com.android.systemui.communal.domain.interactor.communalInteractor import com.android.systemui.communal.domain.interactor.communalSceneInteractor import com.android.systemui.communal.domain.interactor.communalSettingsInteractor import com.android.systemui.communal.domain.interactor.communalTutorialInteractor +import com.android.systemui.communal.domain.interactor.setCommunalV2ConfigEnabled import com.android.systemui.communal.domain.model.CommunalContentModel import com.android.systemui.communal.shared.log.CommunalMetricsLogger import com.android.systemui.communal.shared.model.CommunalContentSize @@ -73,6 +76,8 @@ import com.android.systemui.keyguard.shared.model.StatusBarState import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.keyguard.shared.model.TransitionStep import com.android.systemui.keyguard.ui.transitions.blurConfig +import com.android.systemui.kosmos.collectLastValue +import com.android.systemui.kosmos.runTest import com.android.systemui.kosmos.testDispatcher import com.android.systemui.kosmos.testScope import com.android.systemui.log.logcatLogBuffer @@ -96,6 +101,7 @@ import com.android.systemui.statusbar.KeyguardIndicationController import com.android.systemui.testKosmos import com.android.systemui.user.data.repository.FakeUserRepository import com.android.systemui.user.data.repository.fakeUserRepository +import com.android.systemui.util.settings.fakeSettings import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.test.advanceTimeBy @@ -940,6 +946,36 @@ class CommunalViewModelTest(flags: FlagsParameterization) : SysuiTestCase() { assertThat(isUiBlurred).isFalse() } + @Test + @EnableFlags(FLAG_GLANCEABLE_HUB_V2) + fun swipeToCommunal() = + kosmos.runTest { + setCommunalV2ConfigEnabled(true) + val mainUser = fakeUserRepository.asMainUser() + fakeKeyguardRepository.setKeyguardShowing(true) + fakeUserRepository.setUserUnlocked(mainUser.id, true) + fakeUserTracker.set(userInfos = listOf(mainUser), selectedUserIndex = 0) + fakeSettings.putIntForUser( + Settings.Secure.SCREENSAVER_ACTIVATE_ON_SLEEP, + 1, + mainUser.id, + ) + + val viewModel = createViewModel() + val swipeToHubEnabled by collectLastValue(viewModel.swipeToHubEnabled) + assertThat(swipeToHubEnabled).isFalse() + + batteryRepository.fake.setDevicePluggedIn(true) + assertThat(swipeToHubEnabled).isTrue() + + keyguardTransitionRepository.sendTransitionStep( + from = KeyguardState.LOCKSCREEN, + to = KeyguardState.AOD, + transitionState = TransitionState.STARTED, + ) + assertThat(swipeToHubEnabled).isFalse() + } + private suspend fun setIsMainUser(isMainUser: Boolean) { val user = if (isMainUser) MAIN_USER_INFO else SECONDARY_USER_INFO with(userRepository) { diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/PerDisplayInstanceRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/PerDisplayInstanceRepositoryImplTest.kt index 299105e2dabd..e41d46ce90af 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/PerDisplayInstanceRepositoryImplTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/PerDisplayInstanceRepositoryImplTest.kt @@ -20,6 +20,7 @@ import android.view.Display import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase +import com.android.systemui.dump.dumpManager import com.android.systemui.kosmos.testScope import com.android.systemui.kosmos.useUnconfinedTestDispatcher import com.android.systemui.testKosmos @@ -29,6 +30,9 @@ import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test import org.junit.runner.RunWith +import org.mockito.ArgumentMatchers.anyString +import org.mockito.kotlin.eq +import org.mockito.kotlin.verify @RunWith(AndroidJUnit4::class) @SmallTest @@ -99,6 +103,11 @@ class PerDisplayInstanceRepositoryImplTest : SysuiTestCase() { assertThat(fakePerDisplayInstanceProviderWithTeardown.destroyed).isEmpty() } + @Test + fun start_registersDumpable() { + verify(kosmos.dumpManager).registerNormalDumpable(anyString(), eq(underTest)) + } + private fun createDisplay(displayId: Int): Display = display(type = Display.TYPE_INTERNAL, id = displayId) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java index 2c852c3f6185..94db429c2225 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java @@ -598,7 +598,8 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase { mKeyguardClockPositionAlgorithm, mMSDLPlayer, mBrightnessMirrorShowingRepository, - new BlurConfig(0f, 0f)); + new BlurConfig(0f, 0f), + () -> mKosmos.getFakeShadeDisplaysRepository()); mNotificationPanelViewController.initDependencies( mCentralSurfaces, null, diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java index f54c36754d31..89263fc42739 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java @@ -195,9 +195,7 @@ public class NotificationPanelViewControllerTest extends NotificationPanelViewCo @EnableFlags(Flags.FLAG_SHADE_WINDOW_GOES_AROUND) public void updateSystemUiStateFlags_updatesSysuiStateInteractor() { var DISPLAY_ID = 10; - var displayMock = display(TYPE_INTERNAL, /* flags= */ 0, /* id= */DISPLAY_ID, - /* state= */ null); - when(mView.getDisplay()).thenReturn(displayMock); + mKosmos.getFakeShadeDisplaysRepository().setPendingDisplayId(DISPLAY_ID); mNotificationPanelViewController.updateSystemUiStateFlags(); diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt index 4f301031e77c..fad66581682f 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt @@ -108,7 +108,7 @@ class DefaultClockProviderTest : SysuiTestCase() { verify(mockSmallClockView).setColors(DOZE_COLOR, Color.MAGENTA) verify(mockLargeClockView).setColors(DOZE_COLOR, Color.MAGENTA) - clock.initialize(true, 0f, 0f, {}) + clock.initialize(true, 0f, 0f, null) val expectedColor = 0 verify(mockSmallClockView).setColors(DOZE_COLOR, expectedColor) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/condition/ConditionExtensionsTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/condition/ConditionExtensionsTest.kt index 4bd02e0fab22..17509dc6a80f 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/condition/ConditionExtensionsTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/condition/ConditionExtensionsTest.kt @@ -19,6 +19,10 @@ import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class ConditionExtensionsTest : SysuiTestCase() { private lateinit var testScope: TestScope + private val testCallback = + Condition.Callback { + // This is a no-op + } @Before fun setUp() { @@ -33,7 +37,7 @@ class ConditionExtensionsTest : SysuiTestCase() { assertThat(condition.isConditionSet).isFalse() - condition.start() + condition.testStart() assertThat(condition.isConditionSet).isTrue() assertThat(condition.isConditionMet).isTrue() } @@ -46,7 +50,7 @@ class ConditionExtensionsTest : SysuiTestCase() { assertThat(condition.isConditionSet).isFalse() - condition.start() + condition.testStart() assertThat(condition.isConditionSet).isTrue() assertThat(condition.isConditionMet).isFalse() } @@ -56,7 +60,7 @@ class ConditionExtensionsTest : SysuiTestCase() { testScope.runTest { val flow = emptyFlow<Boolean>() val condition = flow.toCondition(scope = this, Condition.START_EAGERLY) - condition.start() + condition.testStop() assertThat(condition.isConditionSet).isFalse() assertThat(condition.isConditionMet).isFalse() @@ -72,7 +76,7 @@ class ConditionExtensionsTest : SysuiTestCase() { strategy = Condition.START_EAGERLY, initialValue = true, ) - condition.start() + condition.testStart() assertThat(condition.isConditionSet).isTrue() assertThat(condition.isConditionMet).isTrue() @@ -88,7 +92,7 @@ class ConditionExtensionsTest : SysuiTestCase() { strategy = Condition.START_EAGERLY, initialValue = false, ) - condition.start() + condition.testStart() assertThat(condition.isConditionSet).isTrue() assertThat(condition.isConditionMet).isFalse() @@ -99,7 +103,7 @@ class ConditionExtensionsTest : SysuiTestCase() { testScope.runTest { val flow = MutableStateFlow(false) val condition = flow.toCondition(scope = this, strategy = Condition.START_EAGERLY) - condition.start() + condition.testStart() assertThat(condition.isConditionSet).isTrue() assertThat(condition.isConditionMet).isFalse() @@ -110,7 +114,7 @@ class ConditionExtensionsTest : SysuiTestCase() { flow.value = false assertThat(condition.isConditionMet).isFalse() - condition.stop() + condition.testStop() } @Test @@ -120,10 +124,18 @@ class ConditionExtensionsTest : SysuiTestCase() { val condition = flow.toCondition(scope = this, strategy = Condition.START_EAGERLY) assertThat(flow.subscriptionCount.value).isEqualTo(0) - condition.start() + condition.testStart() assertThat(flow.subscriptionCount.value).isEqualTo(1) - condition.stop() + condition.testStop() assertThat(flow.subscriptionCount.value).isEqualTo(0) } + + fun Condition.testStart() { + addCallback(testCallback) + } + + fun Condition.testStop() { + removeCallback(testCallback) + } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/condition/FakeCondition.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/condition/FakeCondition.java index 5416536305fc..da660e2f6009 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/condition/FakeCondition.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/condition/FakeCondition.java @@ -40,7 +40,7 @@ public class FakeCondition extends Condition { } @Override - protected int getStartStrategy() { + public int getStartStrategy() { return START_EAGERLY; } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationStackAppearanceIntegrationTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationStackAppearanceIntegrationTest.kt index 0f1cdac05f7a..fdc6657acd1d 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationStackAppearanceIntegrationTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationStackAppearanceIntegrationTest.kt @@ -31,6 +31,8 @@ import com.android.systemui.kosmos.testScope import com.android.systemui.scene.domain.interactor.sceneInteractor import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.scene.shared.model.fakeSceneDataSource +import com.android.systemui.shade.domain.interactor.enableDualShade +import com.android.systemui.shade.domain.interactor.enableSingleShade import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimBounds import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimShape import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationTransitionThresholds.EXPANSION_FOR_DELAYED_STACK_FADE_IN @@ -69,6 +71,7 @@ class NotificationStackAppearanceIntegrationTest : SysuiTestCase() { @Test fun updateBounds() = testScope.runTest { + kosmos.enableSingleShade() val radius = MutableStateFlow(32) val leftOffset = MutableStateFlow(0) val shape by @@ -106,7 +109,7 @@ class NotificationStackAppearanceIntegrationTest : SysuiTestCase() { ) ) - // When: QuickSettings shows up full screen + // When: QuickSettings shows up full screen on single shade. fakeKeyguardRepository.setStatusBarState(StatusBarState.SHADE) val transitionState = MutableStateFlow<ObservableTransitionState>( @@ -115,6 +118,10 @@ class NotificationStackAppearanceIntegrationTest : SysuiTestCase() { sceneInteractor.setTransitionState(transitionState) // Then: shape is null assertThat(shape).isNull() + + // Same scenario on Dual Shade, shape should have clipping bounds + kosmos.enableDualShade() + assertThat(shape).isNotNull() } @Test diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorImplTest.kt index 216fd2d54a1e..3116143504eb 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorImplTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorImplTest.kt @@ -57,7 +57,8 @@ class PromotedNotificationContentExtractorImplTest : SysuiTestCase() { private val underTest = kosmos.promotedNotificationContentExtractor private val systemClock = kosmos.fakeSystemClock - private val rowImageInflater = RowImageInflater.newInstance(previousIndex = null) + private val rowImageInflater = + RowImageInflater.newInstance(previousIndex = null, reinflating = false) private val imageModelProvider by lazy { rowImageInflater.useForContentModel() } @Test diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImplTest.kt index ce3aee1d88d2..31413b06e5fd 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImplTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImplTest.kt @@ -41,7 +41,6 @@ import com.android.systemui.statusbar.NotificationLockscreenUserManager.REDACTIO import com.android.systemui.statusbar.NotificationLockscreenUserManager.RedactionType import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips import com.android.systemui.statusbar.notification.ConversationNotificationProcessor -import com.android.systemui.statusbar.notification.collection.EntryAdapter import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.promoted.FakePromotedNotificationContentExtractor import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUi @@ -204,7 +203,7 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() { val result = NotificationRowContentBinderImpl.InflationProgress( packageContext = mContext, - rowImageInflater = RowImageInflater.newInstance(null), + rowImageInflater = RowImageInflater.newInstance(null, reinflating = false), remoteViews = NewRemoteViews(), contentModel = NotificationContentModel(headsUpStatusBarModel), promotedContent = null, diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockController.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockController.kt index 4c1f6450dd78..b52db83d513c 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockController.kt +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockController.kt @@ -42,9 +42,13 @@ interface ClockController { isDarkTheme: Boolean, dozeFraction: Float, foldFraction: Float, - onBoundsChanged: (RectF) -> Unit, + clockListener: ClockEventListener?, ) /** Optional method for dumping debug information */ fun dump(pw: PrintWriter) } + +interface ClockEventListener { + fun onBoundsChanged(bounds: RectF) +} diff --git a/packages/SystemUI/shared/src/com/android/systemui/dagger/qualifiers/DisplaySpecific.kt b/packages/SystemUI/pods/com/android/systemui/dagger/qualifiers/DisplaySpecific.kt index 3e39ae9a3fe5..0c46e0784a31 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/dagger/qualifiers/DisplaySpecific.kt +++ b/packages/SystemUI/pods/com/android/systemui/dagger/qualifiers/DisplaySpecific.kt @@ -18,4 +18,7 @@ package com.android.systemui.dagger.qualifiers import javax.inject.Qualifier /** Annotates a class that is display specific. */ -@Qualifier @MustBeDocumented @Retention(AnnotationRetention.RUNTIME) annotation class DisplaySpecific +@Qualifier +@MustBeDocumented +@Retention(AnnotationRetention.RUNTIME) +public annotation class DisplaySpecific diff --git a/packages/SystemUI/shared/src/com/android/systemui/dagger/qualifiers/Main.java b/packages/SystemUI/pods/com/android/systemui/dagger/qualifiers/Main.java index 7b097740ff11..7b097740ff11 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/dagger/qualifiers/Main.java +++ b/packages/SystemUI/pods/com/android/systemui/dagger/qualifiers/Main.java diff --git a/packages/SystemUI/shared/src/com/android/systemui/dagger/qualifiers/UiBackground.java b/packages/SystemUI/pods/com/android/systemui/dagger/qualifiers/UiBackground.java index 3d37468c322a..3d37468c322a 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/dagger/qualifiers/UiBackground.java +++ b/packages/SystemUI/pods/com/android/systemui/dagger/qualifiers/UiBackground.java diff --git a/packages/SystemUI/shared/Android.bp b/packages/SystemUI/shared/Android.bp index ae3a76e2d2ca..8d7cab41ea20 100644 --- a/packages/SystemUI/shared/Android.bp +++ b/packages/SystemUI/shared/Android.bp @@ -51,6 +51,7 @@ android_library { ":wm_shell-shared-aidls", ], static_libs: [ + "com.android.systemui.dagger-api", "BiometricsSharedLib", "PlatformAnimationLib", "PluginCoreLib", diff --git a/packages/SystemUI/shared/src/com/android/systemui/dagger/qualifiers/Tracing.kt b/packages/SystemUI/shared/src/com/android/systemui/dagger/qualifiers/Tracing.kt deleted file mode 100644 index 9b7cd704aa2f..000000000000 --- a/packages/SystemUI/shared/src/com/android/systemui/dagger/qualifiers/Tracing.kt +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.systemui.dagger.qualifiers - -import javax.inject.Qualifier - -@Qualifier @MustBeDocumented @Retention(AnnotationRetention.RUNTIME) annotation class Tracing diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/condition/CombinedCondition.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/condition/CombinedCondition.kt index a2b6e2cf9ae3..f276a82baaef 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/condition/CombinedCondition.kt +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/condition/CombinedCondition.kt @@ -41,7 +41,7 @@ class CombinedCondition constructor( private val scope: CoroutineScope, private val conditions: Collection<Condition>, - @Evaluator.ConditionOperand private val operand: Int + @Evaluator.ConditionOperand private val operand: Int, ) : Condition(scope, null, false) { private var job: Job? = null @@ -54,7 +54,7 @@ constructor( lazilyEvaluate( conditions = groupedConditions.getOrDefault(true, emptyList()), - filterUnknown = true + filterUnknown = true, ) .distinctUntilChanged() .flatMapLatest { overriddenValue -> @@ -62,7 +62,7 @@ constructor( if (overriddenValue == null) { lazilyEvaluate( conditions = groupedConditions.getOrDefault(false, emptyList()), - filterUnknown = false + filterUnknown = false, ) } else { flowOf(overriddenValue) @@ -188,7 +188,6 @@ constructor( return startStrategy } - override fun getStartStrategy(): Int { - return _startStrategy - } + override val startStrategy: Int + get() = _startStrategy } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/condition/Condition.java b/packages/SystemUI/shared/src/com/android/systemui/shared/condition/Condition.java deleted file mode 100644 index 670feeba6e8a..000000000000 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/condition/Condition.java +++ /dev/null @@ -1,306 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.shared.condition; - -import android.util.Log; - -import androidx.annotation.IntDef; -import androidx.annotation.NonNull; -import androidx.lifecycle.Lifecycle; -import androidx.lifecycle.LifecycleEventObserver; -import androidx.lifecycle.LifecycleOwner; - -import kotlinx.coroutines.CoroutineScope; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.ref.WeakReference; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Iterator; -import java.util.List; - -/** - * Base class for a condition that needs to be fulfilled in order for {@link Monitor} to inform - * its callbacks. - */ -public abstract class Condition { - private final String mTag = getClass().getSimpleName(); - - private final ArrayList<WeakReference<Callback>> mCallbacks = new ArrayList<>(); - private final boolean mOverriding; - private final CoroutineScope mScope; - private Boolean mIsConditionMet; - private boolean mStarted = false; - - /** - * By default, conditions have an initial value of false and are not overriding. - */ - public Condition(CoroutineScope scope) { - this(scope, false, false); - } - - /** - * Constructor for specifying initial state and overriding condition attribute. - * - * @param initialConditionMet Initial state of the condition. - * @param overriding Whether this condition overrides others. - */ - protected Condition(CoroutineScope scope, Boolean initialConditionMet, boolean overriding) { - mIsConditionMet = initialConditionMet; - mOverriding = overriding; - mScope = scope; - } - - /** - * Starts monitoring the condition. - */ - protected abstract void start(); - - /** - * Stops monitoring the condition. - */ - protected abstract void stop(); - - /** - * Condition should be started as soon as there is an active subscription. - */ - public static final int START_EAGERLY = 0; - /** - * Condition should be started lazily only if needed. But once started, it will not be cancelled - * unless there are no more active subscriptions. - */ - public static final int START_LAZILY = 1; - /** - * Condition should be started lazily only if needed, and can be stopped when not needed. This - * should be used for conditions which are expensive to keep running. - */ - public static final int START_WHEN_NEEDED = 2; - - @Retention(RetentionPolicy.SOURCE) - @IntDef({START_EAGERLY, START_LAZILY, START_WHEN_NEEDED}) - @interface StartStrategy { - } - - @StartStrategy - protected abstract int getStartStrategy(); - - /** - * Returns whether the current condition overrides - */ - public boolean isOverridingCondition() { - return mOverriding; - } - - /** - * Registers a callback to receive updates once started. This should be called before - * {@link #start()}. Also triggers the callback immediately if already started. - */ - public void addCallback(@NonNull Callback callback) { - if (shouldLog()) Log.d(mTag, "adding callback"); - mCallbacks.add(new WeakReference<>(callback)); - - if (mStarted) { - callback.onConditionChanged(this); - return; - } - - start(); - mStarted = true; - } - - /** - * Removes the provided callback from further receiving updates. - */ - public void removeCallback(@NonNull Callback callback) { - if (shouldLog()) Log.d(mTag, "removing callback"); - final Iterator<WeakReference<Callback>> iterator = mCallbacks.iterator(); - while (iterator.hasNext()) { - final Callback cb = iterator.next().get(); - if (cb == null || cb == callback) { - iterator.remove(); - } - } - - if (!mCallbacks.isEmpty() || !mStarted) { - return; - } - - stop(); - mStarted = false; - } - - /** - * Wrapper to {@link #addCallback(Callback)} when a lifecycle is in the resumed state - * and {@link #removeCallback(Callback)} when not resumed automatically. - */ - public Callback observe(LifecycleOwner owner, Callback listener) { - return observe(owner.getLifecycle(), listener); - } - - /** - * Wrapper to {@link #addCallback(Callback)} when a lifecycle is in the resumed state - * and {@link #removeCallback(Condition.Callback)} when not resumed automatically. - */ - public Callback observe(Lifecycle lifecycle, Callback listener) { - lifecycle.addObserver((LifecycleEventObserver) (lifecycleOwner, event) -> { - if (event == Lifecycle.Event.ON_RESUME) { - addCallback(listener); - } else if (event == Lifecycle.Event.ON_PAUSE) { - removeCallback(listener); - } - }); - return listener; - } - - /** - * Updates the value for whether the condition has been fulfilled, and sends an update if the - * value changes and any callback is registered. - * - * @param isConditionMet True if the condition has been fulfilled. False otherwise. - */ - protected void updateCondition(boolean isConditionMet) { - if (mIsConditionMet != null && mIsConditionMet == isConditionMet) { - return; - } - - if (shouldLog()) Log.d(mTag, "updating condition to " + isConditionMet); - mIsConditionMet = isConditionMet; - sendUpdate(); - } - - /** - * Clears the set condition value. This is purposefully separate from - * {@link #updateCondition(boolean)} to avoid confusion around {@code null} values. - */ - protected void clearCondition() { - if (mIsConditionMet == null) { - return; - } - - if (shouldLog()) Log.d(mTag, "clearing condition"); - - mIsConditionMet = null; - sendUpdate(); - } - - private void sendUpdate() { - final Iterator<WeakReference<Callback>> iterator = mCallbacks.iterator(); - while (iterator.hasNext()) { - final Callback cb = iterator.next().get(); - if (cb == null) { - iterator.remove(); - } else { - cb.onConditionChanged(this); - } - } - } - - /** - * Returns whether the condition is set. This method should be consulted to understand the - * value of {@link #isConditionMet()}. - * - * @return {@code true} if value is present, {@code false} otherwise. - */ - public boolean isConditionSet() { - return mIsConditionMet != null; - } - - /** - * Returns whether the condition has been met. Note that this method will return {@code false} - * if the condition is not set as well. - */ - public boolean isConditionMet() { - return Boolean.TRUE.equals(mIsConditionMet); - } - - protected final boolean shouldLog() { - return Log.isLoggable(mTag, Log.DEBUG); - } - - protected final String getTag() { - if (isOverridingCondition()) { - return mTag + "[OVRD]"; - } - - return mTag; - } - - /** - * Returns the state of the condition. - * - "Invalid", condition hasn't been set / not monitored - * - "True", condition has been met - * - "False", condition has not been met - */ - protected final String getState() { - if (!isConditionSet()) { - return "Invalid"; - } - return isConditionMet() ? "True" : "False"; - } - - /** - * Creates a new condition which will only be true when both this condition and all the provided - * conditions are true. - */ - public Condition and(@NonNull Collection<Condition> others) { - final List<Condition> conditions = new ArrayList<>(); - conditions.add(this); - conditions.addAll(others); - return new CombinedCondition(mScope, conditions, Evaluator.OP_AND); - } - - /** - * Creates a new condition which will only be true when both this condition and the provided - * condition is true. - */ - public Condition and(@NonNull Condition... others) { - return and(Arrays.asList(others)); - } - - /** - * Creates a new condition which will only be true when either this condition or any of the - * provided conditions are true. - */ - public Condition or(@NonNull Collection<Condition> others) { - final List<Condition> conditions = new ArrayList<>(); - conditions.add(this); - conditions.addAll(others); - return new CombinedCondition(mScope, conditions, Evaluator.OP_OR); - } - - /** - * Creates a new condition which will only be true when either this condition or the provided - * condition is true. - */ - public Condition or(@NonNull Condition... others) { - return or(Arrays.asList(others)); - } - - /** - * Callback that receives updates about whether the condition has been fulfilled. - */ - public interface Callback { - /** - * Called when the fulfillment of the condition changes. - * - * @param condition The condition in question. - */ - void onConditionChanged(Condition condition); - } -} diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/condition/Condition.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/condition/Condition.kt new file mode 100644 index 000000000000..69236b48ed51 --- /dev/null +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/condition/Condition.kt @@ -0,0 +1,270 @@ +/* + * Copyright (C) 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.systemui.shared.condition + +import android.util.Log +import androidx.annotation.IntDef +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.LifecycleEventObserver +import androidx.lifecycle.LifecycleOwner +import java.lang.ref.WeakReference +import kotlinx.coroutines.CoroutineScope + +/** + * Base class for a condition that needs to be fulfilled in order for [Monitor] to inform its + * callbacks. + */ +abstract class Condition +/** + * Constructor for specifying initial state and overriding condition attribute. + * + * @param initialConditionMet Initial state of the condition. + * @param overriding Whether this condition overrides others. + */ +@JvmOverloads +protected constructor( + private val _scope: CoroutineScope, + private var _isConditionMet: Boolean? = false, + /** Returns whether the current condition overrides */ + val isOverridingCondition: Boolean = false, +) { + private val mTag: String = javaClass.simpleName + + private val _callbacks = mutableListOf<WeakReference<Callback>>() + private var _started = false + + /** Starts monitoring the condition. */ + protected abstract fun start() + + /** Stops monitoring the condition. */ + protected abstract fun stop() + + @Retention(AnnotationRetention.SOURCE) + @IntDef(START_EAGERLY, START_LAZILY, START_WHEN_NEEDED) + annotation class StartStrategy + + @get:StartStrategy abstract val startStrategy: Int + + /** + * Registers a callback to receive updates once started. This should be called before [.start]. + * Also triggers the callback immediately if already started. + */ + fun addCallback(callback: Callback) { + if (shouldLog()) Log.d(mTag, "adding callback") + _callbacks.add(WeakReference(callback)) + + if (_started) { + callback.onConditionChanged(this) + return + } + + start() + _started = true + } + + /** Removes the provided callback from further receiving updates. */ + fun removeCallback(callback: Callback) { + if (shouldLog()) Log.d(mTag, "removing callback") + val iterator = _callbacks.iterator() + while (iterator.hasNext()) { + val cb = iterator.next().get() + if (cb == null || cb === callback) { + iterator.remove() + } + } + + if (_callbacks.isNotEmpty() || !_started) { + return + } + + stop() + _started = false + } + + /** + * Wrapper to [.addCallback] when a lifecycle is in the resumed state and [.removeCallback] when + * not resumed automatically. + */ + fun observe(owner: LifecycleOwner, listener: Callback): Callback { + return observe(owner.lifecycle, listener) + } + + /** + * Wrapper to [.addCallback] when a lifecycle is in the resumed state and [.removeCallback] when + * not resumed automatically. + */ + fun observe(lifecycle: Lifecycle, listener: Callback): Callback { + lifecycle.addObserver( + LifecycleEventObserver { lifecycleOwner: LifecycleOwner?, event: Lifecycle.Event -> + if (event == Lifecycle.Event.ON_RESUME) { + addCallback(listener) + } else if (event == Lifecycle.Event.ON_PAUSE) { + removeCallback(listener) + } + } + ) + return listener + } + + /** + * Updates the value for whether the condition has been fulfilled, and sends an update if the + * value changes and any callback is registered. + * + * @param isConditionMet True if the condition has been fulfilled. False otherwise. + */ + protected fun updateCondition(isConditionMet: Boolean) { + if (_isConditionMet != null && _isConditionMet == isConditionMet) { + return + } + + if (shouldLog()) Log.d(mTag, "updating condition to $isConditionMet") + _isConditionMet = isConditionMet + sendUpdate() + } + + /** + * Clears the set condition value. This is purposefully separate from [.updateCondition] to + * avoid confusion around `null` values. + */ + fun clearCondition() { + if (_isConditionMet == null) { + return + } + + if (shouldLog()) Log.d(mTag, "clearing condition") + + _isConditionMet = null + sendUpdate() + } + + private fun sendUpdate() { + val iterator = _callbacks.iterator() + while (iterator.hasNext()) { + val cb = iterator.next().get() + if (cb == null) { + iterator.remove() + } else { + cb.onConditionChanged(this) + } + } + } + + val isConditionSet: Boolean + /** + * Returns whether the condition is set. This method should be consulted to understand the + * value of [.isConditionMet]. + * + * @return `true` if value is present, `false` otherwise. + */ + get() = _isConditionMet != null + + val isConditionMet: Boolean + /** + * Returns whether the condition has been met. Note that this method will return `false` if + * the condition is not set as well. + */ + get() = true == _isConditionMet + + protected fun shouldLog(): Boolean { + return Log.isLoggable(mTag, Log.DEBUG) + } + + val tag: String + get() { + if (isOverridingCondition) { + return "$mTag[OVRD]" + } + + return mTag + } + + val state: String + /** + * Returns the state of the condition. + * - "Invalid", condition hasn't been set / not monitored + * - "True", condition has been met + * - "False", condition has not been met + */ + get() { + if (!isConditionSet) { + return "Invalid" + } + return if (isConditionMet) "True" else "False" + } + + /** + * Creates a new condition which will only be true when both this condition and all the provided + * conditions are true. + */ + fun and(others: Collection<Condition>): Condition { + val conditions: List<Condition> = listOf(this, *others.toTypedArray()) + return CombinedCondition(_scope, conditions, Evaluator.OP_AND) + } + + /** + * Creates a new condition which will only be true when both this condition and the provided + * condition is true. + */ + fun and(vararg others: Condition): Condition { + return and(listOf(*others)) + } + + /** + * Creates a new condition which will only be true when either this condition or any of the + * provided conditions are true. + */ + fun or(others: Collection<Condition>): Condition { + val conditions: MutableList<Condition> = ArrayList() + conditions.add(this) + conditions.addAll(others) + return CombinedCondition(_scope, conditions, Evaluator.OP_OR) + } + + /** + * Creates a new condition which will only be true when either this condition or the provided + * condition is true. + */ + fun or(vararg others: Condition): Condition { + return or(listOf(*others)) + } + + /** Callback that receives updates about whether the condition has been fulfilled. */ + fun interface Callback { + /** + * Called when the fulfillment of the condition changes. + * + * @param condition The condition in question. + */ + fun onConditionChanged(condition: Condition) + } + + companion object { + /** Condition should be started as soon as there is an active subscription. */ + const val START_EAGERLY: Int = 0 + + /** + * Condition should be started lazily only if needed. But once started, it will not be + * cancelled unless there are no more active subscriptions. + */ + const val START_LAZILY: Int = 1 + + /** + * Condition should be started lazily only if needed, and can be stopped when not needed. + * This should be used for conditions which are expensive to keep running. + */ + const val START_WHEN_NEEDED: Int = 2 + } +} diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/condition/ConditionExtensions.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/condition/ConditionExtensions.kt index 84edc3577007..0f535cdfa5cc 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/condition/ConditionExtensions.kt +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/condition/ConditionExtensions.kt @@ -14,7 +14,7 @@ import kotlinx.coroutines.launch fun Flow<Boolean>.toCondition( scope: CoroutineScope, @StartStrategy strategy: Int, - initialValue: Boolean? = null + initialValue: Boolean? = null, ): Condition { return object : Condition(scope, initialValue, false) { var job: Job? = null @@ -28,7 +28,8 @@ fun Flow<Boolean>.toCondition( job = null } - override fun getStartStrategy() = strategy + override val startStrategy: Int + get() = strategy } } @@ -36,13 +37,16 @@ fun Flow<Boolean>.toCondition( fun Condition.toFlow(): Flow<Boolean?> { return callbackFlow { val callback = - Condition.Callback { condition -> - if (condition.isConditionSet) { - trySend(condition.isConditionMet) - } else { - trySend(null) + object : Condition.Callback { + override fun onConditionChanged(condition: Condition) { + if (condition.isConditionSet) { + trySend(condition.isConditionMet) + } else { + trySend(null) + } } } + addCallback(callback) callback.onConditionChanged(this@toFlow) awaitClose { removeCallback(callback) } diff --git a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt index e827b2d54a19..1549b699eee6 100644 --- a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt +++ b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt @@ -56,6 +56,7 @@ import com.android.systemui.log.core.Logger import com.android.systemui.modes.shared.ModesUi import com.android.systemui.plugins.clocks.AlarmData import com.android.systemui.plugins.clocks.ClockController +import com.android.systemui.plugins.clocks.ClockEventListener import com.android.systemui.plugins.clocks.ClockFaceController import com.android.systemui.plugins.clocks.ClockMessageBuffers import com.android.systemui.plugins.clocks.ClockTickRate @@ -148,7 +149,7 @@ constructor( val clockStr = clock.toString() loggers.forEach { it.d({ "New Clock: $str1" }) { str1 = clockStr } } - clock.initialize(isDarkTheme(), dozeAmount.value, 0f, { onClockBoundsChanged.value = it }) + clock.initialize(isDarkTheme(), dozeAmount.value, 0f, clockListener) if (!regionSamplingEnabled) { updateColors() @@ -312,6 +313,13 @@ constructor( private var zenData: ZenData? = null private var alarmData: AlarmData? = null + private val clockListener = + object : ClockEventListener { + override fun onBoundsChanged(bounds: RectF) { + onClockBoundsChanged.value = bounds + } + } + private val configListener = object : ConfigurationController.ConfigurationListener { override fun onThemeChanged() { diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesChecker.java b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesChecker.java index 2d1cd03aea4d..20290f7b5373 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesChecker.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesChecker.java @@ -99,10 +99,7 @@ public class HearingDevicesChecker { private boolean isExclusivelyManagedBluetoothDevice( @NonNull CachedBluetoothDevice cachedDevice) { - if (com.android.settingslib.flags.Flags.enableHideExclusivelyManagedBluetoothDevice()) { - return BluetoothUtils.isExclusivelyManagedBluetoothDevice(mContext, - cachedDevice.getDevice()); - } - return false; + return BluetoothUtils.isExclusivelyManagedBluetoothDevice(mContext, + cachedDevice.getDevice()); } } diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemFactory.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemFactory.kt index bfbc27d3a086..858cc00b86b9 100644 --- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemFactory.kt +++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemFactory.kt @@ -21,7 +21,6 @@ import android.content.Context import com.android.settingslib.bluetooth.BluetoothUtils import com.android.settingslib.bluetooth.CachedBluetoothDevice import com.android.settingslib.bluetooth.LocalBluetoothManager -import com.android.settingslib.flags.Flags import com.android.systemui.res.R private val backgroundOn = R.drawable.settingslib_switch_bar_bg_on @@ -222,12 +221,8 @@ internal class ConnectedDeviceItemFactory : DeviceItemFactory() { isOngoingCall: Boolean, audioSharingAvailable: Boolean, ): Boolean { - return if (Flags.enableHideExclusivelyManagedBluetoothDevice()) { - !BluetoothUtils.isExclusivelyManagedBluetoothDevice(context, cachedDevice.device) && - BluetoothUtils.isConnectedBluetoothDevice(cachedDevice, isOngoingCall) - } else { + return !BluetoothUtils.isExclusivelyManagedBluetoothDevice(context, cachedDevice.device) && BluetoothUtils.isConnectedBluetoothDevice(cachedDevice, isOngoingCall) - } } override fun create(context: Context, cachedDevice: CachedBluetoothDevice): DeviceItem { @@ -250,13 +245,9 @@ internal open class SavedDeviceItemFactory : DeviceItemFactory() { isOngoingCall: Boolean, audioSharingAvailable: Boolean, ): Boolean { - return if (Flags.enableHideExclusivelyManagedBluetoothDevice()) { - !BluetoothUtils.isExclusivelyManagedBluetoothDevice(context, cachedDevice.device) && - cachedDevice.bondState == BluetoothDevice.BOND_BONDED && - !cachedDevice.isConnected - } else { - cachedDevice.bondState == BluetoothDevice.BOND_BONDED && !cachedDevice.isConnected - } + return !BluetoothUtils.isExclusivelyManagedBluetoothDevice(context, cachedDevice.device) && + cachedDevice.bondState == BluetoothDevice.BOND_BONDED && + !cachedDevice.isConnected } override fun create(context: Context, cachedDevice: CachedBluetoothDevice): DeviceItem { @@ -279,18 +270,12 @@ internal class SavedHearingDeviceItemFactory : SavedDeviceItemFactory() { isOngoingCall: Boolean, audioSharingAvailable: Boolean, ): Boolean { - return if (Flags.enableHideExclusivelyManagedBluetoothDevice()) { - !BluetoothUtils.isExclusivelyManagedBluetoothDevice( - context, - cachedDevice.getDevice(), - ) && - cachedDevice.isHearingAidDevice && - cachedDevice.bondState == BluetoothDevice.BOND_BONDED && - !cachedDevice.isConnected - } else { + return !BluetoothUtils.isExclusivelyManagedBluetoothDevice( + context, + cachedDevice.getDevice(), + ) && cachedDevice.isHearingAidDevice && - cachedDevice.bondState == BluetoothDevice.BOND_BONDED && - !cachedDevice.isConnected - } + cachedDevice.bondState == BluetoothDevice.BOND_BONDED && + !cachedDevice.isConnected } } diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ActionIntentCreator.kt b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ActionIntentCreator.kt new file mode 100644 index 000000000000..df6c1b18e3e9 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ActionIntentCreator.kt @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.clipboardoverlay + +import android.content.ClipData +import android.content.ClipDescription +import android.content.ComponentName +import android.content.Context +import android.content.Intent +import android.net.Uri +import android.text.TextUtils +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.res.R +import javax.inject.Inject + +@SysUISingleton +class ActionIntentCreator @Inject constructor() : IntentCreator { + override fun getTextEditorIntent(context: Context?) = + Intent(context, EditTextActivity::class.java).apply { + addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK) + } + + override fun getShareIntent(clipData: ClipData, context: Context?): Intent { + val shareIntent = Intent(Intent.ACTION_SEND) + + // From the ACTION_SEND docs: + // "If using EXTRA_TEXT, the MIME type should be "text/plain"; otherwise it should be the + // MIME type of the data in EXTRA_STREAM" + val uri = clipData.getItemAt(0).uri + shareIntent.apply { + if (uri != null) { + // We don't use setData here because some apps interpret this as "to:". + setType(clipData.description.getMimeType(0)) + // Include URI in ClipData also, so that grantPermission picks it up. + setClipData( + ClipData( + ClipDescription("content", arrayOf(clipData.description.getMimeType(0))), + ClipData.Item(uri), + ) + ) + putExtra(Intent.EXTRA_STREAM, uri) + addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) + } else { + putExtra(Intent.EXTRA_TEXT, clipData.getItemAt(0).coerceToText(context).toString()) + setType("text/plain") + } + } + + return Intent.createChooser(shareIntent, null) + .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK) + .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) + } + + override fun getImageEditIntent(uri: Uri?, context: Context): Intent { + val editorPackage = context.getString(R.string.config_screenshotEditor) + return Intent(Intent.ACTION_EDIT).apply { + if (!TextUtils.isEmpty(editorPackage)) { + setComponent(ComponentName.unflattenFromString(editorPackage)) + } + setDataAndType(uri, "image/*") + addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) + addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK) + putExtra(EXTRA_EDIT_SOURCE, EDIT_SOURCE_CLIPBOARD) + } + } + + override fun getRemoteCopyIntent(clipData: ClipData?, context: Context): Intent { + val remoteCopyPackage = context.getString(R.string.config_remoteCopyPackage) + return Intent(REMOTE_COPY_ACTION).apply { + if (!TextUtils.isEmpty(remoteCopyPackage)) { + setComponent(ComponentName.unflattenFromString(remoteCopyPackage)) + } + + setClipData(clipData) + addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) + addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK) + } + } + + companion object { + private const val EXTRA_EDIT_SOURCE: String = "edit_source" + private const val EDIT_SOURCE_CLIPBOARD: String = "clipboard" + private const val REMOTE_COPY_ACTION: String = "android.intent.action.REMOTE_COPY" + } +} diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java index ac747845267c..314b6e7f5a28 100644 --- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java +++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java @@ -65,7 +65,6 @@ import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.broadcast.BroadcastSender; import com.android.systemui.clipboardoverlay.dagger.ClipboardOverlayModule.OverlayWindowContext; import com.android.systemui.dagger.qualifiers.Background; -import com.android.systemui.flags.FeatureFlags; import com.android.systemui.res.R; import com.android.systemui.screenshot.TimeoutHandler; @@ -94,13 +93,13 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv private final ClipboardOverlayWindow mWindow; private final TimeoutHandler mTimeoutHandler; private final ClipboardOverlayUtils mClipboardUtils; - private final FeatureFlags mFeatureFlags; private final Executor mBgExecutor; private final ClipboardImageLoader mClipboardImageLoader; private final ClipboardTransitionExecutor mTransitionExecutor; private final ClipboardOverlayView mView; private final ClipboardIndicationProvider mClipboardIndicationProvider; + private final IntentCreator mIntentCreator; private Runnable mOnSessionCompleteListener; private Runnable mOnRemoteCopyTapped; @@ -189,13 +188,13 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv BroadcastDispatcher broadcastDispatcher, BroadcastSender broadcastSender, TimeoutHandler timeoutHandler, - FeatureFlags featureFlags, ClipboardOverlayUtils clipboardUtils, @Background Executor bgExecutor, ClipboardImageLoader clipboardImageLoader, ClipboardTransitionExecutor transitionExecutor, ClipboardIndicationProvider clipboardIndicationProvider, - UiEventLogger uiEventLogger) { + UiEventLogger uiEventLogger, + IntentCreator intentCreator) { mContext = context; mBroadcastDispatcher = broadcastDispatcher; mClipboardImageLoader = clipboardImageLoader; @@ -203,6 +202,7 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv mClipboardIndicationProvider = clipboardIndicationProvider; mClipboardLogger = new ClipboardLogger(uiEventLogger); + mIntentCreator = intentCreator; mView = clipboardOverlayView; mWindow = clipboardOverlayWindow; @@ -211,7 +211,6 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv hideImmediate(); }); - mFeatureFlags = featureFlags; mTimeoutHandler = timeoutHandler; mTimeoutHandler.setDefaultTimeoutMillis(CLIPBOARD_DEFAULT_TIMEOUT_MILLIS); @@ -508,7 +507,8 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv } private void maybeShowRemoteCopy(ClipData clipData) { - Intent remoteCopyIntent = IntentCreator.getRemoteCopyIntent(clipData, mContext); + Intent remoteCopyIntent = mIntentCreator.getRemoteCopyIntent(clipData, mContext); + // Only show remote copy if it's available. PackageManager packageManager = mContext.getPackageManager(); if (packageManager.resolveActivity( @@ -558,19 +558,19 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv private void editImage(Uri uri) { mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_EDIT_TAPPED); - mContext.startActivity(IntentCreator.getImageEditIntent(uri, mContext)); + mContext.startActivity(mIntentCreator.getImageEditIntent(uri, mContext)); animateOut(); } private void editText() { mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_EDIT_TAPPED); - mContext.startActivity(IntentCreator.getTextEditorIntent(mContext)); + mContext.startActivity(mIntentCreator.getTextEditorIntent(mContext)); animateOut(); } private void shareContent(ClipData clip) { mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_SHARE_TAPPED); - mContext.startActivity(IntentCreator.getShareIntent(clip, mContext)); + mContext.startActivity(mIntentCreator.getShareIntent(clip, mContext)); animateOut(); } @@ -717,22 +717,22 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv public void onRemoteCopyButtonTapped() { if (clipboardSharedTransitions()) { finish(CLIPBOARD_OVERLAY_REMOTE_COPY_TAPPED, - IntentCreator.getRemoteCopyIntent(mClipboardModel.getClipData(), mContext)); + mIntentCreator.getRemoteCopyIntent(mClipboardModel.getClipData(), mContext)); } } @Override public void onShareButtonTapped() { if (clipboardSharedTransitions()) { + Intent shareIntent = + mIntentCreator.getShareIntent(mClipboardModel.getClipData(), mContext); switch (mClipboardModel.getType()) { case TEXT: case URI: - finish(CLIPBOARD_OVERLAY_SHARE_TAPPED, - IntentCreator.getShareIntent(mClipboardModel.getClipData(), mContext)); + finish(CLIPBOARD_OVERLAY_SHARE_TAPPED, shareIntent); break; case IMAGE: - finishWithSharedTransition(CLIPBOARD_OVERLAY_SHARE_TAPPED, - IntentCreator.getShareIntent(mClipboardModel.getClipData(), mContext)); + finishWithSharedTransition(CLIPBOARD_OVERLAY_SHARE_TAPPED, shareIntent); break; } } @@ -744,11 +744,11 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv switch (mClipboardModel.getType()) { case TEXT: finish(CLIPBOARD_OVERLAY_EDIT_TAPPED, - IntentCreator.getTextEditorIntent(mContext)); + mIntentCreator.getTextEditorIntent(mContext)); break; case IMAGE: finishWithSharedTransition(CLIPBOARD_OVERLAY_EDIT_TAPPED, - IntentCreator.getImageEditIntent(mClipboardModel.getUri(), mContext)); + mIntentCreator.getImageEditIntent(mClipboardModel.getUri(), mContext)); break; default: Log.w(TAG, "Got preview tapped callback for non-editable type " diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/DefaultIntentCreator.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/DefaultIntentCreator.java new file mode 100644 index 000000000000..4b24536ad28f --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/DefaultIntentCreator.java @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.clipboardoverlay; + +import android.content.ClipData; +import android.content.ClipDescription; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.net.Uri; +import android.text.TextUtils; + +import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.res.R; + +import javax.inject.Inject; + +@SysUISingleton +public class DefaultIntentCreator implements IntentCreator { + private static final String EXTRA_EDIT_SOURCE = "edit_source"; + private static final String EDIT_SOURCE_CLIPBOARD = "clipboard"; + private static final String REMOTE_COPY_ACTION = "android.intent.action.REMOTE_COPY"; + + @Inject + public DefaultIntentCreator() {} + + public Intent getTextEditorIntent(Context context) { + Intent intent = new Intent(context, EditTextActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); + return intent; + } + + public Intent getShareIntent(ClipData clipData, Context context) { + Intent shareIntent = new Intent(Intent.ACTION_SEND); + + // From the ACTION_SEND docs: + // "If using EXTRA_TEXT, the MIME type should be "text/plain"; otherwise it should be the + // MIME type of the data in EXTRA_STREAM" + Uri uri = clipData.getItemAt(0).getUri(); + if (uri != null) { + // We don't use setData here because some apps interpret this as "to:". + shareIntent.setType(clipData.getDescription().getMimeType(0)); + // Include URI in ClipData also, so that grantPermission picks it up. + shareIntent.setClipData(new ClipData( + new ClipDescription( + "content", new String[]{clipData.getDescription().getMimeType(0)}), + new ClipData.Item(uri))); + shareIntent.putExtra(Intent.EXTRA_STREAM, uri); + shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + } else { + shareIntent.putExtra( + Intent.EXTRA_TEXT, clipData.getItemAt(0).coerceToText(context).toString()); + shareIntent.setType("text/plain"); + } + Intent chooserIntent = Intent.createChooser(shareIntent, null) + .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK) + .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + + return chooserIntent; + } + + public Intent getImageEditIntent(Uri uri, Context context) { + String editorPackage = context.getString(R.string.config_screenshotEditor); + Intent editIntent = new Intent(Intent.ACTION_EDIT); + if (!TextUtils.isEmpty(editorPackage)) { + editIntent.setComponent(ComponentName.unflattenFromString(editorPackage)); + } + editIntent.setDataAndType(uri, "image/*"); + editIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + editIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); + editIntent.putExtra(EXTRA_EDIT_SOURCE, EDIT_SOURCE_CLIPBOARD); + return editIntent; + } + + public Intent getRemoteCopyIntent(ClipData clipData, Context context) { + Intent nearbyIntent = new Intent(REMOTE_COPY_ACTION); + + String remoteCopyPackage = context.getString(R.string.config_remoteCopyPackage); + if (!TextUtils.isEmpty(remoteCopyPackage)) { + nearbyIntent.setComponent(ComponentName.unflattenFromString(remoteCopyPackage)); + } + + nearbyIntent.setClipData(clipData); + nearbyIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + nearbyIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); + return nearbyIntent; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/IntentCreator.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/IntentCreator.java index a18b4c84b081..c8a6b05f090b 100644 --- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/IntentCreator.java +++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/IntentCreator.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022 The Android Open Source Project + * 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. @@ -17,79 +17,13 @@ package com.android.systemui.clipboardoverlay; import android.content.ClipData; -import android.content.ClipDescription; -import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.net.Uri; -import android.text.TextUtils; -import com.android.systemui.res.R; - -class IntentCreator { - private static final String EXTRA_EDIT_SOURCE = "edit_source"; - private static final String EDIT_SOURCE_CLIPBOARD = "clipboard"; - private static final String REMOTE_COPY_ACTION = "android.intent.action.REMOTE_COPY"; - - static Intent getTextEditorIntent(Context context) { - Intent intent = new Intent(context, EditTextActivity.class); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); - return intent; - } - - static Intent getShareIntent(ClipData clipData, Context context) { - Intent shareIntent = new Intent(Intent.ACTION_SEND); - - // From the ACTION_SEND docs: - // "If using EXTRA_TEXT, the MIME type should be "text/plain"; otherwise it should be the - // MIME type of the data in EXTRA_STREAM" - Uri uri = clipData.getItemAt(0).getUri(); - if (uri != null) { - // We don't use setData here because some apps interpret this as "to:". - shareIntent.setType(clipData.getDescription().getMimeType(0)); - // Include URI in ClipData also, so that grantPermission picks it up. - shareIntent.setClipData(new ClipData( - new ClipDescription( - "content", new String[]{clipData.getDescription().getMimeType(0)}), - new ClipData.Item(uri))); - shareIntent.putExtra(Intent.EXTRA_STREAM, uri); - shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); - } else { - shareIntent.putExtra( - Intent.EXTRA_TEXT, clipData.getItemAt(0).coerceToText(context).toString()); - shareIntent.setType("text/plain"); - } - Intent chooserIntent = Intent.createChooser(shareIntent, null) - .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK) - .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); - - return chooserIntent; - } - - static Intent getImageEditIntent(Uri uri, Context context) { - String editorPackage = context.getString(R.string.config_screenshotEditor); - Intent editIntent = new Intent(Intent.ACTION_EDIT); - if (!TextUtils.isEmpty(editorPackage)) { - editIntent.setComponent(ComponentName.unflattenFromString(editorPackage)); - } - editIntent.setDataAndType(uri, "image/*"); - editIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); - editIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); - editIntent.putExtra(EXTRA_EDIT_SOURCE, EDIT_SOURCE_CLIPBOARD); - return editIntent; - } - - static Intent getRemoteCopyIntent(ClipData clipData, Context context) { - Intent nearbyIntent = new Intent(REMOTE_COPY_ACTION); - - String remoteCopyPackage = context.getString(R.string.config_remoteCopyPackage); - if (!TextUtils.isEmpty(remoteCopyPackage)) { - nearbyIntent.setComponent(ComponentName.unflattenFromString(remoteCopyPackage)); - } - - nearbyIntent.setClipData(clipData); - nearbyIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); - nearbyIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); - return nearbyIntent; - } +public interface IntentCreator { + Intent getTextEditorIntent(Context context); + Intent getShareIntent(ClipData clipData, Context context); + Intent getImageEditIntent(Uri uri, Context context); + Intent getRemoteCopyIntent(ClipData clipData, Context context); } diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/dagger/ClipboardOverlayModule.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/dagger/ClipboardOverlayModule.java index 6c10eea07ffc..c86a84b17efe 100644 --- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/dagger/ClipboardOverlayModule.java +++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/dagger/ClipboardOverlayModule.java @@ -20,6 +20,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_SCREENSHOT; import static com.android.systemui.Flags.clipboardOverlayMultiuser; import static com.android.systemui.Flags.enableViewCaptureTracing; +import static com.android.systemui.shared.Flags.usePreferredImageEditor; import static com.android.systemui.util.ConvenienceExtensionsKt.toKotlinLazy; import static java.lang.annotation.RetentionPolicy.RUNTIME; @@ -32,7 +33,10 @@ import android.view.WindowManager; import com.android.app.viewcapture.ViewCapture; import com.android.app.viewcapture.ViewCaptureAwareWindowManager; +import com.android.systemui.clipboardoverlay.ActionIntentCreator; import com.android.systemui.clipboardoverlay.ClipboardOverlayView; +import com.android.systemui.clipboardoverlay.DefaultIntentCreator; +import com.android.systemui.clipboardoverlay.IntentCreator; import com.android.systemui.res.R; import com.android.systemui.settings.DisplayTracker; import com.android.systemui.settings.UserTracker; @@ -102,6 +106,17 @@ public interface ClipboardOverlayModule { /* isViewCaptureEnabled= */ enableViewCaptureTracing()); } + @Provides + static IntentCreator provideIntentCreator( + Lazy<DefaultIntentCreator> defaultIntentCreator, + Lazy<ActionIntentCreator> actionIntentCreator) { + if (usePreferredImageEditor()) { + return actionIntentCreator.get(); + } else { + return defaultIntentCreator.get(); + } + } + @Qualifier @Documented @Retention(RUNTIME) diff --git a/packages/SystemUI/src/com/android/systemui/common/ui/ConfigurationState.kt b/packages/SystemUI/src/com/android/systemui/common/ui/ConfigurationState.kt index ba7e17bdd7a5..07cc136e6bc6 100644 --- a/packages/SystemUI/src/com/android/systemui/common/ui/ConfigurationState.kt +++ b/packages/SystemUI/src/com/android/systemui/common/ui/ConfigurationState.kt @@ -23,7 +23,6 @@ import androidx.annotation.ColorInt import androidx.annotation.DimenRes import androidx.annotation.LayoutRes import com.android.settingslib.Utils -import com.android.systemui.statusbar.data.repository.StatusBarConfigurationState import com.android.systemui.statusbar.policy.ConfigurationController import com.android.systemui.statusbar.policy.onDensityOrFontScaleChanged import com.android.systemui.statusbar.policy.onThemeChanged @@ -79,7 +78,7 @@ class ConfigurationStateImpl constructor( @Assisted private val configurationController: ConfigurationController, @Assisted private val context: Context, -) : ConfigurationState, StatusBarConfigurationState { +) : ConfigurationState { private val layoutInflater = LayoutInflater.from(context) diff --git a/packages/SystemUI/src/com/android/systemui/communal/DeviceInactiveCondition.java b/packages/SystemUI/src/com/android/systemui/communal/DeviceInactiveCondition.java index e456310febfd..4be9601f4277 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/DeviceInactiveCondition.java +++ b/packages/SystemUI/src/com/android/systemui/communal/DeviceInactiveCondition.java @@ -102,7 +102,7 @@ public class DeviceInactiveCondition extends Condition { } @Override - protected int getStartStrategy() { + public int getStartStrategy() { return START_EAGERLY; } 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 d7859c985c7b..140db7b7a0b7 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 @@ -367,12 +367,22 @@ constructor( /** See [CommunalSettingsInteractor.isV2FlagEnabled] */ fun v2FlagEnabled(): Boolean = communalSettingsInteractor.isV2FlagEnabled() - val swipeToHubEnabled: StateFlow<Boolean> by lazy { - if (v2FlagEnabled()) { - communalInteractor.shouldShowCommunal - } else { - MutableStateFlow(swipeToHub) - } + val swipeToHubEnabled: Flow<Boolean> by lazy { + val inAllowedDeviceState = + if (swipeToHub) { + MutableStateFlow(true) + } else if (v2FlagEnabled()) { + communalInteractor.shouldShowCommunal + } else { + MutableStateFlow(false) + } + + val inAllowedKeyguardState = + keyguardTransitionInteractor.startedKeyguardTransitionStep.map { + it.to == KeyguardState.LOCKSCREEN || it.to == KeyguardState.GLANCEABLE_HUB + } + + allOf(inAllowedDeviceState, inAllowedKeyguardState) } companion object { diff --git a/packages/SystemUI/src/com/android/systemui/display/data/repository/PerDisplayRepository.kt b/packages/SystemUI/src/com/android/systemui/display/data/repository/PerDisplayRepository.kt index 3458c9549665..d27e33e53dbb 100644 --- a/packages/SystemUI/src/com/android/systemui/display/data/repository/PerDisplayRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/display/data/repository/PerDisplayRepository.kt @@ -126,7 +126,7 @@ constructor( get() = perDisplayInstances.keys private suspend fun start() { - dumpManager.registerDumpable(this) + dumpManager.registerNormalDumpable("PerDisplayRepository-${debugName}", this) displayRepository.displayIds.collectLatest { displayIds -> val toRemove = perDisplayInstances.keys - displayIds toRemove.forEach { displayId -> @@ -184,8 +184,13 @@ constructor( * Provides an instance of a given class **only** for the default display, even if asked for another * display. * - * This is useful in case of flag refactors: it can be provided instead of an instance of + * This is useful in case of **flag refactors**: it can be provided instead of an instance of * [PerDisplayInstanceRepositoryImpl] when a flag related to multi display refactoring is off. + * + * Note that this still requires all instances to be provided by a [PerDisplayInstanceProvider]. If + * you want to provide an existing instance instead for the default display, either implement it in + * a custom [PerDisplayInstanceProvider] (e.g. inject it in the constructor and return it if the + * displayId is zero), or use [SingleInstanceRepositoryImpl]. */ class DefaultDisplayOnlyInstanceRepositoryImpl<T>( override val debugName: String, @@ -198,3 +203,18 @@ class DefaultDisplayOnlyInstanceRepositoryImpl<T>( override fun get(displayId: Int): T? = lazyDefaultDisplayInstance } + +/** + * Always returns [instance] for any display. + * + * This can be used to provide a single instance based on a flag value during a refactor. Similar to + * [DefaultDisplayOnlyInstanceRepositoryImpl], but also avoids creating the + * [PerDisplayInstanceProvider]. This is useful when you want to provide an existing instance only, + * without even instantiating a [PerDisplayInstanceProvider]. + */ +class SingleInstanceRepositoryImpl<T>(override val debugName: String, private val instance: T) : + PerDisplayRepository<T> { + override val displayIds: Set<Int> = setOf(Display.DEFAULT_DISPLAY) + + override fun get(displayId: Int): T? = instance +} diff --git a/packages/SystemUI/src/com/android/systemui/dreams/conditions/AssistantAttentionCondition.java b/packages/SystemUI/src/com/android/systemui/dreams/conditions/AssistantAttentionCondition.java index d81949d08a66..c17094b7456c 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/conditions/AssistantAttentionCondition.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/conditions/AssistantAttentionCondition.java @@ -63,7 +63,7 @@ public class AssistantAttentionCondition extends Condition { } @Override - protected int getStartStrategy() { + public int getStartStrategy() { return START_EAGERLY; } } diff --git a/packages/SystemUI/src/com/android/systemui/dreams/conditions/DreamCondition.java b/packages/SystemUI/src/com/android/systemui/dreams/conditions/DreamCondition.java index c7fe1e1e754b..fb4ed1419688 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/conditions/DreamCondition.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/conditions/DreamCondition.java @@ -63,7 +63,7 @@ public class DreamCondition extends Condition { } @Override - protected int getStartStrategy() { + public int getStartStrategy() { return START_EAGERLY; } } diff --git a/packages/SystemUI/src/com/android/systemui/lowlightclock/DirectBootCondition.kt b/packages/SystemUI/src/com/android/systemui/lowlightclock/DirectBootCondition.kt index 4c1da0198498..57d709835b2c 100644 --- a/packages/SystemUI/src/com/android/systemui/lowlightclock/DirectBootCondition.kt +++ b/packages/SystemUI/src/com/android/systemui/lowlightclock/DirectBootCondition.kt @@ -54,7 +54,6 @@ constructor( job?.cancel() } - override fun getStartStrategy(): Int { - return START_EAGERLY - } + override val startStrategy: Int + get() = START_EAGERLY } diff --git a/packages/SystemUI/src/com/android/systemui/lowlightclock/ForceLowLightCondition.java b/packages/SystemUI/src/com/android/systemui/lowlightclock/ForceLowLightCondition.java index 7f21d0707f63..5ec81a9a94a1 100644 --- a/packages/SystemUI/src/com/android/systemui/lowlightclock/ForceLowLightCondition.java +++ b/packages/SystemUI/src/com/android/systemui/lowlightclock/ForceLowLightCondition.java @@ -131,7 +131,7 @@ public class ForceLowLightCondition extends Condition { } @Override - protected int getStartStrategy() { + public int getStartStrategy() { return START_EAGERLY; } } diff --git a/packages/SystemUI/src/com/android/systemui/lowlightclock/LowLightCondition.java b/packages/SystemUI/src/com/android/systemui/lowlightclock/LowLightCondition.java index e91be5028777..c1a24e7e020e 100644 --- a/packages/SystemUI/src/com/android/systemui/lowlightclock/LowLightCondition.java +++ b/packages/SystemUI/src/com/android/systemui/lowlightclock/LowLightCondition.java @@ -54,7 +54,7 @@ public class LowLightCondition extends Condition { } @Override - protected int getStartStrategy() { + public int getStartStrategy() { // As this condition keeps the lowlight sensor active, it should only run when needed. return START_WHEN_NEEDED; } diff --git a/packages/SystemUI/src/com/android/systemui/lowlightclock/ScreenSaverEnabledCondition.java b/packages/SystemUI/src/com/android/systemui/lowlightclock/ScreenSaverEnabledCondition.java index fd6ce1762a28..81572554cb86 100644 --- a/packages/SystemUI/src/com/android/systemui/lowlightclock/ScreenSaverEnabledCondition.java +++ b/packages/SystemUI/src/com/android/systemui/lowlightclock/ScreenSaverEnabledCondition.java @@ -70,7 +70,7 @@ public class ScreenSaverEnabledCondition extends Condition { } @Override - protected int getStartStrategy() { + public int getStartStrategy() { return START_EAGERLY; } diff --git a/packages/SystemUI/src/com/android/systemui/model/SysUIStateDispatcher.kt b/packages/SystemUI/src/com/android/systemui/model/SysUIStateDispatcher.kt index f95ae2501acb..2bdd9a88ad6c 100644 --- a/packages/SystemUI/src/com/android/systemui/model/SysUIStateDispatcher.kt +++ b/packages/SystemUI/src/com/android/systemui/model/SysUIStateDispatcher.kt @@ -55,7 +55,7 @@ class SysUIStateDispatcher @Inject constructor() { /** Called from each [SysUiState] to propagate new state changes. */ fun dispatchSysUIStateChange(sysUiFlags: Long, displayId: Int) { - if (displayId != Display.DEFAULT_DISPLAY && !ShadeWindowGoesAround.isEnabled) return; + if (displayId != Display.DEFAULT_DISPLAY && !ShadeWindowGoesAround.isEnabled) return listeners.forEach { listener -> listener.onSystemUiStateChanged(sysUiFlags = sysUiFlags, displayId = displayId) } diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java index 7af538105cae..237ec7cfce7f 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java @@ -1129,6 +1129,7 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack mGestureBlockingActivityRunning.get(), mIsInPip, mDisplaySize, mEdgeWidthLeft, mLeftInset, mEdgeWidthRight, mRightInset, mExcludeRegion)); } else if (mAllowGesture || mLogGesture) { + boolean mLastFrameThresholdCrossed = mThresholdCrossed; if (!mThresholdCrossed) { mEndPoint.x = (int) ev.getX(); mEndPoint.y = (int) ev.getY(); @@ -1181,9 +1182,7 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack return; } else if (dx > dy && dx > mTouchSlop) { if (mAllowGesture) { - if (mBackAnimation != null) { - mBackAnimation.onThresholdCrossed(); - } else { + if (mBackAnimation == null) { pilferPointers(); } mThresholdCrossed = true; @@ -1198,6 +1197,9 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack // forward touch mEdgeBackPlugin.onMotionEvent(ev); dispatchToBackAnimation(ev); + if (mBackAnimation != null && mThresholdCrossed && !mLastFrameThresholdCrossed) { + mBackAnimation.onThresholdCrossed(); + } } } } diff --git a/packages/SystemUI/src/com/android/systemui/process/condition/SystemProcessCondition.java b/packages/SystemUI/src/com/android/systemui/process/condition/SystemProcessCondition.java index 694b525e7bc1..ea2bf6a44884 100644 --- a/packages/SystemUI/src/com/android/systemui/process/condition/SystemProcessCondition.java +++ b/packages/SystemUI/src/com/android/systemui/process/condition/SystemProcessCondition.java @@ -48,7 +48,7 @@ public class SystemProcessCondition extends Condition { } @Override - protected int getStartStrategy() { + public int getStartStrategy() { return START_EAGERLY; } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt index feb4d41e49b6..865ae9a37d87 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt @@ -42,6 +42,7 @@ import androidx.compose.ui.res.integerResource import androidx.compose.ui.unit.dp import com.android.compose.animation.scene.ContentScope import com.android.compose.modifiers.padding +import com.android.systemui.common.ui.compose.PagerDots import com.android.systemui.compose.modifiers.sysuiResTag import com.android.systemui.development.ui.compose.BuildNumber import com.android.systemui.development.ui.viewmodel.BuildNumberViewModel diff --git a/packages/SystemUI/src/com/android/systemui/recents/LauncherProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/LauncherProxyService.java index 7a6426c741a6..4be35f147c2f 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/LauncherProxyService.java +++ b/packages/SystemUI/src/com/android/systemui/recents/LauncherProxyService.java @@ -830,7 +830,8 @@ public class LauncherProxyService implements CallbackController<LauncherProxyLis private void notifySystemUiStateFlags(@SystemUiStateFlags long flags, int displayId) { if (SysUiState.DEBUG) { Log.d(TAG_OPS, "Notifying sysui state change to launcher service: proxy=" - + mLauncherProxy + " flags=" + flags + " displayId=" + displayId); + + mLauncherProxy + " display=" + displayId + " flags=" + + QuickStepContract.getSystemUiStateString(flags) + " displayId=" + displayId); } try { if (mLauncherProxy != null) { diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java index 17fb50aa6890..24e7976011f4 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java @@ -140,6 +140,7 @@ import com.android.systemui.scene.shared.flag.SceneContainerFlag; import com.android.systemui.settings.brightness.data.repository.BrightnessMirrorShowingRepository; import com.android.systemui.settings.brightness.domain.interactor.BrightnessMirrorShowingInteractor; import com.android.systemui.shade.data.repository.FlingInfo; +import com.android.systemui.shade.data.repository.ShadeDisplaysRepository; import com.android.systemui.shade.data.repository.ShadeRepository; import com.android.systemui.shade.domain.interactor.ShadeAnimationInteractor; import com.android.systemui.shade.shared.flag.ShadeWindowGoesAround; @@ -204,6 +205,7 @@ import dalvik.annotation.optimization.NeverCompile; import com.google.android.msdl.data.model.MSDLToken; import com.google.android.msdl.domain.MSDLPlayer; +import dagger.Lazy; import kotlin.Unit; import kotlinx.coroutines.CoroutineDispatcher; @@ -394,7 +396,7 @@ public final class NotificationPanelViewController implements /** Whether the notifications are displayed full width (no margins on the side). */ private boolean mIsFullWidth; private boolean mBlockingExpansionForCurrentTouch; - // Following variables maintain state of events when input focus transfer may occur. + // Following variables maintain state of events when input focus transfer may occur. private boolean mExpectingSynthesizedDown; private boolean mLastEventSynthesizedDown; @@ -456,6 +458,7 @@ public final class NotificationPanelViewController implements @Deprecated // Use SysUIStateInteractor instead private final SysUiState mSysUiState; private final SysUIStateDisplaysInteractor mSysUIStateDisplaysInteractor; + private final Lazy<ShadeDisplaysRepository> mShadeDisplaysRepository; private final NotificationShadeDepthController mDepthController; private final NavigationBarController mNavigationBarController; private final int mDisplayId; @@ -637,7 +640,8 @@ public final class NotificationPanelViewController implements KeyguardClockPositionAlgorithm keyguardClockPositionAlgorithm, MSDLPlayer msdlPlayer, BrightnessMirrorShowingRepository brightnessMirrorShowingRepository, - BlurConfig blurConfig) { + BlurConfig blurConfig, + Lazy<ShadeDisplaysRepository> shadeDisplaysRepository) { mBlurConfig = blurConfig; SceneContainerFlag.assertInLegacyMode(); keyguardStateController.addCallback(new KeyguardStateController.Callback() { @@ -745,6 +749,7 @@ public final class NotificationPanelViewController implements mTapAgainViewController = tapAgainViewController; mSysUiState = sysUiState; mSysUIStateDisplaysInteractor = sysUIStateDisplaysInteractor; + mShadeDisplaysRepository = shadeDisplaysRepository; mKeyguardBypassController = bypassController; mUpdateMonitor = keyguardUpdateMonitor; mLockscreenShadeTransitionController = lockscreenShadeTransitionController; @@ -2716,8 +2721,17 @@ public final class NotificationPanelViewController implements } private int getShadeDisplayId() { - if (mView != null && mView.getDisplay() != null) return mView.getDisplay().getDisplayId(); - return Display.DEFAULT_DISPLAY; + if (ShadeWindowGoesAround.isEnabled()) { + var pendingDisplayId = + mShadeDisplaysRepository.get().getPendingDisplayId().getValue(); + // Use the pendingDisplayId from the repository, *not* the Shade's context. + // This ensures correct UI state updates also if this method is called just *before* + // the Shade window moves to another display. + // The pendingDisplayId is guaranteed to be updated before this method is called. + return pendingDisplayId; + } else { + return Display.DEFAULT_DISPLAY; + } } private void setPerDisplaySysUIStateFlags() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt index 3db004848d22..bdfdb8191356 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt @@ -60,6 +60,8 @@ import java.util.Optional import javax.inject.Inject import kotlin.math.max import kotlin.math.sign +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch /** * Responsible for blurring the notification shade window, and applying a zoom effect to the @@ -92,6 +94,7 @@ constructor( private const val MIN_VELOCITY = -MAX_VELOCITY private const val INTERACTION_BLUR_FRACTION = 0.8f private const val ANIMATION_BLUR_FRACTION = 1f - INTERACTION_BLUR_FRACTION + private const val TRANSITION_THRESHOLD = 0.98f private const val TAG = "DepthController" } @@ -156,6 +159,9 @@ constructor( /** * When launching an app from the shade, the animations progress should affect how blurry the * shade is, overriding the expansion amount. + * + * TODO(b/399617511): remove this once [Flags.notificationShadeBlur] is launched and the Shade + * closing is actually instantaneous. */ var blursDisabledForAppLaunch: Boolean = false set(value) { @@ -185,8 +191,12 @@ constructor( return } - shadeAnimation.animateTo(0) - shadeAnimation.finishIfRunning() + if (Flags.notificationShadeBlur()) { + shadeAnimation.skipTo(0) + } else { + shadeAnimation.animateTo(0) + shadeAnimation.finishIfRunning() + } } @Deprecated( message = @@ -487,6 +497,22 @@ constructor( scheduleUpdate() } + fun onTransitionAnimationProgress(progress: Float) { + if (!Flags.notificationShadeBlur() || !Flags.moveTransitionAnimationLayer()) return + // Because the Shade takes a few frames to actually trigger the unblur after a transition + // has ended, we need to disable it manually, or the opening window itself will be blurred + // for a few frames due to relative ordering. We do this towards the end, so that the + // window is already covering the background and the unblur is not visible. + if (progress >= TRANSITION_THRESHOLD && shadeAnimation.radius > 0) { + blursDisabledForAppLaunch = true + } + } + + fun onTransitionAnimationEnd() { + if (!Flags.notificationShadeBlur() || !Flags.moveTransitionAnimationLayer()) return + blursDisabledForAppLaunch = false + } + private fun updateShadeAnimationBlur( expansion: Float, tracking: Boolean, @@ -631,6 +657,20 @@ constructor( springAnimation.addEndListener { _, _, _, _ -> pendingRadius = -1 } } + /** + * Starts an animation to [newRadius], or updates the current one if already ongoing. + * IMPORTANT: do NOT use this method + [finishIfRunning] to instantaneously change the value + * of the animation. The change will NOT be instantaneous. Use [skipTo] instead. + * + * Explanation: + * 1. If idle, [SpringAnimation.animateToFinalPosition] requests a start to the animation. + * 2. On the first frame after an idle animation is requested to start, the animation simply + * acquires the starting value and does nothing else. + * 3. [SpringAnimation.skipToEnd] requests a fast-forward to the end value, but this happens + * during calculation of the next animation value. Because on the first frame no such + * calculation happens (point #2), there is one lagging frame where we still see the old + * value. + */ fun animateTo(newRadius: Int) { if (pendingRadius == newRadius) { return @@ -639,6 +679,19 @@ constructor( springAnimation.animateToFinalPosition(newRadius.toFloat()) } + /** + * Instantaneously set a new blur radius to this animation. Always use this instead of + * [animateTo] and [finishIfRunning] to make sure that the change takes effect in the next + * frame. See the doc for [animateTo] for an explanation. + */ + fun skipTo(newRadius: Int) { + if (pendingRadius == newRadius) return + pendingRadius = newRadius + springAnimation.cancel() + springAnimation.setStartValue(newRadius.toFloat()) + springAnimation.animateToFinalPosition(newRadius.toFloat()) + } + fun finishIfRunning() { if (springAnimation.isRunning) { springAnimation.skipToEnd() diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/StatusBarDataLayerModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/StatusBarDataLayerModule.kt index 5e7e1793ab35..9db2f4b46dae 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/data/StatusBarDataLayerModule.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/StatusBarDataLayerModule.kt @@ -20,9 +20,9 @@ import com.android.systemui.statusbar.data.repository.KeyguardStatusBarRepositor import com.android.systemui.statusbar.data.repository.LightBarControllerStoreModule import com.android.systemui.statusbar.data.repository.RemoteInputRepositoryModule import com.android.systemui.statusbar.data.repository.StatusBarConfigurationControllerModule -import com.android.systemui.statusbar.data.repository.StatusBarConfigurationStateModule import com.android.systemui.statusbar.data.repository.StatusBarContentInsetsProviderStoreModule import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryModule +import com.android.systemui.statusbar.data.repository.StatusBarPerDisplayConfigurationStateModule import com.android.systemui.statusbar.data.repository.SystemEventChipAnimationControllerStoreModule import com.android.systemui.statusbar.phone.data.StatusBarPhoneDataLayerModule import dagger.Module @@ -35,7 +35,7 @@ import dagger.Module LightBarControllerStoreModule::class, RemoteInputRepositoryModule::class, StatusBarConfigurationControllerModule::class, - StatusBarConfigurationStateModule::class, + StatusBarPerDisplayConfigurationStateModule::class, StatusBarContentInsetsProviderStoreModule::class, StatusBarModeRepositoryModule::class, StatusBarPhoneDataLayerModule::class, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarConfigurationStateStore.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarConfigurationStateStore.kt deleted file mode 100644 index 4e6c5ddc3066..000000000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarConfigurationStateStore.kt +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright (C) 2025 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.statusbar.data.repository - -import android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR -import com.android.systemui.CoreStartable -import com.android.systemui.common.ui.ConfigurationState -import com.android.systemui.common.ui.ConfigurationStateImpl -import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.dagger.qualifiers.Background -import com.android.systemui.dagger.qualifiers.Main -import com.android.systemui.display.data.repository.DisplayRepository -import com.android.systemui.display.data.repository.DisplayWindowPropertiesRepository -import com.android.systemui.display.data.repository.PerDisplayStore -import com.android.systemui.display.data.repository.PerDisplayStoreImpl -import com.android.systemui.display.data.repository.SingleDisplayStore -import com.android.systemui.statusbar.core.StatusBarConnectedDisplays -import dagger.Lazy -import dagger.Module -import dagger.Provides -import dagger.multibindings.ClassKey -import dagger.multibindings.IntoMap -import javax.inject.Inject -import kotlinx.coroutines.CoroutineScope - -/** Status bar specific interface to disambiguate from the global [ConfigurationState]. */ -interface StatusBarConfigurationState : ConfigurationState - -/** Provides per display instances of [ConfigurationState], specifically for the Status Bar. */ -interface StatusBarConfigurationStateStore : PerDisplayStore<StatusBarConfigurationState> - -@SysUISingleton -class MultiDisplayStatusBarConfigurationStateStore -@Inject -constructor( - @Background backgroundApplicationScope: CoroutineScope, - displayRepository: DisplayRepository, - private val displayWindowPropertiesRepository: DisplayWindowPropertiesRepository, - private val statusBarConfigurationControllerStore: StatusBarConfigurationControllerStore, - private val factory: ConfigurationStateImpl.Factory, -) : - StatusBarConfigurationStateStore, - PerDisplayStoreImpl<StatusBarConfigurationState>( - backgroundApplicationScope, - displayRepository, - ) { - - init { - StatusBarConnectedDisplays.unsafeAssertInNewMode() - } - - override fun createInstanceForDisplay(displayId: Int): StatusBarConfigurationState? { - val displayWindowProperties = - displayWindowPropertiesRepository.get(displayId, TYPE_STATUS_BAR) ?: return null - val configController = - statusBarConfigurationControllerStore.forDisplay(displayId) ?: return null - return factory.create(displayWindowProperties.context, configController) - } - - override val instanceClass = StatusBarConfigurationState::class.java -} - -@SysUISingleton -class SingleDisplayStatusBarConfigurationStateStore -@Inject -constructor(@Main globalConfigState: ConfigurationState) : - StatusBarConfigurationStateStore, - PerDisplayStore<StatusBarConfigurationState> by SingleDisplayStore( - globalConfigState as StatusBarConfigurationState - ) { - - init { - StatusBarConnectedDisplays.assertInLegacyMode() - } -} - -@Module -object StatusBarConfigurationStateModule { - - @Provides - @SysUISingleton - fun store( - singleDisplayLazy: Lazy<SingleDisplayStatusBarConfigurationStateStore>, - multiDisplayLazy: Lazy<MultiDisplayStatusBarConfigurationStateStore>, - ): StatusBarConfigurationStateStore { - return if (StatusBarConnectedDisplays.isEnabled) { - multiDisplayLazy.get() - } else { - singleDisplayLazy.get() - } - } - - @Provides - @SysUISingleton - @IntoMap - @ClassKey(StatusBarConfigurationStateStore::class) - fun storeAsCoreStartable( - multiDisplayLazy: Lazy<MultiDisplayStatusBarConfigurationStateStore> - ): CoreStartable { - return if (StatusBarConnectedDisplays.isEnabled) { - multiDisplayLazy.get() - } else { - CoreStartable.NOP - } - } -} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarPerDisplayConfigurationStateRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarPerDisplayConfigurationStateRepository.kt new file mode 100644 index 000000000000..3168a22c56ad --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarPerDisplayConfigurationStateRepository.kt @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.data.repository + +import android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR +import com.android.systemui.common.ui.ConfigurationState +import com.android.systemui.common.ui.ConfigurationStateImpl +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.display.data.repository.DisplayWindowPropertiesRepository +import com.android.systemui.display.data.repository.PerDisplayInstanceProvider +import com.android.systemui.display.data.repository.PerDisplayInstanceRepositoryImpl +import com.android.systemui.display.data.repository.PerDisplayRepository +import com.android.systemui.display.data.repository.SingleInstanceRepositoryImpl +import com.android.systemui.statusbar.core.StatusBarConnectedDisplays +import dagger.Lazy +import dagger.Module +import dagger.Provides +import javax.inject.Inject + +@SysUISingleton +class StatusBarPerDisplayConfigurationStateProvider +@Inject +constructor( + private val displayWindowPropertiesRepository: DisplayWindowPropertiesRepository, + private val statusBarConfigurationControllerStore: StatusBarConfigurationControllerStore, + private val factory: ConfigurationStateImpl.Factory, +) : PerDisplayInstanceProvider<ConfigurationState> { + + override fun createInstance(displayId: Int): ConfigurationState? { + val displayWindowProperties = + displayWindowPropertiesRepository.get(displayId, TYPE_STATUS_BAR) ?: return null + val configController = + statusBarConfigurationControllerStore.forDisplay(displayId) ?: return null + return factory.create(displayWindowProperties.context, configController) + } +} + +@Module +object StatusBarPerDisplayConfigurationStateModule { + + @Provides + @SysUISingleton + fun store( + instanceProvider: Lazy<StatusBarPerDisplayConfigurationStateProvider>, + factory: PerDisplayInstanceRepositoryImpl.Factory<ConfigurationState>, + defaultInstance: ConfigurationState, + ): PerDisplayRepository<ConfigurationState> { + val name = "ConfigurationState" + return if (StatusBarConnectedDisplays.isEnabled) { + factory.create(name, instanceProvider.get()) + } else { + SingleInstanceRepositoryImpl(name, defaultInstance) + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerStatusBarViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerStatusBarViewBinder.kt index 30aec8dabcaa..147a5afea306 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerStatusBarViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerStatusBarViewBinder.kt @@ -20,8 +20,8 @@ import android.view.Display import androidx.lifecycle.lifecycleScope import com.android.app.tracing.traceSection import com.android.systemui.common.ui.ConfigurationState +import com.android.systemui.display.data.repository.PerDisplayRepository import com.android.systemui.lifecycle.repeatWhenAttached -import com.android.systemui.statusbar.data.repository.StatusBarConfigurationStateStore import com.android.systemui.statusbar.notification.collection.NotifCollection import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerViewBinder.IconViewStore import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerStatusBarViewModel @@ -36,7 +36,7 @@ class NotificationIconContainerStatusBarViewBinder @Inject constructor( private val viewModel: NotificationIconContainerStatusBarViewModel, - private val configurationStore: StatusBarConfigurationStateStore, + private val configurationStateRepository: PerDisplayRepository<ConfigurationState>, private val defaultConfigurationState: ConfigurationState, private val systemBarUtilsState: SystemBarUtilsState, private val failureTracker: StatusBarIconViewBindingFailureTracker, @@ -57,7 +57,7 @@ constructor( } } val configurationState: ConfigurationState = - configurationStore.forDisplay(displayId) ?: defaultConfigurationState + configurationStateRepository[displayId] ?: defaultConfigurationState lifecycleScope.launch { NotificationIconContainerViewBinder.bind( displayId = displayId, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java index 428b503102b3..68ad4fad31c1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java @@ -1685,13 +1685,11 @@ public class ExpandableNotificationRow extends ActivatableNotificationView } if (notificationRowTransparency() && mBackgroundNormal != null) { if (NotificationBundleUi.isEnabled() && mEntryAdapter != null) { - mBackgroundNormal.setBgIsColorized( - usesTransparentBackground() && mEntryAdapter.isColorized()); + mBackgroundNormal.setBgIsColorized(mEntryAdapter.isColorized()); } else { if (mEntry != null) { mBackgroundNormal.setBgIsColorized( - usesTransparentBackground() - && mEntry.getSbn().getNotification().isColorized()); + mEntry.getSbn().getNotification().isColorized()); } } } @@ -3068,7 +3066,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView mUserLocked = userLocked; mPrivateLayout.setUserExpanding(userLocked); - mPublicLayout.setUserExpanding(userLocked); // This is intentionally not guarded with mIsSummaryWithChildren since we might have had // children but not anymore. if (mChildrenContainer != null) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java index daa598b5f297..51569c1596ef 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java @@ -57,7 +57,6 @@ import com.android.systemui.statusbar.notification.ConversationNotificationProce import com.android.systemui.statusbar.notification.InflationException; import com.android.systemui.statusbar.notification.NmSummarizationUiFlag; import com.android.systemui.statusbar.notification.NotificationUtils; -import com.android.systemui.statusbar.notification.collection.EntryAdapter; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.promoted.PromotedNotificationContentExtractor; import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUiForceExpanded; @@ -441,8 +440,12 @@ public class NotificationContentInflater implements NotificationRowContentBinder NotificationRowContentBinderLogger logger) { return TraceUtils.trace("NotificationContentInflater.createRemoteViews", () -> { InflationProgress result = new InflationProgress(); + + // inflating the contracted view is the legacy invalidation trigger + boolean reinflating = (reInflateFlags & FLAG_CONTENT_VIEW_CONTRACTED) != 0; // create an image inflater - result.mRowImageInflater = RowImageInflater.newInstance(row.mImageModelIndex); + result.mRowImageInflater = RowImageInflater.newInstance(row.mImageModelIndex, + reinflating); if ((reInflateFlags & FLAG_CONTENT_VIEW_CONTRACTED) != 0) { logger.logAsyncTaskProgress(row.getLoggingKey(), "creating contracted remote view"); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt index 3586078c9e82..482b315aa14d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt @@ -499,7 +499,10 @@ constructor( logger.logAsyncTaskProgress(entry.logKey, "loading RON images") inflationProgress.rowImageInflater.loadImagesSynchronously(packageContext) - logger.logAsyncTaskProgress(entry.logKey, "getting row image resolver (on wrong thread!)") + logger.logAsyncTaskProgress( + entry.logKey, + "getting row image resolver (on wrong thread!)", + ) val imageResolver = row.imageResolver // wait for image resolver to finish preloading logger.logAsyncTaskProgress(entry.logKey, "waiting for preloaded images") @@ -685,13 +688,17 @@ constructor( logger: NotificationRowContentBinderLogger, ): InflationProgress { val rowImageInflater = - RowImageInflater.newInstance(previousIndex = row.mImageModelIndex) + RowImageInflater.newInstance( + previousIndex = row.mImageModelIndex, + // inflating the contracted view is the legacy invalidation trigger + reinflating = reInflateFlags and FLAG_CONTENT_VIEW_CONTRACTED != 0, + ) val promotedContent = if (PromotedNotificationContentModel.featureFlagEnabled()) { logger.logAsyncTaskProgress( entry.logKey, - "extracting promoted notification content" + "extracting promoted notification content", ) val imageModelProvider = rowImageInflater.useForContentModel() promotedNotificationContentExtractor @@ -750,7 +757,7 @@ constructor( ) { logger.logAsyncTaskProgress( entry.logKey, - "inflating public single line view model" + "inflating public single line view model", ) if (bindParams.redactionType == REDACTION_TYPE_SENSITIVE_CONTENT) { SingleLineViewInflater.inflateSingleLineViewModel( @@ -852,18 +859,12 @@ constructor( } else null val expanded = if (reInflateFlags and FLAG_CONTENT_VIEW_EXPANDED != 0) { - logger.logAsyncTaskProgress( - row.loggingKey, - "creating expanded remote view", - ) + logger.logAsyncTaskProgress(row.loggingKey, "creating expanded remote view") createExpandedView(builder, bindParams.isMinimized) } else null val headsUp = if (reInflateFlags and FLAG_CONTENT_VIEW_HEADS_UP != 0) { - logger.logAsyncTaskProgress( - row.loggingKey, - "creating heads up remote view", - ) + logger.logAsyncTaskProgress(row.loggingKey, "creating heads up remote view") val isHeadsUpCompact = headsUpStyleProvider.shouldApplyCompactStyle() if (isHeadsUpCompact) { builder.createCompactHeadsUpContentView() @@ -873,10 +874,7 @@ constructor( } else null val public = if (reInflateFlags and FLAG_CONTENT_VIEW_PUBLIC != 0) { - logger.logAsyncTaskProgress( - row.loggingKey, - "creating public remote view" - ) + logger.logAsyncTaskProgress(row.loggingKey, "creating public remote view") if ( LockscreenOtpRedaction.isEnabled && bindParams.redactionType == REDACTION_TYPE_SENSITIVE_CONTENT @@ -1142,7 +1140,7 @@ constructor( override fun setResultView(v: View) { logger.logAsyncTaskProgress( entry.logKey, - "group header view applied" + "group header view applied", ) result.inflatedGroupHeaderView = v as NotificationHeaderView? } @@ -1198,7 +1196,7 @@ constructor( } logger.logAsyncTaskProgress( entry.logKey, - "applying low priority group header view" + "applying low priority group header view", ) applyRemoteView( inflationExecutor = inflationExecutor, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowImageInflater.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowImageInflater.kt index 95ef60fdcefe..7bac17f4c227 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowImageInflater.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowImageInflater.kt @@ -83,9 +83,9 @@ interface RowImageInflater { inline fun featureFlagEnabled() = PromotedNotificationUiAod.isEnabled @JvmStatic - fun newInstance(previousIndex: ImageModelIndex?): RowImageInflater = + fun newInstance(previousIndex: ImageModelIndex?, reinflating: Boolean): RowImageInflater = if (featureFlagEnabled()) { - RowImageInflaterImpl(previousIndex) + RowImageInflaterImpl(previousIndex, reinflating) } else { RowImageInflaterStub } @@ -110,7 +110,8 @@ private object RowImageInflaterStub : RowImageInflater { override fun getNewImageIndex(): ImageModelIndex? = null } -class RowImageInflaterImpl(private val previousIndex: ImageModelIndex?) : RowImageInflater { +class RowImageInflaterImpl(private val previousIndex: ImageModelIndex?, val reinflating: Boolean) : + RowImageInflater { private val providedImages = mutableListOf<LazyImage>() /** @@ -139,10 +140,15 @@ class RowImageInflaterImpl(private val previousIndex: ImageModelIndex?) : RowIma // ensure all entries are stored providedImages.add(newImage) // load the image result from the index into our new object - previousIndex?.findImage(iconData, sizeClass, transform)?.let { - // copy the result into our new object - newImage.result = it - } + previousIndex + // skip the cached image when we are "reinflating" to avoid stale content + // being displayed from the same URI after the app updated the notif + ?.takeUnless { reinflating } + ?.findImage(iconData, sizeClass, transform) + ?.let { + // copy the result into our new object + newImage.result = it + } } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt index a277597e23df..c1aa5f12aa99 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt @@ -266,7 +266,7 @@ constructor( combine(shadeModeInteractor.shadeMode, shadeInteractor.qsExpansion) { shadeMode, qsExpansion -> when (shadeMode) { - is ShadeMode.Dual -> false + is ShadeMode.Dual, is ShadeMode.Split -> true is ShadeMode.Single -> qsExpansion < 0.5f } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java index face1c7512ce..fc721bfae369 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java @@ -109,6 +109,7 @@ import com.android.systemui.AutoReinflateContainer; import com.android.systemui.CoreStartable; import com.android.systemui.DejankUtils; import com.android.systemui.EventLogTags; +import com.android.systemui.Flags; import com.android.systemui.InitController; import com.android.systemui.Prefs; import com.android.systemui.accessibility.floatingmenu.AccessibilityFloatingMenuController; @@ -3183,12 +3184,27 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { new ActivityTransitionAnimator.Listener() { @Override public void onTransitionAnimationStart() { - mKeyguardViewMediator.setBlursDisabledForAppLaunch(true); + if (!Flags.notificationShadeBlur() || !Flags.moveTransitionAnimationLayer()) { + mKeyguardViewMediator.setBlursDisabledForAppLaunch(true); + } + } + + @Override + public void onTransitionAnimationProgress(float linearProgress) { + if (Flags.notificationShadeBlur() && Flags.moveTransitionAnimationLayer()) { + mNotificationShadeDepthControllerLazy.get() + .onTransitionAnimationProgress(linearProgress); + } } @Override public void onTransitionAnimationEnd() { - mKeyguardViewMediator.setBlursDisabledForAppLaunch(false); + if (Flags.notificationShadeBlur() && Flags.moveTransitionAnimationLayer()) { + mNotificationShadeDepthControllerLazy.get() + .onTransitionAnimationEnd(); + } else { + mKeyguardViewMediator.setBlursDisabledForAppLaunch(false); + } } }; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/composable/ModeTileGrid.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/composable/ModeTileGrid.kt index 16f24f1d5821..5d7ce91b332c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/composable/ModeTileGrid.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/composable/ModeTileGrid.kt @@ -34,7 +34,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.android.systemui.Flags -import com.android.systemui.qs.panels.ui.compose.PagerDots +import com.android.systemui.common.ui.compose.PagerDots import com.android.systemui.statusbar.policy.ui.dialog.viewmodel.ModesDialogViewModel @Composable diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemFactoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemFactoryTest.kt index 7c8822bc11ec..96ef8b690145 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemFactoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemFactoryTest.kt @@ -18,8 +18,6 @@ package com.android.systemui.bluetooth.qsdialog import android.bluetooth.BluetoothDevice import android.graphics.drawable.Drawable -import android.platform.test.annotations.DisableFlags -import android.platform.test.annotations.EnableFlags import android.testing.TestableLooper import android.util.Pair import androidx.test.ext.junit.runners.AndroidJUnit4 @@ -29,7 +27,6 @@ import com.android.dx.mockito.inline.extended.StaticMockitoSession import com.android.settingslib.bluetooth.BluetoothUtils import com.android.settingslib.bluetooth.CachedBluetoothDevice import com.android.settingslib.bluetooth.LocalBluetoothManager -import com.android.settingslib.flags.Flags import com.android.systemui.SysuiTestCase import com.android.systemui.res.R import com.google.common.truth.Truth.assertThat @@ -208,42 +205,6 @@ class DeviceItemFactoryTest : SysuiTestCase() { } @Test - @DisableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE) - fun testSavedFactory_isFilterMatched_bondedAndNotConnected_returnsTrue() { - `when`(cachedDevice.bondState).thenReturn(BluetoothDevice.BOND_BONDED) - `when`(cachedDevice.isConnected).thenReturn(false) - - assertThat( - savedDeviceItemFactory.isFilterMatched(context, cachedDevice, isOngoingCall = false) - ) - .isTrue() - } - - @Test - @DisableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE) - fun testSavedFactory_isFilterMatched_connected_returnsFalse() { - `when`(cachedDevice.bondState).thenReturn(BluetoothDevice.BOND_BONDED) - `when`(cachedDevice.isConnected).thenReturn(true) - - assertThat( - savedDeviceItemFactory.isFilterMatched(context, cachedDevice, isOngoingCall = false) - ) - .isFalse() - } - - @Test - @DisableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE) - fun testSavedFactory_isFilterMatched_notBonded_returnsFalse() { - `when`(cachedDevice.bondState).thenReturn(BluetoothDevice.BOND_NONE) - - assertThat( - savedDeviceItemFactory.isFilterMatched(context, cachedDevice, isOngoingCall = false) - ) - .isFalse() - } - - @Test - @EnableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE) fun testSavedFactory_isFilterMatched_exclusivelyManaged_returnsFalse() { `when`(cachedDevice.device).thenReturn(bluetoothDevice) `when`(BluetoothUtils.isExclusivelyManagedBluetoothDevice(any(), any())).thenReturn(true) @@ -255,7 +216,6 @@ class DeviceItemFactoryTest : SysuiTestCase() { } @Test - @EnableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE) fun testSavedFactory_isFilterMatched_notExclusiveManaged_returnsTrue() { `when`(cachedDevice.device).thenReturn(bluetoothDevice) `when`(BluetoothUtils.isExclusivelyManagedBluetoothDevice(any(), any())).thenReturn(false) @@ -269,7 +229,6 @@ class DeviceItemFactoryTest : SysuiTestCase() { } @Test - @EnableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE) fun testSavedFactory_isFilterMatched_notExclusivelyManaged_connected_returnsFalse() { `when`(cachedDevice.device).thenReturn(bluetoothDevice) `when`(BluetoothUtils.isExclusivelyManagedBluetoothDevice(any(), any())).thenReturn(false) @@ -283,35 +242,6 @@ class DeviceItemFactoryTest : SysuiTestCase() { } @Test - @DisableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE) - fun testConnectedFactory_isFilterMatched_bondedAndConnected_returnsTrue() { - `when`(BluetoothUtils.isConnectedBluetoothDevice(any(), any())).thenReturn(true) - - assertThat( - connectedDeviceItemFactory.isFilterMatched( - context, - cachedDevice, - isOngoingCall = false, - ) - ) - .isTrue() - } - - @Test - @DisableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE) - fun testConnectedFactory_isFilterMatched_notConnected_returnsFalse() { - assertThat( - connectedDeviceItemFactory.isFilterMatched( - context, - cachedDevice, - isOngoingCall = false, - ) - ) - .isFalse() - } - - @Test - @EnableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE) fun testConnectedFactory_isFilterMatched_exclusivelyManaged_returnsFalse() { `when`(cachedDevice.device).thenReturn(bluetoothDevice) `when`(BluetoothUtils.isExclusivelyManagedBluetoothDevice(any(), any())).thenReturn(true) @@ -327,7 +257,6 @@ class DeviceItemFactoryTest : SysuiTestCase() { } @Test - @EnableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE) fun testConnectedFactory_isFilterMatched_noExclusiveManager_returnsTrue() { `when`(cachedDevice.device).thenReturn(bluetoothDevice) `when`(BluetoothUtils.isExclusivelyManagedBluetoothDevice(any(), any())).thenReturn(false) @@ -344,7 +273,6 @@ class DeviceItemFactoryTest : SysuiTestCase() { } @Test - @EnableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE) fun testConnectedFactory_isFilterMatched_notExclusivelyManaged_notConnected_returnsFalse() { `when`(cachedDevice.device).thenReturn(bluetoothDevice) `when`(BluetoothUtils.isExclusivelyManagedBluetoothDevice(any(), any())).thenReturn(false) diff --git a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java index 562481567536..5c893da45b8d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java @@ -28,7 +28,6 @@ import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBO import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_SHOWN_EXPANDED; import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_SHOWN_MINIMIZED; import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_SWIPE_DISMISSED; -import static com.android.systemui.flags.Flags.CLIPBOARD_IMAGE_TIMEOUT; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; @@ -60,7 +59,6 @@ import androidx.test.runner.AndroidJUnit4; import com.android.internal.logging.UiEventLogger; import com.android.systemui.SysuiTestCase; import com.android.systemui.broadcast.BroadcastSender; -import com.android.systemui.flags.FakeFeatureFlags; import com.android.systemui.screenshot.TimeoutHandler; import com.android.systemui.settings.FakeDisplayTracker; import com.android.systemui.util.concurrency.FakeExecutor; @@ -102,7 +100,6 @@ public class ClipboardOverlayControllerTest extends SysuiTestCase { @Mock private UiEventLogger mUiEventLogger; private FakeDisplayTracker mDisplayTracker = new FakeDisplayTracker(mContext); - private FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags(); @Mock private Animator mAnimator; @@ -152,8 +149,6 @@ public class ClipboardOverlayControllerTest extends SysuiTestCase { mSampleClipData = new ClipData("Test", new String[]{"text/plain"}, new ClipData.Item("Test Item")); - - mFeatureFlags.set(CLIPBOARD_IMAGE_TIMEOUT, true); // turned off for legacy tests } /** @@ -170,13 +165,13 @@ public class ClipboardOverlayControllerTest extends SysuiTestCase { getFakeBroadcastDispatcher(), mBroadcastSender, mTimeoutHandler, - mFeatureFlags, mClipboardUtils, mExecutor, mClipboardImageLoader, mClipboardTransitionExecutor, mClipboardIndicationProvider, - mUiEventLogger); + mUiEventLogger, + new ActionIntentCreator()); verify(mClipboardOverlayView).setCallbacks(mOverlayCallbacksCaptor.capture()); mCallbacks = mOverlayCallbacksCaptor.getValue(); } @@ -193,7 +188,6 @@ public class ClipboardOverlayControllerTest extends SysuiTestCase { ClipData clipData = new ClipData("", new String[]{"image/png"}, new ClipData.Item(Uri.parse(""))); - mFeatureFlags.set(CLIPBOARD_IMAGE_TIMEOUT, false); mOverlayController.setClipData(clipData, ""); @@ -208,7 +202,6 @@ public class ClipboardOverlayControllerTest extends SysuiTestCase { ClipData clipData = new ClipData("", new String[]{"resource/png"}, new ClipData.Item(Uri.parse(""))); - mFeatureFlags.set(CLIPBOARD_IMAGE_TIMEOUT, false); mOverlayController.setClipData(clipData, ""); @@ -219,7 +212,6 @@ public class ClipboardOverlayControllerTest extends SysuiTestCase { @Test public void test_setClipData_textData_legacy() { - mFeatureFlags.set(CLIPBOARD_IMAGE_TIMEOUT, false); initController(); mOverlayController.setClipData(mSampleClipData, "abc"); @@ -232,7 +224,6 @@ public class ClipboardOverlayControllerTest extends SysuiTestCase { @Test public void test_setClipData_sensitiveTextData_legacy() { - mFeatureFlags.set(CLIPBOARD_IMAGE_TIMEOUT, false); initController(); ClipDescription description = mSampleClipData.getDescription(); @@ -250,7 +241,6 @@ public class ClipboardOverlayControllerTest extends SysuiTestCase { @Test public void test_setClipData_repeatedCalls_legacy() { when(mAnimator.isRunning()).thenReturn(true); - mFeatureFlags.set(CLIPBOARD_IMAGE_TIMEOUT, false); initController(); mOverlayController.setClipData(mSampleClipData, ""); diff --git a/packages/SystemUI/tests/src/com/android/systemui/recents/LauncherProxyServiceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/recents/LauncherProxyServiceTest.kt index 0a5efb7bb286..2d3f538689b3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/recents/LauncherProxyServiceTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/recents/LauncherProxyServiceTest.kt @@ -162,8 +162,7 @@ class LauncherProxyServiceTest : SysuiTestCase() { wakefulnessLifecycle.dispatchFinishedGoingToSleep() clearInvocations(launcherProxy) - wakefulnessLifecycle - .dispatchFinishedWakingUp() + wakefulnessLifecycle.dispatchFinishedWakingUp() verify(launcherProxy) .onSystemUiStateChanged( diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/condition/CombinedConditionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/condition/CombinedConditionTest.kt index 8418598c256b..116a2caa6dda 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shared/condition/CombinedConditionTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shared/condition/CombinedConditionTest.kt @@ -19,9 +19,9 @@ package com.android.systemui.shared.condition import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase -import com.android.systemui.shared.condition.Condition.START_EAGERLY -import com.android.systemui.shared.condition.Condition.START_LAZILY -import com.android.systemui.shared.condition.Condition.START_WHEN_NEEDED +import com.android.systemui.shared.condition.Condition.Companion.START_EAGERLY +import com.android.systemui.shared.condition.Condition.Companion.START_LAZILY +import com.android.systemui.shared.condition.Condition.Companion.START_WHEN_NEEDED import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -40,7 +40,7 @@ class CombinedConditionTest : SysuiTestCase() { scope: CoroutineScope, initialValue: Boolean?, overriding: Boolean = false, - @StartStrategy private val startStrategy: Int = START_WHEN_NEEDED, + @StartStrategy override val startStrategy: Int = START_WHEN_NEEDED, ) : Condition(scope, initialValue, overriding) { private var _started = false val started: Boolean @@ -54,10 +54,6 @@ class CombinedConditionTest : SysuiTestCase() { _started = false } - override fun getStartStrategy(): Int { - return startStrategy - } - fun setValue(value: Boolean?) { value?.also { updateCondition(value) } ?: clearCondition() } @@ -75,13 +71,8 @@ class CombinedConditionTest : SysuiTestCase() { val combinedCondition = CombinedCondition( scope = this, - conditions = - listOf( - eagerCondition, - lazyCondition, - startWhenNeededCondition, - ), - operand = Evaluator.OP_OR + conditions = listOf(eagerCondition, lazyCondition, startWhenNeededCondition), + operand = Evaluator.OP_OR, ) val callback = Condition.Callback {} @@ -124,13 +115,8 @@ class CombinedConditionTest : SysuiTestCase() { val combinedCondition = CombinedCondition( scope = this, - conditions = - listOf( - startWhenNeededCondition, - lazyCondition, - eagerCondition, - ), - operand = Evaluator.OP_AND + conditions = listOf(startWhenNeededCondition, lazyCondition, eagerCondition), + operand = Evaluator.OP_AND, ) val callback = Condition.Callback {} @@ -175,7 +161,7 @@ class CombinedConditionTest : SysuiTestCase() { FakeCondition( scope = this, initialValue = false, - startStrategy = START_WHEN_NEEDED + startStrategy = START_WHEN_NEEDED, ) } .toList() @@ -214,7 +200,7 @@ class CombinedConditionTest : SysuiTestCase() { FakeCondition( scope = this, initialValue = false, - startStrategy = START_WHEN_NEEDED + startStrategy = START_WHEN_NEEDED, ) } .toList() @@ -262,7 +248,7 @@ class CombinedConditionTest : SysuiTestCase() { FakeCondition( scope = this, initialValue = false, - startStrategy = START_WHEN_NEEDED + startStrategy = START_WHEN_NEEDED, ) } .toList() @@ -300,9 +286,9 @@ class CombinedConditionTest : SysuiTestCase() { overridingCondition1, lazyCondition, startWhenNeededCondition, - overridingCondition2 + overridingCondition2, ), - operand = Evaluator.OP_OR + operand = Evaluator.OP_OR, ) val callback = Condition.Callback {} @@ -414,11 +400,7 @@ class CombinedConditionTest : SysuiTestCase() { fun testEmptyConditions() = runSelfCancelingTest { for (operand in intArrayOf(Evaluator.OP_OR, Evaluator.OP_AND)) { val combinedCondition = - CombinedCondition( - scope = this, - conditions = emptyList(), - operand = operand, - ) + CombinedCondition(scope = this, conditions = emptyList(), operand = operand) val callback = Condition.Callback {} combinedCondition.addCallback(callback) @@ -435,9 +417,7 @@ class CombinedConditionTest : SysuiTestCase() { * Executes the given block of execution within the scope of a dedicated [CoroutineScope] which * is then automatically canceled and cleaned-up. */ - private fun runSelfCancelingTest( - block: suspend CoroutineScope.() -> Unit, - ) = + private fun runSelfCancelingTest(block: suspend CoroutineScope.() -> Unit) = runBlocking(IMMEDIATE) { val scope = CoroutineScope(coroutineContext + Job()) block(scope) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/RowImageInflaterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/RowImageInflaterTest.kt index 86689cb88569..d0357603665d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/RowImageInflaterTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/RowImageInflaterTest.kt @@ -21,6 +21,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUiAod +import com.android.systemui.statusbar.notification.row.shared.IconData import com.android.systemui.statusbar.notification.row.shared.ImageModel import com.android.systemui.statusbar.notification.row.shared.ImageModelProvider.ImageSizeClass.SmallSquare import com.google.common.truth.Truth.assertThat @@ -38,20 +39,22 @@ class RowImageInflaterTest : SysuiTestCase() { private val resIcon2 = Icon.createWithResource(context, android.R.drawable.ic_delete) private val badUriIcon = Icon.createWithContentUri("content://com.test/does_not_exist") + private var latestImageModelIndex: ImageModelIndex? = null + @Before fun setUp() { - rowImageInflater = RowImageInflater.newInstance(null) + rowImageInflater = RowImageInflater.newInstance(null, reinflating = false) } @Test fun getNewImageIndex_returnsNullWhenUnused() { - assertThat(rowImageInflater.getNewImageIndex()).isNull() + assertThat(getNewImageIndex()).isNull() } @Test fun getNewImageIndex_returnsEmptyIndexWhenZeroImagesLoaded() { assertThat(getImageModelsForIcons()).isEmpty() - val result = rowImageInflater.getNewImageIndex() + val result = getNewImageIndex() assertThat(result).isNotNull() assertThat(result?.contentsForTesting).isEmpty() } @@ -59,7 +62,7 @@ class RowImageInflaterTest : SysuiTestCase() { @Test fun getNewImageIndex_returnsSingleImageWhenOneImageLoaded() { assertThat(getImageModelsForIcons(resIcon1)).hasSize(1) - val result = rowImageInflater.getNewImageIndex() + val result = getNewImageIndex() assertThat(result).isNotNull() assertThat(result?.contentsForTesting).hasSize(1) } @@ -85,7 +88,7 @@ class RowImageInflaterTest : SysuiTestCase() { assertThat(providedModels[3].drawable).isNotNull() // VERIFY the returned index has all 3 entries, 2 of which have drawables - val indexGen1 = rowImageInflater.getNewImageIndex() + val indexGen1 = getNewImageIndex() assertThat(indexGen1).isNotNull() assertThat(indexGen1?.contentsForTesting).hasSize(3) assertThat(indexGen1?.contentsForTesting?.mapNotNull { it.drawable }).hasSize(2) @@ -96,7 +99,7 @@ class RowImageInflaterTest : SysuiTestCase() { exampleFirstGeneration() // THEN start a new generation of the inflation - rowImageInflater = RowImageInflater.newInstance(rowImageInflater.getNewImageIndex()) + rowImageInflater = RowImageInflater.newInstance(getNewImageIndex(), reinflating = false) getNewImageIndex_returnsEmptyIndexWhenZeroImagesLoaded() } @@ -104,13 +107,47 @@ class RowImageInflaterTest : SysuiTestCase() { @Test fun exampleSecondGeneration_whichLoadsOneImage() { exampleFirstGeneration() + val gen1Index = latestImageModelIndex!! // THEN start a new generation of the inflation - rowImageInflater = RowImageInflater.newInstance(rowImageInflater.getNewImageIndex()) + rowImageInflater = RowImageInflater.newInstance(gen1Index, reinflating = false) getNewImageIndex_returnsSingleImageWhenOneImageLoaded() + val gen2Index = latestImageModelIndex!! + + // VERIFY that the drawable was copied from the previous index + val gen1model = gen1Index.findModel(resIcon1) + val gen2model = gen2Index.findModel(resIcon1) + assertThat(gen2model).isNotSameInstanceAs(gen1model) + assertThat(gen2model.drawable).isSameInstanceAs(gen1model.drawable) } + @Test + fun exampleSecondGeneration_reinflating_whichLoadsOneImage() { + exampleFirstGeneration() + val gen1Index = latestImageModelIndex!! + + // THEN start a new generation of the inflation + rowImageInflater = RowImageInflater.newInstance(gen1Index, reinflating = true) + + getNewImageIndex_returnsSingleImageWhenOneImageLoaded() + val gen2Index = latestImageModelIndex!! + + // VERIFY that the drawable was reloaded rather than copied from the previous index + val gen1model = gen1Index.findModel(resIcon1) + val gen2model = gen2Index.findModel(resIcon1) + assertThat(gen2model).isNotSameInstanceAs(gen1model) + assertThat(gen2model.drawable).isNotSameInstanceAs(gen1model.drawable) + } + + private fun ImageModelIndex.findModel(icon: Icon): LazyImage = + IconData.fromIcon(icon) + .let { iconData -> contentsForTesting.find { it.icon == iconData } } + .also { assertThat(it).isNotNull() }!! + + private fun getNewImageIndex(): ImageModelIndex? = + rowImageInflater.getNewImageIndex().also { latestImageModelIndex = it } + private fun getImageModelsForIcons(vararg icons: Icon): List<ImageModel> { val provider = rowImageInflater.useForContentModel() return icons.map { icon -> diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt index af89403c5397..623989ec5809 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt @@ -67,6 +67,7 @@ import com.android.systemui.scene.shared.model.sceneDataSource import com.android.systemui.scene.ui.view.mockWindowRootViewProvider import com.android.systemui.settings.brightness.data.repository.brightnessMirrorShowingRepository import com.android.systemui.settings.displayTracker +import com.android.systemui.shade.data.repository.fakeShadeDisplaysRepository import com.android.systemui.shade.data.repository.shadeRepository import com.android.systemui.shade.domain.interactor.shadeInteractor import com.android.systemui.shade.domain.interactor.shadeLayoutParams @@ -203,5 +204,6 @@ class KosmosJavaAdapter() { val windowRootViewBlurInteractor by lazy { kosmos.windowRootViewBlurInteractor } val sysuiState by lazy { kosmos.sysUiState } val displayTracker by lazy { kosmos.displayTracker } + val fakeShadeDisplaysRepository by lazy { kosmos.fakeShadeDisplaysRepository } val sysUIStateDispatcher by lazy { kosmos.sysUIStateDispatcher } } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/model/SysUiStateKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/model/SysUiStateKosmos.kt index 848fed6b0590..11bd4c7b7940 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/model/SysUiStateKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/model/SysUiStateKosmos.kt @@ -32,12 +32,7 @@ val Kosmos.sysUiStateFactory by Fixture { object : SysUiStateImpl.Factory { override fun create(displayId: Int): SysUiStateImpl { return spy( - SysUiStateImpl( - displayId, - sceneContainerPlugin, - dumpManager, - sysUIStateDispatcher, - ) + SysUiStateImpl(displayId, sceneContainerPlugin, dumpManager, sysUIStateDispatcher) ) } } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorKosmos.kt index da879d9e314d..2b3158da38f9 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorKosmos.kt @@ -39,7 +39,8 @@ fun Kosmos.setPromotedContent(entry: NotificationEntry) { promotedNotificationContentExtractor.extractContent( entry, Notification.Builder.recoverBuilder(applicationContext, entry.sbn.notification), - RowImageInflater.newInstance(null).useForContentModel(), + RowImageInflater.newInstance(previousIndex = null, reinflating = false) + .useForContentModel(), ) entry.promotedNotificationContentModel = requireNotNull(extractedContent) { "extractContent returned null" } diff --git a/ravenwood/scripts/list-ravenwood-tests.sh b/ravenwood/scripts/list-ravenwood-tests.sh index 05f3fdffdaa7..5d7daeb1c304 100755 --- a/ravenwood/scripts/list-ravenwood-tests.sh +++ b/ravenwood/scripts/list-ravenwood-tests.sh @@ -15,4 +15,20 @@ # List all the ravenwood test modules. -jq -r 'to_entries[] | select( .value.compatibility_suites | index("ravenwood-tests") ) | .key' "$OUT/module-info.json" | sort +set -e + +in="$OUT/module-info.json" +cache="$OUT/ravenwood-test-list.cached.tmp" +cache_temp="$OUT/ravenwood-test-list.temp.tmp" + +if [[ "$in" -nt "$cache" ]] ; then + rm -f "$cache_temp" "$cache" + + # First, create to a temp file, and once it's completed, rename it + # to the actual cache file, so that if the command failed or is interrupted, + # we don't update the cache. + jq -r 'to_entries[] | select( .value.compatibility_suites | index("ravenwood-tests") ) | .key' "$OUT/module-info.json" | sort > "$cache_temp" + mv "$cache_temp" "$cache" +fi + +cat "$cache" diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java index 1503d889c298..eea667ef2f39 100644 --- a/services/core/java/com/android/server/am/ProcessRecord.java +++ b/services/core/java/com/android/server/am/ProcessRecord.java @@ -1443,17 +1443,32 @@ class ProcessRecord implements WindowProcessListener { void onProcessFrozen() { mProfile.onProcessFrozen(); - if (mThread != null) mThread.onProcessPaused(); + final ApplicationThreadDeferred t; + synchronized (mService) { + t = mThread; + } + // Release the lock before calling the notifier, in case that calls back into AM. + if (t != null) t.onProcessPaused(); } void onProcessUnfrozen() { - if (mThread != null) mThread.onProcessUnpaused(); + final ApplicationThreadDeferred t; + synchronized (mService) { + t = mThread; + } + // Release the lock before calling the notifier, in case that calls back into AM. + if (t != null) t.onProcessUnpaused(); mProfile.onProcessUnfrozen(); mServices.onProcessUnfrozen(); } void onProcessFrozenCancelled() { - if (mThread != null) mThread.onProcessPausedCancelled(); + final ApplicationThreadDeferred t; + synchronized (mService) { + t = mThread; + } + // Release the lock before calling the notifier, in case that calls back into AM. + if (t != null) t.onProcessPausedCancelled(); mServices.onProcessFrozenCancelled(); } diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index d402f010281f..7016c11b69e7 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -2442,7 +2442,10 @@ public final class DisplayManagerService extends SystemService { applyDisplayChangedLocked(display); } - if (mDisplayTopologyCoordinator != null) { + // The default display should always be added to the topology. Other displays will be added + // upon calling onDisplayBelongToTopologyChanged(). + if (mDisplayTopologyCoordinator != null + && display.getDisplayIdLocked() == Display.DEFAULT_DISPLAY) { mDisplayTopologyCoordinator.onDisplayAdded(display.getDisplayInfoLocked()); } } @@ -6037,6 +6040,18 @@ public final class DisplayManagerService extends SystemService { } @Override + public void onDisplayBelongToTopologyChanged(int displayId, boolean inTopology) { + if (mDisplayTopologyCoordinator == null) { + return; + } + if (inTopology) { + mDisplayTopologyCoordinator.onDisplayAdded(getDisplayInfo(displayId)); + } else { + mDisplayTopologyCoordinator.onDisplayRemoved(displayId); + } + } + + @Override public void reloadTopologies(final int userId) { // Reload topologies only if the userId matches the current user id. if (userId == mCurrentUserId) { diff --git a/services/core/java/com/android/server/display/DisplayTopologyCoordinator.java b/services/core/java/com/android/server/display/DisplayTopologyCoordinator.java index 2618cf40d113..997fff58b952 100644 --- a/services/core/java/com/android/server/display/DisplayTopologyCoordinator.java +++ b/services/core/java/com/android/server/display/DisplayTopologyCoordinator.java @@ -267,11 +267,6 @@ class DisplayTopologyCoordinator { + "type is EXTERNAL or OVERLAY and !mIsExtendedDisplayEnabled"); return false; } - if (info.displayGroupId != Display.DEFAULT_DISPLAY_GROUP) { - Slog.d(TAG, "Display " + info.displayId + " not allowed in topology because " - + "it is not in the default display group"); - return false; - } return true; } diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java index 6ab30595e46b..098f113dc11a 100644 --- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java +++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java @@ -897,7 +897,7 @@ final class DefaultPermissionGrantPolicy { SearchManager.INTENT_ACTION_GLOBAL_SEARCH, userId); grantPermissionsToSystemPackage(pm, voiceSearchPackage, userId, PHONE_PERMISSIONS, CALENDAR_PERMISSIONS, NEARBY_DEVICES_PERMISSIONS, - COARSE_BACKGROUND_LOCATION_PERMISSIONS); + COARSE_BACKGROUND_LOCATION_PERMISSIONS, CONTACTS_PERMISSIONS); revokeRuntimePermissions(pm, voiceSearchPackage, FINE_LOCATION_PERMISSIONS, false, userId); } diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java index d620e98d3437..bac732637d8d 100644 --- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java +++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java @@ -1173,12 +1173,19 @@ public class WallpaperManagerService extends IWallpaperManager.Stub return; } - final ComponentName wpService = mWallpaper.getComponent(); // The broadcast of package update could be delayed after service disconnected. Try // to re-bind the service for 10 seconds. mWallpaper.mBindSource = BindSource.CONNECTION_TRY_TO_REBIND; - if (bindWallpaperComponentLocked( - wpService, true, false, mWallpaper, null)) { + boolean success; + if (liveWallpaperContentHandling()) { + success = bindWallpaperDescriptionLocked( + mWallpaper.getDescription(), /* force= */ true, + /* fromUser= */ false, mWallpaper, /* reply= */ null); + } else { + success = bindWallpaperComponentLocked(mWallpaper.getComponent(), /* force= */ + true, /* fromUser= */false, mWallpaper, /* reply= */ null); + } + if (success) { mWallpaper.connection.scheduleTimeoutLocked(); } else if (SystemClock.uptimeMillis() - mWallpaper.lastDiedTime < WALLPAPER_RECONNECT_TIMEOUT_MS) { @@ -1189,7 +1196,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub // Timeout Slog.w(TAG, "Reverting to built-in wallpaper!"); clearWallpaperLocked(mWallpaper.mWhich, mWallpaper.userId, false, null); - final String flattened = wpService.flattenToString(); + final String flattened = mWallpaper.getComponent().flattenToString(); EventLog.writeEvent(EventLogTags.WP_WALLPAPER_CRASHED, flattened.substring(0, Math.min(flattened.length(), MAX_WALLPAPER_COMPONENT_LOG_LENGTH))); diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java index 36686fc086f1..2287a687700c 100644 --- a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java +++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java @@ -288,9 +288,10 @@ public class BackgroundActivityStartController { } private boolean isHomeApp(int uid, @Nullable String packageName) { - if (getService().mHomeProcess != null) { - // Fast check - return uid == getService().mHomeProcess.mUid; + WindowProcessController homeProcess = getService().mHomeProcess; + if (homeProcess != null && (homeProcess.mUid != SYSTEM_UID || packageName == null)) { + // Fast check (skip if sharing system UID and we have a package name) + return uid == homeProcess.mUid; } if (packageName == null) { return false; diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 530b507bcc0f..353ccd5836c8 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -3262,12 +3262,11 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp Slog.e(TAG, "ShouldShowSystemDecors shouldn't be updated when the flag is off."); } - final boolean shouldShowContent; if (!allowContentModeSwitch()) { return; } - shouldShowContent = mDisplay.canHostTasks(); + final boolean shouldShowContent = mDisplay.canHostTasks(); if (shouldShowContent == mWmService.mDisplayWindowSettings .shouldShowSystemDecorsLocked(this)) { return; @@ -3277,6 +3276,11 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp if (!shouldShowContent) { clearAllTasksOnDisplay(null /* clearTasksCallback */, false /* isRemovingDisplay */); } + + // If the display is allowed to show content, then it belongs to the display topology; + // vice versa. + mWmService.mDisplayManagerInternal.onDisplayBelongToTopologyChanged(mDisplayId, + /* inTopology= */ shouldShowContent); } /** @@ -3302,6 +3306,10 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp return false; } + if (shouldNeverShowSystemDecorations()) { + return false; + } + // TODO(b/391965805): Remove this after introducing FLAG_ALLOW_CONTENT_MODE_SWITCH. if ((mDisplay.getFlags() & Display.FLAG_REAR) != 0) { return false; @@ -5666,16 +5674,23 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp return type == TRANSIT_OPEN || type == TRANSIT_TO_FRONT; } - /** - * @see Display#FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS - */ - boolean isSystemDecorationsSupported() { + private boolean shouldNeverShowSystemDecorations() { if (mDisplayId == mWmService.mVr2dDisplayId) { // VR virtual display will be used to run and render 2D app within a VR experience. - return false; + return true; } if (!isTrusted()) { // Do not show system decorations on untrusted virtual display. + return true; + } + return false; + } + + /** + * @see Display#FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS + */ + boolean isSystemDecorationsSupported() { + if (shouldNeverShowSystemDecorations()) { return false; } if (mWmService.mDisplayWindowSettings.shouldShowSystemDecorsLocked(this) diff --git a/services/core/java/com/android/server/wm/DisplayWindowSettings.java b/services/core/java/com/android/server/wm/DisplayWindowSettings.java index 48ffccbca935..56579206566f 100644 --- a/services/core/java/com/android/server/wm/DisplayWindowSettings.java +++ b/services/core/java/com/android/server/wm/DisplayWindowSettings.java @@ -240,6 +240,11 @@ class DisplayWindowSettings { mSettingsProvider.updateOverrideSettings(displayInfo, overrideSettings); } + /** + * Returns {@code true} if either the display is the default display, or the display is allowed + * to dynamically add/remove system decorations and the system decorations should be shown on it + * currently. + */ boolean shouldShowSystemDecorsLocked(@NonNull DisplayContent dc) { if (dc.getDisplayId() == Display.DEFAULT_DISPLAY) { // Default display should show system decors. diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index 394fe6f0e8ed..cf201c9f34f0 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -2772,11 +2772,17 @@ class RootWindowContainer extends WindowContainer<DisplayContent> return; } - if (ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT.isTrue() - && display.allowContentModeSwitch()) { - mWindowManager.mDisplayWindowSettings - .setShouldShowSystemDecorsInternalLocked(display, - display.mDisplay.canHostTasks()); + if (ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT.isTrue()) { + if (display.allowContentModeSwitch()) { + mWindowManager.mDisplayWindowSettings + .setShouldShowSystemDecorsInternalLocked(display, + display.mDisplay.canHostTasks()); + } + + final boolean inTopology = mWindowManager.mDisplayWindowSettings + .shouldShowSystemDecorsLocked(display); + mWmService.mDisplayManagerInternal.onDisplayBelongToTopologyChanged(displayId, + inTopology); } startSystemDecorations(display, "displayAdded"); diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 22f0278b3a30..d16c301cec40 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -4497,7 +4497,7 @@ class Task extends TaskFragment { } void onPictureInPictureParamsChanged() { - if (inPinnedWindowingMode() || Flags.enableDesktopWindowingPip()) { + if (inPinnedWindowingMode() || DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PIP.isTrue()) { dispatchTaskInfoChangedIfNeeded(true /* force */); } } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java index a81a0b3251c8..4b98a723fc2d 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java @@ -50,6 +50,7 @@ import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; import android.util.IndentingPrintWriter; +import android.util.PrintWriterPrinter; import com.android.internal.util.Preconditions; import com.android.internal.util.XmlUtils; @@ -1484,5 +1485,12 @@ class ActiveAdmin { pw.print("mProvisioningContext="); pw.println(mProvisioningContext); } + + if (info != null) { + pw.println("DeviceAdminInfo:"); + pw.increaseIndent(); + info.dump(new PrintWriterPrinter(pw), ""); + pw.decreaseIndent(); + } } } diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java index 65585d06ff06..1f8ccde98d35 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java @@ -3914,6 +3914,7 @@ public class DisplayManagerServiceTest { DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector); DisplayManagerService.BinderService displayManagerBinderService = displayManager.new BinderService(); + DisplayManagerInternal localService = displayManager.new LocalService(); Handler handler = displayManager.getDisplayHandler(); waitForIdleHandler(handler); @@ -3922,8 +3923,8 @@ public class DisplayManagerServiceTest { INTERNAL_EVENT_FLAG_TOPOLOGY_UPDATED); waitForIdleHandler(handler); - var topology = initDisplayTopology(displayManager, displayManagerBinderService, callback, - handler, /*shouldEmitTopologyChangeEvent=*/ true); + var topology = initDisplayTopology(displayManager, displayManagerBinderService, + localService, callback, handler, /*shouldEmitTopologyChangeEvent=*/ true); callback.clear(); callback.expectsEvent(TOPOLOGY_CHANGED_EVENT); displayManagerBinderService.setDisplayTopology(topology); @@ -3938,6 +3939,7 @@ public class DisplayManagerServiceTest { DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector); DisplayManagerService.BinderService displayManagerBinderService = displayManager.new BinderService(); + DisplayManagerInternal localService = displayManager.new LocalService(); Handler handler = displayManager.getDisplayHandler(); waitForIdleHandler(handler); @@ -3947,8 +3949,8 @@ public class DisplayManagerServiceTest { STANDARD_DISPLAY_EVENTS); waitForIdleHandler(handler); - var topology = initDisplayTopology(displayManager, displayManagerBinderService, callback, - handler, /*shouldEmitTopologyChangeEvent=*/ false); + var topology = initDisplayTopology(displayManager, displayManagerBinderService, + localService, callback, handler, /*shouldEmitTopologyChangeEvent=*/ false); callback.clear(); callback.expectsEvent(TOPOLOGY_CHANGED_EVENT); // should not happen displayManagerBinderService.setDisplayTopology(topology); @@ -4706,15 +4708,16 @@ public class DisplayManagerServiceTest { private DisplayTopology initDisplayTopology(DisplayManagerService displayManager, DisplayManagerService.BinderService displayManagerBinderService, - FakeDisplayManagerCallback callback, + DisplayManagerInternal localService, FakeDisplayManagerCallback callback, Handler handler, boolean shouldEmitTopologyChangeEvent) { Settings.Global.putInt(mContext.getContentResolver(), DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS, 1); callback.expectsEvent(TOPOLOGY_CHANGED_EVENT); FakeDisplayDevice displayDevice0 = - createFakeDisplayDevice(displayManager, new float[]{60f}, Display.TYPE_INTERNAL); + createFakeDisplayDevice(displayManager, new float[]{60f}, Display.TYPE_EXTERNAL); int displayId0 = getDisplayIdForDisplayDevice(displayManager, displayManagerBinderService, displayDevice0); + waitForIdleHandler(handler); if (shouldEmitTopologyChangeEvent) { callback.waitForExpectedEvent(); } else { @@ -4728,6 +4731,11 @@ public class DisplayManagerServiceTest { int displayId1 = getDisplayIdForDisplayDevice(displayManager, displayManagerBinderService, displayDevice1); waitForIdleHandler(handler); + // Non-default display should not be added until onDisplayBelongToTopologyChanged is called + // with true + callback.waitForNonExpectedEvent(); + localService.onDisplayBelongToTopologyChanged(displayId1, true); + waitForIdleHandler(handler); if (shouldEmitTopologyChangeEvent) { callback.waitForExpectedEvent(); } else { diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayTopologyCoordinatorTest.kt b/services/tests/displayservicetests/src/com/android/server/display/DisplayTopologyCoordinatorTest.kt index 3c134b5d5482..206c90d0481a 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/DisplayTopologyCoordinatorTest.kt +++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayTopologyCoordinatorTest.kt @@ -220,17 +220,6 @@ class DisplayTopologyCoordinatorTest { } @Test - fun addDisplay_notInDefaultDisplayGroup() { - displayInfos[0].displayGroupId = Display.DEFAULT_DISPLAY_GROUP + 1 - - coordinator.onDisplayAdded(displayInfos[0]) - - verify(mockTopology, never()).addDisplay(anyInt(), anyFloat(), anyFloat()) - verify(mockTopologyChangedCallback, never()).invoke(any()) - verify(mockTopologyStore, never()).restoreTopology(any()) - } - - @Test fun updateDisplay() { whenever(mockTopology.updateDisplay(eq(displayInfos[0].displayId), anyFloat(), anyFloat())) .thenReturn(true) @@ -349,17 +338,6 @@ class DisplayTopologyCoordinatorTest { } @Test - fun updateDisplay_notInDefaultDisplayGroup() { - displayInfos[0].displayGroupId = Display.DEFAULT_DISPLAY_GROUP + 1 - - coordinator.onDisplayChanged(displayInfos[0]) - - verify(mockTopology, never()).updateDisplay(anyInt(), anyFloat(), anyFloat()) - verify(mockTopologyCopy, never()).getGraph(any()) - verify(mockTopologyChangedCallback, never()).invoke(any()) - } - - @Test fun removeDisplay() { addDisplay() diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java index e4a1bf603cf0..34e9bed8463a 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java @@ -31,6 +31,7 @@ import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_FIRST; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; +import static com.android.server.display.feature.flags.Flags.FLAG_ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT; import static com.android.server.wm.ActivityRecord.State.FINISHING; import static com.android.server.wm.ActivityRecord.State.PAUSED; import static com.android.server.wm.ActivityRecord.State.PAUSING; @@ -82,6 +83,7 @@ import android.os.UserHandle; import android.platform.test.annotations.EnableFlags; import android.platform.test.annotations.Presubmit; import android.util.Pair; +import android.view.DisplayInfo; import androidx.test.filters.MediumTest; @@ -1379,6 +1381,23 @@ public class RootWindowContainerTests extends WindowTestsBase { verify(controller, never()).notifyTaskProfileLocked(any(), anyInt()); } + @EnableFlags(FLAG_ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT) + @Test + public void testOnDisplayBelongToTopologyChanged() { + final DisplayInfo displayInfo = new DisplayInfo(); + displayInfo.copyFrom(mDisplayInfo); + displayInfo.displayId = DEFAULT_DISPLAY + 1; + final DisplayContent dc = createNewDisplay(displayInfo); + final int displayId = dc.getDisplayId(); + + doReturn(dc).when(mRootWindowContainer).getDisplayContentOrCreate(displayId); + doReturn(true).when(mWm.mDisplayWindowSettings).shouldShowSystemDecorsLocked(dc); + + mRootWindowContainer.onDisplayAdded(displayId); + verify(mWm.mDisplayManagerInternal, times(1)).onDisplayBelongToTopologyChanged(anyInt(), + anyBoolean()); + } + /** * Mock {@link RootWindowContainer#resolveHomeActivity} for returning consistent activity * info for test cases. diff --git a/tests/FlickerTests/Rotation/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt b/tests/FlickerTests/Rotation/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt index d7f91e009c92..2093ccc86653 100644 --- a/tests/FlickerTests/Rotation/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt +++ b/tests/FlickerTests/Rotation/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt @@ -156,19 +156,6 @@ class SeamlessAppRotationTest(flicker: LegacyFlickerTest) : RotationTransition(f flicker.assertLayers { isVisible(testApp) } } - /** Checks that [testApp] layer covers the entire screen during the whole transition */ - @Presubmit - @Test - fun appLayerRotates() { - flicker.assertLayers { - this.invoke("entireScreenCovered") { entry -> - entry.entry.displays.map { display -> - entry.visibleRegion(testApp).coversExactly(display.layerStackSpace) - } - } - } - } - /** {@inheritDoc} */ @Test @Ignore("Not applicable to this CUJ. App is full screen") @@ -225,7 +212,6 @@ class SeamlessAppRotationTest(flicker: LegacyFlickerTest) : RotationTransition(f visibleLayersShownMoreThanOneConsecutiveEntry() visibleWindowsShownMoreThanOneConsecutiveEntry() - runAndIgnoreAssumptionViolation { appLayerRotates() } runAndIgnoreAssumptionViolation { appLayerAlwaysVisible() } runAndIgnoreAssumptionViolation { navBarLayerIsVisibleAtStartAndEnd() } runAndIgnoreAssumptionViolation { navBarWindowIsAlwaysVisible() } diff --git a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml index 7c24a4adca3d..98af8b2f53b5 100644 --- a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml +++ b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml @@ -39,7 +39,6 @@ android:value="true" /> <activity android:name=".SimpleActivity" android:taskAffinity="com.android.server.wm.flicker.testapp.SimpleActivity" - android:theme="@style/CutoutShortEdges" android:label="SimpleActivity" android:exported="true"> <intent-filter> @@ -49,7 +48,6 @@ </activity> <activity android:name=".ImeActivity" android:taskAffinity="com.android.server.wm.flicker.testapp.ImeActivity" - android:theme="@style/CutoutShortEdges" android:label="ImeActivity" android:exported="true"> <intent-filter> @@ -58,7 +56,6 @@ </intent-filter> </activity> <activity android:name=".ImeActivityAutoFocus" - android:theme="@style/CutoutShortEdges" android:taskAffinity="com.android.server.wm.flicker.testapp.ImeActivityAutoFocus" android:windowSoftInputMode="stateVisible" android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout" @@ -81,7 +78,6 @@ </activity> <activity android:name=".SeamlessRotationActivity" android:taskAffinity="com.android.server.wm.flicker.testapp.SeamlessRotationActivity" - android:theme="@style/CutoutShortEdges" android:configChanges="orientation|screenSize|screenLayout|smallestScreenSize" android:showWhenLocked="true" android:label="SeamlessActivity" @@ -92,7 +88,6 @@ </intent-filter> </activity> <activity android:name=".NonResizeableActivity" - android:theme="@style/CutoutShortEdges" android:resizeableActivity="false" android:taskAffinity="com.android.server.wm.flicker.testapp.NonResizeableActivity" android:label="NonResizeableActivity" @@ -174,7 +169,6 @@ </activity> <activity android:name=".LaunchNewActivity" android:taskAffinity="com.android.server.wm.flicker.testapp.LaunchNewActivity" - android:theme="@style/CutoutShortEdges" android:configChanges="orientation|screenSize" android:label="LaunchNewActivity" android:exported="true"> @@ -185,7 +179,6 @@ </activity> <activity android:name=".LaunchNewTaskActivity" android:taskAffinity="com.android.server.wm.flicker.testapp.LaunchNewTaskActivity" - android:theme="@style/CutoutShortEdges" android:configChanges="orientation|screenSize" android:label="LaunchNewTaskActivity" android:exported="true"> @@ -207,7 +200,6 @@ </activity> <activity android:name=".PortraitOnlyActivity" android:taskAffinity="com.android.server.wm.flicker.testapp.PortraitOnlyActivity" - android:theme="@style/CutoutShortEdges" android:screenOrientation="portrait" android:configChanges="orientation|screenSize" android:exported="true"> @@ -219,7 +211,6 @@ <activity android:name=".ImeEditorPopupDialogActivity" android:taskAffinity="com.android.server.wm.flicker.testapp.ImeEditorPopupDialogActivity" android:configChanges="orientation|screenSize" - android:theme="@style/CutoutShortEdges" android:label="ImeEditorPopupDialogActivity" android:exported="true"> <intent-filter> @@ -229,7 +220,6 @@ </activity> <activity android:name=".ShowWhenLockedActivity" android:taskAffinity="com.android.server.wm.flicker.testapp.ShowWhenLockedActivity" - android:theme="@style/CutoutShortEdges" android:configChanges="orientation|screenSize" android:label="ShowWhenLockedActivity" android:showWhenLocked="true" @@ -241,7 +231,6 @@ </activity> <activity android:name=".NotificationActivity" android:taskAffinity="com.android.server.wm.flicker.testapp.NotificationActivity" - android:theme="@style/CutoutShortEdges" android:configChanges="orientation|screenSize" android:label="NotificationActivity" android:exported="true"> @@ -254,7 +243,6 @@ android:name=".ActivityEmbeddingMainActivity" android:label="ActivityEmbedding Main" android:taskAffinity="com.android.server.wm.flicker.testapp.ActivityEmbedding" - android:theme="@style/CutoutShortEdges" android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout" android:exported="true"> <intent-filter> @@ -266,7 +254,6 @@ android:name=".ActivityEmbeddingTrampolineActivity" android:label="ActivityEmbedding Trampoline" android:taskAffinity="com.android.server.wm.flicker.testapp.ActivityEmbedding" - android:theme="@style/CutoutShortEdges" android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout" android:exported="false"> </activity> @@ -274,7 +261,6 @@ android:name=".ActivityEmbeddingSecondaryActivity" android:label="ActivityEmbedding Secondary" android:taskAffinity="com.android.server.wm.flicker.testapp.ActivityEmbedding" - android:theme="@style/CutoutShortEdges" android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout" android:supportsPictureInPicture="true" android:exported="false"/> @@ -282,21 +268,18 @@ android:name=".ActivityEmbeddingThirdActivity" android:label="ActivityEmbedding Third" android:taskAffinity="com.android.server.wm.flicker.testapp.ActivityEmbedding" - android:theme="@style/CutoutShortEdges" android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout" android:exported="false"/> <activity android:name=".ActivityEmbeddingAlwaysExpandActivity" android:label="ActivityEmbedding AlwaysExpand" android:taskAffinity="com.android.server.wm.flicker.testapp.ActivityEmbedding" - android:theme="@style/CutoutShortEdges" android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout" android:exported="false"/> <activity android:name=".ActivityEmbeddingPlaceholderPrimaryActivity" android:label="ActivityEmbedding Placeholder Primary" android:taskAffinity="com.android.server.wm.flicker.testapp.ActivityEmbedding" - android:theme="@style/CutoutShortEdges" android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout" android:exported="false"> </activity> @@ -304,7 +287,6 @@ android:name=".ActivityEmbeddingPlaceholderSecondaryActivity" android:label="ActivityEmbedding Placeholder Secondary" android:taskAffinity="com.android.server.wm.flicker.testapp.ActivityEmbedding" - android:theme="@style/CutoutShortEdges" android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout" android:exported="false"/> <activity android:name=".MailActivity" @@ -334,7 +316,6 @@ android:supportsPictureInPicture="true" android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation" android:taskAffinity="com.android.server.wm.flicker.testapp.PipActivity" - android:theme="@style/CutoutShortEdges" android:launchMode="singleTop" android:label="PipActivity" android:exported="true"> @@ -350,7 +331,6 @@ <activity android:name=".BottomHalfPipLaunchingActivity" android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation" android:taskAffinity="com.android.server.wm.flicker.testapp.BottomHalfPipLaunchingActivity" - android:theme="@style/CutoutShortEdges" android:label="BottomHalfPipLaunchingActivity" android:exported="true"> <intent-filter> @@ -371,7 +351,6 @@ <activity android:name=".SplitScreenActivity" android:resizeableActivity="true" android:taskAffinity="com.android.server.wm.flicker.testapp.SplitScreenActivity" - android:theme="@style/CutoutShortEdges" android:label="SplitScreenPrimaryActivity" android:exported="true" android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"> @@ -383,7 +362,6 @@ <activity android:name=".SplitScreenSecondaryActivity" android:resizeableActivity="true" android:taskAffinity="com.android.server.wm.flicker.testapp.SplitScreenSecondaryActivity" - android:theme="@style/CutoutShortEdges" android:label="SplitScreenSecondaryActivity" android:exported="true" android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"> @@ -396,7 +374,6 @@ </activity> <activity android:name=".SendNotificationActivity" android:taskAffinity="com.android.server.wm.flicker.testapp.SendNotificationActivity" - android:theme="@style/CutoutShortEdges" android:label="SendNotificationActivity" android:exported="true"> <intent-filter> @@ -408,7 +385,6 @@ android:name=".LaunchBubbleActivity" android:label="LaunchBubbleActivity" android:exported="true" - android:theme="@style/CutoutShortEdges" android:launchMode="singleTop"> <intent-filter> <action android:name="android.intent.action.MAIN"/> @@ -420,7 +396,6 @@ android:name=".BubbleActivity" android:label="BubbleActivity" android:exported="false" - android:theme="@style/CutoutShortEdges" android:resizeableActivity="true"/> <activity android:name=".TransferSplashscreenActivity" @@ -468,4 +443,4 @@ </service> </application> <uses-permission android:name="android.permission.ACTIVITY_RECOGNITION"/> -</manifest> +</manifest>
\ No newline at end of file diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_bottom_half_pip.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_bottom_half_pip.xml index 2f9c3aa82057..15e2a798b2cf 100644 --- a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_bottom_half_pip.xml +++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_bottom_half_pip.xml @@ -18,6 +18,7 @@ xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" + android:fitsSystemWindows="true" android:orientation="vertical" android:background="@android:color/holo_blue_bright"> diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_bubble.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_bubble.xml index 7c7b2cacaefb..4bc70996eba8 100644 --- a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_bubble.xml +++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_bubble.xml @@ -16,6 +16,7 @@ --> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:fitsSystemWindows="true" android:layout_width="match_parent" android:layout_height="match_parent"> <Button diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_base_layout.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_base_layout.xml index f0dfdfce035f..e98ffd045d3d 100644 --- a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_base_layout.xml +++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_base_layout.xml @@ -19,5 +19,6 @@ android:id="@+id/root_activity_layout" android:layout_width="match_parent" android:layout_height="match_parent" + android:fitsSystemWindows="true" android:orientation="vertical"> </LinearLayout> diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_main_layout.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_main_layout.xml index 939ba81a47ea..16c906d9ee42 100644 --- a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_main_layout.xml +++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_main_layout.xml @@ -18,6 +18,7 @@ xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" + android:fitsSystemWindows="true" android:background="@android:color/holo_orange_light"> <LinearLayout diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_secondary_activity_layout.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_secondary_activity_layout.xml index 6d4de995bd73..eedb910b2b4e 100644 --- a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_secondary_activity_layout.xml +++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_secondary_activity_layout.xml @@ -19,6 +19,7 @@ android:id="@+id/secondary_activity_layout" android:layout_width="match_parent" android:layout_height="match_parent" + android:fitsSystemWindows="true" android:orientation="vertical"> <Button @@ -49,4 +50,4 @@ android:layout_height="48dp" android:text="Enter pip" /> -</LinearLayout>
\ No newline at end of file +</LinearLayout> diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_ime.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_ime.xml index 507c1b622613..5e5203cb0545 100644 --- a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_ime.xml +++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_ime.xml @@ -17,6 +17,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:background="@android:color/holo_green_light" + android:fitsSystemWindows="true" android:focusableInTouchMode="true" android:orientation="vertical"> diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_launch_new.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_launch_new.xml index fe7bced690f9..2ab5fe7b9d6c 100644 --- a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_launch_new.xml +++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_launch_new.xml @@ -18,6 +18,7 @@ xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" + android:fitsSystemWindows="true" android:background="@android:color/holo_orange_light"> <Button android:id="@+id/launch_second_activity" diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_mail.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_mail.xml index 8a97433ede04..fcef791afe2e 100644 --- a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_mail.xml +++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_mail.xml @@ -19,6 +19,7 @@ xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" + android:fitsSystemWindows="true" android:layout_width="match_parent" android:layout_height="match_parent"> <fragment diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_non_resizeable.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_non_resizeable.xml index 6d5a9dd29248..aeb8423680a3 100644 --- a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_non_resizeable.xml +++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_non_resizeable.xml @@ -18,6 +18,7 @@ xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" + android:fitsSystemWindows="true" android:orientation="vertical" android:background="@android:color/holo_orange_light"> @@ -29,4 +30,4 @@ android:text="NonResizeableActivity" android:textAppearance="?android:attr/textAppearanceLarge"/> -</LinearLayout>
\ No newline at end of file +</LinearLayout> diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_pip.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_pip.xml index 365a0ea017f6..d66b3d74e114 100644 --- a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_pip.xml +++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_pip.xml @@ -18,6 +18,7 @@ xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" + android:fitsSystemWindows="true" android:orientation="vertical" android:background="@android:color/holo_blue_bright"> diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_simple.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_simple.xml index 5d94e5177dcc..42213d6812da 100644 --- a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_simple.xml +++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_simple.xml @@ -18,6 +18,7 @@ xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" + android:fitsSystemWindows="true" android:background="@android:color/holo_orange_light"> </LinearLayout> diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_splitscreen.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_splitscreen.xml index 79e88e49b99e..16c3bc07d55c 100644 --- a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_splitscreen.xml +++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_splitscreen.xml @@ -18,6 +18,7 @@ xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" + android:fitsSystemWindows="true" android:orientation="vertical" android:background="@android:color/holo_green_light"> diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_splitscreen_secondary.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_splitscreen_secondary.xml index ed9feaf1eade..7643cf5de216 100644 --- a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_splitscreen_secondary.xml +++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_splitscreen_secondary.xml @@ -18,6 +18,7 @@ xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" + android:fitsSystemWindows="true" android:orientation="vertical" android:background="@android:color/holo_blue_light"> diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_start_media_projection.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_start_media_projection.xml index c34d2003ef42..79e7bb5de96e 100644 --- a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_start_media_projection.xml +++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_start_media_projection.xml @@ -18,6 +18,7 @@ xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" + android:fitsSystemWindows="true" android:gravity="center" android:orientation="vertical" android:background="@android:color/holo_orange_light"> @@ -39,4 +40,4 @@ android:gravity="center_vertical|center_horizontal" android:text="Start Media Projection with extra intent" android:textAppearance="?android:attr/textAppearanceLarge"/> -</LinearLayout>
\ No newline at end of file +</LinearLayout> diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_surfaceview.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_surfaceview.xml index 0b4693dec6e1..4d12168ca8db 100644 --- a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_surfaceview.xml +++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_surfaceview.xml @@ -19,6 +19,7 @@ android:id="@+id/container" android:layout_width="match_parent" android:layout_height="match_parent" + android:fitsSystemWindows="true" android:background="@android:color/holo_orange_light"> <SurfaceView diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_transparent.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_transparent.xml index 0730ded66ce4..32df5f015414 100644 --- a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_transparent.xml +++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_transparent.xml @@ -15,6 +15,7 @@ ~ limitations under the License. --> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:fitsSystemWindows="true" android:layout_width="match_parent" android:layout_height="match_parent"> diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_transparent_launch.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_transparent_launch.xml index ff4ead95f16e..a0c87fd17fc3 100644 --- a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_transparent_launch.xml +++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_transparent_launch.xml @@ -17,6 +17,7 @@ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" + android:fitsSystemWindows="true" android:orientation="vertical" android:background="@android:color/black"> diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/notification_button.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/notification_button.xml index 78072006f681..da58b3f43c73 100644 --- a/tests/FlickerTests/test-apps/flickerapp/res/layout/notification_button.xml +++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/notification_button.xml @@ -18,10 +18,12 @@ xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" + android:fitsSystemWindows="true" android:background="@android:color/holo_orange_light"> <Button android:id="@+id/post_notification" android:layout_width="wrap_content" android:layout_height="wrap_content" + android:layout_gravity="center_horizontal|center_vertical" android:text="Post Notification" /> </LinearLayout> diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/task_button.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/task_button.xml index 8f75d175d00a..ec3135c0a42d 100644 --- a/tests/FlickerTests/test-apps/flickerapp/res/layout/task_button.xml +++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/task_button.xml @@ -18,6 +18,7 @@ xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" + android:fitsSystemWindows="true" android:background="@android:color/holo_orange_light"> <Button android:id="@+id/launch_new_task" diff --git a/tests/FlickerTests/test-apps/flickerapp/res/values/styles.xml b/tests/FlickerTests/test-apps/flickerapp/res/values/styles.xml index 837d050b73ff..ca3244820893 100644 --- a/tests/FlickerTests/test-apps/flickerapp/res/values/styles.xml +++ b/tests/FlickerTests/test-apps/flickerapp/res/values/styles.xml @@ -36,10 +36,6 @@ <item name="android:windowLayoutInDisplayCutoutMode">default</item> </style> - <style name="CutoutShortEdges" parent="@style/DefaultTheme"> - <item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item> - </style> - <style name="CutoutNever" parent="@style/DefaultTheme"> <item name="android:windowLayoutInDisplayCutoutMode">never</item> </style> @@ -78,4 +74,4 @@ <!-- Here we want to match the duration of our AVD --> <item name="android:windowSplashScreenAnimationDuration">900</item> </style> -</resources> +</resources>
\ No newline at end of file diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeActivity.java index 4418b5a5ff82..30bf616cfe74 100644 --- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeActivity.java +++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeActivity.java @@ -30,7 +30,6 @@ import android.content.Intent; import android.content.IntentFilter; import android.os.Bundle; import android.util.Log; -import android.view.WindowManager; public class ImeActivity extends Activity { @@ -64,10 +63,6 @@ public class ImeActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - WindowManager.LayoutParams p = getWindow().getAttributes(); - p.layoutInDisplayCutoutMode = WindowManager.LayoutParams - .LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; - getWindow().setAttributes(p); setContentView(R.layout.activity_ime); final var filter = new IntentFilter(); diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeEditorPopupDialogActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeEditorPopupDialogActivity.java index 95f933f97bb2..887a15c9ea90 100644 --- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeEditorPopupDialogActivity.java +++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeEditorPopupDialogActivity.java @@ -30,8 +30,6 @@ public class ImeEditorPopupDialogActivity extends Activity { public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); WindowManager.LayoutParams p = getWindow().getAttributes(); - p.layoutInDisplayCutoutMode = WindowManager.LayoutParams - .LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; p.softInputMode = SOFT_INPUT_STATE_ALWAYS_HIDDEN; getWindow().setAttributes(p); LinearLayout layout = new LinearLayout(this); diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/LaunchNewActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/LaunchNewActivity.java index e5710c850f07..97d7a64d5ebf 100644 --- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/LaunchNewActivity.java +++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/LaunchNewActivity.java @@ -19,17 +19,12 @@ package com.android.server.wm.flicker.testapp; import android.app.Activity; import android.content.Intent; import android.os.Bundle; -import android.view.WindowManager; import android.widget.Button; public class LaunchNewActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - WindowManager.LayoutParams p = getWindow().getAttributes(); - p.layoutInDisplayCutoutMode = WindowManager.LayoutParams - .LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; - getWindow().setAttributes(p); setContentView(R.layout.activity_launch_new); Button button = findViewById(R.id.launch_second_activity); diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/LaunchNewTaskActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/LaunchNewTaskActivity.java index 1809781b33e6..402a393e7028 100644 --- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/LaunchNewTaskActivity.java +++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/LaunchNewTaskActivity.java @@ -21,17 +21,12 @@ import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK; import android.app.Activity; import android.content.Intent; import android.os.Bundle; -import android.view.WindowManager; import android.widget.Button; public class LaunchNewTaskActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - WindowManager.LayoutParams p = getWindow().getAttributes(); - p.layoutInDisplayCutoutMode = WindowManager.LayoutParams - .LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; - getWindow().setAttributes(p); setContentView(R.layout.task_button); Button button = findViewById(R.id.launch_new_task); diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/NotificationActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/NotificationActivity.java index d6427abcc65a..61254385e980 100644 --- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/NotificationActivity.java +++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/NotificationActivity.java @@ -45,14 +45,10 @@ public class NotificationActivity extends Activity { requestPermissions(new String[] { POST_NOTIFICATIONS }, 0); } - WindowManager.LayoutParams p = getWindow().getAttributes(); - p.layoutInDisplayCutoutMode = WindowManager.LayoutParams - .LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; - getWindow().setAttributes(p); setContentView(R.layout.notification_button); - Button button = findViewById(R.id.post_notification); - button.setOnClickListener(v -> postNotification()); + ((Button) findViewById(R.id.post_notification)) + .setOnClickListener(v -> postNotification()); createNotificationChannel(); } diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PipActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PipActivity.java index ee25ab2fb66c..e030dcf0db9b 100644 --- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PipActivity.java +++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PipActivity.java @@ -47,8 +47,6 @@ import android.util.DisplayMetrics; import android.util.Log; import android.util.Rational; import android.view.View; -import android.view.Window; -import android.view.WindowManager; import android.widget.CheckBox; import android.widget.RadioButton; @@ -145,12 +143,6 @@ public class PipActivity extends Activity { public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - final Window window = getWindow(); - final WindowManager.LayoutParams layoutParams = window.getAttributes(); - layoutParams.layoutInDisplayCutoutMode = WindowManager.LayoutParams - .LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; - window.setAttributes(layoutParams); - setContentView(R.layout.activity_pip); findViewById(R.id.media_session_start) diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PortraitOnlyActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PortraitOnlyActivity.java index b1876b5e5511..552d843676bb 100644 --- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PortraitOnlyActivity.java +++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PortraitOnlyActivity.java @@ -18,16 +18,11 @@ package com.android.server.wm.flicker.testapp; import android.app.Activity; import android.os.Bundle; -import android.view.WindowManager; public class PortraitOnlyActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - WindowManager.LayoutParams p = getWindow().getAttributes(); - p.layoutInDisplayCutoutMode = WindowManager.LayoutParams - .LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; - getWindow().setAttributes(p); setContentView(R.layout.activity_simple); } } diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/SeamlessRotationActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/SeamlessRotationActivity.java index ce7a0059fa2d..e98c34db0cd9 100644 --- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/SeamlessRotationActivity.java +++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/SeamlessRotationActivity.java @@ -61,8 +61,6 @@ public class SeamlessRotationActivity extends Activity { private void enableSeamlessRotation() { WindowManager.LayoutParams p = getWindow().getAttributes(); p.rotationAnimation = WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS; - p.layoutInDisplayCutoutMode = WindowManager.LayoutParams - .LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; requestWindowFeature(Window.FEATURE_NO_TITLE); getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ShowWhenLockedActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ShowWhenLockedActivity.java index 6f94b74ccf41..a533c9052574 100644 --- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ShowWhenLockedActivity.java +++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ShowWhenLockedActivity.java @@ -18,16 +18,11 @@ package com.android.server.wm.flicker.testapp; import android.app.Activity; import android.os.Bundle; -import android.view.WindowManager; public class ShowWhenLockedActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - WindowManager.LayoutParams p = getWindow().getAttributes(); - p.layoutInDisplayCutoutMode = WindowManager.LayoutParams - .LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; - getWindow().setAttributes(p); setContentView(R.layout.activity_simple); } } diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/SimpleActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/SimpleActivity.java index 699abf87d341..c56eefe32189 100644 --- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/SimpleActivity.java +++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/SimpleActivity.java @@ -18,16 +18,11 @@ package com.android.server.wm.flicker.testapp; import android.app.Activity; import android.os.Bundle; -import android.view.WindowManager; public class SimpleActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - WindowManager.LayoutParams p = getWindow().getAttributes(); - p.layoutInDisplayCutoutMode = WindowManager.LayoutParams - .LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; - getWindow().setAttributes(p); setContentView(R.layout.activity_simple); } } diff --git a/tools/codegen/src/com/android/codegen/Debug.kt b/tools/codegen/src/com/android/codegen/Debug.kt index de3184468540..6423c4f7f6c0 100644 --- a/tools/codegen/src/com/android/codegen/Debug.kt +++ b/tools/codegen/src/com/android/codegen/Debug.kt @@ -21,7 +21,7 @@ import com.github.javaparser.ast.Node fun Node.dump(indent: String = ""): String { return buildString { append(indent) - appendln(dumpOneLineNoChildren()) + appendLine(dumpOneLineNoChildren()) childNodes.forEach { child -> append(child.dump(indent + " ")) } diff --git a/tools/codegen/src/com/android/codegen/FeatureFlag.kt b/tools/codegen/src/com/android/codegen/FeatureFlag.kt index 24150d637a7b..f305429f516f 100644 --- a/tools/codegen/src/com/android/codegen/FeatureFlag.kt +++ b/tools/codegen/src/com/android/codegen/FeatureFlag.kt @@ -22,6 +22,6 @@ enum class FeatureFlag(val onByDefault: Boolean, val desc: String = "") { CONST_DEFS(true, "@Int/StringDef's based on declared static constants"), FOR_EACH_FIELD(false, "forEachField((name, value) -> ...)"); - val kebabCase = name.toLowerCase().replace("_", "-") - val upperCamelCase = name.split("_").map { it.toLowerCase().capitalize() }.joinToString("") + val kebabCase = name.lowercase().replace("_", "-") + val upperCamelCase = name.split("_").map { it.lowercase().capitalize() }.joinToString("") } diff --git a/tools/codegen/src/com/android/codegen/FileInfo.kt b/tools/codegen/src/com/android/codegen/FileInfo.kt index cc3a15654956..ca04f1eb9ab7 100644 --- a/tools/codegen/src/com/android/codegen/FileInfo.kt +++ b/tools/codegen/src/com/android/codegen/FileInfo.kt @@ -126,7 +126,7 @@ class FileInfo( +"\n}" } // Print general code as-is - is CodeChunk.Code -> chunk.lines.forEach { stringBuilder.appendln(it) } + is CodeChunk.Code -> chunk.lines.forEach { stringBuilder.appendLine(it) } // Recursively render data classes is CodeChunk.DataClass -> chunk.chunks.forEach { print(it) } } @@ -175,11 +175,11 @@ class FileInfo( /** Debug info */ override fun toString(): String { return buildString { - appendln("class $name $range") + appendLine("class $name $range") nested.forEach { - appendln(it) + appendLine(it) } - appendln("end $name") + appendLine("end $name") } } } diff --git a/tools/codegen/src/com/android/codegen/Generators.kt b/tools/codegen/src/com/android/codegen/Generators.kt index d3a8b033dfff..710960249661 100644 --- a/tools/codegen/src/com/android/codegen/Generators.kt +++ b/tools/codegen/src/com/android/codegen/Generators.kt @@ -43,7 +43,7 @@ fun ClassPrinter.generateConstDef(consts: List<Pair<VariableDeclarator, FieldDec } var AnnotationName = prefix.split("_") .filterNot { it.isBlank() } - .map { it.toLowerCase().capitalize() } + .map { it.lowercase().capitalize() } .joinToString("") val annotatedConst = consts.find { it.second.annotations.isNonEmpty } if (annotatedConst != null) { @@ -122,7 +122,7 @@ fun FileInfo.generateAidl() { if (aidl.exists()) return aidl.writeText(buildString { sourceLines.dropLastWhile { !it.startsWith("package ") }.forEach { - appendln(it) + appendLine(it) } append("\nparcelable ${mainClass.nameAsString};\n") }) diff --git a/tools/codegen/src/com/android/codegen/Utils.kt b/tools/codegen/src/com/android/codegen/Utils.kt index a40bdd7ba8e1..96314435d1b7 100644 --- a/tools/codegen/src/com/android/codegen/Utils.kt +++ b/tools/codegen/src/com/android/codegen/Utils.kt @@ -62,7 +62,7 @@ fun String.toLowerCamel(): String { if (length >= 2 && this[0] == 'm' && this[1].isUpperCase()) return substring(1).capitalize() if (all { it.isLetterOrDigit() }) return decapitalize() return split("[^a-zA-Z0-9]".toRegex()) - .map { it.toLowerCase().capitalize() } + .map { it.lowercase().capitalize() } .joinToString("") .decapitalize() } |