Engage SDK Codelab

1. Introduction

Last Updated: 2025-05-14

What is Engage SDK?

Engage SDK can help boost your app's engagement by bringing your personalized app content to where users are across multiple on-device Google surfaces. With Engage SDK, your app can deliver personalized (1:1) recommendations and continuation content to engage your installed users before they open your app.

Content Surfaces

Collections

Entertainment Space

Play Store (coming soon)

Bring your content directly to the user's home screen with a new Play Store widget.

Create new touchpoints for entertainment content on select Android tablets.

Access additional surfaces, starting with the Play Store this summer.

What you'll build

When you have completed this codelab, you will have an Android video app that will be able to send content to a Google surface.

What you'll need

  • The latest Android SDK.
  • The latest Android Studio
  • One mobile device with Android 10+.
  • A USB data cable to connect your mobile device to your development computer.

Experience

  • You will need to have Java or Kotlin experience.
  • You will need to have Android development knowledge.

2. Run the sample app

To get you started, download the sample app code to help you follow along with this codelab.

Clone the repository if you have git installed.

git clone https://github.com/googlesamples/engage-sdk-samples.git

Alternatively, click this link to download the source code and unpack the downloaded zip file.

This project uses the Gradle build system. To build this project, use the gradlew build command or use "Import Project" in Android Studio.

After downloading the code, you will see two sample projects.

  • For Java developers - Use the read sample app

The app is a basic books library. The user can select a book from a list and can then start reading the book. The app demonstrates how the Recommendations & Continuation data is published.

  • For Kotlin developers - Use the watch sample app

The app is a basic video library. The user can select a video from a list and can then start watching the video. The app demonstrates how the Recommendations & Continuation data is published.

For more resources on learning Android development, visit the Developer Guides at developer.android.com

3. Build an Entity

In this codelab we will refer to Engage SDK Read integration guide and Engage SDK Watch integration guide for read and watch sample apps respectively.

Entity is an object representing a single item in a cluster. An entity can be an ebook, a movie, or any relevant content type.

For read content, the SDK has following Entity Types

  • EbookEntity
  • AudiobookEntity
  • BookSeriesEntity

For watch content, the SDK has following Entity Types

  • MovieEntity
  • TvShowEntity
  • TvSeasonEntity
  • TvEpisodeEntity
  • LiveStreamingVideoEntity
  • VideoClipEntity

In the read sample app, go to the file EbookToEntityConverter.java which contains methods to build an EbookEntity for publishing.

EbookEntity.Builder entityBuilder = new EbookEntity.Builder()
        .setName("NAME OF EBOOK")
        .addAuthor("AUTHOR NAME")
        .setActionLinkUri(Uri.parse("DEEPLINK URI OF THIS EBOOK"))
        ...
         .build()

In the watch sample app, go to the file ItemToEntityConverter.kt contains methods to build a MovieEntity for publishing.

val movieBuilder: MovieEntity.Builder =
      MovieEntity.Builder()
        .setName("NAME OF THE MOVIE")
        .addPosterImage(
          Image.Builder()
            .setImageUri(
              Uri.parse("android.resource://movie")
            )
            .setImageWidthInPixel(408)
            .setImageHeightInPixel(960)
            .setImageTheme(ImageTheme.IMAGE_THEME_LIGHT)
            .build()
        )
        .setPlayBackUri(Uri.parse(movie.playbackUri))
        .setReleaseDateEpochMillis(movie.releaseDate)
        .setAvailability(movie.availability)
        .setDurationMillis(movie.durationMillis)
        .addGenre(movie.genre)
          ..
           .build()

Similarly within your application, you can also convert your own data items to corresponding Engage Entities that you would like to publish.

4. Build a Recommendation Cluster

Now that we have built an Entity, we can group them together in a Cluster.

Clusters are a collection of content bundled together. They can be visualized as a UI view that contains a group of content items from a single developer partner.

e8ec28fa54ac7eec.pngFigure.

Entertainment Space UI showing a Recommendation Cluster with Ebook Entities from a single partner.

For most categories, including read content and watch content, the SDK has following Cluster Types

  • Recommendation clusters can be personalized based on your user's behavior in your app and organized by theme, like new releases, price drops, or the user's favorite topics. Each app can provide up-to-5 recommendation clusters per user.
  • Continuation cluster helps users resume in-progress content, like an unfinished movie or ebook, in a single UI grouping with content from multiple apps.
  • Featured cluster can spotlight your hero content in a multi-app cluster using a larger, more premium UI template.

