Fix live timer activity launches
We add a new method to ActivityStarter to bypass the normal
showWhenLocked checks we do when starting an activity. This is due to
the fact that timers use a trampoline activity which is not
showWhenLocked, which then triggers a showWhenLocked activity. We
therefore do not want to trigger auth for these activities.
As this change is no longer a breaking change, the flag guarding this is
also removed.
Fixes: 345741071
Test: atest WidgetInteractionHandlerTest
Test: atest SmartspaceInteractionHandlerTest
Flag: com.android.systemui.communal_hub
Change-Id: I28dc5169642df8672578d28f9e3c47840277a32c
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index c61f996..1cbf67e 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -1048,15 +1048,6 @@
bug: "343505271"
}
-flag {
- name: "glanceable_hub_animate_timer_activity_starts"
- namespace: "systemui"
- description: "Properly animates activity starts from live timers on the glanceable hub"
- bug: "345741071"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
flag {
name: "new_touchpad_gestures_tutorial"
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt
index b353b5a..18085ab 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt
@@ -25,9 +25,9 @@
import androidx.compose.ui.unit.IntRect
import com.android.compose.animation.scene.SceneScope
import com.android.compose.theme.LocalAndroidColorScheme
+import com.android.systemui.communal.smartspace.SmartspaceInteractionHandler
import com.android.systemui.communal.ui.compose.section.AmbientStatusBarSection
import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
-import com.android.systemui.communal.widgets.WidgetInteractionHandler
import com.android.systemui.keyguard.ui.composable.blueprint.BlueprintAlignmentLines
import com.android.systemui.keyguard.ui.composable.section.LockSection
import com.android.systemui.statusbar.phone.SystemUIDialogFactory
@@ -38,7 +38,7 @@
@Inject
constructor(
private val viewModel: CommunalViewModel,
- private val interactionHandler: WidgetInteractionHandler,
+ private val interactionHandler: SmartspaceInteractionHandler,
private val dialogFactory: SystemUIDialogFactory,
private val lockSection: LockSection,
private val ambientStatusBarSection: AmbientStatusBarSection,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
index eccb072..67dd99d 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
@@ -132,7 +132,6 @@
import com.android.compose.theme.LocalAndroidColorScheme
import com.android.compose.ui.graphics.painter.rememberDrawablePainter
import com.android.internal.R.dimen.system_app_widget_background_radius
-import com.android.systemui.Flags.glanceableHubAnimateTimerActivityStarts
import com.android.systemui.communal.domain.model.CommunalContentModel
import com.android.systemui.communal.shared.model.CommunalContentSize
import com.android.systemui.communal.shared.model.CommunalScenes
@@ -1107,9 +1106,7 @@
modifier = modifier,
factory = { context ->
SmartspaceAppWidgetHostView(context).apply {
- if (glanceableHubAnimateTimerActivityStarts()) {
- interactionHandler?.let { setInteractionHandler(it) }
- }
+ interactionHandler?.let { setInteractionHandler(it) }
updateAppWidget(model.remoteViews)
}
},
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/smartspace/SmartspaceInteractionHandlerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/smartspace/SmartspaceInteractionHandlerTest.kt
new file mode 100644
index 0000000..9e24ef8
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/smartspace/SmartspaceInteractionHandlerTest.kt
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2024 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.communal.smartspace
+
+import android.app.PendingIntent
+import android.content.Intent
+import android.view.View
+import android.widget.FrameLayout
+import android.widget.RemoteViews.RemoteResponse
+import androidx.core.util.component1
+import androidx.core.util.component2
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.communal.widgets.SmartspaceAppWidgetHostView
+import com.android.systemui.plugins.ActivityStarter
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.isNull
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.notNull
+import org.mockito.kotlin.refEq
+import org.mockito.kotlin.verify
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class SmartspaceInteractionHandlerTest : SysuiTestCase() {
+ private val activityStarter = mock<ActivityStarter>()
+
+ private val testIntent =
+ PendingIntent.getActivity(
+ context,
+ /* requestCode = */ 0,
+ Intent("action"),
+ PendingIntent.FLAG_IMMUTABLE
+ )
+ private val testResponse = RemoteResponse.fromPendingIntent(testIntent)
+
+ private val underTest: SmartspaceInteractionHandler by lazy {
+ SmartspaceInteractionHandler(activityStarter)
+ }
+
+ @Test
+ fun launchAnimatorIsUsedForSmartspaceView() {
+ val parent = FrameLayout(context)
+ val view = SmartspaceAppWidgetHostView(context)
+ parent.addView(view)
+ val (fillInIntent, activityOptions) = testResponse.getLaunchOptions(view)
+
+ underTest.onInteraction(view, testIntent, testResponse)
+
+ verify(activityStarter)
+ .startPendingIntentWithoutDismissing(
+ eq(testIntent),
+ eq(false),
+ isNull(),
+ notNull(),
+ refEq(fillInIntent),
+ refEq(activityOptions.toBundle()),
+ )
+ }
+
+ @Test
+ fun launchAnimatorIsNotUsedForRegularView() {
+ val parent = FrameLayout(context)
+ val view = View(context)
+ parent.addView(view)
+ val (fillInIntent, activityOptions) = testResponse.getLaunchOptions(view)
+
+ underTest.onInteraction(view, testIntent, testResponse)
+
+ verify(activityStarter)
+ .startPendingIntentWithoutDismissing(
+ eq(testIntent),
+ eq(false),
+ isNull(),
+ isNull(),
+ refEq(fillInIntent),
+ refEq(activityOptions.toBundle()),
+ )
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/WidgetInteractionHandlerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/WidgetInteractionHandlerTest.kt
index df7b291..bf6a2b0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/WidgetInteractionHandlerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/WidgetInteractionHandlerTest.kt
@@ -75,26 +75,6 @@
}
@Test
- fun launchAnimatorIsUsedForSmartspaceView() {
- val parent = FrameLayout(context)
- val view = SmartspaceAppWidgetHostView(context)
- parent.addView(view)
- val (fillInIntent, activityOptions) = testResponse.getLaunchOptions(view)
-
- underTest.onInteraction(view, testIntent, testResponse)
-
- verify(activityStarter)
- .startPendingIntentMaybeDismissingKeyguard(
- eq(testIntent),
- eq(false),
- isNull(),
- notNull(),
- refEq(fillInIntent),
- refEq(activityOptions.toBundle()),
- )
- }
-
- @Test
fun launchAnimatorIsNotUsedForRegularView() {
val parent = FrameLayout(context)
val view = View(context)
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java
index de65931..7cf56aa 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java
@@ -58,6 +58,19 @@
@Nullable ActivityTransitionAnimator.Controller animationController);
/**
+ * Similar to {@link #startPendingIntentMaybeDismissingKeyguard(PendingIntent, Runnable,
+ * ActivityTransitionAnimator.Controller)} but will always not dismiss the keyguard when
+ * launching activities. This should be avoided and other alternatives should be used.
+ */
+ void startPendingIntentWithoutDismissing(
+ PendingIntent intent,
+ boolean dismissShade,
+ Runnable intentSentUiThreadCallback,
+ @Nullable ActivityTransitionAnimator.Controller animationController,
+ @Nullable Intent fillInIntent,
+ @Nullable Bundle extraOptions);
+
+ /**
* Similar to {@link #startPendingIntentDismissingKeyguard}, except that it supports launching
* activities on top of the keyguard. If the activity supports {@code showOverLockscreen}, it
* will show over keyguard without first dimissing it. If it doesn't support it, calling this
diff --git a/packages/SystemUI/src/com/android/systemui/communal/smartspace/SmartspaceInteractionHandler.kt b/packages/SystemUI/src/com/android/systemui/communal/smartspace/SmartspaceInteractionHandler.kt
new file mode 100644
index 0000000..a88b777
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/smartspace/SmartspaceInteractionHandler.kt
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2024 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.communal.smartspace
+
+import android.app.ActivityOptions
+import android.app.PendingIntent
+import android.content.Intent
+import android.view.View
+import android.widget.RemoteViews
+import com.android.systemui.animation.ActivityTransitionAnimator
+import com.android.systemui.communal.util.InteractionHandlerDelegate
+import com.android.systemui.communal.widgets.SmartspaceAppWidgetHostView
+import com.android.systemui.plugins.ActivityStarter
+import javax.inject.Inject
+
+/**
+ * Handles interactions on smartspace elements on the hub.
+ */
+class SmartspaceInteractionHandler @Inject constructor(
+ private val activityStarter: ActivityStarter,
+) : RemoteViews.InteractionHandler {
+ private val delegate = InteractionHandlerDelegate(
+ findViewToAnimate = { view -> view is SmartspaceAppWidgetHostView },
+ intentStarter = this::startIntent,
+ )
+
+ override fun onInteraction(
+ view: View,
+ pendingIntent: PendingIntent,
+ response: RemoteViews.RemoteResponse
+ ): Boolean = delegate.onInteraction(view, pendingIntent, response)
+
+ private fun startIntent(
+ pendingIntent: PendingIntent,
+ fillInIntent: Intent,
+ extraOptions: ActivityOptions,
+ animationController: ActivityTransitionAnimator.Controller?
+ ): Boolean {
+ activityStarter.startPendingIntentWithoutDismissing(
+ pendingIntent,
+ /* dismissShade = */ false,
+ /* intentSentUiThreadCallback = */ null,
+ animationController,
+ fillInIntent,
+ extraOptions.toBundle()
+ )
+ return true
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/util/InteractionHandlerDelegate.kt b/packages/SystemUI/src/com/android/systemui/communal/util/InteractionHandlerDelegate.kt
new file mode 100644
index 0000000..40b182d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/util/InteractionHandlerDelegate.kt
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2024 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.communal.util
+
+import android.app.ActivityOptions
+import android.app.PendingIntent
+import android.content.Intent
+import android.view.View
+import android.widget.RemoteViews
+import androidx.core.util.component1
+import androidx.core.util.component2
+import com.android.systemui.animation.ActivityTransitionAnimator
+
+
+/** A delegate that can be used to launch activities from [RemoteViews] */
+class InteractionHandlerDelegate(
+ private val findViewToAnimate: (View) -> Boolean,
+ private val intentStarter: IntentStarter,
+) : RemoteViews.InteractionHandler {
+
+ /**
+ * Responsible for starting the pending intent for launching activities.
+ */
+ fun interface IntentStarter {
+ fun startPendingIntent(
+ intent: PendingIntent,
+ fillInIntent: Intent,
+ activityOptions: ActivityOptions,
+ controller: ActivityTransitionAnimator.Controller?,
+ ): Boolean
+ }
+
+ override fun onInteraction(
+ view: View,
+ pendingIntent: PendingIntent,
+ response: RemoteViews.RemoteResponse
+ ): Boolean {
+ val launchOptions = response.getLaunchOptions(view)
+ return when {
+ pendingIntent.isActivity -> {
+ // Forward the fill-in intent and activity options retrieved from the response
+ // to populate the pending intent, so that list items can launch respective
+ // activities.
+ val hostView = getNearestParent(view)
+ val animationController =
+ hostView?.let(ActivityTransitionAnimator.Controller::fromView)
+ val (fillInIntent, activityOptions) = launchOptions
+ intentStarter.startPendingIntent(
+ pendingIntent,
+ fillInIntent,
+ activityOptions,
+ animationController
+ )
+ }
+
+ else -> RemoteViews.startPendingIntent(view, pendingIntent, launchOptions)
+ }
+ }
+
+ private fun getNearestParent(child: View): View? {
+ var view: Any? = child
+ while (view is View) {
+ if (findViewToAnimate(view)) return view
+ view = view.parent
+ }
+ return null
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetInteractionHandler.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetInteractionHandler.kt
index e88a8b5..cbc6c97 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetInteractionHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetInteractionHandler.kt
@@ -18,15 +18,11 @@
import android.app.ActivityOptions
import android.app.PendingIntent
-import android.appwidget.AppWidgetHostView
import android.content.Intent
-import android.util.Pair
import android.view.View
import android.widget.RemoteViews
-import androidx.core.util.component1
-import androidx.core.util.component2
import com.android.systemui.animation.ActivityTransitionAnimator
-import com.android.systemui.common.ui.view.getNearestParent
+import com.android.systemui.communal.util.InteractionHandlerDelegate
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.plugins.ActivityStarter
import javax.inject.Inject
@@ -37,38 +33,32 @@
constructor(
private val activityStarter: ActivityStarter,
) : RemoteViews.InteractionHandler {
+
+ private val delegate = InteractionHandlerDelegate(
+ findViewToAnimate = { view -> view is CommunalAppWidgetHostView },
+ intentStarter = this::startIntent,
+ )
+
override fun onInteraction(
view: View,
pendingIntent: PendingIntent,
response: RemoteViews.RemoteResponse
- ): Boolean {
- val launchOptions = response.getLaunchOptions(view)
- return when {
- pendingIntent.isActivity ->
- // Forward the fill-in intent and activity options retrieved from the response
- // to populate the pending intent, so that list items can launch respective
- // activities.
- startActivity(view, pendingIntent, launchOptions)
- else -> RemoteViews.startPendingIntent(view, pendingIntent, launchOptions)
- }
- }
+ ): Boolean = delegate.onInteraction(view, pendingIntent, response)
- private fun startActivity(
- view: View,
+
+ private fun startIntent(
pendingIntent: PendingIntent,
- launchOptions: Pair<Intent, ActivityOptions>,
+ fillInIntent: Intent,
+ extraOptions: ActivityOptions,
+ controller: ActivityTransitionAnimator.Controller?
): Boolean {
- val hostView = view.getNearestParent<AppWidgetHostView>()
- val animationController = hostView?.let(ActivityTransitionAnimator.Controller::fromView)
- val (fillInIntent, activityOptions) = launchOptions
-
activityStarter.startPendingIntentMaybeDismissingKeyguard(
pendingIntent,
/* dismissShade = */ false,
/* intentSentUiThreadCallback = */ null,
- animationController,
+ controller,
fillInIntent,
- activityOptions.toBundle(),
+ extraOptions.toBundle(),
)
return true
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt
index 2ab7aa9..fae0a46 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt
@@ -85,6 +85,26 @@
)
}
+ override fun startPendingIntentWithoutDismissing(
+ intent: PendingIntent,
+ dismissShade: Boolean,
+ intentSentUiThreadCallback: Runnable?,
+ animationController: ActivityTransitionAnimator.Controller?,
+ fillInIntent: Intent?,
+ extraOptions: Bundle?
+ ) {
+ activityStarterInternal.startPendingIntentDismissingKeyguard(
+ intent = intent,
+ intentSentUiThreadCallback = intentSentUiThreadCallback,
+ animationController = animationController,
+ showOverLockscreen = true,
+ skipLockscreenChecks = true,
+ dismissShade = dismissShade,
+ fillInIntent = fillInIntent,
+ extraOptions = extraOptions,
+ )
+ }
+
override fun startPendingIntentMaybeDismissingKeyguard(
intent: PendingIntent,
intentSentUiThreadCallback: Runnable?,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternal.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternal.kt
index c9becb4..cff9f5e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternal.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternal.kt
@@ -39,6 +39,7 @@
associatedView: View? = null,
animationController: ActivityTransitionAnimator.Controller? = null,
showOverLockscreen: Boolean = false,
+ skipLockscreenChecks: Boolean = false,
fillInIntent: Intent? = null,
extraOptions: Bundle? = null,
)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternalImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternalImpl.kt
index e580f64..dbb95e6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternalImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternalImpl.kt
@@ -40,6 +40,7 @@
associatedView: View?,
animationController: ActivityTransitionAnimator.Controller?,
showOverLockscreen: Boolean,
+ skipLockscreenChecks: Boolean,
fillInIntent: Intent?,
extraOptions: Bundle?
) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImpl.kt
index e5fc4e2..37646f4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImpl.kt
@@ -228,6 +228,7 @@
associatedView: View?,
animationController: ActivityTransitionAnimator.Controller?,
showOverLockscreen: Boolean,
+ skipLockscreenChecks: Boolean,
fillInIntent: Intent?,
extraOptions: Bundle?,
) {
@@ -246,10 +247,10 @@
val actuallyShowOverLockscreen =
showOverLockscreen &&
intent.isActivity &&
- activityIntentHelper.wouldPendingShowOverLockscreen(
+ (skipLockscreenChecks || activityIntentHelper.wouldPendingShowOverLockscreen(
intent,
lockScreenUserManager.currentUserId
- )
+ ))
val animate =
!willLaunchResolverActivity &&