Google Play Cubes

Google Play Cubes

文档基于 engage sdk v1.4.0

Google Play Cubes 是什么?

Cubes 是 Google 开发的一个新的功能。

|300

|300

|300

|300

|300

Google Play Engage Shopping for app 交互流程

Google_Cubes_for_shein.jpg|1000

Engage 开发者集成流程

Engage 开发者集成工作流程  |  Android 开发者  |  Android Developers

Shared files

Verification app 验证应用程序

engage_verify_app_dev_03052024.apk

该 APP 的包名为:com.google.android.engage.verifyapp,用于验证 APP 发布集群测试

查看 Engage SDK:

public final class zzac {
	@VisibleForTesting  
	static final Intent zza = (new Intent("com.google.android.engage.BIND_APP_ENGAGE_SERVICE")).setPackage("com.android.vending");  
	static final Intent zzb = (new Intent("com.google.android.engage.BIND_APP_ENGAGE_SERVICE")).setPackage("com.google.android.engage.verifyapp");
}

如果同时存在 2 个 App,优先级:verifyapp > Google Play

内容发布指南

#Engage SDK Cluster publishing guidelines (Engage SDK 集群发布指南 )

1、使用 SDK 调试模式进行开发

build.gradle 文件中添加 Engage SDK,并遵循相应的类别特定集成指南来完成集成。

dependencies {
    // Make sure you also include the repository in your project's build.gradle file.
    implementation 'com.google.android.engage:engage-core:1.4.0'
}

不同类别的,按照不同类别的集成文档集成,如 Shopping:#Shopping 类别集成指南

2、安装验证应用程序

验证应用程序(engage_verify_app_dev_03052024.apk)是一个 Android 应用程序,开发人员可以使用它来验证集成是否正常工作。该应用程序包括帮助开发人员验证数据和广播意图的功能。

3、验证数据在验证应用程序中是否可见

验证数据在验证应用程序中是否可见

|300

|300

|300

4、验证广播意图流

要验证广播意图,请单击 UI 顶部的按钮来触发发送广播的逻辑。

|300

完成步骤 1 到 4 后,您就完成了集成测试。

5、调试完成后,您必须更新清单文件中的元数据

<application>
  <meta-data
	android:name="com.google.android.engage.service.ENV"
	android:value="PRODUCTION"></meta-data>
</application>

警告:在将应用程序发布到 Play 商店之前,请确保元数据已更新。

6、Send Google the release-ready APK

将您的可发布 APK 文件的副本作为电子邮件附件发送至 engage-developers@google.com 。 Google 将验证整个流程是否按我们的预期运行。完成后,应用程序就可以提交到生产环境进行发布。

警告:确保 APK 的包名称与发布到 Play 商店的包名称匹配。

7、Publish the APK to the Play Store

获得批准后,将 APK 发布到 Play 商店。发布后,将发布的版本号发送电子邮件至 engage-developers@google.com 。谷歌将采取后续措施予以回应。

Shopping 类别集成指南

Engage SDK Shopping:第三方技术集成说明  |  Android 开发者  |  Android Developers

Engage SDK Shopping 是 Google 给合作伙伴在其 APP 外展示其购物相关内容,在一块新的 Surface 区域或者 Google Surface 如 Entertainment 区域。

Cluster types

Shopping 下面有 6 种 cluster type:

RecommendationFeaturedShopping CartShopping List and Reorder

V 1.4.0 新增了 Order Track

Recommendation cluster 推荐集群(多个)

Recommendation clusters 展示来自各个开发者合作伙伴提供的个性化的 shopping 建议。这些推荐可以针对用户进行个性化或一般化(如:趋势项目)。使用它们来显示你认为合适的产品、活动、销售、促销或订阅。

你的 Recommendations 采用下面的结构:

Featured cluster 展示了来自多个开发合作伙伴的精选购物实体 (ShoppingEntity),以一个 UI 分组的形式呈现。在 UI 的顶部附近,有一个单独的Featured cluster ,优先于所有Recommendation cluster。每个开发合作伙伴都可以在Featured cluster 中发布一个购物实体 (ShoppingEntity)。

Shopping Cart cluster 购物车集群(单个)

Shopping Cart cluster 在一个 UI 分组中展示多个开发者合作伙伴的购物车商品一撇,促使用户完成未完成的购物车。只有一个 Shopping Cart cluster,在 UI 的顶部附近显示,优先于所有的 Recommendation clusters。每个开发者合作伙伴在只能在 Shopping Cart cluster 广播一个 ShoppingCart

你的 Shopping Cart 采用下面的结构:

Shopping List cluster 购物清单集群(单个)

Shopping List cluster 展示了来自多个开发者合作伙伴的购物清单一撇,将用户引导返回 APP 去更新和完成他们的清单。只有一个 Shopping List cluster。

Reorder cluster 购物重新订购集群

Reorder cluster 展示了来自多个开发者合作伙伴的上一笔订单的一撇,以一个 UI 界面分组的方式提示用户再去下单。只有一个 Record cluster。

Reorder cluster 必须显示用户先前订单中的所有商品数量,并且还必须包含以下内容之一:

  1. 用户先前订单中 X 件商品的图片
  2. 用户先前订单中 X 件商品的标签

Order Track 物流追踪 v1.4.0新增的

展示优先级

先展示 ShoppingCart,然后 Featured,然后 Recommendations

Pre-work 前期工作

