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

Commit 142c8f55 authored by Helen Cheuk's avatar Helen Cheuk Committed by Android (Google) Code Review
Browse files

Merge "[Contextual Edu] Launch tutorial for specific gesture in education notification" into main

parents 3fa3f616 ac0894d0
Loading
Loading
Loading
Loading
+67 −15
Original line number Diff line number Diff line
@@ -20,18 +20,20 @@ import android.app.Dialog
import android.app.Notification
import android.app.NotificationManager
import android.content.applicationContext
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.contextualeducation.GestureType
import com.android.systemui.contextualeducation.GestureType.ALL_APPS
import com.android.systemui.contextualeducation.GestureType.BACK
import com.android.systemui.contextualeducation.GestureType.HOME
import com.android.systemui.contextualeducation.GestureType.OVERVIEW
import com.android.systemui.education.data.repository.fakeEduClock
import com.android.systemui.education.domain.interactor.KeyboardTouchpadEduInteractor
import com.android.systemui.education.domain.interactor.contextualEducationInteractor
import com.android.systemui.education.domain.interactor.keyboardTouchpadEduInteractor
import com.android.systemui.education.ui.view.ContextualEduUiCoordinator
import com.android.systemui.education.ui.viewmodel.ContextualEduViewModel
import com.android.systemui.inputdevice.tutorial.ui.view.KeyboardTouchpadTutorialActivity
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.kosmos.testScope
import com.android.systemui.res.R
@@ -56,11 +58,13 @@ import org.mockito.kotlin.any
import org.mockito.kotlin.mock
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
import platform.test.runner.parameterized.ParameterizedAndroidJunit4
import platform.test.runner.parameterized.Parameters

