Rekomendacje na Androidzie N i starszych

Podczas oglądania treści na telewizorach użytkownicy zazwyczaj wolą ograniczyć ilość informacji wejściowych, zanim zaczną oglądać treści. Idealny scenariusz dla wielu użytkowników telewizora to siadanie, włączanie i oglądanie. Ogólnie rzecz biorąc, najprostszy sposób na skierowanie użytkowników do treści, które przypadły im do gustu, to ta, którą preferują.

Uwaga: opisane tutaj interfejsy API służą tylko do tworzenia rekomendacji w aplikacjach na urządzeniach z Androidem w wersji do 7.1 (poziom interfejsu API 25) włącznie. Aby dostarczać rekomendacje dla aplikacji działających w Androidzie 8.0 (poziom interfejsu API 26) lub nowszym, aplikacja musi korzystać z kanałów rekomendacji.

Platforma Androida wspomaga minimalną interakcję z danymi wejściowymi, wyświetlając wiersz rekomendacji na ekranie głównym. Rekomendacje treści wyświetlają się w pierwszym wierszu ekranu głównego telewizora po pierwszym użyciu urządzenia. Dzięki rekomendacjom z katalogu treści Twojej aplikacji możesz zachęcić użytkowników do powrotu.

Rysunek 1. Przykład wiersza rekomendacji.

Z tego przewodnika dowiesz się, jak tworzyć rekomendacje i udostępniać je na platformie Androida, aby użytkownicy mogli łatwo odkrywać i odtwarzać treści w Twojej aplikacji. Zapoznaj się również z przykładową implementacją w przykładowej aplikacji Leanback.

Sprawdzone metody tworzenia rekomendacji

Rekomendacje pomagają użytkownikom szybko znajdować treści i aplikacje, które im się podobają. Tworzenie rekomendacji wysokiej jakości, które będą przydatne dla użytkowników, to ważny czynnik zapewniający doskonałe wrażenia z korzystania z Twojej aplikacji na telewizory. Z tego powodu dobrze przemyśl, które rekomendacje przedstawiasz użytkownikom, i starannie nimi zarządzaj.

Typy rekomendacji

Tworząc rekomendacje, odsyłaj użytkowników do niepełnych aktywności lub zaproponuj działania, które rozszerzy ich zakres na powiązane treści. Oto kilka konkretnych rodzajów rekomendacji, które warto wziąć pod uwagę:

  • rekomendacje treści kontynuacji następnego odcinka, aby użytkownicy mogli wznowić oglądanie serialu. Możesz też korzystać z rekomendacji dotyczących kontynuacji w przypadku wstrzymanych filmów, programów telewizyjnych lub podcastów, aby użytkownicy mogli wrócić do oglądania wstrzymanych treści kilkoma kliknięciami.
  • Rekomendacje nowych treści, np. pierwszego odcinka serialu, jeśli użytkownik obejrzał inny serial. Poza tym, jeśli Twoja aplikacja umożliwia użytkownikom subskrybowanie, obserwowanie lub śledzenie treści, używaj nowych rekomendacji dotyczących nieobejrzanych elementów na liście śledzonych treści.
  • Rekomendacje dotyczące powiązanych treści przygotowane na podstawie danych o tym, jak dany użytkownik przeglądał filmy.

Więcej informacji o projektowaniu kart rekomendacji z myślą o zapewnieniu wygody użytkownikom znajdziesz w wierszu rekomendacji w specyfikacji projektowej Androida TV.

Odśwież rekomendacje

Odświeżając rekomendacje, nie ograniczaj ich do usuwania i publikowania ponownie, ponieważ powoduje to ich wyświetlanie na końcu wiersza rekomendacji. Po odtworzeniu elementu treści, na przykład filmu, usuń go z rekomendacji.

Dostosuj rekomendacje

Możesz dostosować karty rekomendacji, tak aby przedstawiały informacje o marce, ustawiając takie elementy interfejsu jak pierwszy plan i tło karty, kolor, ikona aplikacji, tytuł czy podtytuł. Więcej informacji znajdziesz w sekcji Wiersz rekomendacji w specyfikacji projektowej Androida TV.

