mirror of
https://github.com/android/skills.git
synced 2026-09-14 20:07:09 +08:00
Updates skills (2026-07-29 08:28)
This commit is contained in:
@@ -6,7 +6,7 @@ description: Helps developers integrate, debug, and resolve Play Engage SDK impl
|
||||
license: Complete terms in LICENSE.txt
|
||||
metadata:
|
||||
author: Google LLC
|
||||
last-updated: '2026-07-09'
|
||||
last-updated: '2026-07-18'
|
||||
keywords:
|
||||
- android
|
||||
- engage
|
||||
@@ -23,48 +23,48 @@ required Engage entities for each vertical.
|
||||
|
||||
Follow these steps to assist the developer:
|
||||
|
||||
1. **Identify Vertical and Cluster:**
|
||||
1. **Identify vertical and cluster:**
|
||||
|
||||
- Ask the developer which vertical their app belongs to based on **[references/schemas/](references/schemas)**.
|
||||
- Check if the integration is for TV or Mobile. Read the TV-specific sections in [patterns.md](references/patterns.md) as well if the integration is for TV.
|
||||
- Use `{VERTICAL}.md` in the **[references/schemas/](references/schemas)** directory to identify the corresponding Engage entities and the `client` class name. The `client` field in the JSON provides the full class name. (e.g., `com.google.android.engage.food.service.AppEngageFoodClient`).
|
||||
- **Note:** Initializing the client class requires a `Context` parameter (e.g., `AppEngageFoodClient(context)`).
|
||||
- Check if the integration is for TV or mobile. If the integration is for TV, read the TV-specific sections in [patterns.md](references/patterns.md) as well.
|
||||
- Use `{VERTICAL}.md` in the **[references/schemas/](references/schemas)** directory to identify the corresponding Engage entities and the `client` class name. The `client` field in the JSON provides the full class name. For example, `com.google.android.engage.food.service.AppEngageFoodClient`.
|
||||
- **Note:** Initializing the client class requires a `Context` parameter. For example, `AppEngageFoodClient(context)`.
|
||||
- Always refer to [common.md](references/common.md) for common entities.
|
||||
- Ask which cluster type they want to publish from the supported cluster types for that vertical.
|
||||
- Find the method to call from `{VERTICAL}.md` in the **[references/schemas/](references/schemas)** directory for the specified cluster. Each method will specify the request it expects.
|
||||
- Get the request structure from [requests.md](references/requests.md) and clusters from [clusters.md](references/clusters.md). Then suggest and use sources to fill the fields in the request structure correctly, along with the required entities and clusters.
|
||||
2. **Generate Structured Boilerplate Code:**
|
||||
2. **Generate structured boilerplate code:**
|
||||
|
||||
- Create a new directory for all Engage-related code. Name the directory to match the naming convention of the existing codebase.
|
||||
- Generate the following classes using templates in [patterns.md](references/patterns.md):
|
||||
- `Constants`: Holds constant values like attempt counts, publish types.
|
||||
- `ItemToEntityConverter`: Converts app's local models to Engage's Entity models.
|
||||
- `ClusterRequestFactory`: Constructs the publish requests.
|
||||
- `EngageWorker`: Handles the actual publishing and publish errors using WorkManager.
|
||||
- `EngagePublisher`: Orchestrates periodic and one-time jobs.
|
||||
- `EngageBroadcastReceiver`: Listens for AppEngageService intents and starts a one-time publish job from `EngagePublisher`. **Important** : Implement both **static registration** and **dynamic registration** patterns, including the companion object `register` method inside the `EngageBroadcastReceiver` class.
|
||||
3. **Suggest Entity Mapping:**
|
||||
- `Constants`: holds constant values such as attempt counts and publish types.
|
||||
- `ItemToEntityConverter`: converts the app's local models to Engage's Entity models.
|
||||
- `ClusterRequestFactory`: constructs the publish requests.
|
||||
- `EngageWorker`: handles the actual publishing and publish errors using WorkManager.
|
||||
- `EngagePublisher`: orchestrates periodic and one-time jobs.
|
||||
- `EngageBroadcastReceiver`: listens for AppEngageService intents and starts a one-time publish job from `EngagePublisher`. **Important** : Implement both **static registration** and **dynamic registration** patterns, including the companion object `register` method inside the `EngageBroadcastReceiver` class.
|
||||
3. **Suggest entity mapping:**
|
||||
|
||||
- Ask the developer to provide their local model schema(e.g., a data class or a JSON snippet).
|
||||
- Ask the developer to provide their local model schema (for example, a data class or a JSON snippet).
|
||||
- If they haven't provided one, share entities from `{VERTICAL}.md` in the **[references/schemas/](references/schemas)** directory as a guide.
|
||||
- Once the local model is identified, suggest a mapping to the corresponding Engage entity.
|
||||
- Generate the conversion logic using the `ItemToEntityConverter` pattern in [patterns.md](references/patterns.md) and add it to the generated `{ENGAGE_CODE_DIR}/ItemToEntityConverter`
|
||||
4. **Suggest Data Source:**
|
||||
- Generate the conversion logic using the `ItemToEntityConverter` pattern in [patterns.md](references/patterns.md) and add it to the generated `{ENGAGE_CODE_DIR}/ItemToEntityConverter`.
|
||||
4. **Suggest data source:**
|
||||
|
||||
- Ask the developer to provide the source of actual data you'll publish.
|
||||
- Once the source of data is identified, use the source of data to fetch data in app's local model schema.
|
||||
- Use `{ENGAGE_CODE_DIR}/ItemToEntityConverter` to convert this data to Engage entity.
|
||||
- Once you identify the data source, use it to fetch the data in the app's local model schema.
|
||||
- Use `{ENGAGE_CODE_DIR}/ItemToEntityConverter` to convert this data to an Engage entity.
|
||||
- Use obtained Engage entity model data with `{ENGAGE_CODE_DIR}/
|
||||
ClusterRequestFactory` to get cluster requests.
|
||||
- Call corresponding cluster publishing method obtained from `{VERTICAL}.md` in the **[references/schemas/](references/schemas)** directory with the obtained request in previous step in `{ENGAGE_CODE_DIR}/EngageWorker`.
|
||||
5. **Gradle and Manifest Updates:**
|
||||
5. **Gradle and manifest updates:**
|
||||
|
||||
- Suggest updates to `build.gradle` and `AndroidManifest.xml`.
|
||||
- For mobile apps, use [patterns.md](references/patterns.md).
|
||||
- For TV apps, use the TV-specific sections in [patterns.md](references/patterns.md).
|
||||
- Provide the necessary `implementation` dependencies for `build.gradle` or `build.gradle.kts` from [patterns.md](references/patterns.md).
|
||||
- Provide the `<receiver>` and `<service>` declarations for `AndroidManifest.xml`.
|
||||
- Note: There's no separate import according to vertical except TV. For each vertical other than TV 'com.google.android.engage:engage-core:1.5.12' is enough.
|
||||
- Note: Except for TV, there aren't any vertical-specific imports. For all other verticals, `com.google.android.engage:engage-core:1.6.0` is sufficient.
|
||||
6. **Debugging:**
|
||||
|
||||
- Perform a Gradle sync.
|
||||
@@ -72,51 +72,45 @@ Follow these steps to assist the developer:
|
||||
- Fix import errors. For package `com.google.android.engage` or classes starting with `AppEngage`, verify the package name in the `{VERTICAL}.md` in **[references/schemas/](references/schemas)** directory or [common.md](references/common.md).
|
||||
- Fix any other errors.
|
||||
- Execute a full Gradle build and resolve any remaining compilation issues. Repeat this step until the Gradle build is successful.
|
||||
7. **User Checklist:**
|
||||
At the end of code generation, notify the user to go through this checklist
|
||||
to verify that the integration is complete and as intended:
|
||||
\[ \] Verify that all the engage related files are created in
|
||||
`{ENGAGE_CODE_DIR}/`:
|
||||
- `Constants`
|
||||
- `ItemToEntityConverter`
|
||||
- `ClusterRequestFactory`
|
||||
- `EngageWorker`
|
||||
- `{cluster_type}Publisher`
|
||||
- `EngageBroadcastReceiver`
|
||||
\[ \] Verify that app's local model is converted to Engage entity by populating
|
||||
the fields correctly in the model in `{ENGAGE_CODE_DIR}/
|
||||
ItemToEntityConverter`.
|
||||
\[ \] Verify that `{ENGAGE_CODE_DIR}/EngageWorker` uses the data source
|
||||
identified in Step 4.
|
||||
\[ \] Verify that `EngageBroadcastReceiver.register(context)` is called within
|
||||
the `Application` class or `MainActivity` to register the receiver
|
||||
dynamically.
|
||||
\[ \] Verify that `AndroidManifest.xml` contains the static `<receiver>`
|
||||
declaration for `EngageBroadcastReceiver` with the necessary intent actions.
|
||||
7. **User checklist:** At the end of code generation, notify the user to go
|
||||
through this checklist to verify that the integration is complete and as
|
||||
intended:
|
||||
|
||||
- \[ \] Verify that all the Engage-related files are created in `{ENGAGE_CODE_DIR}/`:
|
||||
- `Constants`
|
||||
- `ItemToEntityConverter`
|
||||
- `ClusterRequestFactory`
|
||||
- `EngageWorker`
|
||||
- `{cluster_type}Publisher`
|
||||
- `EngageBroadcastReceiver`
|
||||
- \[ \] Verify that app's local model is converted to Engage entity by populating the fields correctly in the model in `{ENGAGE_CODE_DIR}/ItemToEntityConverter`.
|
||||
- \[ \] Verify that all image URIs in `ItemToEntityConverter` point to images matching the strict aspect ratio requirements of the vertical (for example, 16:9, 1:1, 2:3).
|
||||
- \[ \] Verify that `{ENGAGE_CODE_DIR}/EngageWorker` uses the data source identified in Step 4.
|
||||
- \[ \] Verify that `EngageBroadcastReceiver.register(context)` is called within the `Application` class or `MainActivity` to register the receiver dynamically.
|
||||
- \[ \] Verify that `AndroidManifest.xml` contains the static `<receiver>` declaration for `EngageBroadcastReceiver` with the necessary intent actions.
|
||||
- **Important** : Explicitly instruct the developer to call `EngageBroadcastReceiver.register(context)` inside their custom `Application` class `onCreate()` (or their main activity `onCreate()`) to dynamically register the receiver. Stress that **both** static and dynamic registrations are required for the integration to function.
|
||||
|
||||
## Reference Materials
|
||||
## Reference materials
|
||||
|
||||
- **FAQ:** [Engage FAQ](references/android/guide/playcore/engage/faq.md) - Refer to this document for answers to frequently
|
||||
asked questions from developers.
|
||||
|
||||
- **Vertical-Specific Guides:**
|
||||
- **Vertical-specific guides:**
|
||||
|
||||
- [Food Vertical](references/android/guide/playcore/engage/food.md)
|
||||
- [Watch Vertical](references/android/guide/playcore/engage/watch.md)
|
||||
- [Listen Vertical](references/android/guide/playcore/engage/listen.md)
|
||||
- [Read Vertical](references/android/guide/playcore/engage/read.md)
|
||||
- [Shopping Vertical](references/android/guide/playcore/engage/shopping.md)
|
||||
- [Social Vertical](references/android/guide/playcore/engage/social.md)
|
||||
- [Social Vertical](https://developer.android.com/guide/playcore/engage/social)
|
||||
- [Travel Vertical](references/android/guide/playcore/engage/travel.md)
|
||||
- [Health \& Fitness Vertical](references/android/guide/playcore/engage/healthandfitness.md)
|
||||
- [Health and Fitness Vertical](references/android/guide/playcore/engage/healthandfitness.md)
|
||||
- [Other Verticals](references/android/guide/playcore/engage/otherverticals.md)
|
||||
- [TV Getting Started](references/android/guide/playcore/engage/tv/getting-started.md)
|
||||
- [TV Recommendations](references/android/guide/playcore/engage/tv/recommendations.md)
|
||||
- [TV Continue Watching](references/android/guide/playcore/engage/tv/continue-watching/index.md)
|
||||
- [TV Entitlements](references/android/guide/playcore/engage/tv/entitlements.md)
|
||||
- **Vertical-Specific Schemas:**
|
||||
- **Vertical-specific schemas:**
|
||||
|
||||
- [Food Schema](references/schemas/food.md)
|
||||
- [Watch Schema](references/schemas/watch.md)
|
||||
|
||||
@@ -1,702 +0,0 @@
|
||||
Boost app engagement by reaching your users where they are. Integrate Engage SDK
|
||||
to deliver personalized recommendations and continuation content directly to
|
||||
users across multiple on-device surfaces, like
|
||||
**[Collections](https://android-developers.googleblog.com/2024/07/introducing-collections-powered-by-engage-sdk.html)** , **[Entertainment
|
||||
Space](https://blog.google/products/android/entertainment-space/)** , and the Play Store. The integration adds
|
||||
less than 50 KB (compressed) to the average APK and takes most apps about a
|
||||
week of developer time. Learn more at our **[business
|
||||
site](http://play.google.com/console/about/programs/EngageSDK)**.
|
||||
|
||||
This guide contains instructions for developer partners to deliver social media
|
||||
content to Engage content surfaces.
|
||||
|
||||
## Integration detail
|
||||
|
||||
The following section captures the integration detail.
|
||||
|
||||
### Terminology
|
||||
|
||||
***Recommendation*** clusters show personalized suggestions from an individual
|
||||
developer partner.
|
||||
|
||||
Your recommendations take the following structure:
|
||||
|
||||
**Recommendation Cluster**: UI view that contains a group of recommendations
|
||||
from the same developer partner.
|
||||
|
||||
Each Recommendation Cluster consists of one of the following two types of
|
||||
entities :
|
||||
|
||||
- PortraitMediaEntity
|
||||
- SocialPostEntity
|
||||
|
||||
**PortraitMediaEntity** must contain 1 portrait image for the post. Profile and
|
||||
Interaction related metadata are optional.
|
||||
|
||||
- Post
|
||||
|
||||
- Image in portrait mode and Timestamp, or
|
||||
- Image in portrait mode + text content and Timestamp
|
||||
- Profile
|
||||
|
||||
- Avatar, Name or Handle, Additional image
|
||||
- Interactions
|
||||
|
||||
- Count and label only, or
|
||||
- Count and visual (icon)
|
||||
|
||||
**SocialPostEntity** contains profile, post and interaction related metadata.
|
||||
|
||||
- Profile
|
||||
|
||||
- Avatar, Name or Handle, additional text, additional image
|
||||
- Post
|
||||
|
||||
- Text and Timestamp, or
|
||||
- Rich media (image or rich URL) and Timestamp, or
|
||||
- Text and rich media (image or rich URL) and Timestamp, or
|
||||
- Video preview (thumbnail and duration) and Timestamp
|
||||
- Interactions
|
||||
|
||||
- Count \& label only, or
|
||||
- Count \& visual (icon)
|
||||
|
||||
### Pre-work
|
||||
|
||||
Minimum API level: 19
|
||||
|
||||
Add the `com.google.android.engage:engage-core` library to your app:
|
||||
|
||||
dependencies {
|
||||
// Make sure you also include that repository in your project's build.gradle file.
|
||||
implementation 'com.google.android.engage:engage-core:1.6.0'
|
||||
}
|
||||
|
||||
### Summary
|
||||
|
||||
The design is based on an implementation of a [bound
|
||||
service](https://developer.android.com/guide/components/bound-services).
|
||||
|
||||
The data a client can publish is subject to the following limits for different
|
||||
cluster types:
|
||||
|
||||
| Cluster type | Cluster limits | Minimum entity limits in a cluster | Maximum entity limits in a cluster |
|
||||
|---|---|---|---|
|
||||
| Recommendation Cluster(s) | At most 7 | At least 1 (`PortraitMediaEntity`, or `SocialPostEntity`) | At most 50 (`PortraitMediaEntity`, or `SocialPostEntity`) |
|
||||
|
||||
### Step 1: Provide entity data
|
||||
|
||||
The SDK has defined different entities to represent each item type. The SDK
|
||||
supports the following entities for the Social category:
|
||||
|
||||
1. `PortraitMediaEntity`
|
||||
2. `SocialPostEntity`
|
||||
|
||||
The charts below outline available attributes and requirements for each type.
|
||||
|
||||
#### `PortraitMediaEntity`
|
||||
|
||||
| Attribute | Requirement | Description | Format |
|
||||
|---|---|---|---|
|
||||
| Action URI | **Required** for all surfaces other than Google TV | Deep Link to the entity in the provider app. Note: You can use deep links for attribution. [Refer to this FAQ](https://developer.android.com/guide/playcore/engage/faq#deeplinks-attribution) | URI |
|
||||
| PlatformSpecificPlayback | **Required** for Google TV surface | Deep Link to the entity in the provider app for platforms like Google TV and Mobile. | List of PlatformSpecificPlayback objects |
|
||||
| Recommendation Reason | Optional | The justification for recommending the content to the user. | RecommendationReason object |
|
||||
| Comments Summary | Optional | Summary of comments for the post. | String |
|
||||
| **Post related metadata (Required)** ||||
|
||||
| Image(s) | Required | Image(s) should be in **portrait aspect ratio.** The UI may show only 1 image when multiple images are provided. However, the UI may provide visual indication that there are more images in the app. *If the post is a video, the provider should provide a thumbnail of the video to be shown as an image.* | See [Image Specifications](https://developer.android.com/guide/playcore/engage/social#image-specs) for guidance. |
|
||||
| Text content | Optional | The main text of a post, update, etc. | String (recommended max 140 chars) |
|
||||
| Timestamp | Optional | Time when the post was published. | Epoch timestamp in milliseconds |
|
||||
| Is video content | Optional | Is the post a video? | boolean |
|
||||
| Video duration | Optional | The duration of the video in milliseconds. | Long |
|
||||
| **Profile related metadata (Optional)** ||||
|
||||
| Name | Required | Profile name or id or handle, eg "John Doe", "@TeamPixel" | String(recommended max 25 chars) |
|
||||
| Avatar | Required | Profile picture or avatar image of the user. **Square 1:1 image** | See [Image Specifications](https://developer.android.com/guide/playcore/engage/social#image-specs) for guidance. |
|
||||
| Additional Image | Optional | Profile badge. for example - verified badge **Square 1:1 image** | See [Image Specifications](https://developer.android.com/guide/playcore/engage/social#image-specs) for guidance. |
|
||||
| **Interactions related metadata (Optional)** ||||
|
||||
| Count | Optional | Indicate the number of interactions, for example - "3.7 M.". **Note:** If both Count and Count Value are provided, Count will be used. **Note:** Partners should use either **Count** or **CountWithOptionalLabel**. | String |
|
||||
| CountWithOptionalLabel | Optional | Indicate the number of interactions with an optional label, for example - "3.7 M Likes.". **Note:** If both CountWithOptionalLabel and Count Value are provided, one of them will be used. **Note:** Partners should use either **Count** or **CountWithOptionalLabel**. | String |
|
||||
| Count Value | Optional | The number of interactions as a value. **Note:** Provide Count Value instead of Count if your app doesn't handle logic on how a large number should be optimized for different display sizes. If both Count and Count Value are provided, Count is used. | Long |
|
||||
| Label | Optional | Indicate what the interaction label is for. For example - "Likes". | String |
|
||||
| Visual | Optional | Indicate what the interaction is for. For example - Image showing Likes icon, emoji. Can provide more than 1 image, though not all may not be shown on all form factors. **Note:** Must be Square 1:1 image | See [Image Specifications](https://developer.android.com/guide/playcore/engage/social#image-specs) for guidance. |
|
||||
| **DisplayTimeWindow (Optional) - Set a time window for a content to be shown on the surface** ||||
|
||||
| Start Timestamp | Optional | The epoch timestamp after which the content should be shown on the surface. If not set, content is eligible to be shown on the surface. | Epoch timestamp in milliseconds |
|
||||
| End Timestamp | Optional | The epoch timestamp after which the content is no longer shown on the surface. If not set, content is eligible to be shown on the surface. | Epoch timestamp in milliseconds |
|
||||
|
||||
#### `SocialPostEntity`
|
||||
|
||||
| Attribute | Requirement | Description | Format |
|
||||
|---|---|---|---|
|
||||
| Action URI | **Required** | Deep Link to the entity in the provider app. Note: You can use deep links for attribution. [Refer to this FAQ](https://developer.android.com/guide/playcore/engage/faq#deeplinks-attribution) | URI |
|
||||
| PlatformSpecificPlayback URIs | **Required** for Google TV surface | Deep Link to the entity in the provider app for platforms like Google TV and Mobile. | List of PlatformSpecificPlayback objects |
|
||||
| Recommendation Reason | Optional | The justification for recommending the content to the user. | RecommendationReason object |
|
||||
| Comments Summary | Optional | Summary of comments for the post. | String |
|
||||
| **Post related metadata (Required)** At least one of TextContent, Image or WebContent is required ||||
|
||||
| Image(s) | Optional | Image(s) should be in **portrait aspect ratio.** The UI may show only 1 image when multiple images are provided. However, the UI may provide visual indication that there are more images in the app. *If the post is a video, the provider should provide a thumbnail of the video to be shown as an image.* | See [Image Specifications](https://developer.android.com/guide/playcore/engage/social#image-specs) for guidance. |
|
||||
| Text content | Optional | The main text of a post, update, etc. | String (recommended max 140 chars) |
|
||||
| **Video Content (Optional)** ||||
|
||||
| Duration | Required | The duration of the video in milliseconds. | Long |
|
||||
| Image | Required | Preview image of the video content. | See [Image Specifications](https://developer.android.com/guide/playcore/engage/social#image-specs) for guidance. |
|
||||
| **Link Preview (Optional)** ||||
|
||||
| Link Preview - Title | Required | Text to indicate the title of the web page content | String |
|
||||
| Link Preview - Hostname | Required | Text to indicate the web page owner, eg "INSIDER" | String |
|
||||
| Link Preview - Image | Optional | Hero image for the web content | See [Image Specifications](https://developer.android.com/guide/playcore/engage/social#image-specs) for guidance. |
|
||||
| Timestamp | Optional | Time when the post was published. | Epoch timestamp in milliseconds |
|
||||
| **Profile related metadata (Optional)** ||||
|
||||
| Name | Required | Profile name or id or handle, eg "John Doe", "@TeamPixel." | String(recommended max 25 chars) |
|
||||
| Additional Text | Optional | Could be used as profile id or handle or additional metadata For example "@John-Doe", "5M followers", "You might like", "Trending", "5 new posts" | String(recommended max 40 chars) |
|
||||
| Avatar | Required | Profile picture or avatar image of the user. **Square 1:1 image** | See [Image Specifications](https://developer.android.com/guide/playcore/engage/social#image-specs) for guidance. |
|
||||
| Additional Image | Optional | Profile badge, for example - verified badge **Square 1:1 image** | See [Image Specifications](https://developer.android.com/guide/playcore/engage/social#image-specs) for guidance. |
|
||||
| **Interactions related metadata (Optional)** ||||
|
||||
| Count | Required | Indicate the number of interactions, for example - "3.7 M." **Note:** Partners should use either **Count** or **CountWithOptionalLabel**. | String |
|
||||
| CountWithOptionalLabel | Required | Indicate the number of interactions with an optional label, for example - "3.7 M Likes." **Note:** Partners should use either **Count** or **CountWithOptionalLabel**. | String |
|
||||
| Label | Optional If not provided, **Visual** must be provided. | Indicate what the interaction is for. For example - "Likes." | String (recommended max 20 chars for count + label combined) |
|
||||
| Visual | Optional If not provided, **Label** must be provided. | Indicate what the interaction is for. For example - Image showing Likes icon, emoji. Can provide more than 1 image, though not all may not be shown on all form factors. **Square 1:1 image** | See [Image Specifications](https://developer.android.com/guide/playcore/engage/social#image-specs) for guidance. |
|
||||
| **DisplayTimeWindow (Optional) - Set a time window for a content to be shown on the surface** ||||
|
||||
| Start Timestamp | Optional | The epoch timestamp after which the content should be shown on the surface. If not set, content is eligible to be shown on the surface. | Epoch timestamp in milliseconds |
|
||||
| End Timestamp | Optional | The epoch timestamp after which the content is no longer shown on the surface. If not set, content is eligible to be shown on the surface. | Epoch timestamp in milliseconds |
|
||||
|
||||
#### Image specifications
|
||||
|
||||
The images are required to be hosted on public CDNs so that Google can access
|
||||
them.
|
||||
|
||||
*File formats*
|
||||
|
||||
PNG, JPG, static GIF, WebP
|
||||
|
||||
*Maximum file size*
|
||||
|
||||
5120 KB
|
||||
|
||||
*Additional recommendations*
|
||||
|
||||
- **Image safe area:** Put your important content in the center 80% of the image.
|
||||
- Use a transparent background so that the image can be properly displayed in Dark and Light theme settings.
|
||||
|
||||
### Step 2: Provide Cluster data
|
||||
|
||||
It is recommended to have the content publish job executed in the background
|
||||
(for example, using [WorkManager](https://developer.android.com/topic/libraries/architecture/workmanager))
|
||||
and scheduled on a regular basis or on an event basis (for example, every time
|
||||
the user opens the app or when the user just followed a new account)
|
||||
|
||||
`AppEngageSocialClient` is responsible for publishing social clusters.
|
||||
|
||||
There are following APIs to publish clusters in the client:
|
||||
|
||||
- `isServiceAvailable`
|
||||
- `publishRecommendationClusters`
|
||||
- `publishUserAccountManagementRequest`
|
||||
- `updatePublishStatus`
|
||||
- `deleteRecommendationsClusters`
|
||||
- `deleteUserManagementCluster`
|
||||
- `deleteClusters`
|
||||
|
||||
#### `isServiceAvailable`
|
||||
|
||||
This API is used to check if the service is available for integration and
|
||||
whether the content can be presented on the device.
|
||||
|
||||
##### For Engage SDK v1.6.0 and higher (Recommended)
|
||||
|
||||
You can check the service availability for every cluster type that you intend to
|
||||
publish. The `isServiceAvailable` API accepts a request object,
|
||||
`ServiceAvailabilityRequest`, which contains the cluster types for which service
|
||||
availability needs to be checked. You can find the `ClusterType` enum values
|
||||
required for `ServiceAvailabilityRequest` from the following table.
|
||||
|
||||
| Cluster Type | Cluster Type Constant | Integer Value |
|
||||
|---|---|---|
|
||||
| Unknown | `TYPE_UNKNOWN` | 0 |
|
||||
| Recommendation Cluster | `TYPE_RECOMMENDATION` | 1 |
|
||||
| Featured Cluster | `TYPE_FEATURED` | 2 |
|
||||
| Continuation Cluster | `TYPE_CONTINUATION` | 3 |
|
||||
| User Management Cluster | `TYPE_ENGAGEMENT` | 8 |
|
||||
| Subscription Cluster | `TYPE_SUBSCRIPTION` | 12 |
|
||||
|
||||
### Kotlin
|
||||
|
||||
val request = ServiceAvailabilityRequest.Builder()
|
||||
.addIntendedClusterType(ClusterType.TYPE_CONTINUATION)
|
||||
.addIntendedClusterType(ClusterType.TYPE_RECOMMENDATION)
|
||||
.build()
|
||||
|
||||
client.isServiceAvailable(request).addOnCompleteListener { task ->
|
||||
if (task.isSuccessful) {
|
||||
val availabilityMap = task.result
|
||||
if (availabilityMap[ClusterType.TYPE_CONTINUATION] == true) {
|
||||
// Proceed with publishing continuation content
|
||||
}
|
||||
if (availabilityMap[ClusterType.TYPE_RECOMMENDATION] == true) {
|
||||
// Proceed with publishing recommendation content
|
||||
}
|
||||
} else {
|
||||
// The IPC call itself fails, proceed with error handling logic here,
|
||||
// such as retry.
|
||||
}
|
||||
}
|
||||
|
||||
### Java
|
||||
|
||||
ServiceAvailabilityRequest request =
|
||||
new ServiceAvailabilityRequest.Builder()
|
||||
.addIntendedClusterType(ClusterType.TYPE_CONTINUATION)
|
||||
.addIntendedClusterType(ClusterType.TYPE_RECOMMENDATION)
|
||||
.build();
|
||||
|
||||
client.isServiceAvailable(request).addOnCompleteListener(task -> {
|
||||
if (task.isSuccessful()) {
|
||||
Map<Integer, Boolean> availabilityMap = task.getResult();
|
||||
if (Boolean.TRUE.equals(availabilityMap.get(ClusterType.TYPE_CONTINUATION))) {
|
||||
// Proceed with publishing continuation content
|
||||
}
|
||||
if (Boolean.TRUE.equals(availabilityMap.get(ClusterType.TYPE_RECOMMENDATION))) {
|
||||
// Proceed with publishing recommendation content
|
||||
}
|
||||
} else {
|
||||
// The IPC call itself fails, proceed with error handling logic here,
|
||||
// such as retry.
|
||||
}
|
||||
});
|
||||
|
||||
###### Conditional Service Availability Feature
|
||||
|
||||
Some integrated apps request a special configuration that enables and disables
|
||||
the Engage service intermittently in order to reduce their serving cost. This
|
||||
intermittent content ingestion strategy, although possible, negatively affects
|
||||
the user and the product -- stale content will not be presented and some surfaces
|
||||
will not be served at all.
|
||||
|
||||
Starting with v1.6.0, the Engage SDK allows checking availability for specific
|
||||
cluster types. If you are interested in opting into this feature for any cluster type,
|
||||
please contact engage-developers@google.com.
|
||||
|
||||
##### For SDK versions prior to v1.6.0 (Deprecated)
|
||||
|
||||
### Kotlin
|
||||
|
||||
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.
|
||||
}
|
||||
}
|
||||
|
||||
### Java
|
||||
|
||||
client.isServiceAvailable().addOnCompleteListener(task - > {
|
||||
if (task.isSuccessful()) {
|
||||
// Handle success
|
||||
if(task.getResult()) {
|
||||
// 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.
|
||||
}
|
||||
});
|
||||
|
||||
> [!NOTE]
|
||||
> **Note:** We highly recommend keeping a periodic job running to check if the service becomes available at a later point in time. The availability of the service may change with Android version upgrades, app upgrades, installs, and uninstalls. By ensuring periodic job checks at a certain time interval, data can be published once the service becomes available.
|
||||
|
||||
#### `publishRecommendationClusters`
|
||||
|
||||
This API is used to publish a list `RecommendationCluster` objects.
|
||||
|
||||
A `RecommendationCluster` object can have the following attributes:
|
||||
|
||||
| Attribute | Requirement | Description |
|
||||
|---|---|---|
|
||||
| List of SocialPostEntity, or PortraitMediaEntity | **Required** | A list of entities that make up the recommendations for this Recommendation Cluster. Entities in a single cluster must be of the same type. |
|
||||
| Title | **Required** | The title for the Recommendation Cluster (for example, *Latest from your friends*). **Recommended text size: under 25 chars** (Text that is too long may show ellipses) |
|
||||
| Subtitle | Optional | The subtitle for the Recommendation Cluster. |
|
||||
| Action Uri | Optional | The deep link to the page in the partner app where users can see the complete list of recommendations. Note: You can use deep links for attribution. [Refer to this FAQ](https://developer.android.com/guide/playcore/engage/faq#deeplinks-attribution) |
|
||||
|
||||
> [!IMPORTANT]
|
||||
> **Important:** The publish APIs are upsert APIs; it replaces the existing content. **Don't** call delete and publish APIs subsequently to replace the content as the publish APIs do that inherently.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> **Important:** For social apps, it's critical to update recommendations after each app usage. Social app users are more interested in the most recent recommendations and ideally would like to see a post at most once.
|
||||
|
||||
### Kotlin
|
||||
|
||||
client.publishRecommendationClusters(
|
||||
PublishRecommendationClustersRequest.Builder()
|
||||
.addRecommendationCluster(
|
||||
RecommendationCluster.Builder()
|
||||
.addEntity(entity1)
|
||||
.addEntity(entity2)
|
||||
.setTitle("Latest from your friends")
|
||||
.build())
|
||||
.build())
|
||||
|
||||
### Java
|
||||
|
||||
client.publishRecommendationClusters(
|
||||
new PublishRecommendationClustersRequest.Builder()
|
||||
.addRecommendationCluster(
|
||||
new RecommendationCluster.Builder()
|
||||
.addEntity(entity1)
|
||||
.addEntity(entity2)
|
||||
.setTitle("Latest from your friends")
|
||||
.build())
|
||||
.build());
|
||||
|
||||
When the service receives the request, the following actions take place within
|
||||
one transaction:
|
||||
|
||||
- All existing Recommendation Cluster data is removed.
|
||||
- Data from the request is parsed and stored in new Recommendation Clusters.
|
||||
|
||||
In case of an error, the entire request is rejected and the existing state is
|
||||
maintained.
|
||||
|
||||
#### `publishUserAccountManagementRequest`
|
||||
|
||||
This API is used to publish a Sign In card . The signin action directs users to
|
||||
the app's sign in page so that the app can publish content (or provide more
|
||||
personalized content)
|
||||
|
||||
The following metadata is part of the 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 |
|
||||
| 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) |
|
||||
| Subtitle | Optional | Optional Subtitle on the Card |
|
||||
|
||||
> [!IMPORTANT]
|
||||
> **Important:** The publish APIs are upsert APIs; it replaces the existing content. **Don't** call delete and publish APIs subsequently to replace the content as the publish APIs do that inherently.
|
||||
|
||||
### Kotlin
|
||||
|
||||
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());
|
||||
|
||||
### Java
|
||||
|
||||
SignInCardEntity SIGN_IN_CARD_ENTITY =
|
||||
new SignInCardEntity.Builder()
|
||||
.addPosterImage(
|
||||
new 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(
|
||||
new PublishUserAccountManagementRequest.Builder()
|
||||
.setSignInCardEntity(SIGN_IN_CARD_ENTITY)
|
||||
.build());
|
||||
|
||||
When the service receives the request, the following actions take place within
|
||||
one transaction:
|
||||
|
||||
- Existing `UserAccountManagementCluster` data from the developer partner is removed.
|
||||
- Data from the request is parsed and stored in the updated UserAccountManagementCluster Cluster.
|
||||
|
||||
In case of an error, the entire request is rejected and the existing state is
|
||||
maintained.
|
||||
|
||||
#### `updatePublishStatus`
|
||||
|
||||
If for any internal business reason, none of the clusters is published,
|
||||
we **strongly recommend** updating the publish status using the
|
||||
**updatePublishStatus** API.
|
||||
This is important because :
|
||||
|
||||
- Providing the status in all scenarios, even when the content is published (STATUS == PUBLISHED), is critical to populate dashboards that use this explicit status to convey the health and other metrics of your integration.
|
||||
- If no content is published but the integration status isn't broken (STATUS == NOT_PUBLISHED), Google can avoid triggering alerts in the app health dashboards. It confirms that content is not published due to an **expected** situation from the provider's standpoint.
|
||||
- It helps developers provide insights into when the data is published versus not.
|
||||
- Google may use the status codes to nudge the user to do certain actions in the app so they can see the app content or overcome it.
|
||||
|
||||
The list of eligible publish status codes are :
|
||||
|
||||
// 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
|
||||
|
||||
If the content is not published due to a user not logged in,
|
||||
Google would recommend publishing the Sign In Card.
|
||||
If for any reason providers are not able to publish the Sign In Card
|
||||
then we recommend calling the **updatePublishStatus** API
|
||||
with the status code **NOT_PUBLISHED_REQUIRES_SIGN_IN**
|
||||
|
||||
### Kotlin
|
||||
|
||||
client.updatePublishStatus(
|
||||
PublishStatusRequest.Builder()
|
||||
.setStatusCode(AppEngagePublishStatusCode.NOT_PUBLISHED_REQUIRES_SIGN_IN)
|
||||
.build())
|
||||
|
||||
### Java
|
||||
|
||||
client.updatePublishStatus(
|
||||
new PublishStatusRequest.Builder()
|
||||
.setStatusCode(AppEngagePublishStatusCode.NOT_PUBLISHED_REQUIRES_SIGN_IN)
|
||||
.build());
|
||||
|
||||
#### `deleteRecommendationClusters`
|
||||
|
||||
This API is used to delete the content of Recommendation Clusters.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> **Important:** Delete APIs should only be called when there is no content to publish. **Don't** call delete and publish APIs subsequently to replace the content as the publish APIs do that inherently. Reach out to [`engage-developers@google.com`](mailto:engage-developers@google.com) before using delete APIs.
|
||||
|
||||
### Kotlin
|
||||
|
||||
client.deleteRecommendationClusters()
|
||||
|
||||
### Java
|
||||
|
||||
client.deleteRecommendationClusters();
|
||||
|
||||
When the service receives the request, it removes the existing data from the
|
||||
Recommendation Clusters. In case of an error, the entire request is rejected
|
||||
and the existing state is maintained.
|
||||
|
||||
#### `deleteUserManagementCluster`
|
||||
|
||||
This API is used to delete the content of UserAccountManagement Cluster.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> **Important:** Delete APIs should only be called when there is no content to publish. **Don't** call delete and publish APIs subsequently to replace the content as the publish APIs do that inherently. Reach out to [`engage-developers@google.com`](mailto:engage-developers@google.com) before using delete APIs.
|
||||
|
||||
### Kotlin
|
||||
|
||||
client.deleteUserManagementCluster()
|
||||
|
||||
### Java
|
||||
|
||||
client.deleteUserManagementCluster();
|
||||
|
||||
When the service receives the request, it removes the existing data from the
|
||||
UserAccountManagement Cluster. In case of an error, the entire request is
|
||||
rejected and the existing state is maintained.
|
||||
|
||||
#### `deleteClusters`
|
||||
|
||||
This API is used to delete the content of a given cluster type.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> **Important:** Delete APIs should only be called when there is no content to publish. **Don't** call delete and publish APIs subsequently to replace the content as the publish APIs do that inherently. Reach out to [`engage-developers@google.com`](mailto:engage-developers@google.com) before using delete APIs.
|
||||
|
||||
### Kotlin
|
||||
|
||||
client.deleteClusters(
|
||||
DeleteClustersRequest.Builder()
|
||||
.addClusterType(ClusterType.TYPE_RECOMMENDATION)
|
||||
...
|
||||
.build())
|
||||
|
||||
### Java
|
||||
|
||||
client.deleteClusters(
|
||||
new DeleteClustersRequest.Builder()
|
||||
.addClusterType(ClusterType.TYPE_RECOMMENDATION)
|
||||
...
|
||||
.build());
|
||||
|
||||
When the service receives the request, it removes the existing data from all
|
||||
clusters matching the specified cluster types. Clients can choose to pass one or
|
||||
many cluster types. In case of an error, the entire request is rejected and the
|
||||
existing state is maintained.
|
||||
|
||||
#### Error handling
|
||||
|
||||
It is highly recommended to listen to the task result from the publish APIs such
|
||||
that a follow-up action can be taken to recover and resubmit an successful task.
|
||||
|
||||
client.publishRecommendationClusters(
|
||||
new PublishRecommendationClustersRequest.Builder()
|
||||
.addRecommendationCluster(...)
|
||||
.build())
|
||||
.addOnCompleteListener(
|
||||
task -> {
|
||||
if (task.isSuccessful()) {
|
||||
// do something
|
||||
} else {
|
||||
Exception exception = task.getException();
|
||||
if (exception instanceof AppEngageException) {
|
||||
@AppEngageErrorCode
|
||||
int errorCode = ((AppEngageException) exception).getErrorCode();
|
||||
if (errorCode == AppEngageErrorCode.SERVICE_NOT_FOUND) {
|
||||
// do something
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
The error is returned as an `AppEngageException` with the cause included as an
|
||||
error code.
|
||||
|
||||
| Error code | Error name | Note |
|
||||
|---|---|---|
|
||||
| `1` | `SERVICE_NOT_FOUND` | The service is not available on the given device. |
|
||||
| `2` | `SERVICE_NOT_AVAILABLE` | The service is available on the given device, but it is not available at the time of the call (for example, it is explicitly disabled). |
|
||||
| `3` | `SERVICE_CALL_EXECUTION_FAILURE` | The task execution failed due to threading issues. In this case, it can be retried. |
|
||||
| `4` | `SERVICE_CALL_PERMISSION_DENIED` | The caller is not allowed to make the service call. |
|
||||
| `5` | `SERVICE_CALL_INVALID_ARGUMENT` | The request contains invalid data (for example, more than the allowed number of clusters). |
|
||||
| `6` | `SERVICE_CALL_INTERNAL` | There is an error on the service side. |
|
||||
| `7` | `SERVICE_CALL_RESOURCE_EXHAUSTED` | The service call is made too frequently. |
|
||||
|
||||
### Step 3: Handle broadcast intents
|
||||
|
||||
In addition to making publish content API calls through a job, it is also
|
||||
required to set up a
|
||||
[`BroadcastReceiver`](https://developer.android.com/reference/android/content/BroadcastReceiver) to receive
|
||||
the request for a content publish.
|
||||
|
||||
The goal of broadcast intents is mainly for app reactivation and forcing data
|
||||
sync. Broadcast intents are not designed to be sent very frequently. It is only
|
||||
triggered when the Engage Service determines the content might be stale (for
|
||||
example, a week old). That way, there is more confidence that the user can have
|
||||
a fresh content experience, even if the application has not been executed for a
|
||||
long period of time.
|
||||
|
||||
The `BroadcastReceiver` must be set up in the following two ways:
|
||||
|
||||
- Dynamically register an instance of the `BroadcastReceiver` class using
|
||||
`Context.registerReceiver()`. This enables communication from applications
|
||||
that are still live in memory.
|
||||
|
||||
### Kotlin
|
||||
|
||||
class AppEngageBroadcastReceiver : BroadcastReceiver(){
|
||||
// Trigger recommendation cluster publish when PUBLISH_RECOMMENDATION
|
||||
// broadcast is received
|
||||
}
|
||||
|
||||
fun registerBroadcastReceivers(context: Context){
|
||||
var context = context
|
||||
context = context.applicationContext
|
||||
|
||||
// Register Recommendation Cluster Publish Intent
|
||||
context.registerReceiver(AppEngageBroadcastReceiver(),
|
||||
IntentFilter(Intents.ACTION_PUBLISH_RECOMMENDATION),
|
||||
com.google.android.engage.service.BroadcastReceiverPermissions.BROADCAST_REQUEST_DATA_PUBLISH_PERMISSION,
|
||||
/*scheduler=*/null)
|
||||
}
|
||||
|
||||
### Java
|
||||
|
||||
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),
|
||||
com.google.android.engage.service.BroadcastReceiverPermissions.BROADCAST_REQUEST_DATA_PUBLISH_PERMISSION,
|
||||
/*scheduler=*/null);
|
||||
}
|
||||
|
||||
- Statically declare an implementation with the `<receiver>` tag in your
|
||||
`AndroidManifest.xml` file. This allows the application to receive broadcast
|
||||
intents when it is not running, and also allows the application to publish
|
||||
the content.
|
||||
|
||||
<application>
|
||||
<receiver
|
||||
android:name=".AppEngageBroadcastReceiver"
|
||||
android:permission="com.google.android.engage.REQUEST_ENGAGE_DATA"
|
||||
android:exported="true"
|
||||
android:enabled="true">
|
||||
<intent-filter>
|
||||
<action android:name="com.google.android.engage.action.PUBLISH_RECOMMENDATION" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
</application>
|
||||
|
||||
The following [intents](https://developer.android.com/reference/android/content/Intent) will be sent by the
|
||||
service:
|
||||
|
||||
- `com.google.android.engage.action.PUBLISH_RECOMMENDATION` It is recommended to start a `publishRecommendationClusters` call when receiving this intent.
|
||||
|
||||
## Integration workflow
|
||||
|
||||
For a step-by-step guide on verifying your integration after it is complete, see
|
||||
[Engage developer integration workflow](https://developer.android.com/guide/playcore/engage/workflow).
|
||||
|
||||
## FAQs
|
||||
|
||||
See [Engage SDK Frequently Asked Questions](https://developer.android.com/guide/playcore/engage/faq) for
|
||||
FAQs.
|
||||
|
||||
## Contact
|
||||
|
||||
Contact
|
||||
[`engage-developers@google.com`](mailto:engage-developers@google.com) if there are
|
||||
any questions during the integration process. Our team will reply as soon as
|
||||
possible.
|
||||
|
||||
## Next steps
|
||||
|
||||
After completing this integration, your next steps are as follows:
|
||||
|
||||
- Send an email to [`engage-developers@google.com`](mailto:engage-developers@google.com) and attach your integrated APK that is ready for testing by Google.
|
||||
- Google performs a verification and reviews internally to make sure the integration works as expected. If changes are needed, Google contacts you with any necessary details.
|
||||
- When testing is complete and no changes are needed, Google contacts you to notify you that you can start publishing the updated and integrated APK to the Play Store.
|
||||
- After Google has confirmed that your updated APK has been published to the Play Store, your **Recommendation**, clusters will be published and visible to users.
|
||||
@@ -1,4 +1,4 @@
|
||||
This file defines the structure of various clusters in the Engage SDK.
|
||||
The Engage SDK defines cluster structures as shown in this reference:
|
||||
|
||||
{
|
||||
"clusters": {
|
||||
@@ -107,7 +107,7 @@ This file defines the structure of various clusters in the Engage SDK.
|
||||
},
|
||||
"recommendationClusterType": {
|
||||
"type": "@RecommendationClusterType int",
|
||||
"requirement": "Optional",
|
||||
"requirement": "Required",
|
||||
"setter": "setRecommendationClusterType(@RecommendationClusterType int)",
|
||||
"getter": "getRecommendationClusterType()"
|
||||
}
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
|
||||
Setting up the `BroadcastReceiver` correctly requires **both** static and
|
||||
dynamic registration. Static registration allows the app to receive broadcasts
|
||||
even when it is not running, while dynamic registration is required on newer
|
||||
even when it isn't running, while dynamic registration is required on newer
|
||||
Android versions to safely receive broadcasts when the app is live in memory.
|
||||
|
||||
### BroadcastReceiver Implementation
|
||||
### BroadcastReceiver implementation
|
||||
|
||||
|
||||
```kotlin
|
||||
@@ -69,13 +69,13 @@ class EngageBroadcastReceiver : BroadcastReceiver() {
|
||||
|
||||
<br />
|
||||
|
||||
### Static Registration (AndroidManifest.xml)
|
||||
### Static registration in AndroidManifest.xml
|
||||
|
||||
Add the `<receiver>` tag inside the `<application>` block in
|
||||
`AndroidManifest.xml`
|
||||
|
||||
|
||||
```kotlin
|
||||
```xml
|
||||
<!-- Add the `<receiver>` tag inside the `<application>` block in `AndroidManifest.xml`:-->
|
||||
<receiver
|
||||
android:name="com.example.snippets.engage.EngageBroadcastReceiver"
|
||||
@@ -102,11 +102,13 @@ import android.content.Context
|
||||
import android.util.Log
|
||||
import androidx.work.CoroutineWorker
|
||||
import androidx.work.WorkerParameters
|
||||
import com.google.android.engage.common.datamodel.ClusterType
|
||||
import com.google.android.engage.service.AppEngageErrorCode
|
||||
import com.google.android.engage.service.AppEngageException
|
||||
import com.google.android.engage.service.AppEngagePublishClient
|
||||
import com.google.android.engage.service.AppEngagePublishStatusCode
|
||||
import com.google.android.engage.service.PublishStatusRequest
|
||||
import com.google.android.engage.service.ServiceAvailabilityRequest
|
||||
import com.google.android.gms.tasks.Task
|
||||
import kotlinx.coroutines.tasks.await
|
||||
|
||||
@@ -125,15 +127,25 @@ class EngageWorker(context: Context, workerParams: WorkerParameters) : Coroutine
|
||||
return Result.failure()
|
||||
}
|
||||
|
||||
// Check if engage service is available before publishing.
|
||||
val isAvailable = client.isServiceAvailable.await()
|
||||
|
||||
// If the service is not available, do not attempt to publish and indicate failure.
|
||||
if (!isAvailable) {
|
||||
return Result.failure()
|
||||
val publishType = inputData.getString(Constants.PUBLISH_TYPE_KEY)
|
||||
val intendedClusterType = when (publishType) {
|
||||
Constants.PUBLISH_TYPE_RECOMMENDATIONS -> ClusterType.TYPE_RECOMMENDATION
|
||||
Constants.PUBLISH_TYPE_FEATURED -> ClusterType.TYPE_FEATURED
|
||||
Constants.PUBLISH_TYPE_CONTINUATION -> ClusterType.TYPE_CONTINUATION
|
||||
Constants.PUBLISH_TYPE_USER_ACCOUNT_MANAGEMENT -> ClusterType.TYPE_ENGAGEMENT
|
||||
else -> ClusterType.TYPE_UNKNOWN
|
||||
}
|
||||
|
||||
if (intendedClusterType != ClusterType.TYPE_UNKNOWN) {
|
||||
val request = ServiceAvailabilityRequest.Builder()
|
||||
.addIntendedClusterType(intendedClusterType)
|
||||
.build()
|
||||
val availabilityMap = client.isServiceAvailable(request).await()
|
||||
if (availabilityMap[intendedClusterType] != true) {
|
||||
return Result.failure()
|
||||
}
|
||||
}
|
||||
|
||||
val publishType = inputData.getString(Constants.PUBLISH_TYPE_KEY)
|
||||
return when (publishType) {
|
||||
Constants.PUBLISH_TYPE_RECOMMENDATIONS -> publishRecommendations()
|
||||
// Constants.PUBLISH_TYPE_FEATURED -> publishFeatured()
|
||||
@@ -411,16 +423,22 @@ object ItemToEntityConverter {
|
||||
|
||||
<br />
|
||||
|
||||
## Dependency Specifications (libs.versions.toml)
|
||||
> **Strict image aspect ratio requirement** : Play Engage has strict
|
||||
> requirements for image aspect ratios depending on the vertical and entity type
|
||||
> (e.g., 16:9 for landscape, 1:1 for square, 2:3 for portrait). Ensure your
|
||||
> `ItemToEntityConverter` maps images that conform to these strict requirements
|
||||
> to avoid cropping or content rejection by Play.
|
||||
|
||||
This skill specifies all dependencies following in `libs.versions.toml` format.
|
||||
## Dependency specifications (libs.versions.toml)
|
||||
|
||||
This skill specifies all dependencies using the `libs.versions.toml` format.
|
||||
Adapt these definitions to other formats (such as standard Groovy `build.gradle`
|
||||
or Kotlin DSL `build.gradle.kts` implementation lines) as required by the
|
||||
project.
|
||||
|
||||
[versions]
|
||||
engage-core = "1.5.12"
|
||||
engage-tv = "1.0.6"
|
||||
engage-core = "1.6.0"
|
||||
engage-tv = "1.1.0"
|
||||
playServicesOssLicenses = "17.5.1"
|
||||
workManager = "2.11.2"
|
||||
coroutines = "1.10.2"
|
||||
@@ -435,22 +453,47 @@ project.
|
||||
kotlinx-coroutines-play-services = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-play-services", version.ref = "coroutines" }
|
||||
kotlinx-coroutines-test = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-test", version.ref = "coroutines" }
|
||||
|
||||
## TV Integrations
|
||||
## Dual content rating fields for Watch and TV
|
||||
|
||||
For Watch and TV integrations, you must populate both the new `contentRatings`
|
||||
(which uses `RatingSystem`) and the legacy `contentRatingsLegacies` (which uses
|
||||
`String` lists) to ensure compatibility across all Google Play surfaces.
|
||||
|
||||
|
||||
```kotlin
|
||||
fun convertMovie(movie: MovieData): MovieEntity {
|
||||
val ratingSystem = RatingSystem.Builder()
|
||||
.setAgencyName("MPAA")
|
||||
.setRating("PG-13")
|
||||
.build()
|
||||
return MovieEntity.Builder()
|
||||
.setEntityId(movie.id)
|
||||
.setName(movie.title)
|
||||
// ... other fields
|
||||
.addContentRating(ratingSystem) // Recommended API
|
||||
.addContentRatingsLegacy(listOf("MPAA:PG-13")) // Legacy API for backward compatibility
|
||||
.build()
|
||||
}
|
||||
```
|
||||
|
||||
<br />
|
||||
|
||||
## TV integrations
|
||||
|
||||
The following patterns and configurations are specific to Android TV
|
||||
integrations.
|
||||
|
||||
### AndroidManifest.xml (TV)
|
||||
### AndroidManifest.xml for TV
|
||||
|
||||
|
||||
```kotlin
|
||||
```xml
|
||||
<!-- Mandatory for TV integrations -->
|
||||
<uses-permission android:name="com.android.providers.tv.permission.WRITE_EPG_DATA" />
|
||||
```
|
||||
|
||||
<br />
|
||||
|
||||
### PlatformSpecificUri Example
|
||||
### PlatformSpecificUri example
|
||||
|
||||
|
||||
```kotlin
|
||||
@@ -468,7 +511,7 @@ val platformSpecificPlaybackUris = listOf(
|
||||
|
||||
<br />
|
||||
|
||||
### AccountProfile Example
|
||||
### AccountProfile example
|
||||
|
||||
|
||||
```kotlin
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
This file defines the request structures for publishing various data models in
|
||||
Defines the request structures for publishing various data models in
|
||||
the Engage SDK.
|
||||
|
||||
{
|
||||
@@ -15,7 +15,8 @@ the Engage SDK.
|
||||
"type": "@NonNull AccountProfile",
|
||||
"requirement": "Optional",
|
||||
"setter": "setAccountProfile(@NonNull AccountProfile)",
|
||||
"getter": "getAccountProfile()"
|
||||
"getter": "getAccountProfile()",
|
||||
"description": "Required for personalization and cross-device syncing of recommendations."
|
||||
},
|
||||
"syncAcrossDevices": {
|
||||
"type": "Boolean",
|
||||
@@ -227,5 +228,17 @@ the Engage SDK.
|
||||
"getter": "getReservationCluster()"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ServiceAvailabilityRequest": {
|
||||
"package": "com.google.android.engage.service.ServiceAvailabilityRequest",
|
||||
"fields": {
|
||||
"intendedClusterTypes": {
|
||||
"type": "List<Integer>",
|
||||
"requirement": "Required",
|
||||
"adder": "addIntendedClusterType(@ClusterType int)",
|
||||
"adderAll": "addAllIntendedClusterTypes(List<Integer>)",
|
||||
"getter": "getIntendedClusterTypes()"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -319,7 +319,7 @@ This file defines the schema for the FOOD vertical in the Engage SDK.
|
||||
}
|
||||
},
|
||||
"methods": {
|
||||
"isServiceAvailable": null,
|
||||
"isServiceAvailable": "ServiceAvailabilityRequest",
|
||||
"publishRecommendationClusters": "PublishRecommendationClustersRequest",
|
||||
"publishFeaturedCluster": "PublishFeaturedClusterRequest",
|
||||
"publishFoodShoppingCarts": "PublishFoodShoppingCartsRequest",
|
||||
|
||||
@@ -825,7 +825,7 @@ This file defines the schema for the LISTEN vertical in the Engage SDK.
|
||||
}
|
||||
},
|
||||
"methods": {
|
||||
"isServiceAvailable": null,
|
||||
"isServiceAvailable": "ServiceAvailabilityRequest",
|
||||
"publishRecommendationClusters": "PublishRecommendationClustersRequest",
|
||||
"publishFeaturedCluster": "PublishFeaturedClusterRequest",
|
||||
"publishContinuationCluster": "PublishContinuationClusterRequest",
|
||||
|
||||
@@ -203,7 +203,7 @@ This file defines the schema for the OTHER vertical in the Engage SDK.
|
||||
}
|
||||
},
|
||||
"methods": {
|
||||
"isServiceAvailable": null,
|
||||
"isServiceAvailable": "ServiceAvailabilityRequest",
|
||||
"publishRecommendationClusters": "PublishRecommendationClustersRequest",
|
||||
"publishFeaturedCluster": "PublishFeaturedClusterRequest",
|
||||
"publishContinuationCluster": "PublishContinuationClusterRequest",
|
||||
|
||||
@@ -395,7 +395,7 @@ This file defines the schema for the READ vertical in the Engage SDK.
|
||||
}
|
||||
},
|
||||
"methods": {
|
||||
"isServiceAvailable": null,
|
||||
"isServiceAvailable": "ServiceAvailabilityRequest",
|
||||
"publishRecommendationClusters": "PublishRecommendationClustersRequest",
|
||||
"publishFeaturedCluster": "PublishFeaturedClusterRequest",
|
||||
"publishContinuationCluster": "PublishContinuationClusterRequest",
|
||||
|
||||
@@ -86,7 +86,7 @@ This file defines the schema for the SHOPPING vertical in the Engage SDK.
|
||||
}
|
||||
},
|
||||
"methods": {
|
||||
"isServiceAvailable": null,
|
||||
"isServiceAvailable": "ServiceAvailabilityRequest",
|
||||
"publishRecommendationClusters": "PublishRecommendationClustersRequest",
|
||||
"publishFeaturedCluster": "PublishFeaturedClusterRequest",
|
||||
"publishShoppingCart": "PublishShoppingCartClusterRequest",
|
||||
|
||||
@@ -217,7 +217,7 @@ This file defines the schema for the SOCIAL vertical in the Engage SDK.
|
||||
}
|
||||
},
|
||||
"methods": {
|
||||
"isServiceAvailable": null,
|
||||
"isServiceAvailable": "ServiceAvailabilityRequest",
|
||||
"publishRecommendationClusters": "PublishRecommendationClustersRequest",
|
||||
"publishUserAccountManagementRequest": "PublishUserAccountManagementRequest",
|
||||
"updatePublishStatus": "PublishStatusRequest",
|
||||
|
||||
@@ -861,7 +861,7 @@ This file defines the schema for the TRAVEL vertical in the Engage SDK.
|
||||
}
|
||||
},
|
||||
"methods": {
|
||||
"isServiceAvailable": null,
|
||||
"isServiceAvailable": "ServiceAvailabilityRequest",
|
||||
"publishRecommendationClusters": "PublishRecommendationClustersRequest",
|
||||
"publishFeaturedCluster": "PublishFeaturedClusterRequest",
|
||||
"publishUserAccountManagementRequest": "PublishUserAccountManagementRequest",
|
||||
|
||||
@@ -1209,7 +1209,7 @@ This file defines the schema for the TV vertical in the Engage SDK.
|
||||
}
|
||||
},
|
||||
"methods": {
|
||||
"isServiceAvailable": null,
|
||||
"isServiceAvailable": "ServiceAvailabilityRequest",
|
||||
"publishRecommendationClusters": "PublishRecommendationClustersRequest",
|
||||
"publishFeaturedCluster": "PublishFeaturedClusterRequest",
|
||||
"publishContinuationCluster": "PublishContinuationClusterRequest",
|
||||
|
||||
@@ -1120,7 +1120,7 @@ This file defines the schema for the WATCH vertical in the Engage SDK.
|
||||
}
|
||||
},
|
||||
"methods": {
|
||||
"isServiceAvailable": null,
|
||||
"isServiceAvailable": "ServiceAvailabilityRequest",
|
||||
"publishRecommendationClusters": "PublishRecommendationClustersRequest",
|
||||
"publishFeaturedCluster": "PublishFeaturedClusterRequest",
|
||||
"publishContinuationCluster": "PublishContinuationClusterRequest",
|
||||
|
||||
Reference in New Issue
Block a user