在 Android SDK 中向流程传递数据

自定义标签是一种自定义变量,其值来自您的应用。在变量设置中选择 Value comes from outside the app 后,您的应用即可通过 Adapty SDK 为其设置值。例如,某个文本元素包含”Coins:“和一个自定义标签。如果您的应用为该标签传入”120”,用户看到的内容就是”Coins: 120”。

开始之前

您需要使用 Adapty SDK v4 或更高版本。v4 之前的版本不支持显示流程。

将自定义标签添加到流程

要在流程中显示应用的数据,请在编辑工具中设置自定义标签:

  1. 为应用传递的每个值创建一个自定义标签。

    Create variable form with a name, a description, the String value type, and an initial value
  2. 将每个标签插入文本元素。

  3. 记录每个标签的完整名称,包括其所属分组。应用代码在传递对应值时需使用该名称。例如,若 user 分组中包含 streak 标签,则该标签的完整名称为 user.streak。

    Custom tab of the Add variable dropdown with the streak tag in the user group

传入自定义标签值

创建一个 AdaptyUiTagResolver,并将其作为 tagResolver 参数传入 AdaptyUI.getFlowView。SDK 会以每个自定义标签的完整名称(例如 user.streak)调用该解析器。解析器返回以下值之一:

  • 字符串:用户将看到该字符串替代标签内容。必须是人类可读的格式,例如”1,250”,而非”1250.0”。Adapty 不会对其进行格式化或翻译。
  • null:用户将看到该标签的初始值作为备用内容。
  • 空字符串:用户将看到标签位置显示为空。

如果你自己创建 AdaptyFlowView,请将 resolver 传入其 showFlow 方法。在 Jetpack Compose 中,将其传入 AdaptyFlowScreen。关于每种调用方式的示例,请参阅展示流程与付费墙。

要测试自定义标签,请在添加设置其值的代码后,在应用中打开流程。编辑工具画布和在设备上预览只显示初始值:Adapty 移动应用不会设置自定义标签的值。

如果你的应用已经为旧版付费墙编辑工具的付费墙设置了自定义标签值,请了解流程中的自定义标签与旧版的区别。

用户在没有值时看到的内容

在以下情况下,用户会看到自定义标签的初始值作为备用内容:

  • 您的应用未为该标签传递任何值。
  • 用户尚未将应用更新到能传递该值的版本。
  • 应用代码中的名称与标签的完整名称不匹配,包括字母大小写。
  • 同一文本元素中的另一个自定义标签没有值。此时该文本元素中的每个自定义标签都会显示其初始值。

如需在标签位置不显示任何内容,请传入空字符串。Adapty 将空字符串视为有效值,因此不会显示备用内容。

示例:根据用户引导答案显示 BMI

流程可以收集用户的答案,自定义标签可以显示来自你应用的值。将两者结合,即可向用户展示由你的应用根据其答案计算得出的结果,例如得分、推荐套餐或健康指标:

  1. 流程收集答案并将其发送给你的应用。
  2. 你的应用计算结果。
  3. 你的应用将结果作为自定义标签值传递给下一个流程。

在此示例中,一个用户引导流程收集用户的体重和身高。当用户在最后一屏关闭用户引导流程后,你的应用会计算其身体质量指数(BMI)。然后它会打开一个付费墙流程,显示该 BMI 值。

设置流程

  1. 创建两个流程:一个用于收集用户体重和身高的用户引导流程,以及一个展示 BMI 的付费墙流程。

  2. 在用户引导流程中添加两个 Number 输入框:

    • 一个用于输入体重(千克),Element ID 设为 weight。
    • 一个用于输入身高(厘米),Element ID 设为 height。

    两个输入框的 Format 均设为 Integer。输入框会将用户输入的内容原样以文本形式发送,示例代码对 “70.5” 和 “70,5” 的解析结果并不相同。整数没有小数分隔符,因此在任何语言环境下代码的解析结果都一致。

  3. 为确保您的应用能收到两个值,请在用户引导流程的最后添加一个不含输入框的屏幕。在该屏幕上添加一个带有 Close flow 操作的按钮。输入框会在失去焦点或用户切换到其他屏幕时发送其值,而 Close flow 操作不会触发发送。示例代码只有在用户到达最后一屏时才会打开付费墙。

  4. 在付费墙流程中,创建一个名为 bmi 的自定义标签。在 Initial value 字段中输入一个破折号(—)。如果您的应用未传入 BMI 值,用户将看到该破折号。

  5. 在付费墙流程中添加一个文本元素,内容为”Your BMI:“。在这些文字后面插入 bmi 自定义标签。

  6. 将用户引导流程添加到某个版位,例如 onboarding。将付费墙流程添加到 ID 为 bmi_paywall 的版位。将 BMI 传递给付费墙中的代码通过此 ID 获取付费墙。

