Recomendações no Android N e versões anteriores

Ao interagir com TVs, os usuários geralmente preferem fornecer informações mínimas antes de assistir conteúdo. Um cenário ideal para muitos usuários de TV seria: sentar, ligar e assistir. Menos etapas levar os usuários ao conteúdo de que gostam geralmente é o caminho que eles preferem.

Observação: use as APIs descritas aqui para fazer recomendações em apps executados nas versões do Android até a 7.1 (nível 25 da API). Para fornecer recomendações para apps em execução no Android 8.0 (nível 26 da API) e versões mais recentes, seu app precisa usar canais de recomendação.

O framework do Android auxilia na interação de entrada mínima fornecendo uma linha de recomendações. na tela inicial. As recomendações de conteúdo aparecem como a primeira linha da tela inicial da TV depois de o dispositivo foi usado pela primeira vez. Contribuir com recomendações do catálogo de conteúdo do seu app pode ajudar trazer os usuários de volta para seu app.

Figura 1. Um exemplo da linha de recomendações.

Este guia ensina como criar recomendações e fornecê-las ao framework do Android para que os usuários possam descobrir e aproveitar o conteúdo do seu app com facilidade. Consulte também o exemplo de implementação em as App de amostra do Leanback ,

Práticas recomendadas para recomendações

As recomendações ajudam os usuários a encontrar rapidamente o conteúdo e os apps de que gostam. Criando recomendações de alta qualidade e relevantes para os usuários é um fator importante na criação de uma uma ótima experiência do usuário com seu app de TV. Por isso, você deve considerar cuidadosamente recomendações que você apresenta ao usuário e as gerencia de perto.

Tipos de recomendações

Ao criar recomendações, você deve vincular os usuários de volta a atividades de visualização incompletas ou sugerir atividades que o estendam ao conteúdo relacionado. Aqui estão alguns tipos específicos de que você deve considerar:

  • Recomendações de conteúdo de continuação para os usuários retomarem o próximo episódio assistir uma série. Ou use as recomendações de continuação para filmes, programas de TV ou podcasts pausados para que os usuários possam voltar a assistir o conteúdo pausado com apenas alguns cliques.
  • Recomendações de novos conteúdos, como para a primeira execução de um novo episódio, se o usuário terminar de assistir outra série. Além disso, se o app permite que os usuários se inscrevam, sigam ou acompanhem conteúdo do canal, use as novas recomendações de conteúdo para itens não assistidos na lista de conteúdo rastreado.
  • Recomendações de conteúdos relacionados baseadas no histórico de visualização dos usuários.

Para mais informações sobre como criar cards de recomendação para oferecer a melhor experiência do usuário, consulte Linha de recomendação nas especificações de design do Android TV.

Atualizar as recomendações

Ao atualizar as recomendações, não as remova e reposicione, porque isso faz com que as recomendações apareçam no final da linha de recomendações. Quando um item de conteúdo, como um filme, já foi reproduzido, removê-la das recomendações.

Personalizar recomendações

Para personalizar cards de recomendação para transmitir informações de marca, defina a interface do usuário como a imagem de primeiro e segundo plano, a cor, o ícone do app, o título e as subtítulos do cartão. Para saber mais, consulte Linha de recomendação nas especificações de design do Android TV.

Recomendações em grupo

Se quiser, você também pode agrupar as recomendações com base na fonte delas. Por exemplo, seu app pode fornecer dois grupos de recomendações: recomendações para conteúdo em que o usuário está inscrito, e recomendações para novos conteúdos em alta que o usuário pode não conhecer.

O sistema classifica e ordena recomendações para cada grupo separadamente ao criar ou atualizar a linha de recomendação. Ao fornecer informações de grupo para suas recomendações, você garante para que suas recomendações não sejam ordenadas abaixo das recomendações não relacionadas.

Usar NotificationCompat.Builder.setGroup() para definir a string de chave de grupo de uma recomendação. Para exemplo, para marcar uma recomendação como pertencente a um grupo que contém novo conteúdo em alta, talvez você chame setGroup("trending").

Criar um serviço de recomendações

As recomendações de conteúdo são criadas com o processamento em segundo plano. Para que seu aplicativo contribuir para as recomendações, crie um serviço que adicione periodicamente listagens de seus catálogo do app à lista de recomendações do sistema.

O exemplo de código a seguir ilustra como estender IntentService para crie um serviço de recomendação para seu aplicativo:

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 este serviço seja reconhecido e executado pelo sistema, registre-o usando seu manifesto do app. O snippet de código a seguir ilustra como declarar essa classe como um serviço:

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

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

Criar recomendações

Quando o serviço de recomendações começar a ser executado, ele precisa criar recomendações e transmiti-las para framework do Android. O framework recebe as recomendações como objetos Notification que usam um modelo específico e são marcados com um categoria.

Como definir os valores

Para definir os valores dos elementos da interface para o card de recomendação, crie uma classe builder que segue ao padrão builder descrito a seguir. Primeiro, defina os valores do card de recomendação os elementos.

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;
        }
...

Criar a notificação

Depois de definir os valores, crie a notificação, atribuindo os valores do builder classe à notificação e chamando NotificationCompat.Builder.build().

Além disso, lembre-se de chamar setLocalOnly() para que a notificação NotificationCompat.BigPictureStyle não apareça em outros dispositivos.

O exemplo de código a seguir demonstra como criar uma recomendação.

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;
    }
}

Executar serviço de recomendações

O serviço de recomendação do seu app precisa ser executado periodicamente para criar recomendações. Para executar seu serviço, crie uma classe que execute um timer e invoque em intervalos regulares. O exemplo de código a seguir estende a classe BroadcastReceiver para iniciar a execução periódica de um serviço de recomendação a cada meia 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 implementação da classe BroadcastReceiver precisa ser executada após o início do aparelho de TV em que ele está instalado. Para fazer isso, registre esta classe em seu aplicativo com um filtro de intent que detecta a conclusão do processo de inicialização do dispositivo. A o exemplo de código a seguir demonstra como adicionar essa configuração ao manifesto:

<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:receber uma notificação de conclusão da inicialização exige que o app solicita a permissão RECEIVE_BOOT_COMPLETED. Para saber mais, consulte ACTION_BOOT_COMPLETED.

Na sua classe de serviço de recomendações onHandleIntent() , publique a recomendação para o gerente da seguinte maneira:

Kotlin

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

Java

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