Recomendaciones para Android N y versiones anteriores

Cuando los usuarios interactúan con TVs, por lo general, prefieren dar información mínima antes de mirar contenido. Una situación ideal para muchos usuarios consiste en sentarse, encender la TV y mirar el contenido. Por lo general, el proceso que prefiere es el que se realiza con la menor cantidad de pasos para lograr que los usuarios accedan al contenido que disfrutan.

Nota: Usa las APIs que se describen aquí para hacer recomendaciones solo en las apps con Android 7.1 y versiones posteriores (nivel de API 25) inclusive. Si deseas proporcionar recomendaciones para las apps que se ejecutan en Android 8.0 (nivel de API 26) y versiones posteriores, tu app debe usar los canales de recomendaciones.

El framework de Android ayuda con la interacción de entrada mínima proporcionando una fila de recomendaciones en la pantalla principal. Las recomendaciones de contenido aparecen como la primera fila de la pantalla principal de la TV después del primer uso del dispositivo. Aportar recomendaciones del catálogo de contenido de tu app puede ayudar a que los usuarios vuelvan a usarla.

Figura 1: Ejemplo de la fila de recomendaciones.

En esta guía, aprenderás a crear recomendaciones y proporcionarlas en el framework de Android para que los usuarios puedan descubrir y disfrutar fácilmente el contenido de tu app. Consulta también la implementación de muestra en la app de ejemplo de Leanback.

Prácticas recomendadas para las recomendaciones

Las recomendaciones permiten que los usuarios encuentren rápidamente el contenido y las apps que les gustan. Crear recomendaciones que sean de alta calidad y relevantes para los usuarios es un factor importante a la hora de crear una excelente experiencia del usuario con tu app para TV. Por este motivo, debes considerar cuidadosamente qué recomendaciones presentas a los usuarios y administrarlas de cerca.

Tipos de recomendaciones

Cuando creas recomendaciones, debes vincular a los usuarios con actividades de visualización incompletas o sugerir actividades que las extiendan a contenido relacionado. Estos son algunos tipos específicos de recomendaciones que debes tener en cuenta:

  • Recomendaciones de contenido de continuación para el siguiente episodio a fin de que los usuarios sigan mirando una serie También puedes usar recomendaciones de continuación para películas, programas de TV o podcasts pausados para que los usuarios puedan volver a mirar contenido en pausa con unos pocos clics.
  • Recomendaciones de contenido nuevo, como para un episodio que todavía no se reprodujo, si el usuario terminó de mirar otra serie Además, si tu app permite que los usuarios se suscriban, sigan o hagan un seguimiento de contenido, usa recomendaciones de contenido nuevo para elementos sin mirar en su lista de contenido con seguimiento.
  • Recomendaciones de contenido relacionado basadas en el comportamiento de visualización histórico de los usuarios.

Si quieres obtener más información para diseñar tarjetas de recomendaciones para brindar la mejor experiencia del usuario, consulta la Fila de recomendaciones en las especificaciones de diseño de Android TV.

Cómo actualizar las recomendaciones

Cuando actualices recomendaciones, no las quites y vuelvas a publicarlas, ya que esto hará que aparezcan al final de la fila de recomendaciones. Una vez que se haya reproducido un elemento, como una película, quítalo de las recomendaciones.

Cómo personalizar las recomendaciones

Puedes personalizar las tarjetas de recomendaciones para que transmitan información de desarrollo de la marca. Para ello, configura los elementos de la interfaz de usuario, como la imagen de fondo y en primer plano de la tarjeta, el color, el ícono de la app, el título y el subtítulo. Para obtener más información, consulta la Fila de recomendaciones en las especificaciones de diseño de Android TV.

Recomendaciones de grupo

También puedes agrupar las recomendaciones según su origen. Por ejemplo, tu app podría proporcionar dos grupos de recomendaciones: recomendaciones sobre el contenido al que se suscribió el usuario y recomendaciones sobre contenido del momento que el usuario podría no estar al tanto.