Rekomendacje dotyczące grup

Opcjonalnie możesz grupować rekomendacje na podstawie źródła rekomendacji. Na przykład aplikacja może przedstawiać 2 grupy rekomendacji: rekomendacje treści subskrybowanych przez użytkownika i rekomendacje nowych popularnych treści, których użytkownik może nie znać.

Podczas tworzenia lub aktualizowania wiersza rekomendacji system osobno ustala ranking i zamawia rekomendacje dla każdej grupy. Podając informacje o grupach na potrzeby rekomendacji, masz pewność, że rekomendacje nie będą uporządkowane pod niepowiązanymi rekomendacjami.

Użyj NotificationCompat.Builder.setGroup(), aby ustawić ciąg klucza grupy dla rekomendacji. Aby na przykład oznaczyć rekomendację jako należącą do grupy zawierającej nowe treści zyskujące popularność, możesz użyć wywołania setGroup("trending").

Utwórz usługę rekomendacji

Rekomendacje treści są tworzone w trakcie przetwarzania w tle. Aby Twoja aplikacja mogła tworzyć rekomendacje, utwórz usługę, która będzie okresowo dodawać informacje z katalogu aplikacji do systemowej listy rekomendacji.

Poniższy przykładowy kod ilustruje, jak rozszerzyć IntentService w celu utworzenia usługi rekomendacji dla aplikacji:

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

Aby usługa została rozpoznana i uruchomiona przez system, zarejestruj ją za pomocą manifestu aplikacji. Fragment kodu poniżej pokazuje, jak zadeklarować tę klasę jako usługę:

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

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

Tworzenie rekomendacji

Po uruchomieniu usługi rekomendacji musi ona tworzyć rekomendacje i przekazywać je do platformy Androida. Platforma odbiera rekomendacje jako obiekty Notification, które używają określonego szablonu i są oznaczone określoną kategorią.

Ustawianie wartości

Aby ustawić wartości elementów interfejsu dla karty rekomendacji, utwórz klasę konstruktora zgodną z podanym poniżej wzorcem konstruktora. Najpierw ustawiasz wartości elementów karty rekomendacji.

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

Utwórz powiadomienie

Po ustawieniu wartości utwórz powiadomienie, przypisz do powiadomienia wartości z klasy konstruktora i wywołaj funkcję NotificationCompat.Builder.build().

Pamiętaj też, by zadzwonić pod numer setLocalOnly(), żeby powiadomienie z aplikacji NotificationCompat.BigPictureStyle nie pojawiło się na innych urządzeniach.

Poniższy przykładowy kod pokazuje, jak utworzyć rekomendację.

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

Uruchamianie usługi rekomendacji

Usługa rekomendacji aplikacji musi być okresowo uruchamiana, aby możliwe było tworzenie bieżących rekomendacji. Aby uruchomić usługę, utwórz klasę, która uruchamia licznik czasu i wywołuje go w regularnych odstępach czasu. Ten przykładowy kod rozszerza klasę BroadcastReceiver w celu rozpoczęcia okresowego wykonywania usługi rekomendacji co pół godziny:

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

Ta implementacja klasy BroadcastReceiver musi zostać uruchomiona po uruchomieniu telewizora, na którym jest zainstalowana. W tym celu zarejestruj tę klasę w pliku manifestu aplikacji za pomocą filtra intencji, który nasłuchuje do końca procesu uruchamiania urządzenia. Ten przykładowy kod pokazuje, jak dodać tę konfigurację do pliku manifestu:

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

Ważne: otrzymanie powiadomienia o zakończeniu rozruchu wymaga, aby aplikacja zażądała uprawnienia RECEIVE_BOOT_COMPLETED. Więcej informacji: ACTION_BOOT_COMPLETED.

W metodzie onHandleIntent() klasy usługi rekomendacji opublikuj rekomendację menedżerowi w ten sposób:

Kotlin

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

Java

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