Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 77789bdb authored by William Leshner's avatar William Leshner Committed by Android (Google) Code Review
Browse files

Merge "Add an education tooltip to "go to dream" button on hub." into main

parents 0f96a7e5 a9b4bc5b
Loading
Loading
Loading
Loading
+5 −12
Original line number Diff line number Diff line
@@ -156,14 +156,8 @@ constructor(

                val bottomAreaPlaceable = bottomAreaMeasurable.measure(noMinConstraints)

                val screensaverButtonSizeInt = screensaverButtonSize.roundToPx()
                val screensaverButtonPlaceable =
                    screensaverButtonMeasurable?.measure(
                        Constraints.fixed(
                            width = screensaverButtonSizeInt,
                            height = screensaverButtonSizeInt,
                        )
                    )
                    screensaverButtonMeasurable?.measure(noMinConstraints)

                val communalGridPlaceable =
                    communalGridMeasurable.measure(
@@ -181,12 +175,12 @@ constructor(
                    screensaverButtonPlaceable?.place(
                        x =
                            constraints.maxWidth -
                                screensaverButtonSizeInt -
                                screensaverButtonPaddingInt,
                                screensaverButtonPaddingInt -
                                screensaverButtonPlaceable.width,
                        y =
                            constraints.maxHeight -
                                screensaverButtonSizeInt -
                                screensaverButtonPaddingInt,
                                screensaverButtonPaddingInt -
                                screensaverButtonPlaceable.height,
                    )
                }
            }
@@ -194,7 +188,6 @@ constructor(
    }

    companion object {
        private val screensaverButtonSize: Dp = 64.dp
        private val screensaverButtonPadding: Dp = 24.dp

        // TODO(b/382739998): Remove these hardcoded values once lock icon size and bottom area
+126 −15
Original line number Diff line number Diff line
@@ -16,14 +16,37 @@

package com.android.systemui.communal.ui.compose.section

import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.shape.CornerSize
import androidx.compose.material3.IconButtonDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.CornerRadius
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.RoundRect
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.geometry.toRect
import androidx.compose.ui.graphics.Outline
import androidx.compose.ui.graphics.Path
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.res.stringResource
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
import com.android.compose.PlatformIconButton
import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
import com.android.systemui.communal.ui.compose.extensions.observeTaps
import com.android.systemui.communal.ui.viewmodel.CommunalToDreamButtonViewModel
import com.android.systemui.lifecycle.rememberViewModel
import com.android.systemui.res.R
@@ -43,18 +66,48 @@ constructor(

        val viewModel =
            rememberViewModel("CommunalToDreamButtonSection") { viewModelFactory.create() }
        val shouldShowDreamButtonOnHub by
            viewModel.shouldShowDreamButtonOnHub.collectAsStateWithLifecycle(false)

        if (!shouldShowDreamButtonOnHub) {
        if (!viewModel.shouldShowDreamButtonOnHub) {
            return
        }

        if (viewModel.shouldShowTooltip) {
            Column(
                modifier =
                    Modifier.widthIn(max = tooltipMaxWidth).pointerInput(Unit) {
                        observeTaps { viewModel.setDreamButtonTooltipDismissed() }
                    }
            ) {
                Tooltip(
                    pointerOffsetDp = buttonSize.div(2),
                    text = stringResource(R.string.glanceable_hub_to_dream_button_tooltip),
                )
                GoToDreamButton(
                    modifier = Modifier.width(buttonSize).height(buttonSize).align(Alignment.End)
                ) {
                    viewModel.onShowDreamButtonTap()
                }
            }
        } else {
            GoToDreamButton(modifier = Modifier.width(buttonSize).height(buttonSize)) {
                viewModel.onShowDreamButtonTap()
            }
        }
    }

    companion object {
        private val buttonSize = 64.dp
        private val tooltipMaxWidth = 350.dp
    }
}

@Composable
private fun GoToDreamButton(modifier: Modifier, onClick: () -> Unit) {
    PlatformIconButton(
            onClick = { viewModel.onShowDreamButtonTap() },
        modifier = modifier,
        onClick = onClick,
        iconResource = R.drawable.ic_screensaver_auto,
            contentDescription =
                stringResource(R.string.accessibility_glanceable_hub_to_dream_button),
        contentDescription = stringResource(R.string.accessibility_glanceable_hub_to_dream_button),
        colors =
            IconButtonDefaults.filledIconButtonColors(
                contentColor = MaterialTheme.colorScheme.onPrimaryContainer,
@@ -62,4 +115,62 @@ constructor(
            ),
    )
}

@Composable
private fun Tooltip(pointerOffsetDp: Dp, text: String) {
    Surface(
        color = MaterialTheme.colorScheme.surface,
        shape = TooltipShape(pointerSizeDp = 12.dp, pointerOffsetDp = pointerOffsetDp),
    ) {
        Text(
            modifier = Modifier.padding(start = 32.dp, top = 16.dp, end = 32.dp, bottom = 32.dp),
            color = MaterialTheme.colorScheme.onSurface,
            text = text,
        )
    }

    Spacer(modifier = Modifier.height(4.dp))
}

private class TooltipShape(private val pointerSizeDp: Dp, private val pointerOffsetDp: Dp) : Shape {

    override fun createOutline(
        size: Size,
        layoutDirection: LayoutDirection,
        density: Density,
    ): Outline {

        val pointerSizePx = with(density) { pointerSizeDp.toPx() }
        val pointerOffsetPx = with(density) { pointerOffsetDp.toPx() }
        val cornerRadius = CornerRadius(CornerSize(16.dp).toPx(size, density))
        val bubbleSize = size.copy(height = size.height - pointerSizePx)

        val path =
            Path().apply {
                addRoundRect(
                    RoundRect(
                        rect = bubbleSize.toRect(),
                        topLeft = cornerRadius,
                        topRight = cornerRadius,
                        bottomRight = cornerRadius,
                        bottomLeft = cornerRadius,
                    )
                )
                addPath(
                    Path().apply {
                        moveTo(0f, 0f)
                        lineTo(pointerSizePx / 2f, pointerSizePx)
                        lineTo(pointerSizePx, 0f)
                        close()
                    },
                    offset =
                        Offset(
                            x = bubbleSize.width - pointerOffsetPx - pointerSizePx / 2f,
                            y = bubbleSize.height,
                        ),
                )
            }

        return Outline.Generic(path)
    }
}
+28 −0
Original line number Diff line number Diff line
@@ -115,6 +115,34 @@ class CommunalPrefsRepositoryImplTest : SysuiTestCase() {
            assertThat(isHubOnboardingDismissed).isFalse()
        }

    @Test
    fun isDreamButtonTooltipDismissedValue_byDefault_isFalse() =
        testScope.runTest {
            val isDreamButtonTooltipDismissed by
                collectLastValue(underTest.isDreamButtonTooltipDismissed(MAIN_USER))
            assertThat(isDreamButtonTooltipDismissed).isFalse()
        }

    @Test
    fun isDreamButtonTooltipDismissedValue_onSet_isTrue() =
        testScope.runTest {
            val isDreamButtonTooltipDismissed by
                collectLastValue(underTest.isDreamButtonTooltipDismissed(MAIN_USER))

            underTest.setDreamButtonTooltipDismissed(MAIN_USER)
            assertThat(isDreamButtonTooltipDismissed).isTrue()
        }

    @Test
    fun isDreamButtonTooltipDismissedValue_onSetForDifferentUser_isStillFalse() =
        testScope.runTest {
            val isDreamButtonTooltipDismissed by
                collectLastValue(underTest.isDreamButtonTooltipDismissed(MAIN_USER))

            underTest.setDreamButtonTooltipDismissed(SECONDARY_USER)
            assertThat(isDreamButtonTooltipDismissed).isFalse()
        }

    @Test
    fun getSharedPreferences_whenFileRestored() =
        testScope.runTest {
+37 −0
Original line number Diff line number Diff line
@@ -108,6 +108,43 @@ class CommunalPrefsInteractorTest : SysuiTestCase() {
            assertThat(isHubOnboardingDismissed).isFalse()
        }

    @Test
    fun setDreamButtonTooltipDismissed_currentUser() =
        testScope.runTest {
            setSelectedUser(MAIN_USER)
            val isDreamButtonTooltipDismissed by
                collectLastValue(underTest.isDreamButtonTooltipDismissed)

            assertThat(isDreamButtonTooltipDismissed).isFalse()
            underTest.setDreamButtonTooltipDismissed(MAIN_USER)
            assertThat(isDreamButtonTooltipDismissed).isTrue()
        }

    @Test
    fun setDreamButtonTooltipDismissed_anotherUser() =
        testScope.runTest {
            setSelectedUser(MAIN_USER)
            val isDreamButtonTooltipDismissed by
                collectLastValue(underTest.isDreamButtonTooltipDismissed)

            assertThat(isDreamButtonTooltipDismissed).isFalse()
            underTest.setDreamButtonTooltipDismissed(SECONDARY_USER)
            assertThat(isDreamButtonTooltipDismissed).isFalse()
        }

    @Test
    fun isDreamButtonTooltipDismissed_userSwitch() =
        testScope.runTest {
            setSelectedUser(MAIN_USER)
            underTest.setDreamButtonTooltipDismissed(MAIN_USER)
            val isDreamButtonTooltipDismissed by
                collectLastValue(underTest.isDreamButtonTooltipDismissed)

            assertThat(isDreamButtonTooltipDismissed).isTrue()
            setSelectedUser(SECONDARY_USER)
            assertThat(isDreamButtonTooltipDismissed).isFalse()
        }

    private suspend fun setSelectedUser(user: UserInfo) {
        with(kosmos.fakeUserRepository) {
            setUserInfos(listOf(user))
+33 −6
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.systemui.communal.ui.viewmodel

import android.content.pm.UserInfo
import android.platform.test.annotations.EnableFlags
import android.provider.Settings
import android.service.dream.dreamManager
@@ -24,15 +25,17 @@ import androidx.test.filters.SmallTest
import com.android.internal.logging.uiEventLoggerFake
import com.android.systemui.Flags.FLAG_GLANCEABLE_HUB_V2
import com.android.systemui.SysuiTestCase
import com.android.systemui.communal.data.repository.fakeCommunalPrefsRepository
import com.android.systemui.communal.domain.interactor.HubOnboardingInteractorTest.Companion.MAIN_USER
import com.android.systemui.communal.shared.log.CommunalUiEvent
import com.android.systemui.flags.Flags.COMMUNAL_SERVICE_ENABLED
import com.android.systemui.flags.fakeFeatureFlagsClassic
import com.android.systemui.kosmos.collectLastValue
import com.android.systemui.kosmos.runCurrent
import com.android.systemui.kosmos.runTest
import com.android.systemui.kosmos.testScope
import com.android.systemui.lifecycle.activateIn
import com.android.systemui.plugins.activityStarter
import com.android.systemui.settings.fakeUserTracker
import com.android.systemui.statusbar.policy.batteryController
import com.android.systemui.testKosmos
import com.android.systemui.user.data.repository.fakeUserRepository
@@ -52,7 +55,6 @@ import org.mockito.kotlin.whenever
class CommunalToDreamButtonViewModelTest : SysuiTestCase() {
    private val kosmos = testKosmos()
    private val testScope = kosmos.testScope
    private val uiEventLoggerFake = kosmos.uiEventLoggerFake
    private val underTest: CommunalToDreamButtonViewModel by lazy {
        kosmos.communalToDreamButtonViewModel
    }
@@ -68,9 +70,9 @@ class CommunalToDreamButtonViewModelTest : SysuiTestCase() {
        with(kosmos) {
            runTest {
                whenever(batteryController.isPluggedIn()).thenReturn(true)
                runCurrent()

                val shouldShowButton by collectLastValue(underTest.shouldShowDreamButtonOnHub)
                assertThat(shouldShowButton).isTrue()
                assertThat(underTest.shouldShowDreamButtonOnHub).isTrue()
            }
        }

@@ -79,9 +81,9 @@ class CommunalToDreamButtonViewModelTest : SysuiTestCase() {
        with(kosmos) {
            runTest {
                whenever(batteryController.isPluggedIn()).thenReturn(false)
                runCurrent()

                val shouldShowButton by collectLastValue(underTest.shouldShowDreamButtonOnHub)
                assertThat(shouldShowButton).isFalse()
                assertThat(underTest.shouldShowDreamButtonOnHub).isFalse()
            }
        }

@@ -123,6 +125,23 @@ class CommunalToDreamButtonViewModelTest : SysuiTestCase() {
            }
        }

    @Test
    fun shouldShowDreamButtonTooltip_trueWhenNotDismissed() =
        kosmos.runTest {
            runCurrent()
            assertThat(underTest.shouldShowTooltip).isTrue()
        }

    @Test
    fun shouldShowDreamButtonTooltip_falseWhenDismissed() =
        kosmos.runTest {
            setSelectedUser(MAIN_USER)
            fakeCommunalPrefsRepository.setDreamButtonTooltipDismissed(MAIN_USER)
            runCurrent()

            assertThat(underTest.shouldShowTooltip).isFalse()
        }

    @Test
    fun onShowDreamButtonTap_eventLogged() =
        with(kosmos) {
@@ -134,4 +153,12 @@ class CommunalToDreamButtonViewModelTest : SysuiTestCase() {
                    .isEqualTo(CommunalUiEvent.COMMUNAL_HUB_SHOW_DREAM_BUTTON_TAP.id)
            }
        }

    private suspend fun setSelectedUser(user: UserInfo) {
        with(kosmos.fakeUserRepository) {
            setUserInfos(listOf(user))
            setSelectedUserInfo(user)
        }
        kosmos.fakeUserTracker.set(userInfos = listOf(user), selectedUserIndex = 0)
    }
}
Loading