'Android App Actions showing up in Google Home Routines

Recently integrated with the in-app promo SDK to bring awareness to our users that you can engage with the Google Assistant to trigger our app shortcuts. I am using shortcuts.xml to provide Google Assistant capabilities for these shortcuts since they are static shortcuts that don't require authentication for our users. The app shortcuts are pretty basic and only use the actions.intent.OPEN_APP_FEATURE intent. When using the in-app promo SDK, it appears to not find the app shortcuts, and when looking at isShortcutPresent in the onSuccessListener, it returns false indicating we should show our promo to allow the user to add this shortcut to their assistant. This does allow the user to add these actions to their assistant, but it results in seeing an additional Google Assistant routine in the Google Home app on the routines screen. This routine sometimes doesn't work, and just sends a toast message saying App isn't installed. You can also see the shortcut in the Google app under Settings > Google Assistant > Routines. It just shows up under "Your shortcuts" I can get the shortcut working again if I remove it in the Google app and then re-engage with my in-app promo in the app. I'm primarily wondering if this is expected? Or is something up with the setup I have for this kind of flow?

shortcuts.xml

<?xml version="1.0" encoding="utf-8"?>
<shortcuts
    xmlns:android="http://schemas.android.com/apk/res/android">

    <capability android:name="actions.intent.OPEN_APP_FEATURE">
        <intent android:action="android.intent.action.VIEW">
            <url-template android:value="app://customlink/shortcut/{actionType}" />
            <parameter
                android:name="feature"
                android:key="actionType" />
        </intent>
    </capability>

    <shortcut
        android:enabled="true"
        android:icon="@drawable/shortcut_one_icon"
        android:shortcutDisabledMessage="@string/shortcut_one_disabled"
        android:shortcutId="shortcutOne"
        android:shortcutLongLabel="@string/shortcut_one_long"
        android:shortcutShortLabel="@string/shortcut_one_short">
        <intent
            android:action="android.intent.action.VIEW"
            android:data="app://customlink/shortcut/shortcutOne"
            android:targetClass="com.someGeneric.package.view.main.MainActivity"
            android:targetPackage="com.someGeneric.package" />
        <capability-binding android:key="actions.intent.OPEN_APP_FEATURE">
            <parameter-binding
                android:key="feature"
                android:value="@array/shortcut_one_triggers" />
        </capability-binding>
    </shortcut>

    <shortcut
        android:enabled="true"
        android:icon="@drawable/shortcut_two_icon"
        android:shortcutDisabledMessage="@string/shortcut_two_disabled"
        android:shortcutId="shortcutTwo"
        android:shortcutLongLabel="@string/shortcut_two_long"
        android:shortcutShortLabel="@string/shortcut_two_short">
        <intent
            android:action="android.intent.action.VIEW"
            android:data="app://customlink/shortcut/shortcutTwo"
            android:targetClass="com.someGeneric.package.view.main.MainActivity"
            android:targetPackage="com.someGeneric.package" />
        <capability-binding android:key="actions.intent.OPEN_APP_FEATURE">
            <parameter-binding
                android:key="feature"
                android:value="@array/shortcut_two_triggers" />
        </capability-binding>
    </shortcut>

</shortcuts>

GoogleAssistantShortcutFragment.kt

...

private var shortcutSuggestionsClient: AssistantShortcutSuggestionsClient? = null

...