El sistema clasifica y ordena las recomendaciones para cada grupo por separado cuando se crea o actualiza la fila de recomendaciones. Si proporcionas información del grupo de tus recomendaciones, puedes asegurarte de que no se ordenen debajo de recomendaciones no relacionadas.

Usa NotificationCompat.Builder.setGroup() para establecer la string de clave del grupo de una recomendación. Por ejemplo, para marcar que una recomendación pertenece a un grupo que incluye contenido de tendencias nuevas, puedes llamar a setGroup("trending").

Cómo crear un servicio de recomendaciones

Las recomendaciones de contenido se crean con procesos en segundo plano. Para que tu aplicación contribuya con recomendaciones, crea un servicio que agregue de forma periódica fichas del catálogo de tu app a la lista de recomendaciones del sistema.

En el siguiente código de ejemplo, se muestra cómo extender IntentService para crear un servicio de recomendación para tu aplicación:

Kotlin

class UpdateRecommendationsService : IntentService("RecommendationService") {
    override protected fun onHandleIntent(intent: Intent) {
        Log.d(TAG, "Updating recommendation cards")
        val recommendations = VideoProvider.getMovieList()
        if (recommendations == null) return

        var count = 0

        try {
            val builder = RecommendationBuilder()
                    .setContext(applicationContext)
                    .setSmallIcon(R.drawable.videos_by_google_icon)

            for (entry in recommendations.entrySet()) {
                for (movie in entry.getValue()) {
                    Log.d(TAG, "Recommendation - " + movie.getTitle())

                    builder.setBackground(movie.getCardImageUrl())
                            .setId(count + 1)
                            .setPriority(MAX_RECOMMENDATIONS - count)
                            .setTitle(movie.getTitle())
                            .setDescription(getString(R.string.popular_header))
                            .setImage(movie.getCardImageUrl())
                            .setIntent(buildPendingIntent(movie))
                            .build()
                    if (++count >= MAX_RECOMMENDATIONS) {
                        break
                    }
                }
                if (++count >= MAX_RECOMMENDATIONS) {
                    break
                }
            }
        } catch (e: IOException) {
            Log.e(TAG, "Unable to update recommendation", e)
        }
    }

    private fun buildPendingIntent(movie: Movie): PendingIntent {
        val detailsIntent = Intent(this, DetailsActivity::class.java)
        detailsIntent.putExtra("Movie", movie)

        val stackBuilder = TaskStackBuilder.create(this)
        stackBuilder.addParentStack(DetailsActivity::class.java)
        stackBuilder.addNextIntent(detailsIntent)

        // Ensure a unique PendingIntents, otherwise all
        // recommendations end up with the same PendingIntent
        detailsIntent.setAction(movie.getId().toString())

        val intent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT)
        return intent
    }

    companion object {
        private val TAG = "UpdateRecommendationsService"
        private val MAX_RECOMMENDATIONS = 3
    }
}

Java

public class UpdateRecommendationsService extends IntentService {
    private static final String TAG = "UpdateRecommendationsService";
    private static final int MAX_RECOMMENDATIONS = 3;

    public UpdateRecommendationsService() {
        super("RecommendationService");
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        Log.d(TAG, "Updating recommendation cards");
        HashMap<String, List<Movie>> recommendations = VideoProvider.getMovieList();
        if (recommendations == null) return;

        int count = 0;

        try {
            RecommendationBuilder builder = new RecommendationBuilder()
                    .setContext(getApplicationContext())
                    .setSmallIcon(R.drawable.videos_by_google_icon);

            for (Map.Entry<String, List<Movie>> entry : recommendations.entrySet()) {
                for (Movie movie : entry.getValue()) {
                    Log.d(TAG, "Recommendation - " + movie.getTitle());

                    builder.setBackground(movie.getCardImageUrl())
                            .setId(count + 1)
                            .setPriority(MAX_RECOMMENDATIONS - count)
                            .setTitle(movie.getTitle())
                            .setDescription(getString(R.string.popular_header))
                            .setImage(movie.getCardImageUrl())
                            .setIntent(buildPendingIntent(movie))
                            .build();

                    if (++count >= MAX_RECOMMENDATIONS) {
                        break;
                    }
                }
                if (++count >= MAX_RECOMMENDATIONS) {
                    break;
                }
            }
        } catch (IOException e) {
            Log.e(TAG, "Unable to update recommendation", e);
        }
    }