We will build a Recommendation cluster for both read and watch content.

In the read sample app, go to the file GetRecommendationClusters.java, this shows an example of how to build a Recommendation cluster.

RecommendationCluster.Builder clusterBuilder = new RecommendationCluster.Builder();
// Set the cluster title
clusterBuilder.setTitle("For You");
for (int id : ebookIds) {
  //Create an ebook entity.
  EbookEntity entity = EbookToEntityConverter.convert(id); 
  // Add the ebook entity to the cluster
  clusterBuilder.addEntity(entity);
}
// Build the cluster
return clusterBuilder.build();

In the watch sample app, go to the file ClusterRequestFactory.kt, this shows an example of how to build a Recommendation cluster.

// Loads all the movie data 
val recommendationsList = movieDao.loadMovieIsCurrentlyWatching(false)
val recommendationCluster = RecommendationCluster.Builder()
for (item in recommendationsList) {
   //Create a movie entity.
    val movieEntity = ItemToEntityConverter.convertMovie(item)
    // Add the movie entity to the cluster
    recommendationCluster.addEntity(movieEntity)
}
// Build the cluster
return recommendationCluster.build

5. Publish the Recommendation Cluster

Now that we have learnt how to build an Entity and how to group these Entities into a Cluster. The next step is to learn how to publish the cluster.

AppEngagePublishClient is responsible for making the connection to publish the cluster.

Step 1: Initialize the client.

// Java version
AppEngagePublishClient client = new AppEngagePublishClient(context);
// Kotlin version
val client = AppEngagePublishClient(context)

Step 2: Create the request to publish the cluster

In the read sample app, check the setRecommendations method in EngageServiceWorker.java

// Initialize the builder
PublishRecommendationClustersRequest.Builder publishRequestBuilder = new PublishRecommendationClustersRequest.Builder();

// Add all Recommendation Clusters
for (RecommendationCluster cluster : clusters) {
   publishRequestBuilder.addRecommendationCluster(cluster);
}
// Build the request    
publishRequestBuilder.build();

In the watch sample app, check the constructRecommendationClustersRequest method in ClusterRequestFactory.kt

// Initialize the builder
val request = PublishRecommendationClustersRequest.Builder()
// Add all Recommendation Cluster
.addRecommendationCluster(recommendationCluster)
// Build the request    
.build()

Step 3: Call the publishRecommendationClusters method in AppEngagePublishClient

In the read sample app, check the setRecommendations method in EngageServiceWorker.java

client.publishRecommendationClusters(publishRequest);

In the watch sample app, check the publishRecommendations method in EngageServiceWorker.kt

client.publishRecommendationClusters(request)

Use isServiceAvailable API

Before calling publish API, it is required to call isServiceAvailable to make sure publishing is allowed.

In the read sample app, check the startWork method in EngageServiceWorker.java

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.
          client.publishRecommendationClusters(request)
        } else {
          // Service is not available, do not publish.
        }
    } else {
      // The IPC call itself fails, proceed with error handling logic here,
      // such as retry.
    }
}

In the watch sample app, check the doWork method in EngageServiceWorker.kt

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.
          client.publishRecommendationClusters(request)
        } else {
          // Service is not available, do not publish.
        }
    } else {
      // The IPC call itself fails, proceed with error handling logic here,
      // such as retry.
    }
}

6. Publish Status

We recommend using the updatePublishStatus API to indicate why content isn't published. This helps Google avoid false alerts when your app intentionally doesn't publish content, guide users to take corrective actions to access your content, and provide insights to you about your content publishing health.

See the developer site for status codes. For example, if no content is shown because a user needs to sign in, use NOT_PUBLISHED_REQUIRES_SIGN_IN. We'll apply this code in the next step.

In the read sample app, check the publishAndSetResult method in EngageServiceWorker.kt

int publishStatusCode;
              if (loggedInAccount.isPresent()) {
                // If an account is logged in and content is published
                publishStatusCode = AppEngagePublishStatusCode.PUBLISHED;
              } else {
                // If an account is not logged in and no content is published
                publishStatusCode = AppEngagePublishStatusCode.NOT_PUBLISHED_REQUIRES_SIGN_IN;
              }
              setPublishStatus(client, publishStatusCode);
            })

In the watch sample app, check the publishUserAccountManagement method in EngageServiceWorker.kt