private fun setupShortcutList() {
    shortcutSuggestionsClient = AssistantShortcutSuggestionsClient.builder()
            .setContext(requireContext())
            .setVerifyIntents(true)
            .build()

    bind.apply {
        shortcutOneButton.googleAssistantShortcutTitle.text = resources.getString(R.string.shortcut_one_short)
        shortcutTwoButton.googleAssistantShortcutTitle.text = resources.getString(R.string.shortcut_two_short)
    }

    val shortcutOneIntent = AppShortcutIntent.builder()
            .setIntentName(OPEN_APP_FEATURE_INTENT)
            .setPackageName(requireContext().packageName)
            .setIntentParamName(FEATURE)
            .setIntentParamValue("shortcutOne")
            .build()
    val shortcutTwoIntent = AppShortcutIntent.builder()
            .setIntentName(OPEN_APP_FEATURE_INTENT)
            .setPackageName(requireContext().packageName)
            .setIntentParamName(FEATURE)
            .setIntentParamValue("shortcutTwo")
            .build()

    lookupShortcut(
            shortcutIntent = shortcutOneIntent,
            canAddShortcut = {
                    bind.shortcutOneButton.addToGoogleAssistantPill.setOnClickListener {
                        val shortcutOneSuggestion = viewModel.buildAppShortcutSuggestion(
                                shortcutIntent = shortcutOneIntent,
                                triggerPhrase = SHORTCUT_ONE_INITIAL_PHRASE
                        )
                        triggerAppShortcutSuggestion(shortcutOneSuggestion)
                    }
            },
            shortcutExists = {
                    setShortcutButtonToAdded(
                            shortcutContainerBinding = bind.shortcutOneButton
                    )
            },
            onError = { setShortcutButtonToError(bind.shortcutOneButton) }
    )

    lookupShortcut(
            shortcutIntent = shortcutTwoIntent,
            canAddShortcut = {
                bind.shortcutTwoButton.addToGoogleAssistantPill.setOnClickListener {
                    val shortcutTwoSuggestion = viewModel.buildAppShortcutSuggestion(
                            shortcutIntent = shortcutTwoIntent,
                            triggerPhrase = SHORTCUT_TWO_INITIAL_PHRASE
                    )
                    triggerAppShortcutSuggestion(shortcutTwoSuggestion)
                }
            },
            shortcutExists = {
                setShortcutButtonToAdded(
                        shortcutContainerBinding = bind.shortcutTwoButton
                )
            },
            onError = { setShortcutButtonToError(bind.shortcutTwoButton) }
    )
}

/**
 * canAddShortcut - Function to call to add click listener to UI to trigger Google Assistant add suggestion
 * shortcutExists - Function to update UI to show the user they already have this added to their Assistant
 * onError - Function to update the UI to show the user there was an error checking for the shortcut.
 */
private fun lookupShortcut(
        shortcutIntent: AppShortcutIntent,
        canAddShortcut: () -> Unit,
        shortcutExists: () -> Unit,
        onError: () -> Unit
) {
    // We put this inside of a lifecycleScope because we were seeing some crashes around this code
    // trying to update our ViewBinding in the background when the Fragment had been destroyed.
    viewLifecycleOwner.lifecycleScope.launch {
        shortcutSuggestionsClient?.lookupShortcut(shortcutIntent)
                ?.addOnSuccessListener { shortcutLookupResult ->
                        if (!shortcutLookupResult.isShortcutPresent) {
                            // app can suggest to create a shortcut
                            canAddShortcut()
                        } else {
                            // app can remind that the user has a shortcut for this app action
                            shortcutExists()
                        }
                }
                ?.addOnFailureListener { e ->
                    FirebaseCrashlytics.getInstance().apply {
                        log("Shortcut lookup failed - $shortcutIntent")
                        recordException(e)
                    }
                    onError()
                }
    }
}

private fun triggerAppShortcutSuggestion(shortcutSuggestion: AppShortcutSuggestion) {
    try {
        shortcutSuggestionsClient?.createShortcutSuggestionIntent(shortcutSuggestion)
                ?.addOnSuccessListener { intent ->
                    ContextCompat.startActivity(
                            requireContext(),
                            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK),
                            null
                    )
                }
                ?.addOnFailureListener { e ->
                    FirebaseCrashlytics.getInstance().log("Failed to get shortcut suggestion intent - $shortcutSuggestion")
                    FirebaseCrashlytics.getInstance().recordException(e)
                }
    } catch (e: Exception) {
        FirebaseCrashlytics.getInstance().recordException(e)
    }
}

GoogleAssistantShortcutsViewModel.kt

fun buildAppShortcutSuggestion(shortcutIntent: AppShortcutIntent, triggerPhrase: String): AppShortcutSuggestion {
    return AppShortcutSuggestion.builder()
            .setAppShortcutIntent(shortcutIntent)
            .setCommand(triggerPhrase)
            .build()
}


Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source