@SmallTest
@RunWith(AndroidJUnit4::class)
@RunWith(ParameterizedAndroidJunit4::class)
@OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
class ContextualEduUiCoordinatorTest : SysuiTestCase() {
class ContextualEduUiCoordinatorTest(private val gestureType: GestureType) : SysuiTestCase() {
    private val kosmos = testKosmos()
    private val testScope = kosmos.testScope
    private val interactor = kosmos.contextualEducationInteractor
@@ -112,23 +116,23 @@ class ContextualEduUiCoordinatorTest : SysuiTestCase() {
    @Test
    fun showDialogOnNewEdu() =
        testScope.runTest {
            triggerEducation(BACK)
            triggerEducation(gestureType)
            verify(dialog).show()
        }

    @Test
    fun showNotificationOn2ndEdu() =
        testScope.runTest {
            triggerEducation(BACK)
            triggerEducation(gestureType)
            eduClock.offset(minDurationForNextEdu)
            triggerEducation(BACK)
            triggerEducation(gestureType)
            verify(notificationManager).notifyAsUser(any(), anyInt(), any(), any())
        }

    @Test
    fun dismissDialogAfterTimeout() =
        testScope.runTest {
            triggerEducation(BACK)
            triggerEducation(gestureType)
            advanceTimeBy(timeoutMillis + 1)
            verify(dialog).dismiss()
        }
@@ -142,26 +146,59 @@ class ContextualEduUiCoordinatorTest : SysuiTestCase() {
        }

    @Test
    fun verifyBackEduToastContent() =
    fun verifyEduToastContent() =
        testScope.runTest {
            triggerEducation(BACK)
            assertThat(toastContent).isEqualTo(context.getString(R.string.back_edu_toast_content))
            triggerEducation(gestureType)

            val expectedContent =
                when (gestureType) {
                    BACK -> R.string.back_edu_toast_content
                    HOME -> R.string.home_edu_toast_content
                    OVERVIEW -> R.string.overview_edu_toast_content
                    ALL_APPS -> R.string.all_apps_edu_toast_content
                }

            assertThat(toastContent).isEqualTo(context.getString(expectedContent))
        }

    @Test
    fun verifyBackEduNotificationContent() =
    fun verifyEduNotificationContent() =
        testScope.runTest {
            val notificationCaptor = ArgumentCaptor.forClass(Notification::class.java)
            triggerEducation(BACK)
            triggerEducation(gestureType)

            eduClock.offset(minDurationForNextEdu)
            triggerEducation(BACK)
            triggerEducation(gestureType)

            verify(notificationManager)
                .notifyAsUser(any(), anyInt(), notificationCaptor.capture(), any())

            val expectedTitle =
                when (gestureType) {
                    BACK -> R.string.back_edu_notification_title
                    HOME -> R.string.home_edu_notification_title
                    OVERVIEW -> R.string.overview_edu_notification_title
                    ALL_APPS -> R.string.all_apps_edu_notification_title
                }

            val expectedContent =
                when (gestureType) {
                    BACK -> R.string.back_edu_notification_content
                    HOME -> R.string.home_edu_notification_content
                    OVERVIEW -> R.string.overview_edu_notification_content
                    ALL_APPS -> R.string.all_apps_edu_notification_content
                }

            val expectedTutorialClassName =
                when (gestureType) {
                    OVERVIEW -> TUTORIAL_ACTION
                    else -> KeyboardTouchpadTutorialActivity::class.qualifiedName
                }

            verifyNotificationContent(
                R.string.back_edu_notification_title,
                R.string.back_edu_notification_content,
                expectedTitle,
                expectedContent,
                expectedTutorialClassName,
                notificationCaptor.value,
            )
        }
@@ -169,6 +206,7 @@ class ContextualEduUiCoordinatorTest : SysuiTestCase() {
    private fun verifyNotificationContent(
        titleResId: Int,
        contentResId: Int,
        expectedTutorialClassName: String?,
        notification: Notification,
    ) {
        val expectedContent = context.getString(contentResId)
@@ -177,6 +215,10 @@ class ContextualEduUiCoordinatorTest : SysuiTestCase() {
        val actualTitle = notification.getString(Notification.EXTRA_TITLE)
        assertThat(actualContent).isEqualTo(expectedContent)
        assertThat(actualTitle).isEqualTo(expectedTitle)
        val actualTutorialClassName =
            notification.contentIntent.intent.component?.className
                ?: notification.contentIntent.intent.action
        assertThat(actualTutorialClassName).isEqualTo(expectedTutorialClassName)
    }

    private fun Notification.getString(key: String): String =
@@ -188,4 +230,14 @@ class ContextualEduUiCoordinatorTest : SysuiTestCase() {
        }
        runCurrent()
    }

    companion object {
        @JvmStatic
        @Parameters(name = "{0}")
        fun getGestureTypes(): List<GestureType> {
            return listOf(BACK, HOME, OVERVIEW, ALL_APPS)
        }

        private const val TUTORIAL_ACTION: String = "com.android.systemui.action.TOUCHPAD_TUTORIAL"
    }
}
+37 −10
Original line number Diff line number Diff line
@@ -29,6 +29,11 @@ import android.view.accessibility.AccessibilityManager
import androidx.core.app.NotificationCompat
import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.CoreStartable
import com.android.systemui.contextualeducation.GestureType
import com.android.systemui.contextualeducation.GestureType.ALL_APPS
import com.android.systemui.contextualeducation.GestureType.BACK
import com.android.systemui.contextualeducation.GestureType.HOME
import com.android.systemui.contextualeducation.GestureType.OVERVIEW
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.education.ui.viewmodel.ContextualEduNotificationViewModel
@@ -37,6 +42,10 @@ import com.android.systemui.education.ui.viewmodel.ContextualEduViewModel
import com.android.systemui.inputdevice.tutorial.ui.view.KeyboardTouchpadTutorialActivity
import com.android.systemui.inputdevice.tutorial.ui.view.KeyboardTouchpadTutorialActivity.Companion.INTENT_TUTORIAL_ENTRY_POINT_CONTEXTUAL_EDU
import com.android.systemui.inputdevice.tutorial.ui.view.KeyboardTouchpadTutorialActivity.Companion.INTENT_TUTORIAL_ENTRY_POINT_KEY
import com.android.systemui.inputdevice.tutorial.ui.view.KeyboardTouchpadTutorialActivity.Companion.INTENT_TUTORIAL_SCOPE_KEY
import com.android.systemui.inputdevice.tutorial.ui.view.KeyboardTouchpadTutorialActivity.Companion.INTENT_TUTORIAL_SCOPE_KEYBOARD
import com.android.systemui.inputdevice.tutorial.ui.view.KeyboardTouchpadTutorialActivity.Companion.INTENT_TUTORIAL_SCOPE_TOUCHPAD_BACK
import com.android.systemui.inputdevice.tutorial.ui.view.KeyboardTouchpadTutorialActivity.Companion.INTENT_TUTORIAL_SCOPE_TOUCHPAD_HOME
import com.android.systemui.res.R
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
@@ -59,6 +68,8 @@ constructor(
        private const val CHANNEL_ID = "ContextualEduNotificationChannel"
        private const val TAG = "ContextualEduUiCoordinator"
        private const val NOTIFICATION_ID = 1000
        private const val TUTORIAL_ACTION: String = "com.android.systemui.action.TOUCHPAD_TUTORIAL"
        private const val SYSTEMUI_PACKAGE_NAME: String = "com.android.systemui"
    }

    @Inject
@@ -125,7 +136,7 @@ constructor(
                .setSmallIcon(R.drawable.ic_settings)
                .setContentTitle(model.title)
                .setContentText(model.message)
                .setContentIntent(createPendingIntent())
                .setContentIntent(createPendingIntent(model.gestureType))
                .setPriority(NotificationCompat.PRIORITY_DEFAULT)
                .setAutoCancel(true)
                .addExtras(extras)
@@ -138,21 +149,37 @@ constructor(
        )
    }

    private fun createPendingIntent(): PendingIntent {
    private fun createPendingIntent(gestureType: GestureType): PendingIntent {
        val intent =
            Intent(context, KeyboardTouchpadTutorialActivity::class.java).apply {
                addCategory(Intent.CATEGORY_DEFAULT)
                flags = Intent.FLAG_ACTIVITY_NEW_TASK
                putExtra(
                    INTENT_TUTORIAL_ENTRY_POINT_KEY,
                    INTENT_TUTORIAL_ENTRY_POINT_CONTEXTUAL_EDU,
                )
            when (gestureType) {
                BACK -> createKeyboardTouchpadTutorialIntent(INTENT_TUTORIAL_SCOPE_TOUCHPAD_BACK)
                HOME -> createKeyboardTouchpadTutorialIntent(INTENT_TUTORIAL_SCOPE_TOUCHPAD_HOME)
                ALL_APPS -> createKeyboardTouchpadTutorialIntent(INTENT_TUTORIAL_SCOPE_KEYBOARD)
                OVERVIEW -> createTouchpadTutorialIntent()
            }

        return PendingIntent.getActivity(
            context,
            /* requestCode= */ 0,
            intent,
            PendingIntent.FLAG_IMMUTABLE,
            // FLAG_UPDATE_CURRENT to avoid caching of intent extras and always use latest values
            PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT,
        )
    }

    private fun createKeyboardTouchpadTutorialIntent(tutorialType: String): Intent {
        return Intent(context, KeyboardTouchpadTutorialActivity::class.java).apply {
            addCategory(Intent.CATEGORY_DEFAULT)
            flags = Intent.FLAG_ACTIVITY_NEW_TASK
            putExtra(INTENT_TUTORIAL_SCOPE_KEY, tutorialType)
            putExtra(INTENT_TUTORIAL_ENTRY_POINT_KEY, INTENT_TUTORIAL_ENTRY_POINT_CONTEXTUAL_EDU)
        }
    }

    private fun createTouchpadTutorialIntent(): Intent {
        return Intent(TUTORIAL_ACTION).apply {
            flags = Intent.FLAG_ACTIVITY_NEW_TASK
            setPackage(SYSTEMUI_PACKAGE_NAME)
        }
    }
}
+3 −0
Original line number Diff line number Diff line
@@ -16,11 +16,14 @@

package com.android.systemui.education.ui.viewmodel

import com.android.systemui.contextualeducation.GestureType

sealed class ContextualEduContentViewModel(open val userId: Int)

data class ContextualEduNotificationViewModel(
    val title: String,
    val message: String,
    val gestureType: GestureType,
    override val userId: Int,
) : ContextualEduContentViewModel(userId)

+1 −0
Original line number Diff line number Diff line
@@ -68,6 +68,7 @@ constructor(
                    ContextualEduNotificationViewModel(
                        getEduTitle(it),
                        getEduContent(it),
                        it.gestureType,
                        it.userId,
                    )
                } else {