private suspend fun publishUserAccountManagement(): Result {
    val publishTask: Task<Void>
    val statusCode: Int
    if (db.accountDao().isAccountSignedIn()) {
      // If an account is logged in and content is published
statusCode = AppEngagePublishStatusCode.PUBLISHED
    } else {
     // If an account is not logged in and no content is published
      statusCode = AppEngagePublishStatusCode.NOT_PUBLISHED_REQUIRES_SIGN_IN
    }
    return publishAndProvideResult(publishTask, statusCode)
  }

7. Work Manager & Broadcast Intents

Work Manager

We recommend executing the content publish job in the background (using WorkManager). Schedule it regularly (e.g. daily) and/or based on user events (e.g. when the app opens or after the user finishes a reading session). The examples below focus on a scheduled publication.

In the read sample app, queuePeriodicSetEngageStateWorker in the file SetEngageState.java shows an example of how to set up a WorkManager

// Create a work manager
WorkManager workManager = WorkManager.getInstance(appContext);

// Set up a periodic work request for 24 hrs.
PeriodicWorkRequest publishRequest =
        new PeriodicWorkRequest.Builder(
                EngageServiceWorker.class, /* repeatInterval= */ 24, TimeUnit.HOURS)
            .setInputData(clusterToPublishData)
            .build();
// Add the work request to queue
workManager.enqueueUniquePeriodicWork(
        publishWorkName, ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE, publishRequest);

In the watch sample app, periodicallyCallEngageServiceWorker in Publisher.kt shows an example of how to set up a WorkManager

// Set up a periodic work request for 24 hrs.
val workRequest = PeriodicWorkRequestBuilder<EngageServiceWorker>(
          repeatInterval = 24,
          repeatIntervalTimeUnit = TimeUnit.HOURS
        )
        .setInputData(workDataOf(PUBLISH_TYPE to publishType))
        .build()

// Create a work manager and add the work request to queue
WorkManager.getInstance(context)
.enqueueUniquePeriodicWork(
workerName,
ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE,
workRequest
)

Broadcast Intents

In addition to making publish content API calls through a job, it is also required to set up a 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 still live in memory.

Open the file MainActivity.java in read sample app to check the following :

private void registerReceiver() {
    BroadcastReceiver publishReceiver = new EngageServiceBroadcastReceiver();
    IntentFilter filter = new IntentFilter();
    filter.addAction(Intents.ACTION_PUBLISH_RECOMMENDATION);
    filter.addAction(Intents.ACTION_PUBLISH_FEATURED);
    filter.addAction(Intents.ACTION_PUBLISH_CONTINUATION);
    int flags = ContextCompat.RECEIVER_EXPORTED;
    ContextCompat.registerReceiver(getApplicationContext(), publishReceiver, filter, flags);
  }
  • 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.

Open the file AndroidManifest.xml in read sample app to check the following :

<receiver
        android:name=".publish.EngageServiceBroadcastReceiver"
        android:exported="true">
      <intent-filter>
        <action android:name="com.google.android.engage.action.PUBLISH_RECOMMENDATION" />
        <action android:name="com.google.android.engage.action.PUBLISH_FEATURED" />
        <action android:name="com.google.android.engage.action.PUBLISH_CONTINUATION" />
             </intent-filter>
 </receiver>

The following intents are sent by the service:

  • com.google.android.engage.action.PUBLISH_RECOMMENDATION It is recommended to start a publishRecommendationClusters call when receiving this intent.
  • com.google.android.engage.action.PUBLISH_FEATURED It is recommended to start a publishFeaturedCluster call when receiving this intent.
  • com.google.android.engage.action.PUBLISH_CONTINUATION It is recommended to start a publishContinuationCluster call when receiving this intent.

8. Use Verification App for Testing

Download and use the verification app to verify your integration

The verification app should display each cluster as a separate row.

  • Enter the name of the package that is publishing the data.

d1ad850cd02991d.png

  • Verify that all entities in the cluster are published.

3953d00488212411.png

  • Verify that all fields in the entity are published. For each item in the row, developers can click on the poster image to verify the intent.

23cd19224397adf3.png

Verify the broadcast intent flow

Use the verification app to verify the broadcast intent, click the button at the top of the UI to trigger the logic for sending broadcasts.

9cb0b5315057fbe1.png

9. Congratulations

You now know how to add Engage SDK to your Android app.

For more details, see the Engage SDK developer guide and Business Site.

Resources

Based on the type of content being published the SDK contains different types of Entity classes. You can see the list of available entities in the integration guide for each vertical listed below -