Wielozadaniowość na telewizorze

W Androidzie 14 (poziom API 34) wprowadziliśmy kilka ulepszeń do interfejsów API obrazu w obrazie (PiP), które umożliwiają wielozadaniowość. Choć obsługa obrazu w obrazie została wprowadzona w Androidzie 8.0 (poziom interfejsu API 26), nie była ona powszechnie obsługiwana w Androidzie TV i w ogóle nie była obsługiwana w Google TV przed Androidem 13. Wielozadaniowość na telewizorze korzysta z trybu PIP, dzięki czemu na ekranie mogą współistnieć 2 oddzielne aplikacje: jedna działająca w trybie pełnoekranowym, a druga w trybie obraz w obrazie. Aplikacje działające w jednym z tych trybów mają różne wymagania.

Domyślnie aplikacja PIP nakłada się na aplikację pełnoekranową. Działa to mniej więcej tak samo jak w przypadku obrazu w obrazie na Androidzie.

Pamiętaj, że przy integrowaniu funkcji wielozadaniowych aplikacja musi zadeklarować typy użycia zgodnie ze wskazówkami dotyczącymi jakości aplikacji na telewizory.

Uruchamianie aplikacji w trybie PIP

Na telewizorach z Androidem 14 (poziom interfejsu API 34) lub nowszym uruchom aplikację w trybie obrazu w obrazie, wywołując metodę enterPictureInPictureMode(). Telewizory z wcześniejszymi wersjami Androida nie obsługują trybu PIP.

Oto przykład wdrożenia logiki przycisku umożliwiającego przejście w tryb PiP:

Kotlin

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    pictureInPictureButton.visibility =
        if (requireActivity().packageManager.hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)) {
            pictureInPictureButton.setOnClickListener {
                val aspectRatio = Rational(view.width, view.height)
                val params = PictureInPictureParams.Builder()
                    .setAspectRatio(aspectRatio)
                    .build()
                val result = requireActivity().enterPictureInPictureMode(params)
            }
            View.VISIBLE
        } else {
            View.GONE
        }
}

Java

@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
    if (requireActivity().getPackageManager().hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)) {
        pictureInPictureButton.setVisibility(View.VISIBLE);
        pictureInPictureButton.setOnClickListener(v -> {
            Rational aspectRatio = new Rational(view.getWidth(), view.getHeight());
            PictureInPictureParams params = new PictureInPictureParams.Builder()
                    .setAspectRatio(aspectRatio)
                    .setTitle("My Streaming App")
                    .setSubtitle("My On-Demand Content")
                    .build();
            Boolean result = requireActivity().enterPictureInPictureMode(params);
        });
    } else {
        pictureInPictureButton.setVisibility(View.GONE);
    }
}

Działanie jest dodawane tylko wtedy, gdy urządzenie ma funkcję systemową FEATURE_PICTURE_IN_PICTURE. Poza tym po wywołaniu tego działania format obrazu w trybie obraz w obrazie jest ustawiany na taki, który odpowiada formatowi odtwarzanego filmu.

Pamiętaj o dodaniu tytułu i podtytułu, aby poinformować użytkownika, do czego służy dany obraz PIP.

Współistniej z aplikacjami działającymi w trybie PIP

Gdy aplikacja działa w trybie pełnego ekranu, może wymagać przystosowania do innych aplikacji działających w trybie PIP.

Przejrzyste interfejsy API

W niektórych przypadkach aplikacja PIP może nakładać ważne komponenty interfejsu na pełnoekranową aplikację. Aby temu zaradzić, dostępne są przejrzyste interfejsy API, za pomocą których aplikacje mogą identyfikować kluczowe komponenty interfejsu, których nie należy na siebie nakładać. System próbuje uwzględnić żądania, aby uniknąć zasłonięcia tych komponentów, zmieniając ustawienie okna PIP.

Pozostawiaj oczywiste

Aby wskazać, że widok nie powinien być nakładany, użyj preferKeepClear w układzie XML, jak w tym przykładzie:

<TextView
    android:id="@+id/important_text"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:preferKeepClear="true"
    android:text="@string/app_name"/>

Możesz też to zrobić automatycznie za pomocą setPreferKeepClear():

Kotlin

private lateinit var binding: MyLayoutBinding

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    binding = MyLayoutBinding.inflate(layoutInflater)
    setContentView(binding.root)
    binding.importantText.isPreferKeepClear = true
}

Java

private MyLayoutBinding binding;

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    binding = MyLayoutBinding.inflate(getLayoutInflater());
    setContentView(binding.getRoot());
    binding.importantText.setPreferKeepClear(true);
}

Może się zdarzyć, że nie musisz zachowywać pustego obszaru View, a jedynie jego część. setPreferKeepClearRects() pozwala określić regiony elementu View, które nie powinny być nałożone. Interfejsy, które natywnie nie korzystają z interfejsów View, takie jak Flutter, Jetpack Compose i komponent WebView, mogą zawierać podsekcje, w przypadku których regiony muszą pozostawać puste. Tego interfejsu API można używać w takich przypadkach.

Typy użycia

Twoja aplikacja musi zadeklarować atrybut wartości metadanych com.google.android.tv.pip.category, który odpowiada głównemu typowi lub rodzajom użycia trybu obraz w obrazie. Każdy <activity>, który ma ustawiony parametr android:supportsPictureInPicture="true", powinien zadeklarować ten atrybut z odpowiednią wartością z tabeli poniżej.

Rodzaje wykorzystania, które nie należą do żadnej z tych kategorii, a w szczególności odtwarzanie treści multimedialnych, są niedozwolone w trybie obrazu w obrazie na telewizorze.

Wartość Opis
communication Przypadki użycia komunikacji, na przykład rozmowy wideo lub głosowe.
smartHome Integracje z inteligentnymi domami, takimi jak połączone dzwonki do drzwi czy elektroniczna niania.
health Zastosowania związane ze zdrowiem, na przykład do śledzenia aktywności fizycznej lub zdrowia.
ticker Przypadki użycia znacznika, np. wyniki sportowe na żywo, wiadomości i akcje giełdowe.

Kilka wartości jest rozdzielonych pionową kreską (|). Na przykład:

<meta-data android:name="com.google.android.tv.pip.category" android:value="smartHome|health" />