    private PendingIntent buildPendingIntent(Movie movie) {
        Intent detailsIntent = new Intent(this, DetailsActivity.class);
        detailsIntent.putExtra("Movie", movie);

        TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
        stackBuilder.addParentStack(DetailsActivity.class);
        stackBuilder.addNextIntent(detailsIntent);
        // Ensure a unique PendingIntents, otherwise all
        // recommendations end up with the same PendingIntent
        detailsIntent.setAction(Long.toString(movie.getId()));

        PendingIntent intent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
        return intent;
    }
}

Para que el sistema reconozca este servicio y lo ejecute, regístralo con el manifiesto de tu app. En el siguiente fragmento de código, se muestra cómo declarar esta clase como un servicio:

<manifest ... >
  <application ... >
    ...

    <service
            android:name="com.example.android.tvleanback.UpdateRecommendationsService"
            android:enabled="true" />
  </application>
</manifest>

Cómo compilar las recomendaciones

Una vez que tu servicio de recomendaciones comienza a ejecutarse, debe crear recomendaciones y pasarlas al framework de Android. El framework recibe las recomendaciones como objetos Notification que usan una plantilla específica y están marcados con una categoría específica.

Cómo configurar los valores

Si deseas establecer los valores de los elementos de la IU para la tarjeta de recomendaciones, crea una clase de compilador que siga el patrón de compilador que se describe a continuación. Primero, establece los valores de los elementos de la tarjeta de recomendaciones.

Kotlin

class RecommendationBuilder {
    ...

    fun setTitle(title: String): RecommendationBuilder {
        this.title = title
        return this
    }

    fun setDescription(description: String): RecommendationBuilder {
        this.description = description
        return this
    }

    fun setImage(uri: String): RecommendationBuilder {
        imageUri = uri
        return this
    }

    fun setBackground(uri: String): RecommendationBuilder {
        backgroundUri = uri
        return this
    }

...

Java

public class RecommendationBuilder {
    ...

    public RecommendationBuilder setTitle(String title) {
            this.title = title;
            return this;
        }

        public RecommendationBuilder setDescription(String description) {
            this.description = description;
            return this;
        }

        public RecommendationBuilder setImage(String uri) {
            imageUri = uri;
            return this;
        }

        public RecommendationBuilder setBackground(String uri) {
            backgroundUri = uri;
            return this;
        }
...

Cómo crear la notificación

Una vez que hayas definido los valores, compila la notificación, asigna los valores de la clase de compilador y llama a NotificationCompat.Builder.build().

Además, asegúrate de llamar a setLocalOnly() para que la notificación NotificationCompat.BigPictureStyle no se muestre en otros dispositivos.

En el siguiente código, se muestra cómo compilar una recomendación.

Kotlin

class RecommendationBuilder {
    ...

    @Throws(IOException::class)
    fun build(): Notification {
        ...

        val notification = NotificationCompat.BigPictureStyle(
        NotificationCompat.Builder(context)
                .setContentTitle(title)
                .setContentText(description)
                .setPriority(priority)
                .setLocalOnly(true)
                .setOngoing(true)
                .setColor(context.resources.getColor(R.color.fastlane_background))
                .setCategory(Notification.CATEGORY_RECOMMENDATION)
                .setLargeIcon(image)
                .setSmallIcon(smallIcon)
                .setContentIntent(intent)
                .setExtras(extras))
                .build()

        return notification
    }
}

Java

public class RecommendationBuilder {
    ...

