Handle user interaction

Glance simplifies handling user interaction via the Action classes. Glance's Action classes define the actions a user can take, and you can specify the operation performed in response to the action. You can apply an Action to any component with the GlanceModifier.clickable method.

App widgets live on a remote process, so the actions are defined at creation time and the execution happens in the remote process. In native RemoteViews, this is done via PendingIntents.

The following actions are described on this page:

Launch an activity

To launch an activity on user interaction, provide the actionStartActivity function to a Button or other composable via the GlanceModifier.clickable(..) modifier.

Provide one of the following in actionStartActivity:

Glance translates the Action into a PendingIntent with the provided target and parameters. In the following example, the NavigationActivity is launched when a user clicks the button:

@Composable
fun MyContent() {
    // ..
    Button(
        text = "Go Home",
        onClick = actionStartActivity<MyActivity>()
    )
}

Launch a service

Similar to launching an activity, launch a service on user interaction using one of the actionStartService methods.

Provide one of the following in actionStartService:

@Composable
fun MyButton() {
    // ..
    Button(
        text = "Sync",
        onClick = actionStartService<SyncService>(
            isForegroundService = true // define how the service is launched
        )
    )
}

Send a broadcast event

Send a broadcast event on user interaction using one of the actionSendBroadcast methods:

Provide one of the following in actionSendBroadcast:

@Composable
fun MyButton() {
    // ..
    Button(
        text = "Send",
        onClick = actionSendBroadcast<MyReceiver>()
    )
}

Perform custom actions

Instead of launching a specific target, Glance can use a lambda action or an actionRunCallback to perform an action, such as updating the UI or state on user interaction.

Run lambda actions

You can use lambda functions as callbacks to the UI interactions.

For example, pass the lambda function to the GlanceModifier.clickable modifier:

Text(
    text = "Submit",
    modifier = GlanceModifier.clickable {
        submitData()
    }
)

Or, pass it to the onClick parameter on composables that support it:

Button(
    text = "Submit",
    onClick = {
        submitData()
    }
)

Run ActionCallback

Alternatively, use the actionRunCallback methods to perform an action on user interaction. To do this, provide a custom implementation of the ActionCallback:

@Composable
private fun MyContent() {
    // ..
    Image(
        provider = ImageProvider(R.drawable.ic_hourglass_animated),
        modifier = GlanceModifier.clickable(
            onClick = actionRunCallback<RefreshAction>()
        ),
        contentDescription = "Refresh"
    )
}

class RefreshAction : ActionCallback {
    override suspend fun onAction(
        context: Context,
        glanceId: GlanceId,
        parameters: ActionParameters
    ) {
        // TODO implement
    }
}

On the user click, the suspend onAction method of the provided ActionCallback is called, executing the defined logic (i.e., requesting refresh data).

To update the widget after the action is performed, create a new instance and call update(..). For more details, see the Manage GlanceAppWidget state section.

class RefreshAction : ActionCallback {
    override suspend fun onAction(
        context: Context,
        glanceId: GlanceId,
        parameters: ActionParameters
    ) {
        // do some work but offset long-term tasks (e.g a Worker)
        MyAppWidget().update(context, glanceId)
    }
}

Provide parameters to actions

To provide additional information to an action, use the ActionParameters API to create a typed key-value pair. For example, to define the clicked destination:

private val destinationKey = ActionParameters.Key<String>(
    NavigationActivity.KEY_DESTINATION
)

class MyAppWidget : GlanceAppWidget() {

    // ..

    @Composable
    private fun MyContent() {
        // ..
        Button(
            text = "Home",
            onClick = actionStartActivity<NavigationActivity>(
                actionParametersOf(destinationKey to "home")
            )
        )
        Button(
            text = "Work",
            onClick = actionStartActivity<NavigationActivity>(
                actionParametersOf(destinationKey to "work")
            )
        )
    }

    override suspend fun provideGlance(context: Context, id: GlanceId) {
        provideContent { MyContent() }
    }
}

Underneath, the parameters are included in the intent used to launch the activity, allowing the target Activity to retrieve it.

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val destination = intent.extras?.getString(KEY_DESTINATION) ?: return
        // ...
    }
}

The parameters are also provided to the ActionCallback. Use the defined Parameters.Key to retrieve the value:

class RefreshAction : ActionCallback {

    private val destinationKey = ActionParameters.Key<String>(
        NavigationActivity.KEY_DESTINATION
    )

    override suspend fun onAction(
        context: Context,
        glanceId: GlanceId,
        parameters: ActionParameters
    ) {
        val destination: String = parameters[destinationKey] ?: return
        // ...
    }
}