dependencies {
    // Make sure you also include that repository in your project's build.gradle file.
    implementation 'com.google.android.engage:engage-core:1.4.0'
}

For more information, see Package visibility in Android 11.

Summary

前提条件:绑定了 SDK 提供的 Service?

对于不同的集群类型,客户端可以发布的数据受到以下限制:

Cluster type 簇型 Cluster limits 集群限制 Maximum entity limits in a cluster
集群中的最大实体限制
Recommendation Cluster(s)
推荐集群
最多 5 个 最多 25 个 ShoppingEntity
Featured Cluster 特色集群 最多 1 个 最多 1 ShoppingEntity
Shopping Cart Cluster 购物车集群 最多 1 个 最多 1 ShoppingCart
Shopping List Cluster 购物清单集群 最多 1 个 最多 1 ShoppingListEntity
Shopping Reorder Cluster
购物重新订购集群
最多 1 个 最多 1 ReorderEntity

步骤一:提供 Entity 数据

SDK 定义了不同的 Entity 来表示每种类型,Shopping 类别支持以下 Entity

  1. ShoppingEntity
  2. ShoppingCart
  3. ShoppingList
  4. Reorder

下图概述了每种类型的可用属性和要求。

ShoppingEntity

ShoppingEntity 对象代表开发者合作伙伴希望发布的 product, promotion, deal, subscription, or event

Attribute Requirement Description Format API
Poster images 必需的 海报图片,必须至少提供一张图像。 请参阅图像规格 Image Specifications 以获取指导。#Image specifications addPosterImageaddPosterImages
Action Uri 必需的 操作的 Uri:指向应用程序中显示实体详细信息的页面的 DeepLink。

Refer to this FAQ
注意:您可以使用深层链接进行归因。请参阅此常见问题解答
Uri setActionLinkUri(Uri)
Title 可选 实体的名称。 文本

建议文本大小:90 个字符以下(文本太长可能会显示省略号)
setTitle
Price - current Conditionally required 有条件需要 实体的当前价格。

如果提供了删除线价格,则必须提供。
文本 setPrice
Price - strikethrough 可选 删除线价格:实体的原始价格,在 UI 中被删除。 文本 setPrice
Callout 可选 标注实体的促销、活动或更新(如果有)。 文本

建议文本大小:45 个字符以下(文本太长可能会显示省略号)
setCallout
Callout fine print 可选 标注细则 文本

建议文本大小:45 个字符以下(文本太长可能会显示省略号)
setCalloutFinePrint
Rating 可选 评级 Rating setRating

Rating(可选,注意:我们使用标准的星级评分系统显示所有评级)

Attribute Requirement Description Format API
Rating - max value 必选 评级最大值

如果提供了 currentValue,必须提供 maxValue
Number >= 0.0 setRating
Rating - current value 必选 实体评级的当前值

如果提供了 maxValue,必须提供 currentValue
Number >= 0.0 setRating
Rating - count 可选 实体的评级计数。 String setRating