    public Notification build() throws IOException {
        ...

        Notification notification = new NotificationCompat.BigPictureStyle(
                new NotificationCompat.Builder(context)
                        .setContentTitle(title)
                        .setContentText(description)
                        .setPriority(priority)
                        .setLocalOnly(true)
                        .setOngoing(true)
                        .setColor(context.getResources().getColor(R.color.fastlane_background))
                        .setCategory(Notification.CATEGORY_RECOMMENDATION)
                        .setLargeIcon(image)
                        .setSmallIcon(smallIcon)
                        .setContentIntent(intent)
                        .setExtras(extras))
                .build();

        return notification;
    }
}

Ejecuta el servicio de recomendaciones

El servicio de recomendación de tu app debe ejecutarse de forma periódica para crear recomendaciones actuales. Para ejecutar tu servicio, crea una clase que ejecute un cronómetro y lo invoque en intervalos regulares. En el siguiente ejemplo de código, se extiende la clase BroadcastReceiver para iniciar la ejecución periódica de un servicio de recomendación cada media hora:

Kotlin

class BootupActivity : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        Log.d(TAG, "BootupActivity initiated")
        if (intent.action.endsWith(Intent.ACTION_BOOT_COMPLETED)) {
            scheduleRecommendationUpdate(context)
        }
    }

    private fun scheduleRecommendationUpdate(context: Context) {
        Log.d(TAG, "Scheduling recommendations update")
        val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
        val recommendationIntent = Intent(context, UpdateRecommendationsService::class.java)
        val alarmIntent = PendingIntent.getService(context, 0, recommendationIntent, 0)
        alarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
                INITIAL_DELAY,
                AlarmManager.INTERVAL_HALF_HOUR,
                alarmIntent
        )
    }

    companion object {
        private val TAG = "BootupActivity"
        private val INITIAL_DELAY:Long = 5000
    }
}

Java

public class BootupActivity extends BroadcastReceiver {
    private static final String TAG = "BootupActivity";

    private static final long INITIAL_DELAY = 5000;

    @Override
    public void onReceive(Context context, Intent intent) {
        Log.d(TAG, "BootupActivity initiated");
        if (intent.getAction().endsWith(Intent.ACTION_BOOT_COMPLETED)) {
            scheduleRecommendationUpdate(context);
        }
    }

    private void scheduleRecommendationUpdate(Context context) {
        Log.d(TAG, "Scheduling recommendations update");

        AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
        Intent recommendationIntent = new Intent(context, UpdateRecommendationsService.class);
        PendingIntent alarmIntent = PendingIntent.getService(context, 0, recommendationIntent, 0);

        alarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
                INITIAL_DELAY,
                AlarmManager.INTERVAL_HALF_HOUR,
                alarmIntent);
    }
}

Esta implementación de la clase BroadcastReceiver debe ejecutarse después del inicio del dispositivo de TV en el que está instalada. Para ello, registra esa clase en el manifiesto de tu app con un filtro de intents que escuche cuando se complete el proceso de inicio del dispositivo. En el siguiente código de ejemplo, se muestra cómo agregar esta configuración al manifiesto:

<manifest ... >
  <application ... >
    <receiver android:name="com.example.android.tvleanback.BootupActivity"
              android:enabled="true"
              android:exported="false">
      <intent-filter>
        <action android:name="android.intent.action.BOOT_COMPLETED"/>
      </intent-filter>
    </receiver>
  </application>
</manifest>

Importante: Para recibir una notificación de que se completó el inicio, tu app debe solicitar el permiso RECEIVE_BOOT_COMPLETED. Para obtener más información, consulta ACTION_BOOT_COMPLETED.

En el método onHandleIntent() de tu clase de servicio de recomendaciones, publica la recomendación al administrador de la siguiente manera:

Kotlin

val notification = notificationBuilder.build()
notificationManager.notify(id, notification)

Java

Notification notification = notificationBuilder.build();
notificationManager.notify(id, notification);