O Android 14 (nível 34 da API) apresenta algumas melhorias nas APIs de picture-in-picture (PiP) para permitir a multitarefa. Embora o suporte a PiP tenha sido introduzido no Android 8.0 (nível 26 da API), ele não era amplamente compatível com o Android TV e não era compatível com o Google TV antes do Android 13. A multitarefa para TV usa o modo picture-in-picture para permitir que dois apps separados coexistam na tela: um em tela cheia e outro em modo picture-in-picture. Há requisitos diferentes para apps executados em um desses modos.
O comportamento padrão é que o app PiP fique sobreposto ao app em tela cheia. Isso é muito parecido com o comportamento padrão do picture-in-picture do Android.
Ao integrar a multitarefa, o aplicativo precisa declarar os tipos de uso de acordo com as diretrizes de qualidade de apps para TV.
Executar o app no modo picture-in-picture
Em dispositivos de TV com Android 14 (nível 34 da API) ou mais recente, execute o app no modo
picture-in-picture chamando enterPictureInPictureMode()
. Dispositivos de TV com versões anteriores do Android não são compatíveis com o modo picture-in-picture.
Confira um exemplo de como implementar a lógica de um botão para entrar no modo picture-in-picture:
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); } }
A ação só é adicionada se o dispositivo tiver o recurso do sistema
FEATURE_PICTURE_IN_PICTURE
. Além disso, quando a ação é acionada, a proporção do modo picture-in-picture é definida para corresponder à proporção do vídeo que está sendo reproduzido.
Adicione um título e um subtítulo para informar ao usuário sobre o uso geral desse PIP.
Coexistir com apps em execução no modo picture-in-picture
Quando seu app está sendo executado em tela cheia, talvez seja necessário adaptá-lo para outros apps em execução no modo picture-in-picture.
APIs de manter livre
Em alguns casos, o app PiP pode sobrepor componentes importantes da interface no app em tela cheia. Para evitar isso, há APIs de manter limpo que os apps podem usar para identificar componentes críticos da interface que não devem ser sobrepostos. O sistema tenta atender às solicitações para evitar cobrir esses componentes reposicionando a janela PiP.
Para especificar que uma visualização não deve ser sobreposta, use preferKeepClear
no layout XML, como no exemplo a seguir:
<TextView
android:id="@+id/important_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:preferKeepClear="true"
android:text="@string/app_name"/>
Você também pode fazer isso de maneira programática usando 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); }
Às vezes, não é necessário manter um View
inteiro livre, mas apenas uma seção dele. O setPreferKeepClearRects()
pode ser usado para
especificar regiões do View
que não devem ser sobrepostas. Interfaces que não usam
View
s nativamente, como Flutter, Jetpack Compose e WebView, podem ter
subseções que precisam de regiões mantidas limpas. Essa API pode ser usada nesses casos.
Tipos de uso
O app precisa declarar um atributo de valor de metadados de
com.google.android.tv.pip.category
que corresponda ao tipo principal ou
tipos de uso do modo picture-in-picture. Qualquer <activity>
que tenha definido
android:supportsPictureInPicture="true"
precisa declarar esse atributo com um
valor relevante da tabela abaixo.
Tipos de uso que não se enquadram em nenhuma dessas categorias, principalmente qualquer reprodução de conteúdo de mídia, não são permitidos no modo picture-in-picture na TV.
Valor | Descrição |
---|---|
"communication " |
Casos de uso de comunicação, como chamadas de voz ou vídeo. |
"smartHome " |
Integrações de casa inteligente, como campainhas ou babás eletrônicas conectadas. |
"health " |
Casos de uso de saúde, como monitoramento de atividades físicas ou de saúde. |
"ticker " |
Casos de uso de tickers, como placares de esportes ao vivo ou tickers de notícias e ações. |
Vários valores são separados por uma barra vertical (|
). Por exemplo:
<meta-data android:name="com.google.android.tv.pip.category" android:value="smartHome|health" />