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

Commit d36f4fb6 authored by Lucas Dupin's avatar Lucas Dupin Committed by Android (Google) Code Review
Browse files

Merge "Implement autofill and activity launch" into main

parents 2b4b6d5b 0ccd8d11
Loading
Loading
Loading
Loading
+77 −0
Original line number Diff line number Diff line
@@ -17,22 +17,32 @@
package com.android.systemui.ambientcue.data.repository

import android.app.ActivityManager.RunningTaskInfo
import android.app.assist.ActivityId
import android.app.smartspace.SmartspaceAction
import android.app.smartspace.SmartspaceManager
import android.app.smartspace.SmartspaceSession
import android.app.smartspace.SmartspaceSession.OnTargetsAvailableListener
import android.app.smartspace.SmartspaceTarget
import android.content.Intent
import android.content.testableContext
import android.os.Binder
import android.os.Bundle
import android.view.autofill.AutofillId
import android.view.autofill.AutofillManager
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.ambientcue.data.repository.AmbientCueRepositoryImpl.Companion.AMBIENT_CUE_SURFACE
import com.android.systemui.ambientcue.data.repository.AmbientCueRepositoryImpl.Companion.EXTRA_ACTIVITY_ID
import com.android.systemui.ambientcue.data.repository.AmbientCueRepositoryImpl.Companion.EXTRA_AUTOFILL_ID
import com.android.systemui.ambientcue.shared.model.ActionModel
import com.android.systemui.concurrency.fakeExecutor
import com.android.systemui.kosmos.advanceUntilIdle
import com.android.systemui.kosmos.backgroundScope
import com.android.systemui.kosmos.collectLastValue
import com.android.systemui.kosmos.runCurrent
import com.android.systemui.kosmos.runTest
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.shade.data.repository.fakeFocusedDisplayRepository
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
@@ -49,6 +59,8 @@ import org.mockito.kotlin.verify
class AmbientCueRepositoryTest : SysuiTestCase() {
    private val kosmos = testKosmos()
    private val smartSpaceSession = mock<SmartspaceSession>()
    private val autofillManager = mock<AutofillManager>()
    private val activityStarter = mock<ActivityStarter>()
    private val smartSpaceManager =
        mock<SmartspaceManager>() {
            on { createSmartspaceSession(any()) } doReturn smartSpaceSession
@@ -58,6 +70,8 @@ class AmbientCueRepositoryTest : SysuiTestCase() {
        AmbientCueRepositoryImpl(
            backgroundScope = kosmos.backgroundScope,
            smartSpaceManager = smartSpaceManager,
            autofillManager = autofillManager,
            activityStarter = activityStarter,
            executor = kosmos.fakeExecutor,
            applicationContext = kosmos.testableContext,
            focusdDisplayRepository = kosmos.fakeFocusedDisplayRepository,
@@ -124,6 +138,37 @@ class AmbientCueRepositoryTest : SysuiTestCase() {
            assertThat(globallyFocusedTaskId).isEqualTo(TASK_ID)
        }

    @Test
    fun action_performAutofill() =
        kosmos.runTest {
            val actions by collectLastValue(underTest.actions)
            runCurrent()
            verify(smartSpaceSession)
                .addOnTargetsAvailableListener(any(), onTargetsAvailableListenerCaptor.capture())
            onTargetsAvailableListenerCaptor.firstValue.onTargetsAvailable(listOf(autofillTarget))

            val action: ActionModel = actions!!.first()
            action.onPerformAction()
            runCurrent()
            verify(autofillManager)
                .autofillRemoteApp(autofillId, action.label, activityId.token!!, activityId.taskId)
        }

    @Test
    fun action_performStartActivity() =
        kosmos.runTest {
            val actions by collectLastValue(underTest.actions)
            runCurrent()
            verify(smartSpaceSession)
                .addOnTargetsAvailableListener(any(), onTargetsAvailableListenerCaptor.capture())
            onTargetsAvailableListenerCaptor.firstValue.onTargetsAvailable(listOf(intentTarget))

            val action: ActionModel = actions!!.first()
            action.onPerformAction()
            runCurrent()
            verify(activityStarter).startActivity(launchIntent, false)
        }

    companion object {

        private const val TITLE_1 = "title 1"
@@ -144,6 +189,38 @@ class AmbientCueRepositoryTest : SysuiTestCase() {
                    )
            }

        private val autofillId = AutofillId(2)
        private val activityId = ActivityId(1, Binder())
        private val autofillTarget =
            mock<SmartspaceTarget> {
                on { smartspaceTargetId } doReturn AMBIENT_CUE_SURFACE
                on { actionChips } doReturn
                    listOf(
                        SmartspaceAction.Builder("action1-id", "title 1")
                            .setSubtitle("subtitle 1")
                            .setExtras(
                                Bundle().apply {
                                    putParcelable(EXTRA_ACTIVITY_ID, activityId)
                                    putParcelable(EXTRA_AUTOFILL_ID, autofillId)
                                }
                            )
                            .build()
                    )
            }

        private val launchIntent = Intent()
        private val intentTarget =
            mock<SmartspaceTarget> {
                on { smartspaceTargetId } doReturn AMBIENT_CUE_SURFACE
                on { actionChips } doReturn
                    listOf(
                        SmartspaceAction.Builder("action1-id", "title 1")
                            .setSubtitle("subtitle 1")
                            .setIntent(launchIntent)
                            .build()
                    )
            }

        private val invalidTarget1 =
            mock<SmartspaceTarget> {
                on { smartspaceTargetId } doReturn "home"
+1 −2
Original line number Diff line number Diff line
@@ -16,7 +16,6 @@

package com.android.systemui.ambientcue.domain.interactor

import android.content.Intent
import android.content.applicationContext
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
@@ -67,7 +66,7 @@ class AmbientCueInteractorTest : SysuiTestCase() {
                            ),
                        label = "Sunday Morning",
                        attribution = null,
                        intent = Intent(),
                        onPerformAction = {},
                    )
                )
            ambientCueRepository.fake.setActions(testActions)
+34 −0
Original line number Diff line number Diff line
@@ -16,15 +16,20 @@

package com.android.systemui.ambientcue.ui.viewmodel

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.ambientcue.data.repository.ambientCueRepository
import com.android.systemui.ambientcue.data.repository.fake
import com.android.systemui.ambientcue.domain.interactor.ambientCueInteractor
import com.android.systemui.ambientcue.shared.model.ActionModel
import com.android.systemui.kosmos.advanceTimeBy
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.res.R
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import org.junit.Before
@@ -68,4 +73,33 @@ class AmbientCueViewModelTest : SysuiTestCase() {
            runCurrent()
            assertThat(viewModel.isVisible).isTrue()
        }

    @Test
    fun onClick_collapses() =
        kosmos.runTest {
            val testActions =
                listOf(
                    ActionModel(
                        icon =
                            applicationContext.resources.getDrawable(
                                R.drawable.ic_content_paste_spark,
                                applicationContext.theme,
                            ),
                        label = "Sunday Morning",
                        attribution = null,
                        onPerformAction = {},
                    )
                )
            ambientCueRepository.fake.setActions(testActions)
            ambientCueInteractor.setIsVisible(true)
            viewModel.expand()
            runCurrent()

            assertThat(viewModel.isExpanded).isTrue()
            val action: ActionViewModel = viewModel.actions.first()

            // UI Collapses upon clicking on an action
            action.onClick()
            assertThat(viewModel.isExpanded).isFalse()
        }
}
+36 −2
Original line number Diff line number Diff line
@@ -17,17 +17,21 @@
package com.android.systemui.ambientcue.data.repository

import android.app.ActivityTaskManager
import android.app.assist.ActivityId
import android.app.smartspace.SmartspaceConfig
import android.app.smartspace.SmartspaceManager
import android.app.smartspace.SmartspaceSession.OnTargetsAvailableListener
import android.content.Context
import android.util.Log
import android.view.autofill.AutofillId
import android.view.autofill.AutofillManager
import androidx.annotation.VisibleForTesting
import com.android.systemui.ambientcue.shared.model.ActionModel
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.display.data.repository.FocusedDisplayRepository
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.res.R
import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
import java.util.concurrent.Executor
@@ -63,6 +67,8 @@ class AmbientCueRepositoryImpl
constructor(
    @Background private val backgroundScope: CoroutineScope,
    private val smartSpaceManager: SmartspaceManager?,
    private val autofillManager: AutofillManager?,
    private val activityStarter: ActivityStarter,
    @Background executor: Executor,
    @Application applicationContext: Context,
    focusdDisplayRepository: FocusedDisplayRepository,
@@ -86,15 +92,41 @@ constructor(
                            .filter { it.smartspaceTargetId == AMBIENT_CUE_SURFACE }
                            .flatMap { target -> target.actionChips }
                            .map { chip ->
                                val title = chip.title.toString()
                                ActionModel(
                                    icon =
                                        chip.icon?.loadDrawable(applicationContext)
                                            ?: applicationContext.getDrawable(
                                                R.drawable.ic_content_paste_spark
                                            )!!,
                                    intent = chip.intent,
                                    label = chip.title.toString(),
                                    label = title,
                                    attribution = chip.subtitle.toString(),
                                    onPerformAction = {
                                        val intent = chip.intent
                                        val activityId =
                                            chip.extras?.getParcelable<ActivityId>(
                                                EXTRA_ACTIVITY_ID
                                            )
                                        val autofillId =
                                            chip.extras?.getParcelable<AutofillId>(
                                                EXTRA_AUTOFILL_ID
                                            )
                                        val token = activityId?.token
                                        Log.v(
                                            TAG,
                                            "Performing action: $activityId, $autofillId, $intent",
                                        )
                                        if (token != null && autofillId != null) {
                                            autofillManager?.autofillRemoteApp(
                                                autofillId,
                                                title,
                                                token,
                                                activityId.taskId,
                                            )
                                        } else if (intent != null) {
                                            activityStarter.startActivity(intent, false)
                                        }
                                    },
                                )
                            }
                    if (DEBUG) {
@@ -133,6 +165,8 @@ constructor(
    companion object {
        // Surface that PCC wants to push cards into
        @VisibleForTesting const val AMBIENT_CUE_SURFACE = "ambientcue"
        @VisibleForTesting const val EXTRA_ACTIVITY_ID = "activityId"
        @VisibleForTesting const val EXTRA_AUTOFILL_ID = "autofillId"
        // Timeout to hide cuebar if it wasn't interacted with
        private const val TAG = "AmbientCueRepository"
        private const val DEBUG = false
+1 −2
Original line number Diff line number Diff line
@@ -16,12 +16,11 @@

package com.android.systemui.ambientcue.shared.model

import android.content.Intent
import android.graphics.drawable.Drawable

data class ActionModel(
    val icon: Drawable,
    val label: String,
    val attribution: String?,
    val intent: Intent?,
    val onPerformAction: () -> Unit,
)
Loading