diff options
| author | 2025-02-05 12:01:39 -0800 | |
|---|---|---|
| committer | 2025-02-05 12:01:39 -0800 | |
| commit | 459b4736b074be497a598315c61409e701d8da4d (patch) | |
| tree | 4d2928a8d72287356f83b1e5d54d438e8bb3a095 | |
| parent | 38e24effad6e38fb6a1013b1a59d86465b9048eb (diff) | |
| parent | 9e87c396ccd0e64a5c2595ad64349ea850b58e9a (diff) | |
Merge "Pass tap event from keyguard to magic portrait" into main
35 files changed, 766 insertions, 284 deletions
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java index 076f856635b5..957482450893 100644 --- a/core/java/android/app/WallpaperManager.java +++ b/core/java/android/app/WallpaperManager.java @@ -297,6 +297,16 @@ public class WallpaperManager { "android.wallpaper.lockscreen_layout_changed"; /** + * Command for {@link #sendWallpaperCommand}: Include the tap position within the wallpaper + * focal area.The x and y arguments are the absolute tap coordinates, already scaled to match + * the wallpaper's dimensions. + * + * @hide + */ + public static final String COMMAND_LOCKSCREEN_TAP = + "android.wallpaper.lockscreen_tap"; + + /** * Extra passed back from setWallpaper() giving the new wallpaper's assigned ID. * @hide */ diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/common/ui/view/LongPressHandlingViewInteractionHandlerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/common/ui/view/LongPressHandlingViewInteractionHandlerTest.kt index 262c5903ec83..30da8d8d47ad 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/common/ui/view/LongPressHandlingViewInteractionHandlerTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/common/ui/view/LongPressHandlingViewInteractionHandlerTest.kt @@ -44,7 +44,7 @@ class LongPressHandlingViewInteractionHandlerTest : SysuiTestCase() { @Mock private lateinit var postDelayed: (Runnable, Long) -> DisposableHandle @Mock private lateinit var onLongPressDetected: (Int, Int) -> Unit - @Mock private lateinit var onSingleTapDetected: () -> Unit + @Mock private lateinit var onSingleTapDetected: (Int, Int) -> Unit private lateinit var underTest: LongPressHandlingViewInteractionHandler @@ -76,74 +76,51 @@ class LongPressHandlingViewInteractionHandlerTest : SysuiTestCase() { val downX = 123 val downY = 456 dispatchTouchEvents( - Down( - x = downX, - y = downY, - ), - Move( - distanceMoved = ViewConfiguration.getTouchSlop() - 0.1f, - ), + Down(x = downX, y = downY), + Move(distanceMoved = ViewConfiguration.getTouchSlop() - 0.1f), ) delayedRunnable?.run() verify(onLongPressDetected).invoke(downX, downY) - verify(onSingleTapDetected, never()).invoke() + verify(onSingleTapDetected, never()).invoke(any(), any()) } @Test fun longPressButFeatureNotEnabled() = runTest { underTest.isLongPressHandlingEnabled = false - dispatchTouchEvents( - Down( - x = 123, - y = 456, - ), - ) + dispatchTouchEvents(Down(x = 123, y = 456)) assertThat(delayedRunnable).isNull() verify(onLongPressDetected, never()).invoke(any(), any()) - verify(onSingleTapDetected, never()).invoke() + verify(onSingleTapDetected, never()).invoke(any(), any()) } @Test fun longPressButViewNotAttached() = runTest { isAttachedToWindow = false - dispatchTouchEvents( - Down( - x = 123, - y = 456, - ), - ) + dispatchTouchEvents(Down(x = 123, y = 456)) delayedRunnable?.run() verify(onLongPressDetected, never()).invoke(any(), any()) - verify(onSingleTapDetected, never()).invoke() + verify(onSingleTapDetected, never()).invoke(any(), any()) } @Test fun draggedTooFarToBeConsideredAlongPress() = runTest { dispatchTouchEvents( - Down( - x = 123, - y = 456, - ), - Move( - distanceMoved = ViewConfiguration.getTouchSlop() + 0.1f, - ), + Down(x = 123, y = 456), + Move(distanceMoved = ViewConfiguration.getTouchSlop() + 0.1f), ) assertThat(delayedRunnable).isNull() verify(onLongPressDetected, never()).invoke(any(), any()) - verify(onSingleTapDetected, never()).invoke() + verify(onSingleTapDetected, never()).invoke(any(), any()) } @Test fun heldDownTooBrieflyToBeConsideredAlongPress() = runTest { dispatchTouchEvents( - Down( - x = 123, - y = 456, - ), + Down(x = 123, y = 456), Up( distanceMoved = ViewConfiguration.getTouchSlop().toFloat(), gestureDuration = ViewConfiguration.getLongPressTimeout() - 1L, @@ -152,12 +129,10 @@ class LongPressHandlingViewInteractionHandlerTest : SysuiTestCase() { assertThat(delayedRunnable).isNull() verify(onLongPressDetected, never()).invoke(any(), any()) - verify(onSingleTapDetected).invoke() + verify(onSingleTapDetected).invoke(123, 456) } - private fun dispatchTouchEvents( - vararg models: MotionEventModel, - ) { + private fun dispatchTouchEvents(vararg models: MotionEventModel) { models.forEach { model -> underTest.onTouchEvent(model) } } } 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 85e59364d6b6..2dce4aa01d24 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java @@ -167,6 +167,7 @@ import com.android.systemui.user.domain.interactor.UserSwitcherInteractor; import com.android.systemui.util.kotlin.JavaAdapter; import com.android.systemui.util.time.FakeSystemClock; import com.android.systemui.util.time.SystemClock; +import com.android.systemui.wallpapers.ui.viewmodel.WallpaperFocalAreaViewModel; import com.android.wm.shell.animation.FlingAnimationUtils; import dagger.Lazy; @@ -270,6 +271,7 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase { mDreamingToLockscreenTransitionViewModel; @Mock protected KeyguardTransitionInteractor mKeyguardTransitionInteractor; @Mock protected KeyguardTouchHandlingViewModel mKeyuardTouchHandlingViewModel; + @Mock protected WallpaperFocalAreaViewModel mWallpaperFocalAreaViewModel; @Mock protected AlternateBouncerInteractor mAlternateBouncerInteractor; @Mock protected MotionEvent mDownMotionEvent; @Mock protected CoroutineDispatcher mMainDispatcher; @@ -574,6 +576,7 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase { mKeyguardTransitionInteractor, mDumpManager, mKeyuardTouchHandlingViewModel, + mWallpaperFocalAreaViewModel, mKeyguardInteractor, mActivityStarter, mSharedNotificationContainerInteractor, diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/util/WallpaperControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/WallpaperControllerTest.kt index 6271904b2f04..9f2f60342d7c 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/util/WallpaperControllerTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/WallpaperControllerTest.kt @@ -86,7 +86,7 @@ class WallpaperControllerTest : SysuiTestCase() { @Test fun setUnfoldTransitionZoom_defaultUnfoldTransitionIsDisabled_doesNotUpdateWallpaperZoom() { - wallpaperRepository.wallpaperInfo.value = createWallpaperInfo(useDefaultTransition = false) + wallpaperRepository.setWallpaperInfo(createWallpaperInfo(useDefaultTransition = false)) wallaperController.setUnfoldTransitionZoom(0.5f) 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 1f5e7cace8eb..5afc6b1b85a3 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 @@ -27,8 +27,6 @@ import androidx.test.filters.SmallTest import com.android.internal.R 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 @@ -53,8 +51,7 @@ class WallpaperRepositoryImplTest : SysuiTestCase() { private val testDispatcher = StandardTestDispatcher() private val testScope = TestScope(testDispatcher) private val userRepository = FakeUserRepository() - private val keyguardClockRepository = FakeKeyguardClockRepository() - private val keyguardRepository = FakeKeyguardRepository() + private val wallpaperFocalAreaRepository = FakeWallpaperFocalAreaRepository() private val wallpaperManager: WallpaperManager = mock() private val underTest: WallpaperRepositoryImpl by lazy { @@ -63,7 +60,7 @@ class WallpaperRepositoryImplTest : SysuiTestCase() { testDispatcher, fakeBroadcastDispatcher, userRepository, - keyguardRepository, + wallpaperFocalAreaRepository, wallpaperManager, context, ) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/domain/interactor/WallpaperFocalAreaInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/domain/interactor/WallpaperFocalAreaInteractorTest.kt new file mode 100644 index 000000000000..cd6e18a69c4d --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/domain/interactor/WallpaperFocalAreaInteractorTest.kt @@ -0,0 +1,312 @@ +/* + * 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.wallpapers.domain.interactor + +import android.content.mockedContext +import android.content.res.Resources +import android.graphics.PointF +import android.graphics.RectF +import android.util.DisplayMetrics +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.kosmos.currentValue +import com.android.systemui.kosmos.testScope +import com.android.systemui.res.R +import com.android.systemui.shade.data.repository.ShadeRepository +import com.android.systemui.shade.data.repository.shadeRepository +import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository +import com.android.systemui.statusbar.notification.data.repository.setActiveNotifs +import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor +import com.android.systemui.testKosmos +import com.android.systemui.wallpapers.data.repository.fakeWallpaperFocalAreaRepository +import com.android.systemui.wallpapers.data.repository.wallpaperFocalAreaRepository +import com.android.systemui.wallpapers.data.repository.wallpaperRepository +import com.android.systemui.wallpapers.ui.viewmodel.wallpaperFocalAreaViewModel +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.advanceUntilIdle +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.MockitoAnnotations +import org.mockito.kotlin.mock +import org.mockito.kotlin.whenever + +@ExperimentalCoroutinesApi +@SmallTest +@RunWith(AndroidJUnit4::class) +class WallpaperFocalAreaInteractorTest : SysuiTestCase() { + private val kosmos = testKosmos() + private val testScope = kosmos.testScope + lateinit var shadeRepository: ShadeRepository + private lateinit var mockedResources: Resources + lateinit var underTest: WallpaperFocalAreaInteractor + + @Before + fun setup() { + MockitoAnnotations.initMocks(this) + mockedResources = mock<Resources>() + whenever(kosmos.mockedContext.resources).thenReturn(mockedResources) + whenever( + mockedResources.getFloat( + Resources.getSystem() + .getIdentifier( + /* name= */ "config_wallpaperMaxScale", + /* defType= */ "dimen", + /* defPackage= */ "android", + ) + ) + ) + .thenReturn(2f) + underTest = + WallpaperFocalAreaInteractor( + applicationScope = testScope.backgroundScope, + context = kosmos.mockedContext, + wallpaperFocalAreaRepository = kosmos.fakeWallpaperFocalAreaRepository, + shadeRepository = kosmos.shadeRepository, + activeNotificationsInteractor = kosmos.activeNotificationsInteractor, + wallpaperRepository = kosmos.wallpaperRepository, + ) + } + + private fun overrideMockedResources(overrideResources: OverrideResources) { + val displayMetrics = + DisplayMetrics().apply { + widthPixels = overrideResources.screenWidth + heightPixels = overrideResources.screenHeight + density = 2f + } + whenever(mockedResources.displayMetrics).thenReturn(displayMetrics) + whenever(mockedResources.getBoolean(R.bool.center_align_focal_area_shape)) + .thenReturn(overrideResources.centerAlignFocalArea) + } + + @Test + fun focalAreaBounds_withoutNotifications_inHandheldDevices() = + testScope.runTest { + overrideMockedResources( + OverrideResources( + screenWidth = 1000, + screenHeight = 2000, + centerAlignFocalArea = false, + ) + ) + val bounds by collectLastValue(underTest.wallpaperFocalAreaBounds) + kosmos.shadeRepository.setShadeLayoutWide(false) + kosmos.activeNotificationListRepository.setActiveNotifs(0) + kosmos.wallpaperFocalAreaRepository.setShortcutAbsoluteTop(1800F) + kosmos.wallpaperFocalAreaRepository.setNotificationDefaultTop(400F) + kosmos.wallpaperFocalAreaRepository.setNotificationStackAbsoluteBottom(400F) + + assertThat(bounds).isEqualTo(RectF(250f, 700F, 750F, 1400F)) + } + + @Test + fun focalAreaBounds_withNotifications_inHandheldDevices() = + testScope.runTest { + overrideMockedResources( + OverrideResources( + screenWidth = 1000, + screenHeight = 2000, + centerAlignFocalArea = false, + ) + ) + val bounds by collectLastValue(underTest.wallpaperFocalAreaBounds) + kosmos.shadeRepository.setShadeLayoutWide(false) + kosmos.activeNotificationListRepository.setActiveNotifs(1) + kosmos.wallpaperFocalAreaRepository.setShortcutAbsoluteTop(1800F) + kosmos.wallpaperFocalAreaRepository.setNotificationDefaultTop(400F) + kosmos.wallpaperFocalAreaRepository.setNotificationStackAbsoluteBottom(600F) + + assertThat(bounds).isEqualTo(RectF(250f, 800F, 750F, 1400F)) + } + + @Test + fun focalAreaBounds_withNotifications_inUnfoldLandscape() = + testScope.runTest { + overrideMockedResources( + OverrideResources( + screenWidth = 2000, + screenHeight = 1600, + centerAlignFocalArea = false, + ) + ) + val bounds by collectLastValue(underTest.wallpaperFocalAreaBounds) + kosmos.shadeRepository.setShadeLayoutWide(true) + kosmos.activeNotificationListRepository.setActiveNotifs(1) + kosmos.wallpaperFocalAreaRepository.setShortcutAbsoluteTop(1400F) + kosmos.wallpaperFocalAreaRepository.setNotificationDefaultTop(400F) + kosmos.wallpaperFocalAreaRepository.setNotificationStackAbsoluteBottom(600F) + + assertThat(bounds).isEqualTo(RectF(500f, 600F, 1000F, 1100F)) + } + + @Test + fun focalAreaBounds_withoutNotifications_inUnfoldLandscape() = + testScope.runTest { + overrideMockedResources( + OverrideResources( + screenWidth = 2000, + screenHeight = 1600, + centerAlignFocalArea = false, + ) + ) + val bounds by collectLastValue(underTest.wallpaperFocalAreaBounds) + kosmos.shadeRepository.setShadeLayoutWide(true) + kosmos.activeNotificationListRepository.setActiveNotifs(0) + kosmos.wallpaperFocalAreaRepository.setShortcutAbsoluteTop(1400F) + kosmos.wallpaperFocalAreaRepository.setNotificationDefaultTop(400F) + kosmos.wallpaperFocalAreaRepository.setNotificationStackAbsoluteBottom(400F) + + assertThat(bounds).isEqualTo(RectF(1000f, 600F, 1500F, 1100F)) + } + + @Test + fun focalAreaBounds_withNotifications_inUnfoldPortrait() = + testScope.runTest { + overrideMockedResources( + OverrideResources( + screenWidth = 1600, + screenHeight = 2000, + centerAlignFocalArea = false, + ) + ) + val bounds by collectLastValue(underTest.wallpaperFocalAreaBounds) + kosmos.shadeRepository.setShadeLayoutWide(false) + kosmos.activeNotificationListRepository.setActiveNotifs(1) + kosmos.wallpaperFocalAreaRepository.setShortcutAbsoluteTop(1800F) + kosmos.wallpaperFocalAreaRepository.setNotificationDefaultTop(400F) + kosmos.wallpaperFocalAreaRepository.setNotificationStackAbsoluteBottom(600F) + + assertThat(bounds).isEqualTo(RectF(400f, 800F, 1200F, 1400F)) + } + + @Test + fun focalAreaBounds_withoutNotifications_inUnfoldPortrait() = + testScope.runTest { + overrideMockedResources( + OverrideResources( + screenWidth = 1600, + screenHeight = 2000, + centerAlignFocalArea = false, + ) + ) + val bounds by collectLastValue(underTest.wallpaperFocalAreaBounds) + kosmos.shadeRepository.setShadeLayoutWide(false) + kosmos.activeNotificationListRepository.setActiveNotifs(0) + kosmos.wallpaperFocalAreaRepository.setShortcutAbsoluteTop(1800F) + kosmos.wallpaperFocalAreaRepository.setNotificationDefaultTop(400F) + kosmos.wallpaperFocalAreaRepository.setNotificationStackAbsoluteBottom(600F) + + assertThat(bounds).isEqualTo(RectF(400f, 800F, 1200F, 1400F)) + } + + @Test + fun focalAreaBounds_withNotifications_inTabletLandscape() = + testScope.runTest { + overrideMockedResources( + OverrideResources( + screenWidth = 3000, + screenHeight = 2000, + centerAlignFocalArea = true, + ) + ) + val bounds by collectLastValue(underTest.wallpaperFocalAreaBounds) + kosmos.shadeRepository.setShadeLayoutWide(true) + kosmos.activeNotificationListRepository.setActiveNotifs(1) + kosmos.wallpaperFocalAreaRepository.setShortcutAbsoluteTop(1800F) + kosmos.wallpaperFocalAreaRepository.setNotificationDefaultTop(200F) + kosmos.wallpaperFocalAreaRepository.setNotificationStackAbsoluteBottom(200F) + + assertThat(bounds).isEqualTo(RectF(1000f, 600F, 2000F, 1400F)) + } + + @Test + fun focalAreaBounds_withoutNotifications_inTabletLandscape() = + testScope.runTest { + overrideMockedResources( + OverrideResources( + screenWidth = 3000, + screenHeight = 2000, + centerAlignFocalArea = true, + ) + ) + val bounds by collectLastValue(underTest.wallpaperFocalAreaBounds) + kosmos.shadeRepository.setShadeLayoutWide(true) + kosmos.activeNotificationListRepository.setActiveNotifs(0) + kosmos.wallpaperFocalAreaRepository.setShortcutAbsoluteTop(1800F) + kosmos.wallpaperFocalAreaRepository.setNotificationDefaultTop(400F) + kosmos.wallpaperFocalAreaRepository.setNotificationStackAbsoluteBottom(400F) + + assertThat(bounds).isEqualTo(RectF(1000f, 600F, 2000F, 1400F)) + } + + @Test + fun onTap_inFocalBounds() = + testScope.runTest { + kosmos.wallpaperFocalAreaRepository.setTapPosition(PointF(0F, 0F)) + overrideMockedResources( + OverrideResources( + screenWidth = 1000, + screenHeight = 2000, + centerAlignFocalArea = false, + ) + ) + kosmos.wallpaperFocalAreaRepository.setWallpaperFocalAreaBounds( + RectF(250f, 700F, 750F, 1400F) + ) + advanceUntilIdle() + assertThat(currentValue(kosmos.wallpaperFocalAreaRepository.wallpaperFocalAreaBounds)) + .isEqualTo(RectF(250f, 700F, 750F, 1400F)) + underTest.setTapPosition(750F, 750F) + assertThat( + currentValue(kosmos.wallpaperFocalAreaRepository.wallpaperFocalAreaTapPosition) + ) + .isEqualTo(PointF(625F, 875F)) + } + + @Test + fun onTap_outFocalBounds() = + testScope.runTest { + kosmos.wallpaperFocalAreaRepository.setTapPosition(PointF(0F, 0F)) + overrideMockedResources( + OverrideResources( + screenWidth = 1000, + screenHeight = 2000, + centerAlignFocalArea = false, + ) + ) + kosmos.wallpaperFocalAreaViewModel = mock() + kosmos.wallpaperFocalAreaRepository.setWallpaperFocalAreaBounds( + RectF(500F, 500F, 1000F, 1000F) + ) + underTest.setTapPosition(250F, 250F) + assertThat( + currentValue(kosmos.wallpaperFocalAreaRepository.wallpaperFocalAreaTapPosition) + ) + .isEqualTo(PointF(0F, 0F)) + } + + data class OverrideResources( + val screenWidth: Int, + val screenHeight: Int, + val centerAlignFocalArea: Boolean, + ) +} diff --git a/packages/SystemUI/src/com/android/systemui/common/ui/view/LongPressHandlingView.kt b/packages/SystemUI/src/com/android/systemui/common/ui/view/LongPressHandlingView.kt index 9c4736a13b46..dbe3dd09cb13 100644 --- a/packages/SystemUI/src/com/android/systemui/common/ui/view/LongPressHandlingView.kt +++ b/packages/SystemUI/src/com/android/systemui/common/ui/view/LongPressHandlingView.kt @@ -45,11 +45,7 @@ class LongPressHandlingView( longPressDuration: () -> Long, allowedTouchSlop: Int = ViewConfiguration.getTouchSlop(), logger: LongPressHandlingViewLogger? = null, -) : - View( - context, - attrs, - ) { +) : View(context, attrs) { init { setupAccessibilityDelegate() @@ -62,15 +58,10 @@ class LongPressHandlingView( interface Listener { /** Notifies that a long-press has been detected by the given view. */ - fun onLongPressDetected( - view: View, - x: Int, - y: Int, - isA11yAction: Boolean = false, - ) + fun onLongPressDetected(view: View, x: Int, y: Int, isA11yAction: Boolean = false) /** Notifies that the gesture was too short for a long press, it is actually a click. */ - fun onSingleTapDetected(view: View) = Unit + fun onSingleTapDetected(view: View, x: Int, y: Int) = Unit } var listener: Listener? = null @@ -82,23 +73,17 @@ class LongPressHandlingView( postDelayed = { block, timeoutMs -> val dispatchToken = Any() - handler.postDelayed( - block, - dispatchToken, - timeoutMs, - ) + handler.postDelayed(block, dispatchToken, timeoutMs) DisposableHandle { handler.removeCallbacksAndMessages(dispatchToken) } }, isAttachedToWindow = ::isAttachedToWindow, onLongPressDetected = { x, y -> - listener?.onLongPressDetected( - view = this, - x = x, - y = y, - ) + listener?.onLongPressDetected(view = this, x = x, y = y) + }, + onSingleTapDetected = { x, y -> + listener?.onSingleTapDetected(this@LongPressHandlingView, x = x, y = y) }, - onSingleTapDetected = { listener?.onSingleTapDetected(this@LongPressHandlingView) }, longPressDuration = longPressDuration, allowedTouchSlop = allowedTouchSlop, logger = logger, @@ -129,7 +114,7 @@ class LongPressHandlingView( object : AccessibilityDelegate() { override fun onInitializeAccessibilityNodeInfo( v: View, - info: AccessibilityNodeInfo + info: AccessibilityNodeInfo, ) { super.onInitializeAccessibilityNodeInfo(v, info) if ( @@ -143,7 +128,7 @@ class LongPressHandlingView( override fun performAccessibilityAction( host: View, action: Int, - args: Bundle? + args: Bundle?, ): Boolean { return if ( interactionHandler.isLongPressHandlingEnabled && @@ -179,7 +164,7 @@ private fun MotionEvent.toModel(): LongPressHandlingViewInteractionHandler.Motio ) MotionEvent.ACTION_MOVE -> LongPressHandlingViewInteractionHandler.MotionEventModel.Move( - distanceMoved = distanceMoved(), + distanceMoved = distanceMoved() ) MotionEvent.ACTION_UP -> LongPressHandlingViewInteractionHandler.MotionEventModel.Up( diff --git a/packages/SystemUI/src/com/android/systemui/common/ui/view/LongPressHandlingViewInteractionHandler.kt b/packages/SystemUI/src/com/android/systemui/common/ui/view/LongPressHandlingViewInteractionHandler.kt index 4e38a4913fe6..a5d45aff4dc7 100644 --- a/packages/SystemUI/src/com/android/systemui/common/ui/view/LongPressHandlingViewInteractionHandler.kt +++ b/packages/SystemUI/src/com/android/systemui/common/ui/view/LongPressHandlingViewInteractionHandler.kt @@ -17,6 +17,7 @@ package com.android.systemui.common.ui.view +import android.graphics.Point import com.android.systemui.log.LongPressHandlingViewLogger import kotlinx.coroutines.DisposableHandle @@ -32,7 +33,7 @@ class LongPressHandlingViewInteractionHandler( /** Callback reporting the a long-press gesture was detected at the given coordinates. */ private val onLongPressDetected: (x: Int, y: Int) -> Unit, /** Callback reporting the a single tap gesture was detected at the given coordinates. */ - private val onSingleTapDetected: () -> Unit, + private val onSingleTapDetected: (x: Int, y: Int) -> Unit, /** Time for the touch to be considered a long-press in ms */ var longPressDuration: () -> Long, /** @@ -47,19 +48,11 @@ class LongPressHandlingViewInteractionHandler( sealed class MotionEventModel { object Other : MotionEventModel() - data class Down( - val x: Int, - val y: Int, - ) : MotionEventModel() + data class Down(val x: Int, val y: Int) : MotionEventModel() - data class Move( - val distanceMoved: Float, - ) : MotionEventModel() + data class Move(val distanceMoved: Float) : MotionEventModel() - data class Up( - val distanceMoved: Float, - val gestureDuration: Long, - ) : MotionEventModel() + data class Up(val distanceMoved: Float, val gestureDuration: Long) : MotionEventModel() object Cancel : MotionEventModel() } @@ -67,14 +60,18 @@ class LongPressHandlingViewInteractionHandler( var isLongPressHandlingEnabled: Boolean = false var scheduledLongPressHandle: DisposableHandle? = null + /** Record coordinate for last DOWN event for single tap */ + val lastEventDownCoordinate = Point(-1, -1) + fun onTouchEvent(event: MotionEventModel?): Boolean { if (!isLongPressHandlingEnabled) { return false } - return when (event) { is MotionEventModel.Down -> { scheduleLongPress(event.x, event.y) + lastEventDownCoordinate.x = event.x + lastEventDownCoordinate.y = event.y true } is MotionEventModel.Move -> { @@ -92,7 +89,7 @@ class LongPressHandlingViewInteractionHandler( event.gestureDuration < longPressDuration() ) { logger?.dispatchingSingleTap() - dispatchSingleTap() + dispatchSingleTap(lastEventDownCoordinate.x, lastEventDownCoordinate.y) } false } @@ -105,29 +102,20 @@ class LongPressHandlingViewInteractionHandler( } } - private fun scheduleLongPress( - x: Int, - y: Int, - ) { + private fun scheduleLongPress(x: Int, y: Int) { val duration = longPressDuration() logger?.schedulingLongPress(duration) scheduledLongPressHandle = postDelayed( { logger?.longPressTriggered() - dispatchLongPress( - x = x, - y = y, - ) + dispatchLongPress(x = x, y = y) }, duration, ) } - private fun dispatchLongPress( - x: Int, - y: Int, - ) { + private fun dispatchLongPress(x: Int, y: Int) { if (!isAttachedToWindow()) { return } @@ -139,11 +127,11 @@ class LongPressHandlingViewInteractionHandler( scheduledLongPressHandle?.dispose() } - private fun dispatchSingleTap() { + private fun dispatchSingleTap(x: Int, y: Int) { if (!isAttachedToWindow()) { return } - onSingleTapDetected() + onSingleTapDetected(x, y) } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt index 869edfa2b886..23be5c52ab5c 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt @@ -27,7 +27,6 @@ import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.deviceentry.domain.interactor.DeviceEntryHapticsInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor -import com.android.systemui.keyguard.domain.interactor.WallpaperFocalAreaInteractor import com.android.systemui.keyguard.ui.binder.KeyguardBlueprintViewBinder import com.android.systemui.keyguard.ui.binder.KeyguardJankBinder import com.android.systemui.keyguard.ui.binder.KeyguardRootViewBinder @@ -53,6 +52,8 @@ import com.android.systemui.statusbar.VibratorHelper import com.android.systemui.statusbar.phone.ScreenOffAnimationController import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager import com.android.systemui.temporarydisplay.chipbar.ChipbarCoordinator +import com.android.systemui.wallpapers.domain.interactor.WallpaperFocalAreaInteractor +import com.android.systemui.wallpapers.ui.viewmodel.WallpaperFocalAreaViewModel import com.android.systemui.wallpapers.ui.viewmodel.WallpaperViewModel import com.google.android.msdl.domain.MSDLPlayer import java.util.Optional @@ -93,6 +94,7 @@ constructor( @Main private val mainDispatcher: CoroutineDispatcher, private val msdlPlayer: MSDLPlayer, @KeyguardBlueprintLog private val blueprintLog: LogBuffer, + private val wallpaperFocalAreaViewModel: WallpaperFocalAreaViewModel, ) : CoreStartable { private var rootViewHandle: DisposableHandle? = null @@ -148,7 +150,6 @@ constructor( screenOffAnimationController, shadeInteractor, clockInteractor, - wallpaperFocalAreaInteractor, keyguardClockViewModel, deviceEntryHapticsInteractor, vibratorHelper, @@ -157,6 +158,7 @@ constructor( mainDispatcher, msdlPlayer, blueprintLog, + wallpaperFocalAreaViewModel, ) } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardClockRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardClockRepository.kt index 9718e7508df2..a4b9135d708f 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardClockRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardClockRepository.kt @@ -69,16 +69,11 @@ interface KeyguardClockRepository { val previewClock: Flow<ClockController> - /** top of notifications without bcsmartspace in small clock settings */ - val notificationDefaultTop: StateFlow<Float> - val clockEventController: ClockEventController val shouldForceSmallClock: Boolean fun setClockSize(size: ClockSize) - - fun setNotificationDefaultTop(top: Float) } @SysUISingleton @@ -155,14 +150,6 @@ constructor( clockRegistry.createCurrentClock() } - private val _notificationDefaultTop: MutableStateFlow<Float> = MutableStateFlow(0F) - - override val notificationDefaultTop: StateFlow<Float> = _notificationDefaultTop.asStateFlow() - - override fun setNotificationDefaultTop(top: Float) { - _notificationDefaultTop.value = top - } - override val shouldForceSmallClock: Boolean get() = featureFlags.isEnabled(Flags.LOCKSCREEN_ENABLE_LANDSCAPE) && 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 4b36e7a43dcb..acb98ede3e80 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 @@ -17,7 +17,6 @@ package com.android.systemui.keyguard.data.repository import android.graphics.Point -import android.graphics.RectF import com.android.app.tracing.coroutines.launchTraced as launch import com.android.internal.widget.LockPatternUtils import com.android.keyguard.KeyguardUpdateMonitor @@ -255,13 +254,6 @@ interface KeyguardRepository { */ val isEncryptedOrLockdown: Flow<Boolean> - /** The top of shortcut in screen, used by wallpaper to find remaining space in lockscreen */ - val shortcutAbsoluteTop: StateFlow<Float> - - val notificationStackAbsoluteBottom: StateFlow<Float> - - val wallpaperFocalAreaBounds: StateFlow<RectF> - /** * Returns `true` if the keyguard is showing; `false` otherwise. * @@ -328,13 +320,6 @@ interface KeyguardRepository { * otherwise. */ fun isShowKeyguardWhenReenabled(): Boolean - - fun setShortcutAbsoluteTop(top: Float) - - /** Set bottom of notifications from notification stack */ - fun setNotificationStackAbsoluteBottom(bottom: Float) - - fun setWallpaperFocalAreaBounds(bounds: RectF) } /** Encapsulates application state for the keyguard. */ @@ -621,16 +606,6 @@ constructor( private val _isQuickSettingsVisible = MutableStateFlow(false) override val isQuickSettingsVisible: Flow<Boolean> = _isQuickSettingsVisible.asStateFlow() - private val _shortcutAbsoluteTop = MutableStateFlow(0F) - override val shortcutAbsoluteTop = _shortcutAbsoluteTop.asStateFlow() - - private val _notificationStackAbsoluteBottom = MutableStateFlow(0F) - override val notificationStackAbsoluteBottom = _notificationStackAbsoluteBottom.asStateFlow() - - private val _wallpaperFocalAreaBounds = MutableStateFlow(RectF(0F, 0F, 0F, 0F)) - override val wallpaperFocalAreaBounds: StateFlow<RectF> = - _wallpaperFocalAreaBounds.asStateFlow() - init { val callback = object : KeyguardStateController.Callback { @@ -705,18 +680,6 @@ constructor( } } - override fun setShortcutAbsoluteTop(top: Float) { - _shortcutAbsoluteTop.value = top - } - - override fun setNotificationStackAbsoluteBottom(bottom: Float) { - _notificationStackAbsoluteBottom.value = bottom - } - - override fun setWallpaperFocalAreaBounds(bounds: RectF) { - _wallpaperFocalAreaBounds.value = bounds - } - private fun dozeMachineStateToModel(state: DozeMachine.State): DozeStateModel { return when (state) { DozeMachine.State.UNINITIALIZED -> DozeStateModel.UNINITIALIZED diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryModule.kt index 126b375efb7d..4411437188f2 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryModule.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryModule.kt @@ -22,6 +22,8 @@ import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepository import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepositoryImpl import com.android.systemui.keyguard.shared.transition.KeyguardTransitionAnimationCallback import com.android.systemui.keyguard.shared.transition.KeyguardTransitionAnimationCallbackDelegator +import com.android.systemui.wallpapers.data.repository.WallpaperFocalAreaRepository +import com.android.systemui.wallpapers.data.repository.WallpaperFocalAreaRepositoryImpl import dagger.Binds import dagger.Module @@ -73,4 +75,9 @@ interface KeyguardRepositoryModule { fun keyguardSmartspaceRepository( impl: KeyguardSmartspaceRepositoryImpl ): KeyguardSmartspaceRepository + + @Binds + fun bindWallpaperFocalAreaRepository( + impl: WallpaperFocalAreaRepositoryImpl + ): WallpaperFocalAreaRepository } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardClockInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardClockInteractor.kt index ab5fdd608d03..dab4b5f4183c 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardClockInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardClockInteractor.kt @@ -37,6 +37,7 @@ import com.android.systemui.shade.domain.interactor.ShadeInteractor import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor import com.android.systemui.statusbar.notification.domain.interactor.HeadsUpNotificationInteractor import com.android.systemui.util.kotlin.combine +import com.android.systemui.wallpapers.domain.interactor.WallpaperFocalAreaInteractor import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow @@ -62,6 +63,7 @@ constructor( headsUpNotificationInteractor: HeadsUpNotificationInteractor, @Application private val applicationScope: CoroutineScope, val keyguardClockRepository: KeyguardClockRepository, + private val wallpaperFocalAreaInteractor: WallpaperFocalAreaInteractor, ) { private val isOnAod: Flow<Boolean> = keyguardTransitionInteractor.currentKeyguardState.map { it == KeyguardState.AOD } @@ -183,6 +185,6 @@ constructor( } fun setNotificationStackDefaultTop(top: Float) { - keyguardClockRepository.setNotificationDefaultTop(top) + wallpaperFocalAreaInteractor.setNotificationDefaultTop(top) } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt index 3652c17309f4..f84d29f57cc7 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt @@ -52,6 +52,7 @@ import com.android.systemui.shade.ShadeDisplayAware import com.android.systemui.shade.data.repository.ShadeRepository import com.android.systemui.util.kotlin.Utils.Companion.sample as sampleCombine import com.android.systemui.util.kotlin.sample +import com.android.systemui.wallpapers.data.repository.WallpaperFocalAreaRepository import javax.inject.Inject import javax.inject.Provider import kotlinx.coroutines.CoroutineScope @@ -85,6 +86,7 @@ class KeyguardInteractor constructor( private val repository: KeyguardRepository, bouncerRepository: KeyguardBouncerRepository, + private val wallpaperFocalAreaRepository: WallpaperFocalAreaRepository, @ShadeDisplayAware configurationInteractor: ConfigurationInteractor, shadeRepository: ShadeRepository, private val keyguardTransitionInteractor: KeyguardTransitionInteractor, @@ -532,7 +534,7 @@ constructor( } fun setShortcutAbsoluteTop(top: Float) { - repository.setShortcutAbsoluteTop(top) + wallpaperFocalAreaRepository.setShortcutAbsoluteTop(top) } fun setIsKeyguardGoingAway(isGoingAway: Boolean) { @@ -540,7 +542,7 @@ constructor( } fun setNotificationStackAbsoluteBottom(bottom: Float) { - repository.setNotificationStackAbsoluteBottom(bottom) + wallpaperFocalAreaRepository.setNotificationStackAbsoluteBottom(bottom) } suspend fun hydrateTableLogBuffer(tableLogBuffer: TableLogBuffer) { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardLongPressViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardLongPressViewBinder.kt index 2fd9818a38f0..f0e5e16b092b 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardLongPressViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardLongPressViewBinder.kt @@ -43,13 +43,13 @@ object KeyguardLongPressViewBinder { fun bind( view: LongPressHandlingView, viewModel: KeyguardTouchHandlingViewModel, - onSingleTap: () -> Unit, + onSingleTap: (x: Int, y: Int) -> Unit, falsingManager: FalsingManager, ) { view.accessibilityHintLongPressAction = AccessibilityNodeInfo.AccessibilityAction( AccessibilityNodeInfoCompat.ACTION_LONG_CLICK, - view.resources.getString(R.string.lock_screen_settings) + view.resources.getString(R.string.lock_screen_settings), ) view.listener = object : LongPressHandlingView.Listener { @@ -57,7 +57,7 @@ object KeyguardLongPressViewBinder { view: View, x: Int, y: Int, - isA11yAction: Boolean + isA11yAction: Boolean, ) { if ( !isA11yAction && falsingManager.isFalseLongTap(FalsingManager.LOW_PENALTY) @@ -68,12 +68,12 @@ object KeyguardLongPressViewBinder { viewModel.onLongPress(isA11yAction) } - override fun onSingleTapDetected(view: View) { + override fun onSingleTapDetected(view: View, x: Int, y: Int) { if (falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) { return } - onSingleTap() + onSingleTap(x, y) } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt index b8020b19ce86..00710c97d00a 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt @@ -50,7 +50,6 @@ import com.android.systemui.common.ui.view.onTouchListener import com.android.systemui.customization.R as customR import com.android.systemui.deviceentry.domain.interactor.DeviceEntryHapticsInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor -import com.android.systemui.keyguard.domain.interactor.WallpaperFocalAreaInteractor import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.ui.view.layout.sections.AodPromotedNotificationSection import com.android.systemui.keyguard.ui.viewmodel.BurnInParameters @@ -80,6 +79,7 @@ import com.android.systemui.util.ui.AnimatedValue import com.android.systemui.util.ui.isAnimating import com.android.systemui.util.ui.stopAnimating import com.android.systemui.util.ui.value +import com.android.systemui.wallpapers.ui.viewmodel.WallpaperFocalAreaViewModel import com.google.android.msdl.data.model.MSDLToken import com.google.android.msdl.domain.MSDLPlayer import kotlin.math.min @@ -104,7 +104,6 @@ object KeyguardRootViewBinder { screenOffAnimationController: ScreenOffAnimationController, shadeInteractor: ShadeInteractor, clockInteractor: KeyguardClockInteractor, - wallpaperFocalAreaInteractor: WallpaperFocalAreaInteractor, clockViewModel: KeyguardClockViewModel, deviceEntryHapticsInteractor: DeviceEntryHapticsInteractor?, vibratorHelper: VibratorHelper?, @@ -113,6 +112,7 @@ object KeyguardRootViewBinder { mainImmediateDispatcher: CoroutineDispatcher, msdlPlayer: MSDLPlayer?, @KeyguardBlueprintLog blueprintLog: LogBuffer, + wallpaperFocalAreaViewModel: WallpaperFocalAreaViewModel, ): DisposableHandle { val disposables = DisposableHandles() val childViews = mutableMapOf<Int, View>() @@ -368,13 +368,11 @@ object KeyguardRootViewBinder { disposables += view.repeatWhenAttached { repeatOnLifecycle(Lifecycle.State.STARTED) { - if (viewModel.shouldSendFocalArea.value) { + if (wallpaperFocalAreaViewModel.hasFocalArea.value) { launch { - wallpaperFocalAreaInteractor.wallpaperFocalAreaBounds + wallpaperFocalAreaViewModel.wallpaperFocalAreaBounds .filterNotNull() - .collect { - wallpaperFocalAreaInteractor.setWallpaperFocalAreaBounds(it) - } + .collect { wallpaperFocalAreaViewModel.setFocalAreaBounds(it) } } } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt index cff5cebf2011..85eae6ed98b3 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt @@ -55,7 +55,6 @@ import com.android.systemui.customization.R as customR import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main -import com.android.systemui.keyguard.domain.interactor.WallpaperFocalAreaInteractor import com.android.systemui.keyguard.shared.model.ClockSizeSetting import com.android.systemui.keyguard.ui.binder.KeyguardPreviewClockViewBinder import com.android.systemui.keyguard.ui.binder.KeyguardPreviewSmartspaceViewBinder @@ -82,6 +81,7 @@ import com.android.systemui.statusbar.KeyguardIndicationController import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController import com.android.systemui.util.kotlin.DisposableHandles import com.android.systemui.util.settings.SecureSettings +import com.android.systemui.wallpapers.domain.interactor.WallpaperFocalAreaInteractor import dagger.assisted.Assisted import dagger.assisted.AssistedInject import kotlinx.coroutines.CoroutineDispatcher diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt index 53a063d1baf0..62a5ebab29e0 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt @@ -28,7 +28,6 @@ import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.keyguard.domain.interactor.PulseExpansionInteractor -import com.android.systemui.keyguard.domain.interactor.WallpaperFocalAreaInteractor import com.android.systemui.keyguard.shared.model.Edge import com.android.systemui.keyguard.shared.model.KeyguardState.AOD import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING @@ -54,6 +53,7 @@ import com.android.systemui.util.ui.AnimatableEvent import com.android.systemui.util.ui.AnimatedValue import com.android.systemui.util.ui.toAnimatedValueFlow import com.android.systemui.util.ui.zip +import com.android.systemui.wallpapers.domain.interactor.WallpaperFocalAreaInteractor import javax.inject.Inject import kotlin.math.max import kotlinx.coroutines.CoroutineScope @@ -377,8 +377,6 @@ constructor( initialValue = AnimatedValue.NotAnimating(false), ) - val shouldSendFocalArea = wallpaperFocalAreaInteractor.shouldSendFocalArea - fun onNotificationContainerBoundsChanged(top: Float, bottom: Float, animate: Boolean = false) { keyguardInteractor.setNotificationContainerBounds( NotificationContainerBounds(top = top, bottom = bottom, isAnimated = animate) diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java index e535019cd3d7..10d76fbd13a1 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java @@ -187,6 +187,7 @@ import com.android.systemui.unfold.SysUIUnfoldComponent; import com.android.systemui.util.Compile; import com.android.systemui.util.Utils; import com.android.systemui.util.time.SystemClock; +import com.android.systemui.wallpapers.ui.viewmodel.WallpaperFocalAreaViewModel; import com.android.wm.shell.animation.FlingAnimationUtils; import dalvik.annotation.optimization.NeverCompile; @@ -261,7 +262,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump private final ShadeLogger mShadeLog; private final DozeParameters mDozeParameters; private final NotificationStackScrollLayout.OnEmptySpaceClickListener - mOnEmptySpaceClickListener = (x, y) -> onEmptySpaceClick(); + mOnEmptySpaceClickListener = this::onEmptySpaceClick; private final ShadeHeadsUpChangedListener mOnHeadsUpChangedListener = new ShadeHeadsUpChangedListener(); private final ConfigurationListener mConfigurationListener = new ConfigurationListener(); @@ -459,6 +460,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump private final NotificationListContainer mNotificationListContainer; private final NPVCDownEventState.Buffer mLastDownEvents; private final KeyguardClockInteractor mKeyguardClockInteractor; + private final WallpaperFocalAreaViewModel mWallpaperFocalAreaViewModel; private float mMinExpandHeight; private boolean mPanelUpdateWhenAnimatorEnds; private boolean mHasVibratedOnOpen = false; @@ -592,6 +594,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump KeyguardTransitionInteractor keyguardTransitionInteractor, DumpManager dumpManager, KeyguardTouchHandlingViewModel keyguardTouchHandlingViewModel, + WallpaperFocalAreaViewModel wallpaperFocalAreaViewModel, KeyguardInteractor keyguardInteractor, ActivityStarter activityStarter, SharedNotificationContainerInteractor sharedNotificationContainerInteractor, @@ -747,11 +750,13 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump SysUIUnfoldComponent::getKeyguardUnfoldTransition); mKeyguardClockInteractor = keyguardClockInteractor; + mWallpaperFocalAreaViewModel = wallpaperFocalAreaViewModel; + KeyguardLongPressViewBinder.bind( mView.requireViewById(R.id.keyguard_long_press), keyguardTouchHandlingViewModel, - () -> { - onEmptySpaceClick(); + (x, y) -> { + onEmptySpaceClick(x, y); return Unit.INSTANCE; }, mFalsingManager); @@ -2071,7 +2076,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump } } - private void onMiddleClicked() { + private void onMiddleClicked(float x, float y) { switch (mBarState) { case KEYGUARD: if (!mDozingOnDown) { @@ -2090,6 +2095,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump mLockscreenGestureLogger .log(LockscreenUiEvent.LOCKSCREEN_LOCK_SHOW_HINT); mKeyguardIndicationController.showActionToUnlock(); + mWallpaperFocalAreaViewModel.setTapPosition(x, y); } } break; @@ -2834,7 +2840,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump } else if (!mCentralSurfaces.isBouncerShowing() && !mAlternateBouncerInteractor.isVisibleState() && !mKeyguardStateController.isKeyguardGoingAway()) { - onEmptySpaceClick(); + onEmptySpaceClick(x, y); onTrackingStopped(true); } mVelocityTracker.clear(); @@ -3142,8 +3148,8 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump } /** Called when the user performs a click anywhere in the empty area of the panel. */ - private void onEmptySpaceClick() { - onMiddleClicked(); + private void onEmptySpaceClick(float x, float y) { + onMiddleClicked(x, y); } @VisibleForTesting diff --git a/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/WallpaperFocalAreaRepository.kt b/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/WallpaperFocalAreaRepository.kt new file mode 100644 index 000000000000..2c3491b06a90 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/WallpaperFocalAreaRepository.kt @@ -0,0 +1,92 @@ +/* + * 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.wallpapers.data.repository + +import android.graphics.PointF +import android.graphics.RectF +import com.android.systemui.dagger.SysUISingleton +import javax.inject.Inject +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow + +interface WallpaperFocalAreaRepository { + + /** The top of shortcut in screen, used by wallpaper to find remaining space in lockscreen */ + val shortcutAbsoluteTop: StateFlow<Float> + + val notificationStackAbsoluteBottom: StateFlow<Float> + + val wallpaperFocalAreaBounds: StateFlow<RectF> + + val wallpaperFocalAreaTapPosition: StateFlow<PointF> + + /** top of notifications without bcsmartspace in small clock settings */ + val notificationDefaultTop: StateFlow<Float> + + fun setShortcutAbsoluteTop(top: Float) + + /** Set bottom of notifications from notification stack, used as top for focal area bounds */ + fun setNotificationStackAbsoluteBottom(bottom: Float) + + fun setWallpaperFocalAreaBounds(bounds: RectF) + + fun setNotificationDefaultTop(top: Float) + + fun setTapPosition(tapPosition: PointF) +} + +@SysUISingleton +class WallpaperFocalAreaRepositoryImpl @Inject constructor() : WallpaperFocalAreaRepository { + + private val _shortcutAbsoluteTop = MutableStateFlow(0F) + override val shortcutAbsoluteTop = _shortcutAbsoluteTop.asStateFlow() + + private val _notificationStackAbsoluteBottom = MutableStateFlow(0F) + override val notificationStackAbsoluteBottom = _notificationStackAbsoluteBottom.asStateFlow() + + private val _wallpaperFocalAreaBounds = MutableStateFlow(RectF(0F, 0F, 0F, 0F)) + override val wallpaperFocalAreaBounds: StateFlow<RectF> = + _wallpaperFocalAreaBounds.asStateFlow() + + private val _wallpaperFocalAreaTapPosition = MutableStateFlow(PointF(0F, 0F)) + override val wallpaperFocalAreaTapPosition: StateFlow<PointF> = + _wallpaperFocalAreaTapPosition.asStateFlow() + + private val _notificationDefaultTop = MutableStateFlow(0F) + override val notificationDefaultTop: StateFlow<Float> = _notificationDefaultTop.asStateFlow() + + override fun setShortcutAbsoluteTop(top: Float) { + _shortcutAbsoluteTop.value = top + } + + override fun setNotificationStackAbsoluteBottom(bottom: Float) { + _notificationStackAbsoluteBottom.value = bottom + } + + override fun setNotificationDefaultTop(top: Float) { + _notificationDefaultTop.value = top + } + + override fun setWallpaperFocalAreaBounds(bounds: RectF) { + _wallpaperFocalAreaBounds.value = bounds + } + + override fun setTapPosition(point: PointF) { + _wallpaperFocalAreaTapPosition.value = point + } +} 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 79a9630e6887..990c76f60ded 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 @@ -30,7 +30,6 @@ import com.android.internal.R 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 @@ -79,7 +78,7 @@ constructor( @Background private val bgDispatcher: CoroutineDispatcher, broadcastDispatcher: BroadcastDispatcher, userRepository: UserRepository, - keyguardRepository: KeyguardRepository, + wallpaperFocalAreaRepository: WallpaperFocalAreaRepository, private val wallpaperManager: WallpaperManager, private val context: Context, ) : WallpaperRepository { @@ -101,6 +100,7 @@ constructor( .filter { it.selectionStatus == SelectionStatus.SELECTION_COMPLETE } @VisibleForTesting var sendLockscreenLayoutJob: Job? = null + @VisibleForTesting var sendTapInShapeEffectsJob: Job? = null override val wallpaperInfo: StateFlow<WallpaperInfo?> = if (!wallpaperManager.isWallpaperSupported) { @@ -131,7 +131,7 @@ constructor( if (shouldSendNotificationLayout) { sendLockscreenLayoutJob = scope.launch { - keyguardRepository.wallpaperFocalAreaBounds.collect { + wallpaperFocalAreaRepository.wallpaperFocalAreaBounds.collect { wallpaperFocalAreaBounds -> wallpaperManager.sendWallpaperCommand( /* windowToken = */ rootView?.windowToken, @@ -161,8 +161,24 @@ constructor( ) } } + + sendTapInShapeEffectsJob = + scope.launch { + wallpaperFocalAreaRepository.wallpaperFocalAreaTapPosition.collect { + wallpaperFocalAreaTapPosition -> + wallpaperManager.sendWallpaperCommand( + /* windowToken = */ rootView?.windowToken, + /* action = */ WallpaperManager.COMMAND_LOCKSCREEN_TAP, + /* x = */ wallpaperFocalAreaTapPosition.x.toInt(), + /* y = */ wallpaperFocalAreaTapPosition.y.toInt(), + /* z = */ 0, + /* extras = */ null, + ) + } + } } else { sendLockscreenLayoutJob?.cancel() + sendTapInShapeEffectsJob?.cancel() } shouldSendNotificationLayout } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WallpaperFocalAreaInteractor.kt b/packages/SystemUI/src/com/android/systemui/wallpapers/domain/interactor/WallpaperFocalAreaInteractor.kt index 9c744d63a093..187d6c7801c0 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WallpaperFocalAreaInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/wallpapers/domain/interactor/WallpaperFocalAreaInteractor.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2024 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. @@ -14,44 +14,43 @@ * limitations under the License. */ -package com.android.systemui.keyguard.domain.interactor +package com.android.systemui.wallpapers.domain.interactor import android.content.Context import android.content.res.Resources +import android.graphics.PointF import android.graphics.RectF import android.util.TypedValue -import com.android.app.animation.MathUtils.max +import androidx.annotation.VisibleForTesting +import com.android.app.animation.MathUtils import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application -import com.android.systemui.keyguard.data.repository.KeyguardClockRepository -import com.android.systemui.keyguard.data.repository.KeyguardRepository import com.android.systemui.res.R import com.android.systemui.shade.data.repository.ShadeRepository import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor +import com.android.systemui.wallpapers.data.repository.WallpaperFocalAreaRepository import com.android.systemui.wallpapers.data.repository.WallpaperRepository import javax.inject.Inject import kotlin.math.min import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.SharingStarted -import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.flow.distinctUntilChanged @SysUISingleton class WallpaperFocalAreaInteractor @Inject constructor( @Application private val applicationScope: CoroutineScope, - context: Context, - private val keyguardRepository: KeyguardRepository, + private val context: Context, + private val wallpaperFocalAreaRepository: WallpaperFocalAreaRepository, shadeRepository: ShadeRepository, activeNotificationsInteractor: ActiveNotificationsInteractor, - keyguardClockRepository: KeyguardClockRepository, - wallpaperRepository: WallpaperRepository, + val wallpaperRepository: WallpaperRepository, ) { - // When there's notifications in splitshade, the focal area shape effect should be left aligned - private val notificationInShadeWideLayout: Flow<Boolean> = + // When there's notifications in splitshade, the focal area should be left aligned + @VisibleForTesting + val notificationInShadeWideLayout: Flow<Boolean> = combine( shadeRepository.isShadeLayoutWide, activeNotificationsInteractor.areAnyNotificationsPresent, @@ -63,14 +62,15 @@ constructor( } } - val shouldSendFocalArea = wallpaperRepository.shouldSendFocalArea - val wallpaperFocalAreaBounds: StateFlow<RectF?> = + val hasFocalArea = wallpaperRepository.shouldSendFocalArea + + val wallpaperFocalAreaBounds: Flow<RectF> = combine( shadeRepository.isShadeLayoutWide, notificationInShadeWideLayout, - keyguardRepository.notificationStackAbsoluteBottom, - keyguardRepository.shortcutAbsoluteTop, - keyguardClockRepository.notificationDefaultTop, + wallpaperFocalAreaRepository.notificationStackAbsoluteBottom, + wallpaperFocalAreaRepository.shortcutAbsoluteTop, + wallpaperFocalAreaRepository.notificationDefaultTop, ) { isShadeLayoutWide, notificationInShadeWideLayout, @@ -80,6 +80,7 @@ constructor( // Wallpaper will be zoomed in with config_wallpaperMaxScale in lockscreen // so we need to give a bounds taking this scale in consideration val wallpaperZoomedInScale = getSystemWallpaperMaximumScale(context) + val screenBounds = RectF( 0F, @@ -95,12 +96,14 @@ constructor( screenBounds.centerX() + screenBounds.width() / 2F / wallpaperZoomedInScale, screenBounds.centerY() + screenBounds.height() / 2F / wallpaperZoomedInScale, ) + val maxFocalAreaWidth = TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_DIP, FOCAL_AREA_MAX_WIDTH_DP.toFloat(), context.resources.displayMetrics, ) + val (left, right) = // tablet landscape if (context.resources.getBoolean(R.bool.center_align_focal_area_shape)) { @@ -140,32 +143,48 @@ constructor( // handheld / portrait } else { scaledBounds.top + - max(notificationDefaultTop, notificationStackAbsoluteBottom) / + MathUtils.max(notificationDefaultTop, notificationStackAbsoluteBottom) / wallpaperZoomedInScale } val bottom = scaledBounds.bottom - scaledBottomMargin RectF(left, top, right, bottom) } - .stateIn( - applicationScope, - started = SharingStarted.WhileSubscribed(), - initialValue = null, - ) + .distinctUntilChanged() + + fun setFocalAreaBounds(bounds: RectF) { + wallpaperFocalAreaRepository.setWallpaperFocalAreaBounds(bounds) + } - fun setWallpaperFocalAreaBounds(bounds: RectF) { - keyguardRepository.setWallpaperFocalAreaBounds(bounds) + fun setNotificationDefaultTop(top: Float) { + wallpaperFocalAreaRepository.setNotificationDefaultTop(top) + } + + fun setTapPosition(x: Float, y: Float) { + // Focal area should only react to touch event within its bounds + val wallpaperZoomedInScale = getSystemWallpaperMaximumScale(context) + // Because there's a scale applied on wallpaper in lockscreen + // we should map it to the unscaled position on wallpaper + val screenCenterX = context.resources.displayMetrics.widthPixels / 2F + val newX = (x - screenCenterX) / wallpaperZoomedInScale + screenCenterX + val screenCenterY = context.resources.displayMetrics.heightPixels / 2F + val newY = (y - screenCenterY) / wallpaperZoomedInScale + screenCenterY + if (wallpaperFocalAreaRepository.wallpaperFocalAreaBounds.value.contains(newX, newY)) { + wallpaperFocalAreaRepository.setTapPosition(PointF(newX, newY)) + } } companion object { fun getSystemWallpaperMaximumScale(context: Context): Float { - return context.resources.getFloat( - Resources.getSystem() - .getIdentifier( - /* name= */ "config_wallpaperMaxScale", - /* defType= */ "dimen", - /* defPackage= */ "android", - ) - ) + val scale = + context.resources.getFloat( + Resources.getSystem() + .getIdentifier( + /* name= */ "config_wallpaperMaxScale", + /* defType= */ "dimen", + /* defPackage= */ "android", + ) + ) + return if (scale == 0f) 1f else scale } // A max width for focal area shape effects bounds, to avoid diff --git a/packages/SystemUI/src/com/android/systemui/wallpapers/ui/viewmodel/WallpaperFocalAreaViewModel.kt b/packages/SystemUI/src/com/android/systemui/wallpapers/ui/viewmodel/WallpaperFocalAreaViewModel.kt new file mode 100644 index 000000000000..70a97d473c49 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/wallpapers/ui/viewmodel/WallpaperFocalAreaViewModel.kt @@ -0,0 +1,37 @@ +/* + * 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.wallpapers.ui.viewmodel + +import android.graphics.RectF +import com.android.systemui.wallpapers.domain.interactor.WallpaperFocalAreaInteractor +import javax.inject.Inject + +class WallpaperFocalAreaViewModel +@Inject +constructor(private val wallpaperFocalAreaInteractor: WallpaperFocalAreaInteractor) { + val hasFocalArea = wallpaperFocalAreaInteractor.hasFocalArea + + val wallpaperFocalAreaBounds = wallpaperFocalAreaInteractor.wallpaperFocalAreaBounds + + fun setFocalAreaBounds(bounds: RectF) { + wallpaperFocalAreaInteractor.setFocalAreaBounds(bounds) + } + + fun setTapPosition(x: Float, y: Float) { + wallpaperFocalAreaInteractor.setTapPosition(x, y) + } +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardClockRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardClockRepository.kt index 159dd34efbbc..25a9120651a6 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardClockRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardClockRepository.kt @@ -47,10 +47,6 @@ class FakeKeyguardClockRepository() : KeyguardClockRepository { override val previewClock: Flow<ClockController> get() = _previewClock - private val _notificationDefaultTop = MutableStateFlow(0F) - override val notificationDefaultTop: StateFlow<Float> - get() = _notificationDefaultTop - override val clockEventController: ClockEventController get() = mock() @@ -63,10 +59,6 @@ class FakeKeyguardClockRepository() : KeyguardClockRepository { _clockSize.value = size } - override fun setNotificationDefaultTop(top: Float) { - _notificationDefaultTop.value = top - } - fun setSelectedClockSize(size: ClockSizeSetting) { _selectedClockSize.value = size } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt index 1952f26b4e6a..591f493cc129 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt @@ -18,7 +18,6 @@ package com.android.systemui.keyguard.data.repository import android.graphics.Point -import android.graphics.RectF import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.shared.model.BiometricUnlockMode import com.android.systemui.keyguard.shared.model.BiometricUnlockModel @@ -123,18 +122,6 @@ class FakeKeyguardRepository @Inject constructor() : KeyguardRepository { private val _isEncryptedOrLockdown = MutableStateFlow(true) override val isEncryptedOrLockdown: Flow<Boolean> = _isEncryptedOrLockdown - private val _shortcutAbsoluteTop = MutableStateFlow(0F) - override val shortcutAbsoluteTop: StateFlow<Float> - get() = _shortcutAbsoluteTop.asStateFlow() - - private val _notificationStackAbsoluteBottom = MutableStateFlow(0F) - override val notificationStackAbsoluteBottom: StateFlow<Float> - get() = _notificationStackAbsoluteBottom.asStateFlow() - - private val _wallpaperFocalAreaBounds = MutableStateFlow(RectF(0f, 0f, 0f, 0f)) - override val wallpaperFocalAreaBounds: StateFlow<RectF> - get() = _wallpaperFocalAreaBounds.asStateFlow() - private val _isKeyguardEnabled = MutableStateFlow(true) override val isKeyguardEnabled: StateFlow<Boolean> = _isKeyguardEnabled.asStateFlow() @@ -289,18 +276,6 @@ class FakeKeyguardRepository @Inject constructor() : KeyguardRepository { return isShowKeyguardWhenReenabled } - override fun setShortcutAbsoluteTop(top: Float) { - _shortcutAbsoluteTop.value = top - } - - override fun setNotificationStackAbsoluteBottom(bottom: Float) { - _notificationStackAbsoluteBottom.value = bottom - } - - override fun setWallpaperFocalAreaBounds(bounds: RectF) { - _wallpaperFocalAreaBounds.value = bounds - } - override fun setCanIgnoreAuthAndReturnToGone(canWake: Boolean) { _canIgnoreAuthAndReturnToGone.value = canWake } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardClockInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardClockInteractorKosmos.kt index bdb9abb03c5f..8844eb040f02 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardClockInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardClockInteractorKosmos.kt @@ -23,6 +23,7 @@ import com.android.systemui.media.controls.domain.pipeline.interactor.mediaCarou import com.android.systemui.shade.domain.interactor.shadeInteractor import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor import com.android.systemui.statusbar.notification.stack.domain.interactor.headsUpNotificationInteractor +import com.android.systemui.wallpapers.domain.interactor.wallpaperFocalAreaInteractor val Kosmos.keyguardClockInteractor by Kosmos.Fixture { @@ -35,5 +36,6 @@ val Kosmos.keyguardClockInteractor by headsUpNotificationInteractor = headsUpNotificationInteractor, applicationScope = applicationCoroutineScope, keyguardClockRepository = keyguardClockRepository, + wallpaperFocalAreaInteractor = wallpaperFocalAreaInteractor, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorFactory.kt index ee21bdc0b4c2..4e5abb883afd 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorFactory.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorFactory.kt @@ -28,6 +28,8 @@ import com.android.systemui.scene.domain.interactor.SceneInteractor import com.android.systemui.shade.data.repository.FakeShadeRepository import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.whenever +import com.android.systemui.wallpapers.data.repository.FakeWallpaperFocalAreaRepository +import com.android.systemui.wallpapers.data.repository.WallpaperFocalAreaRepository import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow @@ -48,6 +50,8 @@ object KeyguardInteractorFactory { bouncerRepository: FakeKeyguardBouncerRepository = FakeKeyguardBouncerRepository(), configurationRepository: FakeConfigurationRepository = FakeConfigurationRepository(), shadeRepository: FakeShadeRepository = FakeShadeRepository(), + wallpaperFocalAreaRepository: WallpaperFocalAreaRepository = + FakeWallpaperFocalAreaRepository(), sceneInteractor: SceneInteractor = mock(), fromGoneTransitionInteractor: FromGoneTransitionInteractor = mock(), fromLockscreenTransitionInteractor: FromLockscreenTransitionInteractor = mock(), @@ -84,6 +88,7 @@ object KeyguardInteractorFactory { fromAlternateBouncerTransitionInteractor }, applicationScope = testScope, + wallpaperFocalAreaRepository = wallpaperFocalAreaRepository, ), ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorKosmos.kt index 869bae236d5c..f9b0290fe339 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorKosmos.kt @@ -23,6 +23,7 @@ import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.testScope import com.android.systemui.scene.domain.interactor.sceneInteractor import com.android.systemui.shade.data.repository.shadeRepository +import com.android.systemui.wallpapers.data.repository.wallpaperFocalAreaRepository val Kosmos.keyguardInteractor: KeyguardInteractor by Kosmos.Fixture { @@ -38,5 +39,6 @@ val Kosmos.keyguardInteractor: KeyguardInteractor by fromOccludedTransitionInteractor = { fromOccludedTransitionInteractor }, fromAlternateBouncerTransitionInteractor = { fromAlternateBouncerTransitionInteractor }, applicationScope = testScope.backgroundScope, + wallpaperFocalAreaRepository = wallpaperFocalAreaRepository, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt index dc54eabba060..ea3feeaf0854 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt @@ -20,7 +20,6 @@ import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor import com.android.systemui.keyguard.domain.interactor.keyguardInteractor import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor import com.android.systemui.keyguard.domain.interactor.pulseExpansionInteractor -import com.android.systemui.keyguard.domain.interactor.wallpaperFocalAreaInteractor import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.Kosmos.Fixture import com.android.systemui.kosmos.applicationCoroutineScope @@ -30,6 +29,7 @@ import com.android.systemui.statusbar.notification.icon.ui.viewmodel.notificatio import com.android.systemui.statusbar.notification.stack.domain.interactor.notificationsKeyguardInteractor import com.android.systemui.statusbar.phone.dozeParameters import com.android.systemui.statusbar.phone.screenOffAnimationController +import com.android.systemui.wallpapers.domain.interactor.wallpaperFocalAreaInteractor val Kosmos.keyguardRootViewModel by Fixture { KeyguardRootViewModel( diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/wallpapers/data/repository/FakeWallpaperFocalAreaRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/wallpapers/data/repository/FakeWallpaperFocalAreaRepository.kt new file mode 100644 index 000000000000..aeff86ed89bb --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/wallpapers/data/repository/FakeWallpaperFocalAreaRepository.kt @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.wallpapers.data.repository + +import android.graphics.PointF +import android.graphics.RectF +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow + +class FakeWallpaperFocalAreaRepository : WallpaperFocalAreaRepository { + private val _shortcutAbsoluteTop = MutableStateFlow(0F) + override val shortcutAbsoluteTop = _shortcutAbsoluteTop.asStateFlow() + + private val _notificationStackAbsoluteBottom = MutableStateFlow(0F) + override val notificationStackAbsoluteBottom = _notificationStackAbsoluteBottom.asStateFlow() + + private val _wallpaperFocalAreaBounds = MutableStateFlow(RectF(0F, 0F, 0F, 0F)) + override val wallpaperFocalAreaBounds: StateFlow<RectF> = + _wallpaperFocalAreaBounds.asStateFlow() + + private val _wallpaperFocalAreaTapPosition = MutableStateFlow(PointF(0F, 0F)) + override val wallpaperFocalAreaTapPosition: StateFlow<PointF> = + _wallpaperFocalAreaTapPosition.asStateFlow() + + private val _notificationDefaultTop = MutableStateFlow(0F) + override val notificationDefaultTop: StateFlow<Float> = _notificationDefaultTop.asStateFlow() + + override fun setShortcutAbsoluteTop(top: Float) { + _shortcutAbsoluteTop.value = top + } + + override fun setNotificationStackAbsoluteBottom(bottom: Float) { + _notificationStackAbsoluteBottom.value = bottom + } + + override fun setNotificationDefaultTop(top: Float) { + _notificationDefaultTop.value = top + } + + override fun setWallpaperFocalAreaBounds(bounds: RectF) { + _wallpaperFocalAreaBounds.value = bounds + } + + override fun setTapPosition(point: PointF) { + _wallpaperFocalAreaTapPosition.value = point + } +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/wallpapers/data/repository/FakeWallpaperRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/wallpapers/data/repository/FakeWallpaperRepository.kt new file mode 100644 index 000000000000..8689e04e62dd --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/wallpapers/data/repository/FakeWallpaperRepository.kt @@ -0,0 +1,44 @@ +/* + * 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.wallpapers.data.repository + +import android.app.WallpaperInfo +import android.view.View +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow + +/** Fake implementation of the wallpaper repository. */ +class FakeWallpaperRepository : WallpaperRepository { + private val _wallpaperInfo: MutableStateFlow<WallpaperInfo?> = MutableStateFlow(null) + override val wallpaperInfo: StateFlow<WallpaperInfo?> = _wallpaperInfo.asStateFlow() + private val _wallpaperSupportsAmbientMode = MutableStateFlow(false) + override val wallpaperSupportsAmbientMode: Flow<Boolean> = + _wallpaperSupportsAmbientMode.asStateFlow() + override var rootView: View? = null + private val _shouldSendFocalArea = MutableStateFlow(false) + override val shouldSendFocalArea: StateFlow<Boolean> = _shouldSendFocalArea.asStateFlow() + + fun setShouldSendFocalArea(shouldSendFocalArea: Boolean) { + _shouldSendFocalArea.value = shouldSendFocalArea + } + + fun setWallpaperInfo(wallpaperInfo: WallpaperInfo?) { + _wallpaperInfo.value = wallpaperInfo + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/data/repository/FakeWallpaperRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/wallpapers/data/repository/WallpaperFocalAreaRepositoryKosmos.kt index d6343c840d9b..c1032b14d478 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/data/repository/FakeWallpaperRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/wallpapers/data/repository/WallpaperFocalAreaRepositoryKosmos.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 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. @@ -16,15 +16,8 @@ package com.android.systemui.wallpapers.data.repository -import android.app.WallpaperInfo -import android.view.View -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.flowOf +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.Kosmos.Fixture -/** Fake implementation of the wallpaper repository. */ -class FakeWallpaperRepository : WallpaperRepository { - override val wallpaperInfo = MutableStateFlow<WallpaperInfo?>(null) - override val wallpaperSupportsAmbientMode = flowOf(false) - override var rootView: View? = null - override val shouldSendFocalArea = MutableStateFlow(false) -} +val Kosmos.wallpaperFocalAreaRepository by Fixture { fakeWallpaperFocalAreaRepository } +val Kosmos.fakeWallpaperFocalAreaRepository by Fixture { FakeWallpaperFocalAreaRepository() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/wallpapers/data/repository/WallpaperRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/wallpapers/data/repository/WallpaperRepositoryKosmos.kt index f0c0d30e6db4..6b955ffb0b68 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/wallpapers/data/repository/WallpaperRepositoryKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/wallpapers/data/repository/WallpaperRepositoryKosmos.kt @@ -16,24 +16,9 @@ package com.android.systemui.wallpapers.data.repository -import android.content.applicationContext -import com.android.app.wallpaperManager -import com.android.systemui.broadcast.broadcastDispatcher -import com.android.systemui.keyguard.data.repository.keyguardRepository import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.Kosmos.Fixture -import com.android.systemui.kosmos.testDispatcher -import com.android.systemui.kosmos.testScope -import com.android.systemui.user.data.repository.userRepository -val Kosmos.wallpaperRepository by Fixture { - WallpaperRepositoryImpl( - context = applicationContext, - scope = testScope.backgroundScope, - bgDispatcher = testDispatcher, - broadcastDispatcher = broadcastDispatcher, - userRepository = userRepository, - keyguardRepository = keyguardRepository, - wallpaperManager = wallpaperManager, - ) -} +var Kosmos.fakeWallpaperRepository by Kosmos.Fixture { FakeWallpaperRepository() } + +var Kosmos.wallpaperRepository: WallpaperRepository by Kosmos.Fixture { fakeWallpaperRepository } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/WallpaperFocalAreaInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/wallpapers/domain/interactor/WallpaperFocalAreaInteractor.kt index 8fd6f62b315f..88eb5511160b 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/WallpaperFocalAreaInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/wallpapers/domain/interactor/WallpaperFocalAreaInteractor.kt @@ -14,15 +14,14 @@ * limitations under the License. */ -package com.android.systemui.keyguard.domain.interactor +package com.android.systemui.wallpapers.domain.interactor import android.content.applicationContext -import com.android.systemui.keyguard.data.repository.keyguardClockRepository -import com.android.systemui.keyguard.data.repository.keyguardRepository import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.applicationCoroutineScope import com.android.systemui.shade.data.repository.shadeRepository import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor +import com.android.systemui.wallpapers.data.repository.wallpaperFocalAreaRepository import com.android.systemui.wallpapers.data.repository.wallpaperRepository val Kosmos.wallpaperFocalAreaInteractor by @@ -30,10 +29,9 @@ val Kosmos.wallpaperFocalAreaInteractor by WallpaperFocalAreaInteractor( applicationScope = applicationCoroutineScope, context = applicationContext, - keyguardRepository = keyguardRepository, + wallpaperFocalAreaRepository = wallpaperFocalAreaRepository, shadeRepository = shadeRepository, activeNotificationsInteractor = activeNotificationsInteractor, - keyguardClockRepository = keyguardClockRepository, wallpaperRepository = wallpaperRepository, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/wallpapers/ui/viewmodel/WallpaperFocalAreaViewModel.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/wallpapers/ui/viewmodel/WallpaperFocalAreaViewModel.kt new file mode 100644 index 000000000000..7e232c526732 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/wallpapers/ui/viewmodel/WallpaperFocalAreaViewModel.kt @@ -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. + */ + +package com.android.systemui.wallpapers.ui.viewmodel + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.wallpapers.domain.interactor.wallpaperFocalAreaInteractor + +var Kosmos.wallpaperFocalAreaViewModel by + Kosmos.Fixture { + WallpaperFocalAreaViewModel(wallpaperFocalAreaInteractor = wallpaperFocalAreaInteractor) + } |