DisplayTimeWindow(可选,设置要在表面上显示的内容的时间窗口

Attribute Requirement Description Format API
Start Timestamp 可选 开始时间戳


到了该时间戳内容才显示

如果未设置,内容可以显示在表面上
时间戳,世界时(UTC),相对于 1970年1月1日00:00:00 addDisplayTimeWindow/addAllDisplayTimeWindow
End Timestamp 可选 结束时间戳

到了该时间戳内容不显示

如果未设置,内容可以显示在表面上。
时间戳,世界时(UTC),相对于 1970年1月1日00:00:00 addDisplayTimeWindow/addAllDisplayTimeWindow
ShoppingCart
Attribute Requirement Description Format API
Action Uri 必需的 操作的 Uri:合作伙伴应用程序中购物车的 DeepLink。

Refer to this FAQ
注意:您可以使用深层链接进行归因。请参阅此常见问题解答
Uri setActionLinkUri(Uri)
Number of items 必需的 购物车中的商品数量(不仅仅是产品数量)。

>例如:如果购物车中有 3 件相同的衬衫和 1 顶帽子,则该数字应为 4。
Integer >= 1 setNumberOfItems
Action Text 可选 动作文本购物车上按钮的号召性用语文本(例如,您的购物袋)。如果开发人员未提供操作文本,则默认为 " 查看购物车 "。

从 1.1.0 版本开始支持此属性。
String setActionText
Title 可选 购物车的标题(例如,您的购物袋)。

如果开发商未提供标题,则默认为您的购物车。
文本

建议文本大小:25 个字符以下(文本太长可能会显示省略号)
setTitle
Cart images 可选 购物车图片
购物车中每个产品的图片。

最多可提供 10 张图像(按优先顺序排列);实际显示的图像数量取决于设备的外形尺寸。
请参阅图像规格 Image Specifications 以获取指导。#Image specifications addPosterImageaddPosterImages
Item labels 可选 购物清单上的商品标签列表。

显示的标签的实际数量取决于设备的外形尺寸。
文本

建议文本大小:20 个字符以下(文本太长可能会显示省略号)
addItemLabel/addItemLabels

Play Engage Verify App 效果:

image.png|500

Google Play Cubes 上效果:
|300

DisplayTimeWindow(可选,设置要在表面上显示的内容的时间窗口

Attribute Requirement Description Format API
Start Timestamp 可选 开始时间戳


到了该时间戳内容才显示

如果未设置,内容可以显示在表面上
时间戳,世界时(UTC),相对于 1970年1月1日00:00:00 addDisplayTimeWindow/addAllDisplayTimeWindow
End Timestamp 可选 结束时间戳

到了该时间戳内容不显示

如果未设置,内容可以显示在表面上。
时间戳,世界时(UTC),相对于 1970年1月1日00:00:00 0 addDisplayTimeWindow/addAllDisplayTimeWindow
ShoppingList
Attribute Requirement Description Format API
Action Uri 必需的 操作的 Uri:指向合作伙伴应用程序中购物清单的 DeepLink。

Refer to this FAQ
注意:您可以使用深层链接进行归因。请参阅此常见问题解答
Uri setActionLinkUri(Uri)
Number of items 必需的 购物清单中的商品数量。

Integer >= 1 setNumberOfItems
Title 可选 列表的标题(例如,您的杂货清单)。

如果开发者没有提供标题,则默认为购物清单。
文本

建议文本大小:25 个字符以下(文本太长可能会显示省略号)
setTitle
Item labels 可选 购物清单上的商品标签列表。

必须至少提供 1 个标签,最多可提供 10 个标签(按优先顺序排列);显示的实际标签数量取决于设备外形尺寸。
文本

建议文本大小:20 个字符以下(文本太长可能会显示省略号)
addItemLabel/addItemLabels
ShoppingReorderCluster
Attribute Requirement Description Format API
Action Uri 必需的 操作的 Uri:在合作伙伴的应用程序中重新订购的 DeepLink。

Refer to this FAQ
注意:您可以使用深层链接进行归因。请参阅此常见问题解答
Uri setActionLinkUri(Uri)
Number of items 必需的 上一个订单中的商品数量(不仅仅是产品数量)。

> 例如:如果之前的订单中有 3 份小杯咖啡和 1 份牛角面包,则该数字应为 4。
Integer >= 1 setNumberOfItems
Action Text 可选 重新订购按钮的号召性用语文本(例如,再次订购)。

如果开发人员未提供操作文本,则默认重新排序 (Reorder)。

从 1.1.0 版本开始支持此属性。
String setActionText
Title 可选 重新订购商品的标题。

文本

建议文本大小:40 个字符以下(文本太长可能会显示省略号)
setTitle
Poster images 可选 上一个订单中的商品的图像。


最多可提供 10 张图像(按优先顺序排列);实际显示的图像数量取决于设备的外形尺寸。
请参阅图像规格 Image Specifications 以获取指导。#Image specifications addPosterImageaddPosterImages
Item labels 可选(如未提供,应提供海报图片) 上一个订单的商品标签列表。

最多可提供 10 个标签,按优先级顺序排列;显示的实际标签数量取决于设备外形尺寸。
文本

建议文本大小:20 个字符以下(文本太长可能会显示省略号)
addItemLabel/addItemLabels
Image specifications

下面列出了图像资源所需的规格:

Aspect ratio 比例 Minimum pixels 最小像素 Recommended pixels 推荐像素
Square (1x1) 

非 featured clusters 的首选
300x300 1200x1200
Landscape (1.91x1) 

featured clusters 的首选
600x314 1200x628
Portrait (4x5) 480x600 960x1200
ShoppingOrderTrackingCluster

v 1.4.0 文档还未更新

Sign In Card
Attribute Requirement Description
Action Uri Required Deeplink to Action (i.e. navigates to app sign in page)
深层链接到操作(即导航到应用程序登录页面)
Image Optional - If not provided, Title must be provided
可选 - 如果未提供,则必须提供标题
Image Shown on the Card
卡上显示的图像

16x9 aspect ratio images with a resolution of 1264x712
16x9 宽高比图像,分辨率为 1264x712
Title Optional - If not provided, Image must be provided
可选 - 如果未提供,则必须提供图像
Title on the Card
卡上的标题
Action Text Optional Text Shown on the CTA (i.e. Sign in)
CTA 上显示的文本(即登录)
Subtitle Optional Optional Subtitle on the Card
卡上的可选字幕
var SIGN_IN_CARD_ENTITY =
      SignInCardEntity.Builder()
          .addPosterImage(
              Image.Builder()
                  .setImageUri(Uri.parse("http://www.x.com/image.png"))
                  .setImageHeightInPixel(500)
                  .setImageWidthInPixel(500)
                  .build())
          .setActionText("Sign In")
          .setActionUri(Uri.parse("http://xx.com/signin"))
          .build()

appEngagePublishClient.publishUserAccountManagementRequest(
            PublishUserAccountManagementRequest.Builder()
                .setSignInCardEntity(SIGN_IN_CARD_ENTITY)
                .build());

注意:用户登录后,开发者必须通过调用 deleteUserManagementCluster() API 删除卡片。

步骤二:提供 Cluster 数据 publish 发布

建议在后台执行内容发布作业 (publish)(例如,使用 WorkManager)并定期或按事件安排(例如,每次用户打开应用程序或用户刚刚向应用程序添加内容时)他们的购物车)。

AppEngageShoppingClient 负责 publish shopping clusters

以下 API 公开用于在客户端中发布集群:

isServiceAvailable

该 API 用于检查服务是否可集成以及内容是否可以在设备上呈现。

client.isServiceAvailable.addOnCompleteListener { task ->
    if (task.isSuccessful) {
        // Handle IPC call success
        if(task.result) {
          // Service is available on the device, proceed with content publish
          // calls.
        } else {
          // Service is not available, no further action is needed.
        }
    } else {
      // The IPC call itself fails, proceed with error handling logic here,
      // such as retry.
    }
}

注意:我们强烈建议保持定期作业运行,以检查该服务是否在以后的某个时间点可用。该服务的可用性可能会随着 Android 版本升级、应用程序升级、安装和卸载而变化。
通过确保以一定时间间隔进行定期作业检查,一旦服务可用,就可以发布数据。其他 publishXXX API 也是一样。

publishRecommendationClusters

该 API 用于发布 RecommendationCluster 对象,可能是一个列表。

RecommendationCluster 对象可以具有以下属性:

Attribute 属性 Requirement 要求 Description 描述
List of ShoppingEntity 购物实体列表 必需的 构成此 Recommendation Cluster 的推荐的 ShoppingEntity 对象的列表。
Title 标题 必需的 Recommendation Cluster 的标题。

建议文本大小:25 个字符以下(文本太长可能会显示省略号)
Action Uri 操作 URI 可选 指向合作伙伴应用程序中页面的深层链接,用户可以在其中查看完整的推荐列表。

注意:您可以使用深层链接进行归因。请参阅此常见问题解答 Refer to this FAQ

重要提示:发布 API 是更新插入 API; 它替换了现有内容。不要随后调用 delete 和 publish API 来替换内容,因为发布 API 本质上是这样做的。

client.publishRecommendationClusters(
	PublishRecommendationClustersRequest.Builder()
		.addRecommendationCluster(
			RecommendationCluster.Builder()
				.addEntity(entity1)
				.addEntity(entity2)
				.setTitle("Black Friday Deals")
				.build())
		.build())

当服务收到请求时,在一个事务中执行以下操作:

如果发生错误,则将拒绝整个请求并保留现有状态。

此 API 用于发布 FeaturedCluster 对象。

client.publishFeaturedCluster(
	PublishFeaturedClusterRequest.Builder()
		.setFeaturedCluster(
			FeaturedCluster.Builder()
				// ...
				.build())
		.build())

当服务收到请求时,在一个事务中执行以下操作:

如果发生错误,则将拒绝整个请求并保留现有状态。

注意:

效果:

|400

示例:

public final class GetFeaturedCluster {
    private static final String ACTION_LINK_URI = "xxx://applink/ad_landing_recommend?test=1&mallcode=b&page_type=B";
    @RequiresApi(api = Build.VERSION_CODES.O)
    public static FeaturedCluster getFeaturedCluster() {
        FeaturedCluster.Builder clusterBuilder = new FeaturedCluster.Builder();

        Image image = new Image.Builder()
                .setImageUri(Uri.parse("https://img1.baidu.com/it/u=2127952341,1978494606&fm=253&fmt=auto&app=138&f=PNG?w=600&h=477"))
                .setImageHeightInPixel(314)
                .setImageWidthInPixel(600)
                .build();

        for (int i = 0; i < 5; i++) {
            Price price = new Price.Builder()
                    .setCurrentPrice("$" + (100.00 + i))
                    .setStrikethroughPrice("$" + (200.00 + i))
                    .build();

            Rating rating = new Rating.Builder()
                    .setCurrentValue(1.1 + i)
                    .setMaxValue(10.0)
                    .build();

            ShoppingEntity shoppingEntity = new ShoppingEntity.Builder()
                    .addPosterImage(image)
                    .setActionLinkUri(Uri.parse(ACTION_LINK_URI))
                    .setTitle("Goods_" + i + "_" + MyRecommendationCluster.INSTANCE.getCurrentTimestampAndFormat())
                    .setPrice(price)
                    .setCallout("Callout_" + i)
                    .setRating(rating)
                    .build();
            clusterBuilder.addEntity(shoppingEntity);
        }
        return clusterBuilder.build();
    }

    private GetFeaturedCluster() {
    }
}
publishShoppingCart

此 API 用于发布 ShoppingCartCluster 对象。

client.publishShoppingCart(
	PublishShoppingCartRequest.Builder()
		.setShoppingCart(
			ShoppingCart.Builder()
				// ...
				.build())
	.build())

当服务收到请求时,在一个事务中执行以下操作:

如果发生错误,则将拒绝整个请求并保留现有状态。

效果 1:没有 image 和 label

image.png|300

效果 2:有 image

image.png|300

|300

publishShoppingList

此 API 用于发布 FoodShoppingList 对象。

client.publishFoodShoppingList(
	PublishFoodShoppingListRequest.Builder()
		.setFoodShoppingList(
			FoodShoppingListEntity.Builder()
				...
				.build())
		.build())

当服务收到请求时,在一个事务中执行以下操作:

如果发生错误,则将拒绝整个请求并保留现有状态。

publishShoppingReorderCluster

此 API 用于发布 ShoppingReorderCluster 对象。

client.publishShoppingReorderCluster(
	PublishShoppingReorderClusterRequest.Builder()
		.setReorderCluster(
			ShoppingReorderCluster.Builder()
				...
				.build())
		.build())

当服务收到请求时,在一个事务中执行以下操作:

如果发生错误,则将拒绝整个请求并保留现有状态。

publishShoppingOrderTrackingCluster

截止到 sdk v1.4.0,Shopping order tracking 这个是一个新开发的功能

Shopping reorder 说的是我买了一个东西可能之后还会再买一次,比如说日用品,order tracking 是物流追踪

object GetShoppingOrderTrackingCluster {  
  
    private const val ACTION_LINK_URI = "mysheinlink://applink/order_detail"  
  
    fun getShoppingOrderTrackingCluster(): ShoppingOrderTrackingCluster {  
  
        val orderType = 10;  
  
        val builder = with(ShoppingOrderTrackingCluster.Builder()) {  
            // 必选  
            setTitle("order tracking title")  
            setStatus("订单已发货")  
            setShoppingOrderType(orderType)  
            setTrackingId("order trackingId:shin3121sdf489djsij")  
            setOrderTime(System.currentTimeMillis())  
            val readyTimeWindow = OrderReadyTimeWindow.Builder()  
                .setStartTimestampMillis(System.currentTimeMillis() - 10000L)  
                .setEndTimestampMillis(System.currentTimeMillis() + 10000000L)  
                .build()  
            setOrderReadyTimeWindow(readyTimeWindow)  
  
            // 可选  
            setOrderDescription("my order orderDescription")  
            setActionLinkUri(ACTION_LINK_URI.toUri())  
        }  
        return builder.build()  
    }  
}

val request = PublishShoppingOrderTrackingClusterRequest.Builder()  .setShoppingOrderTrackingCluster(GetShoppingOrderTrackingCluster.getShoppingOrderTrackingCluster())  
    .build()
client.publishShoppingOrderTrackingCluster(request)
publishUserAccountManagementRequest(其他类别可以共用)

此 API 用于发布登录卡。登录操作将用户定向到应用的登录页面,以便应用可以发布内容(或提供更个性化的内容)。

以下元数据是登录卡的一部分:

Attribute 属性 Requirement 要求 Description 描述
Action Uri 必须 操作的 DeepLink(即导航到应用登录页面)
Image 可选 - 如果未提供,则必须提供标题 卡片上显示的图像

分辨率为 1264x71216x9 宽高比图像
Title 可选 - 如果未提供,则必须提供图像 卡片上的标题
Action Text 可选 CTA 上显示的文本(即登录)
Subtitle 可选 卡片上的可选子标题
var SIGN_IN_CARD_ENTITY =
  SignInCardEntity.Builder()
	  .addPosterImage(
		  Image.Builder()
			  .setImageUri(Uri.parse("http://www.x.com/image.png"))
			  .setImageHeightInPixel(500)
			  .setImageWidthInPixel(500)
			  .build())
	  .setActionText("Sign In")
	  .setActionUri(Uri.parse("http://xx.com/signin"))
	  .build()

client.publishUserAccountManagementRequest(
	PublishUserAccountManagementRequest.Builder()
		.setSignInCardEntity(SIGN_IN_CARD_ENTITY)
		.build());

当服务收到请求时,在一个事务中执行以下操作:

如果发生错误,则将拒绝整个请求并保留现有状态。

效果:

image.png|500

updatePublishStatus

如果由于任何内部业务原因,未发布任何集群,我们强烈建议使用 updatePublishStatus API 更新发布状态。这很重要,因为

符合条件的发布状态代码列表为:

// Content is published
AppEngagePublishStatusCode.PUBLISHED,

// Content is not published as user is not signed in
AppEngagePublishStatusCode.NOT_PUBLISHED_REQUIRES_SIGN_IN,

// Content is not published as user is not subscribed
AppEngagePublishStatusCode.NOT_PUBLISHED_REQUIRES_SUBSCRIPTION,

// Content is not published as user location is ineligible
AppEngagePublishStatusCode.NOT_PUBLISHED_INELIGIBLE_LOCATION,

// Content is not published as there is no eligible content
AppEngagePublishStatusCode.NOT_PUBLISHED_NO_ELIGIBLE_CONTENT,

// Content is not published as the feature is disabled by the client
// Available in v1.3.1
AppEngagePublishStatusCode.NOT_PUBLISHED_FEATURE_DISABLED_BY_CLIENT,

// Content is not published as the feature due to a client error
// Available in v1.3.1
AppEngagePublishStatusCode.NOT_PUBLISHED_CLIENT_ERROR,

// Content is not published as the feature due to a service error
// Available in v1.3.1
AppEngagePublishStatusCode.NOT_PUBLISHED_SERVICE_ERROR,

// Content is not published due to some other reason
// Reach out to engage-developers@ before using this enum.
AppEngagePublishStatusCode.NOT_PUBLISHED_OTHER

如果由于用户未登录而导致内容未发布,我们建议发布登录卡。如果由于任何原因提供商无法发布登录卡,那么我们建议使用状态代码 NOT_PUBLISHED_REQUIRES_SIGN_IN 调用 updatePublishStatus API

client.updatePublishStatus(
   PublishStatusRequest.Builder()
     .setStatusCode(AppEngagePublishStatusCode.NOT_PUBLISHED_REQUIRES_SIGN_IN)
     .build())
deleteXXX

删除相关发布的 cluster

重要提示:仅当没有要发布的内容时才应调用删除 API。不要随后调用删除和发布 API 来替换内容,因为发布 API 本身就会执行此操作。在使用删除 API 之前,请联系 engage-developers@google.com

client.deleteClusters(
    DeleteClustersRequest.Builder()
      .addClusterType(ClusterType.TYPE_FEATURED)
      .addClusterType(ClusterType.TYPE_RECOMMENDATION)
      // ...
      .build())
Error handling 错误处理
client.publishRecommendationClusters(
	PublishRecommendationClustersRequest.Builder()
	  .addRecommendationCluster(..)
	  .build())
  .addOnCompleteListener { task ->
	if (task.isSuccessful) {
	  // do something
	} else {
	  val exception = task.exception
	  if (exception is AppEngageException) {
		@AppEngageErrorCode val errorCode = exception.errorCode
		if (errorCode == AppEngageErrorCode.SERVICE_NOT_FOUND) {
		  // do something
		}
	  }
	}
  }

步骤三:处理 broadcast intents

除了通过作业发出发布内容 API 调用外,还需要设置 BroadcastReceiver 来接收内容发布请求

目的:广播 intent 主要用于重新激活应用和强制同步数据。不应过于频繁地发送广播 intent。仅在 Engage Service 确定内容可能已过时(例如,内容是一周前的)的情况下,广播 intent 才会触发。这样一来,开发者将更加确信,即使应用长时间未执行,用户也能够获得新鲜的内容体验。

必须通过以下两种方式设置 BroadcastReceiver

 private fun registerBroadcastReceiver(context: Context) {  
    // Register Recommendation Cluster Publish Intent  
    ActivityCompat.registerReceiver(  
        context,  
        mReceiver,  
        IntentFilter(Intents.ACTION_PUBLISH_RECOMMENDATION),  
        ContextCompat.RECEIVER_EXPORTED  
    )  
  
    // Register Featured Cluster Publish Intent  
    ActivityCompat.registerReceiver(  
        context,  
        mReceiver,  
        IntentFilter(Intents.ACTION_PUBLISH_FEATURED),  
        ContextCompat.RECEIVER_EXPORTED  
    )  
  
    // Register Shopping Cart Cluster Publish Intent  
    ActivityCompat.registerReceiver(  
        context,  
        mReceiver,  
        IntentFilter(ShoppingIntents.ACTION_PUBLISH_SHOPPING_CART),  
        ContextCompat.RECEIVER_EXPORTED  
    )  
  
    // Register Shopping List Cluster Publish Intent  
    ActivityCompat.registerReceiver(  
        context,  
        mReceiver,  
        IntentFilter(ShoppingIntents.ACTION_PUBLISH_SHOPPING_LIST),  
        ContextCompat.RECEIVER_EXPORTED  
    )  
  
    // Register Reorder Cluster Publish Intent  
    ActivityCompat.registerReceiver(  
        context,  
        mReceiver,  
        IntentFilter(ShoppingIntents.ACTION_PUBLISH_REORDER_CLUSTER),  
        ContextCompat.RECEIVER_EXPORTED  
    )  
    // Register Reorder Cluster Publish Intent  
    ActivityCompat.registerReceiver(  
        context,  
        mReceiver,  
        IntentFilter(ShoppingIntents.ACTION_PUBLISH_SHOPPING_ORDER_TRACKING_CLUSTER),  
        ContextCompat.RECEIVER_EXPORTED  
    )  
}
<receiver
	android:name=".AppEngageBroadcastReceiver"
	android:enabled="true"
	android:exported="true">
	<intent-filter>
		<action android:name="com.google.android.engage.action.PUBLISH_RECOMMENDATION" />
	</intent-filter>
	<intent-filter>
		<action android:name="com.google.android.engage.action.PUBLISH_FEATURED" />
	</intent-filter>
	<intent-filter>
		<action android:name="com.google.android.engage.action.shopping.PUBLISH_SHOPPING_CART" />
	</intent-filter>
	<intent-filter>
		<action android:name="com.google.android.engage.action.shopping.PUBLISH_SHOPPING_LIST" />
	</intent-filter>
	<intent-filter>
		<action android:name="com.google.android.engage.action.shopping.PUBLISH_REORDER_CLUSTER" />
	</intent-filter>
	<intent-filter>
		<action android:name="com.google.android.engage.action.shopping.PUBLISH_SHOPPING_ORDER_TRACKING_CLUSTER" />
	</intent-filter>
</receiver>

该服务会发送以下 intent

其他类别 SDK 集成

集群 publish 指南

Engage SDK Cluster 发布指南  |  Android 开发者  |  Android Developers

本指南包含一组集群发布指南,开发人员在与 Engage SDK 集成时可以使用这些指南。

Recommendation clusters 推荐集群 s

Cluster Title

我们建议提供一个独特且相关的集群标题,让用户能够更深入地了解集群的内容。

以下是一些基于内容的良好集群标题的示例:

Clusters of books on health 健康书籍簇

Cluster Content 集群内容

发布推荐集群时,开发者必须考虑用户是否登录到开发者的应用程序。

已登录

如果用户登录到开发者应用程序,我们建议发布个性化或用户生成的内容集群。由于个性化和用户生成的内容与用户更相关,因此他们更有动力从 Google 界面访问开发者应用程序。

Recommendation type 推荐类型 Content freshness strategy
内容新鲜度策略
Content freshness guideline
内容新鲜度准则
Personalized recommendations
个性化推荐
Lenient 宽容

We recommend updating the recommendations once per day so users can see new recommendations daily.
我们建议每天更新一次推荐,以便用户每天都可以看到新的推荐。
Because users do not have an exact expectation of what the recommendation content will be, the content freshness strategy can be lenient.
由于用户对推荐内容没有确切的预期,因此内容新鲜度策略可以比较宽松。
User-generated content libraries
用户生成的内容库
Strict 严格的

We recommend updating the content library as users exit the developer app.
我们建议在用户退出开发者应用程序时更新内容库。
It's important for this content to be in sync with the data displayed on Google surfaces. This is because unlike personalized recommendations, the user expects an exact set of content. Any significant delay in publishing will confuse users.
此内容与 Google 界面上显示的数据同步非常重要。这是因为与个性化推荐不同,用户期望一组精确的内容。发布的任何重大延迟都会让用户感到困惑。
Therefore, the content freshness strategy must be strict.
因此,内容新鲜度策略必须严格。
未登录

如果用户未登录开发者应用,我们仍然建议发布集群,以便鼓励用户从 Google Surface 访问开发者应用。

Recommendation type 推荐类型 Content freshness strategy
内容新鲜度策略
Content freshness guideline
内容新鲜度准则
Non-personalized recommendations
非个性化推荐
Lenient 宽容

We recommend updating the recommendations once per day.
我们建议每天更新一次建议。
Because users do not have an exact expectation of what the recommendation content will be, the content freshness strategy can be lenient.
由于用户对推荐内容没有确切的预期,因此内容新鲜度策略可以比较宽松。
Sign-in card in recommendations
推荐中的登录卡
Strict 严格的

We recommend updating the sign-in card state as users exit the developer app.
我们建议在用户退出开发者应用程序时更新登录卡状态。

After users have signed in, developers must delete the card by calling deleteUserManagementCluster() API.
用户登录后,开发者必须通过调用 deleteUserManagementCluster() API 删除卡片。
It's important for the sign-in state to be in sync with the Google surface. It's confusing for a user to see a sign-in card on Google's surface when they're already signed in. Therefore, the content freshness strategy must be strict.
登录状态与 Google 界面同步非常重要。当用户已经登录时,在 Google 界面上看到登录卡会让他们感到困惑。因此,内容新鲜度策略必须严格。

Continuation clusters

已登录

示例:

Continuation type 延续型 Content freshness strategy
内容新鲜度策略
Content freshness guideline
内容新鲜度准则
User-generated continuation clusters
用户生成的延续集群
Strict 严格的

We recommend updating the content library as users exit the developer app.
我们建议在用户退出开发者应用程序时更新内容库。
It's important for this content to be in sync with the data displayed on Google's surfaces. This is because unlike personalized recommendations, the user expects an exact set of content. Any significant delay in publishing will confuse users.
此内容与 Google 界面上显示的数据同步非常重要。这是因为与个性化推荐不同,用户期望一组精确的内容。发布的任何重大延迟都会让用户感到困惑。
Therefore, the content freshness strategy must be strict.
因此,内容新鲜度策略必须严格。
未登录

Continuation clusters 主要面向登录用户;但是,如果您的应用程序支持访客会话,您还可以为注销用户发布 Continuation clusters

User Management Cluster 用户管理集群

User Management Cluster 的主要目的是促使用户在提供商应用程序上执行某些操作。登录操作将用户引导至应用程序的登录页面,以便应用程序可以发布内容(或提供更个性化的内容)

具体见: #Sign In Card

WorkManager for cluster publishing

推荐使用 WorkManager 来发布集群,因为它是后台工作的推荐解决方案,执行必须是机会性的和有保证的。

当用户离开应用程序时,我们建议启动一个后台作业来发布延续集群和推荐集群。处理此逻辑的一个好地方是 Activity.onStop() ,当用户离开应用程序时调用它。

我们建议使用 PeriodicWorkRequest 安排一个每 24 小时发布一次集群的重复作业。通过使用 CANCEL_AND_REENQUEUE 策略来触发工作,开发人员可以确保 WorkManager 在用户每次离开应用程序时发送更新的数据。这有助于防止用户看到过时的数据。

// Define the PublishClusters Worker requiring input
public class PublishClusters extends Worker {

   public PublishClusters(Context appContext, WorkerParameters workerParams) {
       super(appContext, workerParams);
   }

   @NonNull
   @Override
   public Result doWork() {
       // publish clusters
   }
   ...
}

public static void schedulePublishClusters(Context appContext) {
// Create a PeriodicWorkRequest to schedule a recurring job to update
// clusters at a regular interval
PeriodicWorkRequest publishClustersEntertainmentSpace =
// Define the time for the periodic job
       new PeriodicWorkRequest.Builder(PublishClusters.class, 24, TimeUnit.HOURS)
// Set up a tag for the worker.
// Tags are Unique identifier, which can be used to identify that work
// later in order to cancel the work or observe its progress.
          .addTag("Publish Clusters to Entertainment Space")
          .build();

// Trigger Periodic Job, this will ensure that the periodic job is triggered
// only once since we have defined a uniqueWorkName
WorkManager.getInstance(appContext).enqueueUniquePeriodicWork(
// uniqueWorkName
     "publishClustersEntertainmentSpace",
// If a work with the uniqueWorkName is already running, it will cancel the
// existing running jobs and replace it with the new instance.
// ExistingPeriodicWorkPolicy#CANCEL_AND_REENQUEUE
     ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE,
// Recurring Work Request
publishClustersEntertainmentSpace);

}

处理广播 intent

除了通过作业发出发布内容 API 调用外,还需要设置 BroadcastReceiver 来接收内容发布请求。

但是,开发者必须格外小心,不要只依赖于广播,因为广播只有在特定情况(主要是指重新激活应用和强制同步数据的情况)下才会触发。**只有在 Engage Service 确定内容可能已过时的情况下,广播才会触发。**这样一来,即使用户已很长时间没有打开应用,系统也能更有效地保证用户获得新鲜的内容体验。

必须通过以下两种方式设置 BroadcastReceiver

Engage SDK Cluster 发布指南  |  Android Developers

Engage SDK FAQ

publish FAQs

谁管理内容发布工作?

应用程序开发人员管理内容发布作业并向 Engage Service 发送请求。通过这种方式,开发者合作伙伴可以更好地控制何时以及如何向用户发布内容。这可以避免过于频繁地唤醒合作伙伴应用程序来发布内容。

开发者是否需要发布所有集群类型?

虽然从技术上讲,开发人员可以自由地仅发布一个集群,但我们强烈建议包括更多集群。否则,开发者就会错失提高内容互动度的机会。我们强烈建议发布每个垂直领域的所有集群类型。

在应用程序运行时,开发人员合作伙伴应该多久通过 WorkManager 发布数据?

这由开发合作伙伴决定。

Google 建议每天发布一次或两次一般 recommendation 内容,并对 shopping cartreorder 和其他 continuation 内容使用事件驱动的方法(例如,启动工作线程作为用户将商品添加到购物车或用户中途停止电影)。

开发者应该什么时候调用删除 API?

仅当没有要发布的内容时才应调用删除 API。后续不要调用删除、发布接口替换内容;发布 API 会自动删除较早的内容。

Broadcast Intent FAQs

为什么 Android 应用开发者需要注册广播意图?

为了向用户提供新鲜内容,您应该在用户可能不经常使用应用程序的情况下使用广播意图来触发数据同步。

广播在每次 Cutes 页面启动会执行?

不允许后台执行

在注册广播意图时,您可能会遇到以下错误:

Background execution not allowed: receiving Intent
{ act=com.google.android.engage.action.PUBLISH_RECOMMENDATION .. }

您需要动态注册广播接收器。

class AppEngageBroadcastReceiver extends BroadcastReceiver {
	// Trigger recommendation cluster publish when PUBLISH_RECOMMENDATION broadcast
	// is received
}

public static void registerBroadcastReceivers(Context context) {
	context = context.getApplicationContext();
	// Register Recommendation Cluster Publish Intent
	context.registerReceiver(new AppEngageBroadcastReceiver(),
	new IntentFilter(com.google.android.engage.service.Intents.ACTION_PUBLISH_RECOMMENDATION));
	//...
}

疑问

Engage SDK 提供的广播 Action,能否在清单文件注册,是否受 Android 8.0 限制

建议的是同时注册,manifest 静态注册和 application context 动态 都注册一下

官方提供的 samples,两种方式都注册了,需要注意 receiver 的 onReceive 会执行两次

如何在 Google Play Store 上测试?对 Play 商店版本有什么需求吗?

测试的话,如果想在 Play store 测试的话,需要在 verify app 验证通过之后然后发 apk 到我们这里做一下初步的内部校验,然后我们会把你们的 account 加到我们的白名单里就可以了

最新版本的 Play 商店就好,最好是用海外版本的手机

校验需要个 2~3 天

APP 卸载后集群是否还在?

通过在 verify app 测试,APP 卸载后,集群发布状态就被移除了;

在 Google Play 上,如果 App 被卸载了,Google Play Cubes 也不会存在了

可以的,deeplinkonelink 都可以

登录后,SignInCardEntity 要移除吗?

登录后,通过 deleteUserManagementCluster 把 Sign in Card 移除掉

退出登录后,已发布的集群处理

退出登录后,涉及到需要登录才能发布的集群,应当 delete 掉;不需要登录的更新下数据就好了

Cubes 入口显示条件和命令

Cubes 入口显示条件

  1. 美国 VPN
  2. 不要挂抓包代理,如 Charles
  3. Google Play 账号白名单
  4. Google Play 最新的版本

Google Play Cubes 提供 Widget 有入口,

adb 命令启动 Cubes:
也可以用命令:adb shell am start -n com.android.vending/com.google.android.finsky.rubiks.cubes.activity.CubesActivity

访问不了 Cubes,一般是网络问题:

image.png|300

首次启动

image.png|300

接口调用时机,提供哪些数据,数据什么时候更新

接口调用时机?
在调用集群发布后,就需要调用接口;或者之前就要调用接口,把要发布集群所需要的数据准备好

提供哪些数据?

不同的集群提供的数据不一样,具体看上面不同集群所需要的数据

数据什么时候更新?

v1.5.1 sdk 问题

前端同学把这个 feature 给漏掉了,所以现在默认都是 view cart。我们会在之后的版本里更新上

常见错误

发布的卡片加载不出来

Error while loading the image http://img.ltwebstatic.com/images3_pi/2024/02/20/dd/17083979518bfa684b0ee5d2264e922efd9dd0ce98_thumbnail_405x552_thumbnail_1200x1200.jpg 
java.net.UnknownServiceException: CLEARTEXT communication to img.ltwebstatic.com not permitted by network security policy

原因:Image 用了 http 协议

解决:用 https 协议

问题描述: 通过系统 APK 安装器,发布 Cubes 时报错了:
image.png|1500
image.png|1500
或者是下面的报错:

SERVICE_CALL_INVALID_ARGUMENT - The request contains invalid data (e.g. more than allowed number of clusters (5)

通过 adb install 安装的没有报错:

image.png|1500

分析: Google Play Cubes 对 Production 的包的限制,必须是 Google Play 下载安装的包,通过三方浏览器下载的包不能发布 Cubes。

解决: 未发布上线到 Google Play 前,只能通过 adb install 这种方式才能发布内容到 Cubes;上线后应该直接从 Google Play 安装就能发布了,通过扫码下载安装是看不到发布的内容

Ref