Fix activity start on clicking Glance widget from GH
Activites failed to launch when clicking on Glance widget lazy list
because pending intent template is not filled in when calling
`startPendingIntentMaybeDismissingKeyguard`.
The fix is to refactor `startPendingIntentDismissingKeyguard` to accept
the extra params so that the the fill-in intent is set when calling
`PendingIntent#sendAndReturnResult`.
Bug: b/324062788
Test: atest ActivityStarterImplTest
Test: verfied app opened on clicking News, Finance and TV after keyguard is dismissed
Flag: ACONFIG com.android.systemui.communal_hub TEAMFOOD
Change-Id: Ib4bc1dde62a3837b9747b7c4e9c40b17c1fe4b5e
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 69ff5ab..b4f87c4 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
@@ -21,6 +21,8 @@
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
@@ -29,6 +31,7 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.refEq
import org.mockito.Mock
import org.mockito.Mockito.isNull
import org.mockito.Mockito.notNull
@@ -62,6 +65,7 @@
val parent = FrameLayout(context)
val view = CommunalAppWidgetHostView(context)
parent.addView(view)
+ val (fillInIntent, activityOptions) = testResponse.getLaunchOptions(view)
underTest.onInteraction(view, testIntent, testResponse)
@@ -70,6 +74,8 @@
eq(testIntent),
isNull(),
notNull(),
+ refEq(fillInIntent),
+ refEq(activityOptions.toBundle()),
)
}
@@ -78,10 +84,17 @@
val parent = FrameLayout(context)
val view = View(context)
parent.addView(view)
+ val (fillInIntent, activityOptions) = testResponse.getLaunchOptions(view)
underTest.onInteraction(view, testIntent, testResponse)
verify(activityStarter)
- .startPendingIntentMaybeDismissingKeyguard(eq(testIntent), isNull(), isNull())
+ .startPendingIntentMaybeDismissingKeyguard(
+ eq(testIntent),
+ isNull(),
+ isNull(),
+ refEq(fillInIntent),
+ refEq(activityOptions.toBundle()),
+ )
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ActivityStarterImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ActivityStarterImplTest.kt
index 8aa0e3fc..c8062fb 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ActivityStarterImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ActivityStarterImplTest.kt
@@ -16,12 +16,15 @@
package com.android.systemui.statusbar.phone
+import android.app.ActivityOptions
import android.app.PendingIntent
import android.content.Intent
+import android.os.Bundle
import android.os.RemoteException
import android.os.UserHandle
import android.view.View
import android.widget.FrameLayout
+import android.window.SplashScreen.SPLASH_SCREEN_STYLE_SOLID_COLOR
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.keyguard.KeyguardUpdateMonitor
@@ -48,6 +51,7 @@
import com.android.systemui.statusbar.window.StatusBarWindowController
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.nullable
import com.android.systemui.util.mockito.whenever
@@ -173,6 +177,53 @@
)
}
+ fun startPendingIntentDismissingKeyguard_fillInIntentAndExtraOptions_sendAndReturnResult() {
+ val pendingIntent = mock(PendingIntent::class.java)
+ val fillInIntent = mock(Intent::class.java)
+ val parent = FrameLayout(context)
+ val view =
+ object : View(context), LaunchableView {
+ override fun setShouldBlockVisibilityChanges(block: Boolean) {}
+ }
+ parent.addView(view)
+ val controller = ActivityTransitionAnimator.Controller.fromView(view)
+ whenever(pendingIntent.isActivity).thenReturn(true)
+ whenever(keyguardStateController.isShowing).thenReturn(true)
+ whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true)
+ whenever(activityIntentHelper.wouldPendingShowOverLockscreen(eq(pendingIntent), anyInt()))
+ .thenReturn(false)
+
+ // extra activity options to set on pending intent
+ val activityOptions = mock(ActivityOptions::class.java)
+ activityOptions.splashScreenStyle = SPLASH_SCREEN_STYLE_SOLID_COLOR
+ activityOptions.isPendingIntentBackgroundActivityLaunchAllowedByPermission = false
+ val bundleCaptor = argumentCaptor<Bundle>()
+
+ underTest.startPendingIntentMaybeDismissingKeyguard(
+ intent = pendingIntent,
+ animationController = controller,
+ intentSentUiThreadCallback = null,
+ fillInIntent = fillInIntent,
+ extraOptions = activityOptions.toBundle(),
+ )
+ mainExecutor.runAllReady()
+
+ // Fill-in intent is passed and options contain extra values specified
+ verify(pendingIntent)
+ .sendAndReturnResult(
+ eq(context),
+ eq(0),
+ eq(fillInIntent),
+ nullable(),
+ nullable(),
+ nullable(),
+ bundleCaptor.capture()
+ )
+ val options = ActivityOptions.fromBundle(bundleCaptor.value)
+ assertThat(options.isPendingIntentBackgroundActivityLaunchAllowedByPermission).isFalse()
+ assertThat(options.splashScreenStyle).isEqualTo(SPLASH_SCREEN_STYLE_SOLID_COLOR)
+ }
+
@Test
fun startPendingIntentDismissingKeyguard_associatedView_getAnimatorController() {
val pendingIntent = mock(PendingIntent::class.java)
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 1126ec3..072ec99 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java
@@ -17,6 +17,7 @@
import android.annotation.Nullable;
import android.app.PendingIntent;
import android.content.Intent;
+import android.os.Bundle;
import android.os.UserHandle;
import android.view.View;
@@ -67,6 +68,17 @@
@Nullable ActivityTransitionAnimator.Controller animationController);
/**
+ * Similar to {@link #startPendingIntentMaybeDismissingKeyguard(PendingIntent, Runnable,
+ * ActivityTransitionAnimator.Controller)}, but also specifies a fill-in intent and extra
+ * options that could be used to populate the pending intent and launch the activity.
+ */
+ void startPendingIntentMaybeDismissingKeyguard(PendingIntent intent,
+ @Nullable Runnable intentSentUiThreadCallback,
+ @Nullable ActivityTransitionAnimator.Controller animationController,
+ @Nullable Intent fillInIntent,
+ @Nullable Bundle extraOptions);
+
+ /**
* The intent flag can be specified in startActivity().
*/
void startActivity(Intent intent, boolean onlyProvisioned, boolean dismissShade, int flags);
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 4c1e77b..778d8cf 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetInteractionHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetInteractionHandler.kt
@@ -16,9 +16,14 @@
package com.android.systemui.communal.widgets
+import android.app.ActivityOptions
import android.app.PendingIntent
+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.plugins.ActivityStarter
@@ -33,21 +38,33 @@
view: View,
pendingIntent: PendingIntent,
response: RemoteViews.RemoteResponse
- ): Boolean =
- when {
- pendingIntent.isActivity -> startActivity(view, pendingIntent)
- else ->
- RemoteViews.startPendingIntent(view, pendingIntent, response.getLaunchOptions(view))
+ ): 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)
}
+ }
- private fun startActivity(view: View, pendingIntent: PendingIntent): Boolean {
+ private fun startActivity(
+ view: View,
+ pendingIntent: PendingIntent,
+ launchOptions: Pair<Intent, ActivityOptions>,
+ ): Boolean {
val hostView = view.getNearestParent<CommunalAppWidgetHostView>()
val animationController = hostView?.let(ActivityTransitionAnimator.Controller::fromView)
+ val (fillInIntent, activityOptions) = launchOptions
activityStarter.startPendingIntentMaybeDismissingKeyguard(
pendingIntent,
/* intentSentUiThreadCallback = */ null,
- animationController
+ animationController,
+ fillInIntent,
+ activityOptions.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 a55de25..37646ae 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt
@@ -21,6 +21,7 @@
import android.app.TaskStackBuilder
import android.content.Context
import android.content.Intent
+import android.os.Bundle
import android.os.RemoteException
import android.os.UserHandle
import android.provider.Settings
@@ -149,6 +150,23 @@
)
}
+ override fun startPendingIntentMaybeDismissingKeyguard(
+ intent: PendingIntent,
+ intentSentUiThreadCallback: Runnable?,
+ animationController: ActivityTransitionAnimator.Controller?,
+ fillInIntent: Intent?,
+ extraOptions: Bundle?,
+ ) {
+ activityStarterInternal.startPendingIntentDismissingKeyguard(
+ intent = intent,
+ intentSentUiThreadCallback = intentSentUiThreadCallback,
+ animationController = animationController,
+ showOverLockscreen = true,
+ fillInIntent = fillInIntent,
+ extraOptions = extraOptions,
+ )
+ }
+
/**
* TODO(b/279084380): Change callers to just call startActivityDismissingKeyguard and deprecate
* this.
@@ -554,6 +572,8 @@
associatedView: View? = null,
animationController: ActivityTransitionAnimator.Controller? = null,
showOverLockscreen: Boolean = false,
+ fillInIntent: Intent? = null,
+ extraOptions: Bundle? = null,
) {
val animationController =
if (associatedView is ExpandableNotificationRow) {
@@ -614,9 +634,10 @@
val options =
ActivityOptions(
CentralSurfaces.getActivityOptions(
- displayId,
- animationAdapter
- )
+ displayId,
+ animationAdapter
+ )
+ .apply { extraOptions?.let { putAll(it) } }
)
// TODO b/221255671: restrict this to only be set for
// notifications
@@ -625,9 +646,9 @@
ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED
)
return intent.sendAndReturnResult(
- null,
+ context,
0,
- null,
+ fillInIntent,
null,
null,
null,