diff options
30 files changed, 336 insertions, 160 deletions
diff --git a/core/java/android/app/AutomaticZenRule.java b/core/java/android/app/AutomaticZenRule.java index e0a937156906..9d1d9c7b69de 100644 --- a/core/java/android/app/AutomaticZenRule.java +++ b/core/java/android/app/AutomaticZenRule.java @@ -162,7 +162,7 @@ public final class AutomaticZenRule implements Parcelable { * both to fields in the rule itself (such as its name) and items with sub-fields. * @hide */ - public static final int MAX_STRING_LENGTH = 1000; + public static final int MAX_STRING_LENGTH = 500; /** * The maximum string length for the trigger description rule, given UI constraints. diff --git a/libs/WindowManager/Shell/res/values/ids.xml b/libs/WindowManager/Shell/res/values/ids.xml index 122cde04f8e4..c6082b3bd60f 100644 --- a/libs/WindowManager/Shell/res/values/ids.xml +++ b/libs/WindowManager/Shell/res/values/ids.xml @@ -25,6 +25,7 @@ <item type="id" name="action_move_tl_50" /> <item type="id" name="action_move_tl_30" /> <item type="id" name="action_move_rb_full" /> + <item type="id" name="action_swap_apps" /> <!-- For saving PhysicsAnimationLayout animations/animators as view tags. --> <item type="id" name="translation_x_dynamicanimation_tag"/> diff --git a/libs/WindowManager/Shell/res/values/strings.xml b/libs/WindowManager/Shell/res/values/strings.xml index c29b927f61c2..a2231dd64112 100644 --- a/libs/WindowManager/Shell/res/values/strings.xml +++ b/libs/WindowManager/Shell/res/values/strings.xml @@ -90,6 +90,8 @@ <string name="accessibility_action_divider_left_30">Left 30%</string> <!-- Accessibility action for moving docked stack divider to make the right screen full screen [CHAR LIMIT=NONE] --> <string name="accessibility_action_divider_right_full">Right full screen</string> + <!-- Accessibility action for swapping the apps around the divider (double tap action) [CHAR LIMIT=NONE] --> + <string name="accessibility_action_divider_swap">Swap Apps</string> <!-- Accessibility action for moving docked stack divider to make the top screen full screen [CHAR LIMIT=NONE] --> <string name="accessibility_action_divider_top_full">Top full screen</string> diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java index 2c418d34f09a..06044ccc1c61 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java @@ -125,11 +125,13 @@ public class DividerView extends FrameLayout implements View.OnTouchListener { } }; - private final AccessibilityDelegate mHandleDelegate = new AccessibilityDelegate() { + final AccessibilityDelegate mHandleDelegate = new AccessibilityDelegate() { @Override public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) { super.onInitializeAccessibilityNodeInfo(host, info); final DividerSnapAlgorithm snapAlgorithm = mSplitLayout.mDividerSnapAlgorithm; + info.addAction(new AccessibilityAction(R.id.action_swap_apps, + mContext.getString(R.string.accessibility_action_divider_swap))); if (mSplitLayout.isLeftRightSplit()) { info.addAction(new AccessibilityAction(R.id.action_move_tl_full, mContext.getString(R.string.accessibility_action_divider_left_full))); @@ -172,6 +174,11 @@ public class DividerView extends FrameLayout implements View.OnTouchListener { @Override public boolean performAccessibilityAction(@NonNull View host, int action, @Nullable Bundle args) { + if (action == R.id.action_swap_apps) { + mSplitLayout.onDoubleTappedDivider(); + return true; + } + DividerSnapAlgorithm.SnapTarget nextTarget = null; DividerSnapAlgorithm snapAlgorithm = mSplitLayout.mDividerSnapAlgorithm; if (action == R.id.action_move_tl_full) { diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/DividerViewTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/DividerViewTest.java index fd3d3b5b6e2f..8c34c1946702 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/DividerViewTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/DividerViewTest.java @@ -36,6 +36,7 @@ import androidx.test.annotation.UiThreadTest; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; +import com.android.wm.shell.R; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.common.DisplayController; @@ -66,9 +67,9 @@ public class DividerViewTest extends ShellTestCase { public void setup() { MockitoAnnotations.initMocks(this); Configuration configuration = getConfiguration(); - mSplitLayout = new SplitLayout("TestSplitLayout", mContext, configuration, + mSplitLayout = spy(new SplitLayout("TestSplitLayout", mContext, configuration, mSplitLayoutHandler, mCallbacks, mDisplayController, mDisplayImeController, - mTaskOrganizer, SplitLayout.PARALLAX_NONE, mSplitState, mHandler); + mTaskOrganizer, SplitLayout.PARALLAX_NONE, mSplitState, mHandler)); SplitWindowManager splitWindowManager = new SplitWindowManager("TestSplitWindowManager", mContext, configuration, mCallbacks); @@ -98,6 +99,14 @@ public class DividerViewTest extends ShellTestCase { "false", false); } + @Test + public void swapDividerActionForA11y() { + mDividerView.setAccessibilityDelegate(mDividerView.mHandleDelegate); + mDividerView.getAccessibilityDelegate().performAccessibilityAction(mDividerView, + R.id.action_swap_apps, null); + verify(mSplitLayout, times(1)).onDoubleTappedDivider(); + } + private static MotionEvent getMotionEvent(long eventTime, int action, float x, float y) { MotionEvent.PointerProperties properties = new MotionEvent.PointerProperties(); properties.id = 0; diff --git a/media/java/android/media/RoutingSessionInfo.java b/media/java/android/media/RoutingSessionInfo.java index 87bb6eab6c4d..d27d7fc72a38 100644 --- a/media/java/android/media/RoutingSessionInfo.java +++ b/media/java/android/media/RoutingSessionInfo.java @@ -649,9 +649,19 @@ public final class RoutingSessionInfo implements Parcelable { * @param sessionInfo the existing instance to copy data from. */ public Builder(@NonNull RoutingSessionInfo sessionInfo) { + this(sessionInfo, sessionInfo.getOriginalId()); + } + + /** + * Builds upon the given {@code sessionInfo}, using the given {@link #getOriginalId()} for + * the id. + * + * @hide + */ + public Builder(@NonNull RoutingSessionInfo sessionInfo, String originalId) { Objects.requireNonNull(sessionInfo, "sessionInfo must not be null"); - mOriginalId = sessionInfo.mOriginalId; + mOriginalId = originalId; mName = sessionInfo.mName; mClientPackageName = sessionInfo.mClientPackageName; mProviderId = sessionInfo.mProviderId; diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig index bb0d5d7755cf..6c96279711d0 100644 --- a/packages/SystemUI/aconfig/systemui.aconfig +++ b/packages/SystemUI/aconfig/systemui.aconfig @@ -1697,13 +1697,6 @@ flag { } flag { - name: "magic_portrait_wallpapers" - namespace: "systemui" - description: "Magic Portrait related changes in systemui" - bug: "370863642" -} - -flag { name: "notes_role_qs_tile" namespace: "systemui" description: "Enables notes role qs tile which opens default notes role app in app bubbles" diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/common/ui/compose/ContentDescription.kt b/packages/SystemUI/compose/features/src/com/android/systemui/common/ui/compose/ContentDescription.kt index 4a5ad6554dc6..b254963cc5e9 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/common/ui/compose/ContentDescription.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/common/ui/compose/ContentDescription.kt @@ -17,11 +17,13 @@ package com.android.systemui.common.ui.compose import androidx.compose.runtime.Composable +import androidx.compose.runtime.ReadOnlyComposable import androidx.compose.ui.res.stringResource import com.android.systemui.common.shared.model.ContentDescription /** Returns the loaded [String] or `null` if there isn't one. */ @Composable +@ReadOnlyComposable fun ContentDescription.load(): String? { return when (this) { is ContentDescription.Loaded -> description diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/common/ui/compose/Icon.kt b/packages/SystemUI/compose/features/src/com/android/systemui/common/ui/compose/Icon.kt index 82d14369f239..8b0c00535262 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/common/ui/compose/Icon.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/common/ui/compose/Icon.kt @@ -21,9 +21,8 @@ import androidx.compose.material3.LocalContentColor import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.asImageBitmap import androidx.compose.ui.res.painterResource -import androidx.core.graphics.drawable.toBitmap +import com.android.compose.ui.graphics.painter.rememberDrawablePainter import com.android.systemui.common.shared.model.Icon /** @@ -36,7 +35,7 @@ fun Icon(icon: Icon, modifier: Modifier = Modifier, tint: Color = LocalContentCo val contentDescription = icon.contentDescription?.load() when (icon) { is Icon.Loaded -> { - Icon(icon.drawable.toBitmap().asImageBitmap(), contentDescription, modifier, tint) + Icon(rememberDrawablePainter(icon.drawable), contentDescription, modifier, tint) } is Icon.Resource -> Icon(painterResource(icon.res), contentDescription, modifier, tint) } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/common/ui/compose/TextExt.kt b/packages/SystemUI/compose/features/src/com/android/systemui/common/ui/compose/TextExt.kt index 4e8121f14e4b..19adba0f39de 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/common/ui/compose/TextExt.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/common/ui/compose/TextExt.kt @@ -19,6 +19,7 @@ package com.android.systemui.common.ui.compose import android.content.Context import androidx.compose.runtime.Composable +import androidx.compose.runtime.ReadOnlyComposable import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.AnnotatedString import com.android.systemui.common.shared.model.Text @@ -26,6 +27,7 @@ import com.android.systemui.common.shared.model.Text.Companion.loadText /** Returns the loaded [String] or `null` if there isn't one. */ @Composable +@ReadOnlyComposable fun Text.load(): String? { return when (this) { is Text.Loaded -> text diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt index 12b20a53df81..ab31286b78b4 100644 --- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt +++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt @@ -299,8 +299,7 @@ open class ClockRegistry( Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE, ) } - - ClockSettings.fromJson(JSONObject(json)) + json?.let { ClockSettings.fromJson(JSONObject(it)) } } catch (ex: Exception) { logger.e("Failed to parse clock settings", ex) null diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarStarterTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarStarterTest.kt index c06da4bc5080..dc65a9e24407 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarStarterTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarStarterTest.kt @@ -18,6 +18,7 @@ package com.android.systemui.statusbar.core import android.platform.test.annotations.EnableFlags import android.view.Display +import android.view.mockIWindowManager import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase @@ -31,11 +32,13 @@ import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest +import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.mockito.kotlin.never import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever @OptIn(ExperimentalCoroutinesApi::class) @SmallTest @@ -51,75 +54,110 @@ class MultiDisplayStatusBarStarterTest : SysuiTestCase() { private val fakeInitializerStore = kosmos.fakeStatusBarInitializerStore private val fakePrivacyDotStore = kosmos.fakePrivacyDotWindowControllerStore private val fakeLightBarStore = kosmos.fakeLightBarControllerStore + private val windowManager = kosmos.mockIWindowManager + // Lazy, so that @EnableFlags is set before initializer is instantiated. private val underTest by lazy { kosmos.multiDisplayStatusBarStarter } + @Before + fun setup() { + whenever(windowManager.shouldShowSystemDecors(Display.DEFAULT_DISPLAY)).thenReturn(true) + whenever(windowManager.shouldShowSystemDecors(DISPLAY_1)).thenReturn(true) + whenever(windowManager.shouldShowSystemDecors(DISPLAY_2)).thenReturn(true) + whenever(windowManager.shouldShowSystemDecors(DISPLAY_3)).thenReturn(true) + whenever(windowManager.shouldShowSystemDecors(DISPLAY_4_NO_SYSTEM_DECOR)).thenReturn(false) + } + @Test fun start_startsInitializersForCurrentDisplays() = testScope.runTest { - fakeDisplayRepository.addDisplay(displayId = 1) - fakeDisplayRepository.addDisplay(displayId = 2) + fakeDisplayRepository.addDisplay(displayId = DISPLAY_1) + fakeDisplayRepository.addDisplay(displayId = DISPLAY_2) + fakeDisplayRepository.addDisplay(displayId = DISPLAY_4_NO_SYSTEM_DECOR) underTest.start() runCurrent() expect - .that(fakeInitializerStore.forDisplay(displayId = 1).startedByCoreStartable) + .that(fakeInitializerStore.forDisplay(displayId = DISPLAY_1).startedByCoreStartable) .isTrue() expect - .that(fakeInitializerStore.forDisplay(displayId = 2).startedByCoreStartable) + .that(fakeInitializerStore.forDisplay(displayId = DISPLAY_2).startedByCoreStartable) .isTrue() + expect + .that( + fakeInitializerStore + .forDisplay(displayId = DISPLAY_4_NO_SYSTEM_DECOR) + .startedByCoreStartable + ) + .isFalse() } @Test fun start_startsOrchestratorForCurrentDisplays() = testScope.runTest { - fakeDisplayRepository.addDisplay(displayId = 1) - fakeDisplayRepository.addDisplay(displayId = 2) + fakeDisplayRepository.addDisplay(displayId = DISPLAY_1) + fakeDisplayRepository.addDisplay(displayId = DISPLAY_2) + fakeDisplayRepository.addDisplay(displayId = DISPLAY_4_NO_SYSTEM_DECOR) underTest.start() runCurrent() - verify(fakeOrchestratorFactory.createdOrchestratorForDisplay(displayId = 1)!!).start() - verify(fakeOrchestratorFactory.createdOrchestratorForDisplay(displayId = 2)!!).start() + verify(fakeOrchestratorFactory.createdOrchestratorForDisplay(displayId = DISPLAY_1)!!) + .start() + verify(fakeOrchestratorFactory.createdOrchestratorForDisplay(displayId = DISPLAY_2)!!) + .start() + assertThat( + fakeOrchestratorFactory.createdOrchestratorForDisplay( + displayId = DISPLAY_4_NO_SYSTEM_DECOR + ) + ) + .isNull() } @Test fun start_startsPrivacyDotForCurrentDisplays() = testScope.runTest { - fakeDisplayRepository.addDisplay(displayId = 1) - fakeDisplayRepository.addDisplay(displayId = 2) + fakeDisplayRepository.addDisplay(displayId = DISPLAY_1) + fakeDisplayRepository.addDisplay(displayId = DISPLAY_2) + fakeDisplayRepository.addDisplay(displayId = DISPLAY_4_NO_SYSTEM_DECOR) underTest.start() runCurrent() - verify(fakePrivacyDotStore.forDisplay(displayId = 1)).start() - verify(fakePrivacyDotStore.forDisplay(displayId = 2)).start() + verify(fakePrivacyDotStore.forDisplay(displayId = DISPLAY_1)).start() + verify(fakePrivacyDotStore.forDisplay(displayId = DISPLAY_2)).start() + verify(fakePrivacyDotStore.forDisplay(displayId = DISPLAY_4_NO_SYSTEM_DECOR), never()) + .start() } @Test fun start_doesNotStartLightBarControllerForCurrentDisplays() = testScope.runTest { - fakeDisplayRepository.addDisplay(displayId = 1) - fakeDisplayRepository.addDisplay(displayId = 2) + fakeDisplayRepository.addDisplay(displayId = DISPLAY_1) + fakeDisplayRepository.addDisplay(displayId = DISPLAY_2) + fakeDisplayRepository.addDisplay(displayId = DISPLAY_4_NO_SYSTEM_DECOR) underTest.start() runCurrent() - verify(fakeLightBarStore.forDisplay(displayId = 1), never()).start() - verify(fakeLightBarStore.forDisplay(displayId = 2), never()).start() + verify(fakeLightBarStore.forDisplay(displayId = DISPLAY_1), never()).start() + verify(fakeLightBarStore.forDisplay(displayId = DISPLAY_2), never()).start() + verify(fakeLightBarStore.forDisplay(displayId = DISPLAY_4_NO_SYSTEM_DECOR), never()) + .start() } @Test fun start_createsLightBarControllerForCurrentDisplays() = testScope.runTest { - fakeDisplayRepository.addDisplay(displayId = 1) - fakeDisplayRepository.addDisplay(displayId = 2) + fakeDisplayRepository.addDisplay(displayId = DISPLAY_1) + fakeDisplayRepository.addDisplay(displayId = DISPLAY_2) + fakeDisplayRepository.addDisplay(displayId = DISPLAY_4_NO_SYSTEM_DECOR) underTest.start() runCurrent() - assertThat(fakeLightBarStore.perDisplayMocks.keys).containsExactly(1, 2) + assertThat(fakeLightBarStore.perDisplayMocks.keys).containsExactly(1, DISPLAY_2) } @Test @@ -135,121 +173,174 @@ class MultiDisplayStatusBarStarterTest : SysuiTestCase() { } @Test - fun displayAdded_orchestratorForNewDisplayIsStarted() = + fun displayAdded_orchestratorForNewDisplay() = testScope.runTest { underTest.start() runCurrent() - fakeDisplayRepository.addDisplay(displayId = 3) + fakeDisplayRepository.addDisplay(displayId = DISPLAY_3) + fakeDisplayRepository.addDisplay(displayId = DISPLAY_4_NO_SYSTEM_DECOR) runCurrent() - verify(fakeOrchestratorFactory.createdOrchestratorForDisplay(displayId = 3)!!).start() + verify(fakeOrchestratorFactory.createdOrchestratorForDisplay(displayId = DISPLAY_3)!!) + .start() + assertThat( + fakeOrchestratorFactory.createdOrchestratorForDisplay( + displayId = DISPLAY_4_NO_SYSTEM_DECOR + ) + ) + .isNull() } @Test - fun displayAdded_initializerForNewDisplayIsStarted() = + fun displayAdded_initializerForNewDisplay() = testScope.runTest { underTest.start() runCurrent() - fakeDisplayRepository.addDisplay(displayId = 3) + fakeDisplayRepository.addDisplay(displayId = DISPLAY_3) + fakeDisplayRepository.addDisplay(displayId = DISPLAY_4_NO_SYSTEM_DECOR) runCurrent() expect - .that(fakeInitializerStore.forDisplay(displayId = 3).startedByCoreStartable) + .that(fakeInitializerStore.forDisplay(displayId = DISPLAY_3).startedByCoreStartable) .isTrue() + expect + .that( + fakeInitializerStore + .forDisplay(displayId = DISPLAY_4_NO_SYSTEM_DECOR) + .startedByCoreStartable + ) + .isFalse() } @Test - fun displayAdded_privacyDotForNewDisplayIsStarted() = + fun displayAdded_privacyDotForNewDisplay() = testScope.runTest { underTest.start() runCurrent() - fakeDisplayRepository.addDisplay(displayId = 3) + fakeDisplayRepository.addDisplay(displayId = DISPLAY_3) + fakeDisplayRepository.addDisplay(displayId = DISPLAY_4_NO_SYSTEM_DECOR) runCurrent() - verify(fakePrivacyDotStore.forDisplay(displayId = 3)).start() + verify(fakePrivacyDotStore.forDisplay(displayId = DISPLAY_3)).start() + verify(fakePrivacyDotStore.forDisplay(displayId = DISPLAY_4_NO_SYSTEM_DECOR), never()) + .start() } @Test - fun displayAdded_lightBarForNewDisplayIsCreated() = + fun displayAdded_lightBarForNewDisplayCreate() = testScope.runTest { underTest.start() runCurrent() - fakeDisplayRepository.addDisplay(displayId = 3) + fakeDisplayRepository.addDisplay(displayId = DISPLAY_3) + fakeDisplayRepository.addDisplay(displayId = DISPLAY_4_NO_SYSTEM_DECOR) runCurrent() - assertThat(fakeLightBarStore.perDisplayMocks.keys).containsExactly(3) + assertThat(fakeLightBarStore.perDisplayMocks.keys).containsExactly(DISPLAY_3) } @Test - fun displayAdded_lightBarForNewDisplayIsNotStarted() = + fun displayAdded_lightBarForNewDisplayStart() = testScope.runTest { underTest.start() runCurrent() - fakeDisplayRepository.addDisplay(displayId = 3) + fakeDisplayRepository.addDisplay(displayId = DISPLAY_3) + fakeDisplayRepository.addDisplay(displayId = DISPLAY_4_NO_SYSTEM_DECOR) runCurrent() - verify(fakeLightBarStore.forDisplay(displayId = 3), never()).start() + verify(fakeLightBarStore.forDisplay(displayId = DISPLAY_3), never()).start() + verify(fakeLightBarStore.forDisplay(displayId = DISPLAY_4_NO_SYSTEM_DECOR), never()) + .start() } @Test - fun displayAddedDuringStart_initializerForNewDisplayIsStarted() = + fun displayAddedDuringStart_initializerForNewDisplay() = testScope.runTest { underTest.start() - fakeDisplayRepository.addDisplay(displayId = 3) + fakeDisplayRepository.addDisplay(displayId = DISPLAY_3) + fakeDisplayRepository.addDisplay(displayId = DISPLAY_4_NO_SYSTEM_DECOR) runCurrent() expect - .that(fakeInitializerStore.forDisplay(displayId = 3).startedByCoreStartable) + .that(fakeInitializerStore.forDisplay(displayId = DISPLAY_3).startedByCoreStartable) .isTrue() + expect + .that( + fakeInitializerStore + .forDisplay(displayId = DISPLAY_4_NO_SYSTEM_DECOR) + .startedByCoreStartable + ) + .isFalse() } @Test - fun displayAddedDuringStart_orchestratorForNewDisplayIsStarted() = + fun displayAddedDuringStart_orchestratorForNewDisplay() = testScope.runTest { underTest.start() - fakeDisplayRepository.addDisplay(displayId = 3) + fakeDisplayRepository.addDisplay(displayId = DISPLAY_3) + fakeDisplayRepository.addDisplay(displayId = DISPLAY_4_NO_SYSTEM_DECOR) runCurrent() - verify(fakeOrchestratorFactory.createdOrchestratorForDisplay(displayId = 3)!!).start() + verify(fakeOrchestratorFactory.createdOrchestratorForDisplay(displayId = DISPLAY_3)!!) + .start() + assertThat( + fakeOrchestratorFactory.createdOrchestratorForDisplay( + displayId = DISPLAY_4_NO_SYSTEM_DECOR + ) + ) + .isNull() } @Test - fun displayAddedDuringStart_privacyDotForNewDisplayIsStarted() = + fun displayAddedDuringStart_privacyDotForNewDisplay() = testScope.runTest { underTest.start() - fakeDisplayRepository.addDisplay(displayId = 3) + fakeDisplayRepository.addDisplay(displayId = DISPLAY_3) + fakeDisplayRepository.addDisplay(displayId = DISPLAY_4_NO_SYSTEM_DECOR) runCurrent() - verify(fakePrivacyDotStore.forDisplay(displayId = 3)).start() + verify(fakePrivacyDotStore.forDisplay(displayId = DISPLAY_3)).start() + verify(fakePrivacyDotStore.forDisplay(displayId = DISPLAY_4_NO_SYSTEM_DECOR), never()) + .start() } @Test - fun displayAddedDuringStart_lightBarForNewDisplayIsCreated() = + fun displayAddedDuringStart_lightBarForNewDisplayCreate() = testScope.runTest { underTest.start() - fakeDisplayRepository.addDisplay(displayId = 3) + fakeDisplayRepository.addDisplay(displayId = DISPLAY_3) + fakeDisplayRepository.addDisplay(displayId = DISPLAY_4_NO_SYSTEM_DECOR) runCurrent() - assertThat(fakeLightBarStore.perDisplayMocks.keys).containsExactly(3) + assertThat(fakeLightBarStore.perDisplayMocks.keys).containsExactly(DISPLAY_3) } @Test - fun displayAddedDuringStart_lightBarForNewDisplayIsNotStarted() = + fun displayAddedDuringStart_lightBarForNewDisplayStart() = testScope.runTest { underTest.start() - fakeDisplayRepository.addDisplay(displayId = 3) + fakeDisplayRepository.addDisplay(displayId = DISPLAY_3) + fakeDisplayRepository.addDisplay(displayId = DISPLAY_4_NO_SYSTEM_DECOR) runCurrent() - verify(fakeLightBarStore.forDisplay(displayId = 3), never()).start() + verify(fakeLightBarStore.forDisplay(displayId = DISPLAY_3), never()).start() + verify(fakeLightBarStore.forDisplay(displayId = DISPLAY_4_NO_SYSTEM_DECOR), never()) + .start() } + + companion object { + const val DISPLAY_1 = 1 + const val DISPLAY_2 = 2 + const val DISPLAY_3 = 3 + const val DISPLAY_4_NO_SYSTEM_DECOR = 4 + } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/data/repository/WallpaperRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/data/repository/WallpaperRepositoryImplTest.kt index 115edd0d3bb0..2b16c00107c3 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/data/repository/WallpaperRepositoryImplTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/data/repository/WallpaperRepositoryImplTest.kt @@ -25,16 +25,15 @@ import android.platform.test.annotations.EnableFlags import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.internal.R -import com.android.systemui.Flags import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.keyguard.data.repository.FakeKeyguardClockRepository import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository +import com.android.systemui.res.R as SysUIR import com.android.systemui.shared.Flags as SharedFlags import com.android.systemui.user.data.model.SelectedUserModel import com.android.systemui.user.data.model.SelectionStatus import com.android.systemui.user.data.repository.FakeUserRepository -import com.android.systemui.wallpapers.data.repository.WallpaperRepositoryImpl.Companion.MAGIC_PORTRAIT_CLASSNAME import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.StandardTestDispatcher @@ -72,9 +71,12 @@ class WallpaperRepositoryImplTest : SysuiTestCase() { ) } + lateinit var focalAreaTarget: String + @Before fun setUp() { whenever(wallpaperManager.isWallpaperSupported).thenReturn(true) + focalAreaTarget = context.resources.getString(SysUIR.string.focal_area_target) } @Test @@ -248,17 +250,17 @@ class WallpaperRepositoryImplTest : SysuiTestCase() { } @Test - @EnableFlags(Flags.FLAG_MAGIC_PORTRAIT_WALLPAPERS) - fun shouldSendNotificationLayout_setMagicPortraitWallpaper_launchSendLayoutJob() = + @EnableFlags(SharedFlags.FLAG_EXTENDED_WALLPAPER_EFFECTS) + fun shouldSendNotificationLayout_setExtendedEffectsWallpaper_launchSendLayoutJob() = testScope.runTest { val latest by collectLastValue(underTest.shouldSendFocalArea) - val magicPortraitWallpaper = + val extedendEffectsWallpaper = mock<WallpaperInfo>().apply { - whenever(this.component) - .thenReturn(ComponentName(context, MAGIC_PORTRAIT_CLASSNAME)) + whenever(this.component).thenReturn(ComponentName(context, focalAreaTarget)) } + whenever(wallpaperManager.getWallpaperInfoForUser(any())) - .thenReturn(magicPortraitWallpaper) + .thenReturn(extedendEffectsWallpaper) fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly( context, Intent(Intent.ACTION_WALLPAPER_CHANGED), @@ -269,13 +271,16 @@ class WallpaperRepositoryImplTest : SysuiTestCase() { } @Test - @EnableFlags(Flags.FLAG_MAGIC_PORTRAIT_WALLPAPERS) - fun shouldSendNotificationLayout_setNotMagicPortraitWallpaper_cancelSendLayoutJob() = + @EnableFlags(SharedFlags.FLAG_EXTENDED_WALLPAPER_EFFECTS) + fun shouldSendNotificationLayout_setNotExtendedEffectsWallpaper_cancelSendLayoutJob() = testScope.runTest { val latest by collectLastValue(underTest.shouldSendFocalArea) - val magicPortraitWallpaper = MAGIC_PORTRAIT_WP + val extendedEffectsWallpaper = + mock<WallpaperInfo>().apply { + whenever(this.component).thenReturn(ComponentName("", focalAreaTarget)) + } whenever(wallpaperManager.getWallpaperInfoForUser(any())) - .thenReturn(magicPortraitWallpaper) + .thenReturn(extendedEffectsWallpaper) fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly( context, Intent(Intent.ACTION_WALLPAPER_CHANGED), @@ -284,9 +289,7 @@ class WallpaperRepositoryImplTest : SysuiTestCase() { assertThat(underTest.sendLockscreenLayoutJob).isNotNull() assertThat(underTest.sendLockscreenLayoutJob!!.isActive).isEqualTo(true) - val nonMagicPortraitWallpaper = UNSUPPORTED_WP - whenever(wallpaperManager.getWallpaperInfoForUser(any())) - .thenReturn(nonMagicPortraitWallpaper) + whenever(wallpaperManager.getWallpaperInfoForUser(any())).thenReturn(UNSUPPORTED_WP) fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly( context, Intent(Intent.ACTION_WALLPAPER_CHANGED), @@ -303,10 +306,5 @@ class WallpaperRepositoryImplTest : SysuiTestCase() { val USER_WITH_SUPPORTED_WP = UserInfo(/* id= */ 4, /* name= */ "user4", /* flags= */ 0) val SUPPORTED_WP = mock<WallpaperInfo>().apply { whenever(this.supportsAmbientMode()).thenReturn(true) } - - val MAGIC_PORTRAIT_WP = - mock<WallpaperInfo>().apply { - whenever(this.component).thenReturn(ComponentName("", MAGIC_PORTRAIT_CLASSNAME)) - } } } diff --git a/packages/SystemUI/res/values-xlarge-land/config.xml b/packages/SystemUI/res/values-xlarge-land/config.xml index 6d8b64ade259..4c77f30f2e69 100644 --- a/packages/SystemUI/res/values-xlarge-land/config.xml +++ b/packages/SystemUI/res/values-xlarge-land/config.xml @@ -16,5 +16,5 @@ <resources> <item name="shortcut_helper_screen_width_fraction" format="float" type="dimen">0.8</item> - <bool name="center_align_magic_portrait_shape">true</bool> + <bool name="center_align_focal_area_shape">true</bool> </resources> diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index 68e33f27aefa..9b8926e921c9 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -1107,7 +1107,7 @@ <!-- The dream component used when the device is low light environment. --> <string translatable="false" name="config_lowLightDreamComponent"/> - <!--Whether we should position magic portrait shape effects in the center of lockscreen - it's false by default, and only be true in tablet landscape --> - <bool name="center_align_magic_portrait_shape">false</bool> + <!-- Configuration for wallpaper focal area --> + <bool name="center_align_focal_area_shape">false</bool> + <string name="focal_area_target" translatable="false" /> </resources> diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt index 621cc4666d31..aaad10140a92 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt @@ -326,10 +326,7 @@ interface KeyguardRepository { fun setShortcutAbsoluteTop(top: Float) - /** - * Set bottom of notifications from notification stack, and Magic Portrait will layout base on - * this value - */ + /** Set bottom of notifications from notification stack */ fun setNotificationStackAbsoluteBottom(bottom: Float) fun setWallpaperFocalAreaBounds(bounds: RectF) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WallpaperFocalAreaInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WallpaperFocalAreaInteractor.kt index 934afe248a36..9c744d63a093 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WallpaperFocalAreaInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WallpaperFocalAreaInteractor.kt @@ -50,8 +50,7 @@ constructor( keyguardClockRepository: KeyguardClockRepository, wallpaperRepository: WallpaperRepository, ) { - // When there's notifications in splitshade, magic portrait shape effects should be left - // aligned in foldable + // When there's notifications in splitshade, the focal area shape effect should be left aligned private val notificationInShadeWideLayout: Flow<Boolean> = combine( shadeRepository.isShadeLayoutWide, @@ -104,7 +103,7 @@ constructor( ) val (left, right) = // tablet landscape - if (context.resources.getBoolean(R.bool.center_align_magic_portrait_shape)) { + if (context.resources.getBoolean(R.bool.center_align_focal_area_shape)) { Pair( scaledBounds.centerX() - maxFocalAreaWidth / 2F, scaledBounds.centerX() + maxFocalAreaWidth / 2F, @@ -129,7 +128,7 @@ constructor( wallpaperZoomedInScale val top = // tablet landscape - if (context.resources.getBoolean(R.bool.center_align_magic_portrait_shape)) { + if (context.resources.getBoolean(R.bool.center_align_focal_area_shape)) { // no strict constraints for top, use bottom margin to make it symmetric // vertically scaledBounds.top + scaledBottomMargin @@ -169,8 +168,8 @@ constructor( ) } - // A max width for magic portrait shape effects bounds, to avoid it going too large - // in large screen portrait mode + // A max width for focal area shape effects bounds, to avoid + // it becoming too large in large screen portrait mode const val FOCAL_AREA_MAX_WIDTH_DP = 500 } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/SplitShadeKeyguardBlueprint.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/SplitShadeKeyguardBlueprint.kt index 856e1d6ffdb7..8b213be02969 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/SplitShadeKeyguardBlueprint.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/SplitShadeKeyguardBlueprint.kt @@ -23,6 +23,7 @@ import com.android.systemui.keyguard.shared.model.KeyguardSection import com.android.systemui.keyguard.ui.view.layout.sections.AccessibilityActionsSection import com.android.systemui.keyguard.ui.view.layout.sections.AodBurnInSection import com.android.systemui.keyguard.ui.view.layout.sections.AodNotificationIconsSection +import com.android.systemui.keyguard.ui.view.layout.sections.AodPromotedNotificationSection import com.android.systemui.keyguard.ui.view.layout.sections.ClockSection import com.android.systemui.keyguard.ui.view.layout.sections.DefaultDeviceEntrySection import com.android.systemui.keyguard.ui.view.layout.sections.DefaultIndicationAreaSection @@ -58,6 +59,7 @@ constructor( defaultStatusBarSection: DefaultStatusBarSection, splitShadeNotificationStackScrollLayoutSection: SplitShadeNotificationStackScrollLayoutSection, splitShadeGuidelines: SplitShadeGuidelines, + aodPromotedNotificationSection: AodPromotedNotificationSection, aodNotificationIconsSection: AodNotificationIconsSection, aodBurnInSection: AodBurnInSection, clockSection: ClockSection, @@ -76,6 +78,7 @@ constructor( defaultStatusBarSection, splitShadeNotificationStackScrollLayoutSection, splitShadeGuidelines, + aodPromotedNotificationSection, aodNotificationIconsSection, smartspaceSection, aodBurnInSection, diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodPromotedNotificationSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodPromotedNotificationSection.kt index ed1bdb0e2922..ea4acce037b8 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodPromotedNotificationSection.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodPromotedNotificationSection.kt @@ -26,6 +26,7 @@ import androidx.constraintlayout.widget.ConstraintSet.START import androidx.constraintlayout.widget.ConstraintSet.TOP import com.android.systemui.keyguard.shared.model.KeyguardSection import com.android.systemui.res.R +import com.android.systemui.shade.domain.interactor.ShadeInteractor import com.android.systemui.statusbar.notification.promoted.AODPromotedNotification import com.android.systemui.statusbar.notification.promoted.PromotedNotificationLogger import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUiAod @@ -36,6 +37,7 @@ class AodPromotedNotificationSection @Inject constructor( private val viewModelFactory: AODPromotedNotificationViewModel.Factory, + private val shadeInteractor: ShadeInteractor, private val logger: PromotedNotificationLogger, ) : KeyguardSection() { var view: ComposeView? = null @@ -77,9 +79,12 @@ constructor( checkNotNull(view) constraintSet.apply { + val isShadeLayoutWide = shadeInteractor.isShadeLayoutWide.value + val endGuidelineId = if (isShadeLayoutWide) R.id.split_shade_guideline else PARENT_ID + connect(viewId, TOP, R.id.smart_space_barrier_bottom, BOTTOM, 0) connect(viewId, START, PARENT_ID, START, 0) - connect(viewId, END, PARENT_ID, END, 0) + connect(viewId, END, endGuidelineId, END, 0) constrainWidth(viewId, ConstraintSet.MATCH_CONSTRAINT) constrainHeight(viewId, ConstraintSet.WRAP_CONTENT) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarStarter.kt b/packages/SystemUI/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarStarter.kt index b057fb0433fe..eeb7a4066eca 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarStarter.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarStarter.kt @@ -17,6 +17,7 @@ package com.android.systemui.statusbar.core import android.view.Display +import android.view.IWindowManager import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.CoreStartable import com.android.systemui.dagger.SysUISingleton @@ -54,6 +55,7 @@ constructor( private val autoHideControllerStore: AutoHideControllerStore, private val privacyDotWindowControllerStore: PrivacyDotWindowControllerStore, private val lightBarControllerStore: LightBarControllerStore, + private val windowManager: IWindowManager, ) : CoreStartable { init { @@ -68,7 +70,13 @@ constructor( } .onStart { emit(displayRepository.displays.value) } .collect { newDisplays -> - newDisplays.forEach { createAndStartComponentsForDisplay(it) } + newDisplays.forEach { + // TODO(b/393191204): Split navbar, status bar, etc. functionality + // from WindowManager#shouldShowSystemDecors. + if (windowManager.shouldShowSystemDecors(it.displayId)) { + createAndStartComponentsForDisplay(it) + } + } } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt index f7401440cfcb..ece1803e14c3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt @@ -28,6 +28,7 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor import com.android.systemui.keyguard.ui.viewmodel.ViewStateAccessor import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.scene.shared.flag.SceneContainerFlag +import com.android.systemui.shared.Flags.extendedWallpaperEffects import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController import com.android.systemui.statusbar.notification.stack.NotificationStackSizeCalculator import com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer @@ -143,7 +144,7 @@ constructor( } if (!SceneContainerFlag.isEnabled) { - if (Flags.magicPortraitWallpapers()) { + if (extendedWallpaperEffects()) { launch { combine( viewModel.getNotificationStackAbsoluteBottom( diff --git a/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/WallpaperRepository.kt b/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/WallpaperRepository.kt index 9794c619041e..79a9630e6887 100644 --- a/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/WallpaperRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/WallpaperRepository.kt @@ -27,12 +27,13 @@ import android.view.View import androidx.annotation.VisibleForTesting import com.android.app.tracing.coroutines.launchTraced as launch import com.android.internal.R -import com.android.systemui.Flags import com.android.systemui.broadcast.BroadcastDispatcher import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.keyguard.data.repository.KeyguardRepository +import com.android.systemui.res.R as SysUIR import com.android.systemui.shared.Flags.ambientAod +import com.android.systemui.shared.Flags.extendedWallpaperEffects import com.android.systemui.user.data.model.SelectedUserModel import com.android.systemui.user.data.model.SelectionStatus import com.android.systemui.user.data.repository.UserRepository @@ -66,7 +67,7 @@ interface WallpaperRepository { /** Set rootView to get its windowToken afterwards */ var rootView: View? - /** when we use magic portrait wallpapers, we should always get its bounds from keyguard */ + /** some wallpapers require bounds to be sent from keyguard */ val shouldSendFocalArea: StateFlow<Boolean> } @@ -80,7 +81,7 @@ constructor( userRepository: UserRepository, keyguardRepository: KeyguardRepository, private val wallpaperManager: WallpaperManager, - context: Context, + private val context: Context, ) : WallpaperRepository { private val wallpaperChanged: Flow<Unit> = broadcastDispatcher @@ -125,8 +126,8 @@ constructor( override val shouldSendFocalArea = wallpaperInfo .map { - val shouldSendNotificationLayout = - it?.component?.className == MAGIC_PORTRAIT_CLASSNAME + val focalAreaTarget = context.resources.getString(SysUIR.string.focal_area_target) + val shouldSendNotificationLayout = it?.component?.className == focalAreaTarget if (shouldSendNotificationLayout) { sendLockscreenLayoutJob = scope.launch { @@ -167,9 +168,8 @@ constructor( } .stateIn( scope, - // Always be listening for wallpaper changes when magic portrait flag is on - if (Flags.magicPortraitWallpapers()) SharingStarted.Eagerly else WhileSubscribed(), - initialValue = Flags.magicPortraitWallpapers(), + if (extendedWallpaperEffects()) SharingStarted.Eagerly else WhileSubscribed(), + initialValue = extendedWallpaperEffects(), ) private suspend fun getWallpaper(selectedUser: SelectedUserModel): WallpaperInfo? { @@ -177,9 +177,4 @@ constructor( wallpaperManager.getWallpaperInfoForUser(selectedUser.userInfo.id) } } - - companion object { - const val MAGIC_PORTRAIT_CLASSNAME = - "com.google.android.apps.magicportrait.service.MagicPortraitWallpaperService" - } } diff --git a/packages/SystemUI/tests/utils/src/android/view/WindowManagerKosmos.kt b/packages/SystemUI/tests/utils/src/android/view/WindowManagerKosmos.kt index 025f556991f2..80254d58781a 100644 --- a/packages/SystemUI/tests/utils/src/android/view/WindowManagerKosmos.kt +++ b/packages/SystemUI/tests/utils/src/android/view/WindowManagerKosmos.kt @@ -24,4 +24,6 @@ val Kosmos.fakeWindowManager by Kosmos.Fixture { FakeWindowManager(applicationCo val Kosmos.mockWindowManager: WindowManager by Kosmos.Fixture { mock(WindowManager::class.java) } +val Kosmos.mockIWindowManager: IWindowManager by Kosmos.Fixture { mock(IWindowManager::class.java) } + var Kosmos.windowManager: WindowManager by Kosmos.Fixture { mockWindowManager } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepositoryKosmos.kt index ef9bd8282090..5793695a7f01 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepositoryKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepositoryKosmos.kt @@ -62,6 +62,7 @@ val Kosmos.defaultKeyguardBlueprint by defaultSettingsPopupMenuSection = mock(), defaultStatusBarSection = mock(), defaultNotificationStackScrollLayoutSection = mock(), + aodPromotedNotificationSection = mock(), aodNotificationIconsSection = mock(), aodBurnInSection = mock(), clockSection = keyguardClockSection, @@ -69,7 +70,6 @@ val Kosmos.defaultKeyguardBlueprint by keyguardSliceViewSection = mock(), udfpsAccessibilityOverlaySection = mock(), accessibilityActionsSection = mock(), - aodPromotedNotificationSection = mock(), ) } @@ -84,6 +84,7 @@ val Kosmos.splitShadeBlueprint by defaultStatusBarSection = mock(), splitShadeNotificationStackScrollLayoutSection = mock(), splitShadeGuidelines = mock(), + aodPromotedNotificationSection = mock(), aodNotificationIconsSection = mock(), aodBurnInSection = mock(), clockSection = keyguardClockSection, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarOrchestratorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarOrchestratorKosmos.kt index 8c37bd739bc5..bdcab5fd2eca 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarOrchestratorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarOrchestratorKosmos.kt @@ -17,6 +17,7 @@ package com.android.systemui.statusbar.core import android.content.testableContext +import android.view.mockIWindowManager import com.android.systemui.bouncer.domain.interactor.primaryBouncerInteractor import com.android.systemui.display.data.repository.displayRepository import com.android.systemui.display.data.repository.displayScopeRepository @@ -84,5 +85,6 @@ val Kosmos.multiDisplayStatusBarStarter by multiDisplayAutoHideControllerStore, privacyDotWindowControllerStore, lightBarControllerStore, + mockIWindowManager, ) } diff --git a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java index 373287d76442..a1e8f08db0a6 100644 --- a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java +++ b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java @@ -61,7 +61,7 @@ public class DisplayManagerFlags { private final FlagState mDisplayTopology = new FlagState( Flags.FLAG_DISPLAY_TOPOLOGY, - Flags::displayTopology); + DesktopExperienceFlags.DISPLAY_TOPOLOGY::isTrue); private final FlagState mConnectedDisplayErrorHandlingFlagState = new FlagState( Flags.FLAG_ENABLE_CONNECTED_DISPLAY_ERROR_HANDLING, @@ -267,7 +267,7 @@ public class DisplayManagerFlags { private final FlagState mBaseDensityForExternalDisplays = new FlagState( Flags.FLAG_BASE_DENSITY_FOR_EXTERNAL_DISPLAYS, - Flags::baseDensityForExternalDisplays + DesktopExperienceFlags.BASE_DENSITY_FOR_EXTERNAL_DISPLAYS::isTrue ); private final FlagState mFramerateOverrideTriggersRrCallbacks = new FlagState( diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider2.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider2.java index 3eb38a7029e6..b13dee530ee2 100644 --- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider2.java +++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider2.java @@ -56,8 +56,8 @@ import java.util.stream.Stream; */ /* package */ class SystemMediaRoute2Provider2 extends SystemMediaRoute2Provider { - private static final String ROUTE_ID_PREFIX_SYSTEM = "SYSTEM"; - private static final String ROUTE_ID_SYSTEM_SEPARATOR = "."; + private static final String UNIQUE_SYSTEM_ID_PREFIX = "SYSTEM"; + private static final String UNIQUE_SYSTEM_ID_SEPARATOR = "-"; private final PackageManager mPackageManager; @@ -67,6 +67,10 @@ import java.util.stream.Stream; @GuardedBy("mLock") private final Map<String, ProviderProxyRecord> mProxyRecords = new ArrayMap<>(); + @GuardedBy("mLock") + private final Map<String, SystemMediaSessionRecord> mSessionOriginalIdToSessionRecord = + new ArrayMap<>(); + /** * Maps package names to corresponding sessions maintained by {@link MediaRoute2ProviderService * provider services}. @@ -150,7 +154,7 @@ import java.util.stream.Stream; if (currentProxyRecord != null) { currentProxyRecord.releaseSession( requestId, existingSession.getOriginalId()); - existingSessionRecord.removeSelfFromSessionMap(); + existingSessionRecord.removeSelfFromSessionMaps(); } } } @@ -240,6 +244,24 @@ import java.util.stream.Stream; super.setRouteVolume(requestId, routeOriginalId, volume); } + @Override + public void setSessionVolume(long requestId, String sessionOriginalId, int volume) { + if (SYSTEM_SESSION_ID.equals(sessionOriginalId)) { + super.setSessionVolume(requestId, sessionOriginalId, volume); + return; + } + synchronized (mLock) { + var sessionRecord = mSessionOriginalIdToSessionRecord.get(sessionOriginalId); + var proxyRecord = sessionRecord != null ? sessionRecord.getProxyRecord() : null; + if (proxyRecord != null) { + proxyRecord.mProxy.setSessionVolume( + requestId, sessionRecord.getServiceSessionId(), volume); + return; + } + } + notifyRequestFailed(requestId, MediaRoute2ProviderService.REASON_ROUTE_NOT_AVAILABLE); + } + /** * Returns the uid that corresponds to the given name and user handle, or {@link * Process#INVALID_UID} if a uid couldn't be found. @@ -376,16 +398,17 @@ import java.util.stream.Stream; } /** - * Equivalent to {@link #asSystemRouteId}, except it takes a unique route id instead of a - * original id. + * Equivalent to {@link #asUniqueSystemId}, except it takes a unique id instead of an original + * id. */ private static String uniqueIdAsSystemRouteId(String providerId, String uniqueRouteId) { - return asSystemRouteId(providerId, MediaRouter2Utils.getOriginalId(uniqueRouteId)); + return asUniqueSystemId(providerId, MediaRouter2Utils.getOriginalId(uniqueRouteId)); } /** * Returns a unique {@link MediaRoute2Info#getOriginalId() original id} for this provider to - * publish system media routes from {@link MediaRoute2ProviderService provider services}. + * publish system media routes and sessions from {@link MediaRoute2ProviderService provider + * services}. * * <p>This provider will publish system media routes as part of the system routing session. * However, said routes may also support {@link MediaRoute2Info#FLAG_ROUTING_TYPE_REMOTE remote @@ -393,12 +416,12 @@ import java.util.stream.Stream; * we derive a {@link MediaRoute2Info#getOriginalId original id} that is unique among all * original route ids used by this provider. */ - private static String asSystemRouteId(String providerId, String originalRouteId) { - return ROUTE_ID_PREFIX_SYSTEM - + ROUTE_ID_SYSTEM_SEPARATOR + private static String asUniqueSystemId(String providerId, String originalId) { + return UNIQUE_SYSTEM_ID_PREFIX + + UNIQUE_SYSTEM_ID_SEPARATOR + providerId - + ROUTE_ID_SYSTEM_SEPARATOR - + originalRouteId; + + UNIQUE_SYSTEM_ID_SEPARATOR + + originalId; } /** @@ -485,7 +508,7 @@ import java.util.stream.Stream; continue; } String id = - asSystemRouteId(providerInfo.getUniqueId(), sourceRoute.getOriginalId()); + asUniqueSystemId(providerInfo.getUniqueId(), sourceRoute.getOriginalId()); var newRouteBuilder = new MediaRoute2Info.Builder(id, sourceRoute); if ((sourceRoute.getSupportedRoutingTypes() & MediaRoute2Info.FLAG_ROUTING_TYPE_SYSTEM_AUDIO) @@ -534,6 +557,9 @@ import java.util.stream.Stream; RoutingSessionInfo translatedSession; synchronized (mLock) { mSessionRecord = systemMediaSessionRecord; + mSessionOriginalIdToSessionRecord.put( + systemMediaSessionRecord.mOriginalId, + systemMediaSessionRecord); mPackageNameToSessionRecord.put( mClientPackageName, systemMediaSessionRecord); mPendingSessionCreations.remove(mRequestId); @@ -576,24 +602,38 @@ import java.util.stream.Stream; private final String mProviderId; - @GuardedBy("SystemMediaRoute2Provider2.this.mLock") - @NonNull - private RoutingSessionInfo mSourceSessionInfo; + /** + * The {@link RoutingSessionInfo#getOriginalId() original id} with which this session is + * published. + * + * <p>Derived from the service routing session, using {@link #asUniqueSystemId}. + */ + private final String mOriginalId; + + // @GuardedBy("SystemMediaRoute2Provider2.this.mLock") + @NonNull private RoutingSessionInfo mSourceSessionInfo; /** - * The same as {@link #mSourceSessionInfo}, except ids are {@link #asSystemRouteId system + * The same as {@link #mSourceSessionInfo}, except ids are {@link #asUniqueSystemId system * provider ids}. */ - @NonNull - private RoutingSessionInfo mTranslatedSessionInfo; + // @GuardedBy("SystemMediaRoute2Provider2.this.mLock") + @NonNull private RoutingSessionInfo mTranslatedSessionInfo; SystemMediaSessionRecord( @NonNull String providerId, @NonNull RoutingSessionInfo sessionInfo) { mProviderId = providerId; mSourceSessionInfo = sessionInfo; + mOriginalId = + asUniqueSystemId(sessionInfo.getProviderId(), sessionInfo.getOriginalId()); mTranslatedSessionInfo = asSystemProviderSession(sessionInfo); } + // @GuardedBy("SystemMediaRoute2Provider2.this.mLock") + public String getServiceSessionId() { + return mSourceSessionInfo.getOriginalId(); + } + @Override public void onSessionUpdate(@NonNull RoutingSessionInfo sessionInfo) { RoutingSessionInfo translatedSessionInfo = asSystemProviderSession(sessionInfo); @@ -612,31 +652,32 @@ import java.util.stream.Stream; @Override public void onSessionReleased() { synchronized (mLock) { - removeSelfFromSessionMap(); + removeSelfFromSessionMaps(); } notifyGlobalSessionInfoUpdated(); } - @GuardedBy("SystemMediaRoute2Provider2.this.mLock") + // @GuardedBy("SystemMediaRoute2Provider2.this.mLock") @Nullable public ProviderProxyRecord getProxyRecord() { ProviderProxyRecord provider = mProxyRecords.get(mProviderId); if (provider == null) { // Unexpected condition where the proxy is no longer available while there's an // ongoing session. Could happen due to a crash in the provider process. - removeSelfFromSessionMap(); + removeSelfFromSessionMaps(); } return provider; } - @GuardedBy("SystemMediaRoute2Provider2.this.mLock") - private void removeSelfFromSessionMap() { + // @GuardedBy("SystemMediaRoute2Provider2.this.mLock") + private void removeSelfFromSessionMaps() { + mSessionOriginalIdToSessionRecord.remove(mOriginalId); mPackageNameToSessionRecord.remove(mSourceSessionInfo.getClientPackageName()); } private RoutingSessionInfo asSystemProviderSession(RoutingSessionInfo session) { var builder = - new RoutingSessionInfo.Builder(session) + new RoutingSessionInfo.Builder(session, mOriginalId) .setProviderId(mUniqueId) .setSystemSession(true) .clearSelectedRoutes() diff --git a/services/supervision/java/com/android/server/supervision/SupervisionService.java b/services/supervision/java/com/android/server/supervision/SupervisionService.java index ea85710eab44..a96c477c78d2 100644 --- a/services/supervision/java/com/android/server/supervision/SupervisionService.java +++ b/services/supervision/java/com/android/server/supervision/SupervisionService.java @@ -177,23 +177,24 @@ public class SupervisionService extends ISupervisionManager.Stub { } } - /** Ensures that supervision is enabled when the supervision app is the profile owner. */ + /** + * Ensures that supervision is enabled when the supervision app is the profile owner. + * + * <p>The state syncing with the DevicePolicyManager can only enable supervision and never + * disable. Supervision can only be disabled explicitly via calls to the + * {@link #setSupervisionEnabledForUser} method. + */ private void syncStateWithDevicePolicyManager(@UserIdInt int userId) { final DevicePolicyManagerInternal dpmInternal = mInjector.getDpmInternal(); final ComponentName po = dpmInternal != null ? dpmInternal.getProfileOwnerAsUser(userId) : null; if (po != null && po.getPackageName().equals(getSystemSupervisionPackage())) { - setSupervisionEnabledForUserInternal(userId, true, po.getPackageName()); + setSupervisionEnabledForUserInternal(userId, true, getSystemSupervisionPackage()); } else if (po != null && po.equals(getSupervisionProfileOwnerComponent())) { // TODO(b/392071637): Consider not enabling supervision in case profile owner is given // to the legacy supervision profile owner component. setSupervisionEnabledForUserInternal(userId, true, po.getPackageName()); - } else { - // TODO(b/381428475): Avoid disabling supervision when the app is not the profile owner. - // This might only be possible after introducing specific and public APIs to enable - // and disable supervision. - setSupervisionEnabledForUserInternal(userId, false, /* supervisionAppPackage= */ null); } } @@ -279,7 +280,7 @@ public class SupervisionService extends ISupervisionManager.Stub { } @VisibleForTesting - @SuppressLint("MissingPermission") // not needed for a service + @SuppressLint("MissingPermission") // not needed for a system service void registerProfileOwnerListener() { IntentFilter poIntentFilter = new IntentFilter(); poIntentFilter.addAction(DevicePolicyManager.ACTION_PROFILE_OWNER_CHANGED); diff --git a/services/tests/servicestests/src/com/android/server/supervision/SupervisionServiceTest.kt b/services/tests/servicestests/src/com/android/server/supervision/SupervisionServiceTest.kt index b150b1495042..da022780ea75 100644 --- a/services/tests/servicestests/src/com/android/server/supervision/SupervisionServiceTest.kt +++ b/services/tests/servicestests/src/com/android/server/supervision/SupervisionServiceTest.kt @@ -161,6 +161,18 @@ class SupervisionServiceTest { @Test @RequiresFlagsEnabled(Flags.FLAG_ENABLE_SYNC_WITH_DPM) + fun profileOwnerChanged_supervisionAppIsNotProfileOwner_doesNotDisableSupervision() { + service.mInternal.setSupervisionEnabledForUser(USER_ID, true) + whenever(mockDpmInternal.getProfileOwnerAsUser(USER_ID)) + .thenReturn(ComponentName("other.package", "MainActivity")) + + broadcastProfileOwnerChanged(USER_ID) + + assertThat(service.isSupervisionEnabledForUser(USER_ID)).isTrue() + } + + @Test + @RequiresFlagsEnabled(Flags.FLAG_ENABLE_SYNC_WITH_DPM) fun profileOwnerChanged_supervisionAppIsNotProfileOwner_doesNotEnableSupervision() { whenever(mockDpmInternal.getProfileOwnerAsUser(USER_ID)) .thenReturn(ComponentName("other.package", "MainActivity")) @@ -258,7 +270,7 @@ class SupervisionServiceTest { private companion object { const val USER_ID = 100 - val APP_UID = USER_ID * UserHandle.PER_USER_RANGE + const val APP_UID = USER_ID * UserHandle.PER_USER_RANGE } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt index 18f44ddff086..eebe49de0010 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt @@ -18,13 +18,12 @@ package com.android.server.wm.flicker import android.app.Instrumentation import android.content.Intent -import android.os.UserHandle import android.platform.test.annotations.Presubmit -import android.provider.Settings import android.tools.flicker.junit.FlickerBuilderProvider import android.tools.flicker.legacy.FlickerBuilder import android.tools.flicker.legacy.LegacyFlickerTest import android.tools.traces.component.ComponentNameMatcher +import android.tools.traces.executeShellCommand import android.util.Log import androidx.test.platform.app.InstrumentationRegistry import com.android.launcher3.tapl.LauncherInstrumentation @@ -47,11 +46,8 @@ constructor( ) { init { tapl.setExpectedRotationCheckEnabled(true) - Settings.System.putIntForUser( - instrumentation.targetContext.contentResolver, - Settings.System.HIDE_ROTATION_LOCK_TOGGLE_FOR_ACCESSIBILITY, - 0, - UserHandle.USER_CURRENT_OR_SELF + executeShellCommand( + "settings put system hide_rotation_lock_toggle_for_accessibility 1" ) } |