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

Commit 1b60409c authored by Lucas Silva's avatar Lucas Silva
Browse files

Correctly handle activity starts from widgets in glanceable hub

Currently, widgets use the default starter logic which will just trigger
the activity underneath the keyguard if it is locked.

This change sets a custom interaction handler for widgets, which allows
us to customize how the intent is handled. This ensures we trigger the
bouncer if auth is required for the activity to launch.

This change intentionally doesn't tackle activity launch animations,
filed a follow-up bug to handle this.

Fixes: 317402992
Test: atest CommunalEditModeViewModelTest
Test: flashed device with changes and verified tapping stopwatch and
weather widgets correctly trigger the bouncer. Tapping widget which
launches a showWhenLocked activity does not trigger the bouncer.
Flag: ACONFIG com.android.systemui.communal_hub DEVELOPMENT

Change-Id: I3a9a2d57589088e356d46182f04f846cb19b346d
parent b8dad2fb
Loading
Loading
Loading
Loading
+14 −4
Original line number Diff line number Diff line
@@ -326,7 +326,7 @@ private fun CommunalContent(
    elevation: Dp = 0.dp,
) {
    when (model) {
        is CommunalContentModel.Widget -> WidgetContent(model, size, elevation, modifier)
        is CommunalContentModel.Widget -> WidgetContent(viewModel, model, size, elevation, modifier)
        is CommunalContentModel.WidgetPlaceholder -> WidgetPlaceholderContent(size)
        is CommunalContentModel.Smartspace -> SmartspaceContent(model, modifier)
        is CommunalContentModel.Tutorial -> TutorialContent(modifier)
@@ -347,6 +347,7 @@ fun WidgetPlaceholderContent(size: SizeF) {

@Composable
private fun WidgetContent(
    viewModel: BaseCommunalViewModel,
    model: CommunalContentModel.Widget,
    size: SizeF,
    elevation: Dp,
@@ -359,9 +360,18 @@ private fun WidgetContent(
        AndroidView(
            modifier = modifier,
            factory = { context ->
                // The AppWidgetHostView will inherit the interaction handler from the
                // AppWidgetHost. So set the interaction handler here before creating the view, and
                // then clear it after the view is created. This is a workaround due to the fact
                // that the interaction handler cannot be specified when creating the view,
                // and there are race conditions if it is set after the view is created.
                model.appWidgetHost.setInteractionHandler(viewModel.getInteractionHandler())
                val view =
                    model.appWidgetHost
                        .createView(context, model.appWidgetId, model.providerInfo)
                        .apply { updateAppWidgetSize(Bundle.EMPTY, listOf(size)) }
                model.appWidgetHost.setInteractionHandler(null)
                view
            },
            // For reusing composition in lazy lists.
            onReset = {},
+17 −4
Original line number Diff line number Diff line
@@ -33,14 +33,15 @@ import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
import com.android.systemui.communal.ui.viewmodel.CommunalEditModeViewModel
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.kosmos.testScope
import com.android.systemui.media.controls.ui.MediaHost
import com.android.systemui.shade.ShadeViewController
import com.android.systemui.smartspace.data.repository.FakeSmartspaceRepository
import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import javax.inject.Provider
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
@@ -56,7 +57,8 @@ class CommunalEditModeViewModelTest : SysuiTestCase() {
    @Mock private lateinit var shadeViewController: ShadeViewController
    @Mock private lateinit var powerManager: PowerManager

    private lateinit var testScope: TestScope
    private val kosmos = testKosmos()
    private val testScope = kosmos.testScope

    private lateinit var keyguardRepository: FakeKeyguardRepository
    private lateinit var communalRepository: FakeCommunalRepository
@@ -71,8 +73,6 @@ class CommunalEditModeViewModelTest : SysuiTestCase() {
    fun setUp() {
        MockitoAnnotations.initMocks(this)

        testScope = TestScope()

        val withDeps = CommunalInteractorFactory.create()
        keyguardRepository = withDeps.keyguardRepository
        communalRepository = withDeps.communalRepository
@@ -130,4 +130,17 @@ class CommunalEditModeViewModelTest : SysuiTestCase() {
            assertThat(communalContent?.get(1))
                .isInstanceOf(CommunalContentModel.Widget::class.java)
        }

    @Test
    fun interactionHandlerIgnoresClicks() {
        val interactionHandler = underTest.getInteractionHandler()
        assertThat(
                interactionHandler.onInteraction(
                    /* view = */ mock(),
                    /* pendingIntent = */ mock(),
                    /* response = */ mock()
                )
            )
            .isEqualTo(false)
    }
}
+2 −0
Original line number Diff line number Diff line
@@ -31,6 +31,7 @@ import com.android.systemui.communal.domain.interactor.CommunalInteractorFactory
import com.android.systemui.communal.domain.model.CommunalContentModel
import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
import com.android.systemui.communal.widgets.WidgetInteractionHandler
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.media.controls.ui.MediaHost
@@ -84,6 +85,7 @@ class CommunalViewModelTest : SysuiTestCase() {
        underTest =
            CommunalViewModel(
                withDeps.communalInteractor,
                WidgetInteractionHandler(mock()),
                withDeps.tutorialInteractor,
                Provider { shadeViewController },
                powerManager,
+4 −0
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import android.content.ComponentName
import android.os.PowerManager
import android.os.SystemClock
import android.view.MotionEvent
import android.widget.RemoteViews
import com.android.systemui.communal.domain.interactor.CommunalInteractor
import com.android.systemui.communal.domain.model.CommunalContentModel
import com.android.systemui.communal.shared.model.CommunalSceneKey
@@ -101,4 +102,7 @@ abstract class BaseCommunalViewModel(

    /** Called as the UI requests opening the widget editor. */
    open fun onOpenWidgetEditor() {}

    /** Gets the interaction handler used to handle taps on a remote view */
    abstract fun getInteractionHandler(): RemoteViews.InteractionHandler
}
+6 −0
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.systemui.communal.ui.viewmodel

import android.os.PowerManager
import android.widget.RemoteViews
import com.android.systemui.communal.domain.interactor.CommunalInteractor
import com.android.systemui.communal.domain.model.CommunalContentModel
import com.android.systemui.dagger.SysUISingleton
@@ -49,4 +50,9 @@ constructor(

    override fun onReorderWidgets(widgetIdToPriorityMap: Map<Int, Int>) =
        communalInteractor.updateWidgetOrder(widgetIdToPriorityMap)

    override fun getInteractionHandler(): RemoteViews.InteractionHandler {
        // Ignore all interactions in edit mode.
        return RemoteViews.InteractionHandler { _, _, _ -> false }
    }
}
Loading