将 BMI 传递给付费墙

从用户引导流程的 flow_user_input 事件中收集体重和身高数据,方法参见处理流程数据。要判断用户是否已到达最后一屏,可监听 flow_screen_showed 事件中 is_last_screen 为 true 的情况,方法参见追踪流程屏幕浏览。当用户关闭该屏幕后的用户引导流程时,计算 BMI,然后创建付费墙的 flow 视图,并将 BMI 传入 bmi 自定义标签。

将以下监听器传递给用户引导流程的视图:

class OnboardingEventListener(
    private val onBmiCalculated: (bmi: String) -> Unit,
) : AdaptyFlowDefaultEventListener() {

    private val bodyAnswers = mutableMapOf<String, String>()
    private var reachedLastScreen = false

    override fun onAnalyticEvent(
        name: String,
        params: Map<String, Any?>,
        context: Context,
    ) {
        if (name == "flow_screen_showed" && params["is_last_screen"] == true) {
            reachedLastScreen = true
            return
        }
        if (name != "flow_user_input") return

        val elementId = params["element_id"] as? String ?: return
        val value = params["value"] as? String ?: return

        bodyAnswers[elementId] = value
    }

    override fun onFlowClosed() {
        // Reset `bodyAnswers` and `reachedLastScreen` on every close,
        // so that the next run starts without old answers.
        val answers = bodyAnswers.toMap()
        val finished = reachedLastScreen
        bodyAnswers.clear()
        reachedLastScreen = false

        // Number inputs send their values as strings.
        if (!finished) return
        val weight = answers["weight"]?.toDoubleOrNull() ?: return
        val height = answers["height"]?.toDoubleOrNull()?.takeIf { it > 0 } ?: return

        val heightInMeters = height / 100
        val bmi = weight / (heightInMeters * heightInMeters)

        onBmiCalculated(String.format(Locale.getDefault(), "%.1f", bmi))
    }
}

When the user closes the onboarding flow after its last screen, the listener calls onBmiCalculated with the BMI. Pass a lambda for onBmiCalculated that calls the following function. The function fetches the paywall flow, creates its view, and passes the BMI for the bmi custom tag:

fun showBmiPaywall(activity: Activity, bmi: String) {
    Adapty.getFlow("bmi_paywall") { flowResult ->
        when (flowResult) {
            is AdaptyResult.Success -> {
                AdaptyUI.getFlowConfiguration(flowResult.value) { configResult ->
                    when (configResult) {
                        is AdaptyResult.Success -> {
                            val flowView = AdaptyUI.getFlowView(
                                activity,
                                configResult.value,
                                products = null,
                                eventListener = AdaptyFlowDefaultEventListener(),
                                tagResolver = AdaptyUiTagResolver { tag ->
                                    if (tag == "bmi") bmi else null
                                },
                            )
                            // add flowView to your view hierarchy
                        }
                        is AdaptyResult.Error -> {
                            // handle the error
                        }
                    }
                }
            }
            is AdaptyResult.Error -> {
                // handle the error
            }
        }
    }
}

String.format with Locale.getDefault() uses the decimal separator of the user’s locale: “22.7” in English, “22,7” in German.

将 BMI 保存到用户画像

要根据用户的 BMI 向其展示不同的流程,请将其作为自定义属性保存到用户画像中。在 Adapty 看板中,创建一个 Number 类型的自定义属性,键名设为 bmi。然后将每个流程展示给基于该属性构建的目标受众。

在 onFlowClosed 方法中调用 updateProfile,放在计算 bmi 的那行代码之后。传入 Double 类型的值,而不是格式化后的字符串,这样市场细分才能将 BMI 与阈值进行比较:

val params = AdaptyProfileParameters.Builder()
    .withCustomAttribute("bmi", bmi)
    .build()

Adapty.updateProfile(params) { error ->
    if (error != null) {
        // handle the error
    }
}