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

Ao interagir com TVs, os usuários geralmente preferem fornecer o mínimo de informações antes de assistir ao conteúdo. Um cenário ideal para muitos usuários de TV seria: sentar, ligar e assistir. Geralmente, o caminho preferido é aquele com o menor número de etapas possível até o conteúdo que os usuários querem.

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

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 após o primeiro uso do dispositivo. Contribuições de recomendações do catálogo de conteúdo do seu app podem ajudar a trazer os usuários de volta ao app.

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

Esta lição ensina a 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 sem dificuldade. Essa discussão descreve parte do código do app de amostra de Leanback do Android no repositório GitHub da Android TV (link em inglês).

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. Criar recomendações de alta qualidade e relevantes para os usuários é um fator importante na criação de uma ótima experiência do usuário com seu app para TV. Por esse motivo, considere cuidadosamente as recomendações apresentadas ao usuário e gerencie-as de perto.

Tipos de recomendações

Ao criar recomendações, é necessário vincular os usuários a atividades de exibição incompletas ou sugerir atividades que as estendam o conteúdo relacionado. Veja alguns tipos de recomendações específicos a serem considerados:

  • Recomendações de conteúdo de continuação para o próximo episódio para que os usuários continuem assistindo uma série. Ou recomendações de continuação para filmes, programas de TV ou podcasts pausados, que permitem que os usuários voltem a assistir o conteúdo pausado em apenas alguns cliques.
  • Recomendações de novos conteúdos, como para a primeira reprodução de um novo episódio, se o usuário tiver terminado de assistir outra série. Além disso, se o app permitir que os usuários se inscrevam, sigam ou acompanhem o conteúdo, use novas recomendações de conteúdo para itens não assistidos na lista de conteúdos rastreados.
  • 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ções para proporcionar a melhor experiência do usuário possível, consulte a Linha de recomendações (link em inglês) nas especificações de design do Android TV.

Atualizar as recomendações

Ao atualizar as recomendações, não as remova e republique-as. Isso faz com que elas apareçam no final da linha de recomendações. Quando algum conteúdo, como um filme, já tiver sido reproduzido, remova-o das recomendações.

Personalizar recomendações

É possível personalizar os cards de recomendação para transmitir informações de marca definindo elementos da interface do usuário, como a imagem de primeiro plano e de plano de fundo, a cor, o ícone do aplicativo, o título e a legenda do card. Para saber mais, veja a Linha de recomendações (link em inglês) nas especificações de design da 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: para o conteúdo em que o usuário está inscrito e 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ções. Ao fornecer dados de grupo para suas recomendações, você pode garantir que elas não sejam classificadas abaixo das recomendações não relacionadas.

Use NotificationCompat.Builder.setGroup() para definir a string da chave de grupo de uma recomendação. Por exemplo, para marcar uma recomendação como pertencente a um grupo que contém novo conteúdo em alta, 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 o aplicativo contribua com as recomendações, crie um serviço que adicione periodicamente listagens do catálogo do seu app à lista de recomendações do sistema.

O exemplo de código a seguir ilustra como estender IntentService para criar um serviço de recomendações 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 esse serviço seja reconhecido pelo sistema e executado, registre-o usando o 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

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

Como definir os valores

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

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 da classe builder à notificação e chamando NotificationCompat.Builder.build().

Além disso, chame setLocalOnly() para que a notificação NotificationCompat.BigPictureStyle não seja exibida 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 atuais. Para executar seu serviço, crie uma classe que execute um timer e o 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);
        }
    }
    

Essa implementação da classe BroadcastReceiver precisa ser executada após a inicialização do dispositivo de TV em que ela está instalada. Para fazer isso, registre essa classe no manifesto do app com um filtro de intent que detecte a conclusão do processo de inicialização do dispositivo. A amostra 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 requer que seu app solicite a permissão RECEIVE_BOOT_COMPLETED. Para mais informações, consulte ACTION_BOOT_COMPLETED.

No método onHandleIntent() da classe do seu serviço de recomendações, publique a recomendação no gerenciador da seguinte maneira:

Kotlin

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

Java

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