Como Criar Aplicativos para Android TV

Passo a Passo Completo — Do Zero ao Deploy na Play Store

Atu­al­iza­do para Android 14 TV | Lean­back 3.x | Jet­pack Com­pose TV

O Gigante Adormecido que Acordou

Se você ain­da não está desen­vol­ven­do para Android TV, está deixan­do din­heiro na mesa. Não é exagero. O mer­ca­do de smart TVs com Android cresceu mais de 40% ao ano nos últi­mos três anos, e a frag­men­tação que afas­ta­va desen­volve­dores está fican­do para trás — a Google redesen­hou com­ple­ta­mente a exper­iên­cia de desen­volvi­men­to com o Jet­pack Com­pose for TV, tor­nan­do o proces­so mais famil­iar e efi­ciente do que nun­ca.

Android TV e Google TV (a inter­face mais recente, con­struí­da sobre Android TV) rodam em cen­te­nas de mil­hões de dis­pos­i­tivos: tele­vi­sores da Sony, TCL, Philips, Hisense, Chrome­cast com Google TV, NVIDIA Shield, e uma avalanche de set-top box­es e don­gles ao redor do mun­do. Para o desen­volve­dor brasileiro, isso rep­re­sen­ta uma opor­tu­nidade ain­da mais inter­es­sante, dado o cus­to rel­a­ti­va­mente baixo de TVBox­es Android e a pen­e­tração mas­si­va de apar­el­hos de entra­da em regiões de alta den­si­dade pop­u­la­cional.

Este guia foi escrito para quem quer desen­volver de ver­dade — não ape­nas enten­der o con­ceito, mas sair daqui com um app fun­cional e um cam­in­ho claro até a pub­li­cação. Vamos cobrir des­de a con­fig­u­ração do ambi­ente até as nuances da exper­iên­cia de nave­g­ação por con­t­role remo­to, pas­san­do por códi­go real, boas práti­cas e armadil­has que vão te econ­o­mizar horas de frus­tração.

🎯 O que você vai apren­der neste arti­go:
Con­fig­u­ração com­ple­ta do ambi­ente Android Stu­dio para TV, arquite­tu­ra recomen­da­da para apps de TV, uso da bib­liote­ca Lean­back e do Jet­pack Com­pose for TV, nave­g­ação por D‑pad, inte­gração com con­t­role remo­to, design de UI para telas grandes, testes em emu­ladores e dis­pos­i­tivos físi­cos, pub­li­cação na Play Store for TV, e mon­e­ti­za­ção de apps na platafor­ma.


1. Entendendo o Ecossistema Android TV

1.1 Android TV vs Google TV: qual é a diferença?

Antes de escr­ev­er uma lin­ha de códi­go, você pre­cisa enten­der o que está desen­vol­ven­do. Android TV é o sis­tema opera­cional base — é o Android rodan­do em tele­vi­sores, com mod­i­fi­cações na inter­face e nas APIs para ade­quar à exper­iên­cia de sala de estar. Google TV é a cama­da de inter­face lança­da em 2020 que a Google colo­cou por cima do Android TV em seus próprios dis­pos­i­tivos (Chrome­cast com Google TV) e que foi ado­ta­da por fab­ri­cantes como Sony e TCL.

Do pon­to de vista do desen­volve­dor, a difer­ença é peque­na e boa notí­cia: apps desen­volvi­dos para Android TV rodam per­feita­mente no Google TV. A Google man­tém com­pat­i­bil­i­dade total. O que muda é a for­ma como o usuário desco­bre seu app (Google TV tem um sis­tema de recomen­dações mais sofisti­ca­do) e algu­mas APIs rela­cionadas a con­teú­do que você pode aproveitar no Google TV.

1.2 O modelo de navegação: o D‑pad é tudo

Esqueça o toque. Na TV, o usuário nave­ga com o con­t­role remo­to, que se comu­ni­ca com o sis­tema através do pro­to­co­lo D‑pad: cima, baixo, esquer­da, dire­i­ta, sele­cionar (OK), voltar, home, e alguns botões especí­fi­cos de mídia (play, pause, retro­ced­er, avançar).

Esse mod­e­lo de nave­g­ação muda fun­da­men­tal­mente como você pen­sa a inter­face do seu app. Cada ele­men­to inter­a­ti­vo pre­cisa rece­ber e perder foco de for­ma clara. O fluxo de nave­g­ação pre­cisa faz­er sen­ti­do intu­iti­va­mente com os qua­tro dire­cionais. Não existe “clicar em qual­quer lugar” — existe um foco que via­ja pelo lay­out como um cur­sor lento e pre­visív­el.

A Google chama isso de “focus-dri­ven nav­i­ga­tion” e con­stru­iu o sis­tema de foco do Android View e do Com­pose especi­fi­ca­mente para supor­tar isso. Quan­do você declara seu lay­out cor­re­ta­mente, o sis­tema de foco já sabe como nave­g­ar entre os ele­men­tos. Mas quan­do você cria lay­outs com­plex­os ou cus­tomiza­dos, vai pre­cis­ar guiar o sis­tema — e é aí que muitos desen­volve­dores tropeçam.

1.3 Resoluções, densidade e distância de visualização

A TV tem desafios de exibição úni­cos. O usuário está a 2–3 met­ros da tela, o que sig­nifi­ca que ele­men­tos pequenos sim­ples­mente não fun­cionam. A Google recomen­da que qual­quer tex­to legív­el ten­ha pelo menos 20sp e que botões e ele­men­tos focáveis ten­ham pelo menos 48dp de taman­ho de toque — na TV, isso vira pelo menos 96dp para garan­tir vis­i­bil­i­dade.

Res­olução
Pix­els
Den­si­dade
Padrão

HD
1280x720
mdpi (~160dpi)
Mín­i­mo recomen­da­do

Full HD
1920x1080
xhd­pi (~320dpi)
Mais comum

4K Ultra HD
3840x2160
xxxhd­pi (~640dpi)
TVs pre­mi­um

8K
7680x4320
xxxhd­pi+
Emer­gente

A grande maio­r­ia do seu públi­co estará em Full HD (1080p). Dimen­sione seus lay­outs pri­mari­a­mente para essa res­olução, garan­ta que tudo fun­cione em HD, e trate 4K como bônus — o sis­tema de escala do Android cui­da da maior parte, mas ima­gens e ícones pre­cisam de ver­sões de alta res­olução.


2. Configurando o Ambiente de Desenvolvimento

2.1 Requisitos do sistema

Você vai pre­cis­ar de uma máquina razoáv­el. O emu­lador de Android TV é pesa­do — mais pesa­do do que o emu­lador de smart­phone, porque está sim­u­lan­do uma tela grande com com­posição de hard­ware. Se pos­sív­el, use um dis­pos­i­ti­vo físi­co para testes durante o desen­volvi­men­to.

  1. Android Stu­dio Hedge­hog (2023.1.1) ou mais recente
  2. JDK 17 ou supe­ri­or
  3. 8 GB de RAM mín­i­mo (16 GB recomen­da­do)
  4. 10 GB de espaço em dis­co para SDK e emu­ladores
  5. Pla­ca de vídeo com suporte a OpenGL 2.0+ para o emu­lador

2.2 Instalando e configurando o Android Studio

Se você já tem o Android Stu­dio insta­l­a­do para desen­volvi­men­to mobile, a maior parte do tra­bal­ho já está fei­ta. O que você pre­cisa adi­cionar é o SDK do Android TV e as ima­gens de sis­tema para o emu­lador.

  1. Abra o Android Stu­dio e vá em SDK Man­ag­er (File > Set­tings > Android SDK ou atal­ho na tela ini­cial).
  2. Na aba ‘SDK Plat­forms’, mar­que a ver­são do Android que dese­ja supor­tar (recomen­do Android 12 como mín­i­mo, Android 14 como tar­get).
  3. Na aba ‘SDK Tools’, garan­ta que estão insta­l­a­dos: Android Emu­la­tor, Android SDK Plat­form-Tools, Google USB Dri­ver (para conexão com dis­pos­i­tivos físi­cos).
  4. Clique em Apply e aguarde o down­load.

2.3 Criando o emulador de Android TV

Vá em Tools > Device Man­ag­er (ou AVD Man­ag­er em ver­sões anti­gas) e siga os pas­sos:

  1. Clique em ‘Cre­ate Device’.
  2. Na cat­e­go­ria ‘TV’, sele­cione o per­fil de hard­ware. Para desen­volvi­men­to, o ‘Android TV (1080p)’ é sufi­ciente.
  3. Sele­cione uma imagem de sis­tema — use ‘Google TV’ com API 34 para a exper­iên­cia mais próx­i­ma da real­i­dade atu­al.
  4. Nas con­fig­u­rações avançadas, ajuste a RAM para pelo menos 2048 MB e ative a acel­er­ação de hard­ware (HAXM no Windows/Mac, KVM no Lin­ux).
  5. Final­ize e ini­cie o emu­lador para ver­i­ficar que está fun­cio­nan­do.

💡 Dica de Pro­du­tivi­dade
O emu­lador é lento para ini­ciar. Deixe‑o rodan­do durante todo o dia de desen­volvi­men­to — ele con­some RAM mas econ­o­miza tem­po. Use o coman­do ‘adb con­nect 192.168.x.x’ para conec­tar a uma TV físi­ca na mes­ma rede Wi-Fi, o que tor­na o ciclo de desen­volvi­men­to muito mais rápi­do do que o emu­lador.

2.4 Conectando um dispositivo físico via ADB

Desen­volver com um Chrome­cast com Google TV, uma Android TV Box ou uma Smart TV real é infini­ta­mente mais agradáv­el do que usar o emu­lador. O proces­so é sim­ples:

  1. Nas con­fig­u­rações da TV, vá em ‘Sobre’ e toque 7 vezes em ‘Build Num­ber’ para ati­var as Opções do Desen­volve­dor.
  2. Em Opções do Desen­volve­dor, ative ‘Depu­ração USB’ e ‘Depu­ração ADB pela rede’.
  3. Anote o IP da TV (Con­fig­u­rações > Rede > IP).
  4. No ter­mi­nal do seu com­puta­dor, exe­cute: adb connect IP_DA_TV:5555
  5. Con­firme a conexão na TV quan­do solic­i­ta­do.

3. Criando seu Primeiro Projeto Android TV

3.1 Estrutura do projeto

Exis­tem duas abor­da­gens para cri­ar um app Android TV em 2024: usan­do a bib­liote­ca Lean­back (a abor­dagem tradi­cional, basea­da em Frag­ments) ou usan­do o Jet­pack Com­pose for TV (a abor­dagem mod­er­na e recomen­da­da para novos pro­je­tos). Este guia vai cobrir ambas, mas vai focar mais no Com­pose for TV, que é o futuro da platafor­ma.

Crian­do o pro­je­to no Android Stu­dio

  1. Clique em ‘New Project’.
  2. Sele­cione a aba ‘TV’ na lista de tem­plates.
  3. Escol­ha ‘Android TV Activ­i­ty’ (tem­plate Lean­back) ou ‘Emp­ty Activ­i­ty’ (para começar com Com­pose for TV).
  4. Con­fig­ure o nome do pacote, lin­guagem (Kotlin, sem­pre) e SDK mín­i­mo (API 21 como mín­i­mo, API 28 como recomen­da­do).

3.2 O arquivo AndroidManifest.xml para TV

O man­i­festo de um app de TV tem algu­mas declar­ações cru­ci­ais que difer­en­ci­am ele de um app mobile. Sem elas, seu app não aparece na Play Store para TV nem fun­ciona cor­re­ta­mente nos launch­ers de TV.

<manifest xmlns:android="http://schemas.android.com/apk/res/android">    <!-- Declaração crucial: este app suporta TV -->
<uses-feature
android:name="android.software.leanback"
android:required="true" /> <!-- TV não tem touchscreen: declare como não obrigatório -->
<uses-feature
android:name="android.hardware.touchscreen"
android:required="false" /> <application
android:banner="@drawable/app_banner"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/Theme.Leanback"> <activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<!-- Intent de launcher para TV -->
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LEANBACK_LAUNCHER" />
</intent-filter>
</activity> </application>
</manifest>

Atenção ao ban­ner: O atrib­u­to android:banner apon­ta para uma imagem de 320x180dp que aparece no launch­er da TV. Sem essa imagem, seu app pode não apare­cer ou apare­cer com visu­al inad­e­qua­do no launch­er.

3.3 Dependências essenciais

No seu build.gradle (Mod­ule: app), adi­cione as dependên­cias con­forme a abor­dagem escol­hi­da:

dependencies {
// Para Leanback (abordagem tradicional)
implementation 'androidx.leanback:leanback:1.2.0'
implementation 'com.github.bumptech.glide:glide:4.16.0' // Para Jetpack Compose for TV (abordagem moderna)
implementation 'androidx.tv:tv-foundation:1.0.0-alpha10'
implementation 'androidx.tv:tv-material:1.0.0-alpha10' // Dependências comuns
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.7.0'
implementation 'androidx.activity:activity-compose:1.8.2'
implementation 'androidx.compose.ui:ui:1.6.0'
implementation 'androidx.compose.material3:material3:1.2.0' // Navegação
implementation 'androidx.navigation:navigation-compose:2.7.6' // Media (para apps de streaming)
implementation 'androidx.media3:media3-exoplayer:1.2.1'
implementation 'androidx.media3:media3-ui:1.2.1'
}

4. Desenvolvendo a Interface com Jetpack Compose for TV

4.1 A filosofia do design para TV

Antes de escr­ev­er os Com­pos­ables, inter­nal­ize três princí­pios que devem guiar cada decisão de design no seu app de TV:

Con­teú­do primeiro: Na TV, o con­teú­do é rei. A inter­face deve desa­pare­cer e deixar o con­teú­do bril­har. Fun­dos escuros, ele­men­tos de UI dis­cre­tos, e muito espaço em bran­co são padrões por razão.

Foco sem­pre visív­el: O usuário pre­cisa saber exata­mente onde está em cada momen­to. O esta­do de foco deve ser óbvio — não um sutil bril­ho, mas uma mudança clara de cor, taman­ho ou bor­da.

Hier­ar­quia clara: Com con­t­role remo­to, o usuário não pode “escanear” a tela com o olho e clicar dire­ta­mente. A hier­ar­quia visu­al pre­cisa guiar o olhar e o D‑pad de for­ma nat­ur­al e pre­visív­el.

4.2 Componentes fundamentais do tv-material

A bib­liote­ca androidx.tv:tv-material fornece com­po­nentes otimiza­dos para TV que já imple­men­tam o com­por­ta­men­to de foco cor­re­ta­mente. Vamos ver os mais impor­tantes:

Nav­i­ga­tion­Draw­er — Menu lat­er­al

@OptIn(ExperimentalTvMaterial3Api::class)
@Composable
fun TvApp() {
val navDrawerState = rememberDrawerState(DrawerValue.Closed) NavigationDrawer(
drawerState = navDrawerState,
drawerContent = { drawerValue ->
Column(
modifier = Modifier
.padding(16.dp)
.fillMaxHeight()
) {
// Logo do app
Text(
text = "MeuApp TV",
style = MaterialTheme.typography.headlineMedium,
modifier = Modifier.padding(bottom = 24.dp)
) // Items de navegação
navItems.forEach { item ->
NavigationDrawerItem(
selected = currentRoute == item.route,
onClick = { navController.navigate(item.route) },
leadingContent = { Icon(item.icon, item.label) },
content = { Text(item.label) }
)
}
}
}
) {
// Conteúdo principal
MainContent(navController)
}
}

TvLazy­Row — Car­rossel hor­i­zon­tal

O padrão mais comum em apps de TV é a grade de con­teú­do: lin­has hor­i­zon­tais de cards, cada lin­ha rep­re­sen­tan­do uma cat­e­go­ria. O TvLazyRow é a imple­men­tação otimiza­da para isso:

@Composable
fun ContentRow(
title: String,
items: List<ContentItem>,
onItemClick: (ContentItem) -> Unit
) {
Column {
Text(
text = title,
style = MaterialTheme.typography.titleLarge,
modifier = Modifier.padding(start = 48.dp, bottom = 8.dp)
) TvLazyRow(
contentPadding = PaddingValues(horizontal = 48.dp),
horizontalArrangement = Arrangement.spacedBy(16.dp)
) {
items(items) { item ->
ContentCard(
item = item,
onClick = { onItemClick(item) }
)
}
}
}
}

Card com foco — O com­po­nente mais impor­tante

O Card é o cav­a­lo de batal­ha do UI de TV. Ele pre­cisa ter esta­dos de foco bem definidos. Veja como imple­men­tar um Card profis­sion­al:

@OptIn(ExperimentalTvMaterial3Api::class)
@Composable
fun ContentCard(
item: ContentItem,
onClick: () -> Unit,
modifier: Modifier = Modifier
) {
var isFocused by remember { mutableStateOf(false) } Card(
onClick = onClick,
modifier = modifier
.width(240.dp)
.onFocusChanged { isFocused = it.isFocused }
.animateContentSize(),
scale = CardDefaults.scale(
focusedScale = 1.08f // Amplia levemente ao receber foco
),
glow = CardDefaults.glow(
focusedGlow = Glow(
elevationColor = MaterialTheme.colorScheme.primary,
elevation = 8.dp
)
)
) {
Box {
// Imagem do conteúdo
AsyncImage(
model = item.thumbnailUrl,
contentDescription = item.title,
contentScale = ContentScale.Crop,
modifier = Modifier
.fillMaxWidth()
.aspectRatio(16f / 9f)
) // Overlay com informações (visível quando focado)
AnimatedVisibility(
visible = isFocused,
enter = fadeIn() + slideInVertically { it },
exit = fadeOut()
) {
Box(
modifier = Modifier
.fillMaxWidth()
.align(Alignment.BottomCenter)
.background(
Brush.verticalGradient(
listOf(Color.Transparent, Color.Black.copy(0.8f))
)
)
.padding(8.dp)
) {
Column {
Text(item.title, style = MaterialTheme.typography.bodyLarge)
Text(item.subtitle, style = MaterialTheme.typography.bodySmall)
}
}
}
}
}
}

5. Navegação por D‑pad e Gerenciamento de Foco

5.1 Como o sistema de foco funciona

O Android usa um sis­tema de foco basea­do em algo­rit­mo de bus­ca dire­cional. Quan­do o usuário pres­siona o dire­cional para a dire­i­ta, o sis­tema procu­ra o ele­men­to focáv­el mais próx­i­mo na direção dire­i­ta a par­tir do ele­men­to atu­al, con­sideran­do a posição e taman­ho dos ele­men­tos na tela.

Na maio­r­ia dos casos, isso fun­ciona auto­mati­ca­mente. Mas quan­do você tem lay­outs com­plex­os, o sis­tema pode escol­her o ele­men­to erra­do. Ness­es casos, você pre­cisa con­tro­lar o foco man­ual­mente.

5.2 Controlando o foco manualmente no Compose

// Definindo a ordem de foco explicitamente
val (item1, item2, item3) = FocusRequester.createRefs()Button(
onClick = { },
modifier = Modifier
.focusRequester(item1)
.focusProperties {
right = item2 // Ao pressionar direita, vai para item2
down = item3 // Ao pressionar baixo, vai para item3
}
) { Text("Botão 1") }// Solicitando foco programaticamente (ex: ao abrir uma tela)
LaunchedEffect(Unit) {
item1.requestFocus()
}

5.3 Interceptando teclas do controle remoto

Às vezes você pre­cisa inter­cep­tar teclas especí­fi­cas — como o botão de play/pause para con­tro­lar a repro­dução de mídia. Isso é feito com o mod­i­fi­cador onKeyEvent:

@Composable
fun VideoPlayer(viewModel: PlayerViewModel) {
Box(
modifier = Modifier
.fillMaxSize()
.focusable() // Necessário para receber eventos de tecla
.onKeyEvent { keyEvent ->
when {
keyEvent.key == Key.MediaPlayPause &&
keyEvent.type == KeyEventType.KeyUp -> {
viewModel.togglePlayPause()
true // Consumiu o evento
}
keyEvent.key == Key.MediaFastForward -> {
viewModel.seekForward(10_000)
true
}
keyEvent.key == Key.DirectionLeft &&
keyEvent.type == KeyEventType.KeyDown -> {
viewModel.seekBackward(10_000)
true
}
else -> false // Não consumiu, propaga para o sistema
}
}
) {
// Componentes do player aqui
}
}

6. Reprodução de Mídia com ExoPlayer

6.1 Por que ExoPlayer é o padrão

Para apps de stream­ing ou qual­quer repro­dução de vídeo, o Exo­Play­er (ago­ra Media3 Exo­Play­er) é a escol­ha padrão no ecos­sis­tema Android TV. Ele supor­ta HLS, DASH, Smooth Stream­ing, for­matos de con­tain­er comuns, e tem inte­grações com DRM (Widevine) que são necessárias para con­teú­do pro­te­gi­do.

6.2 Implementação básica do player

class PlayerViewModel(application: Application) : AndroidViewModel(application) {    private val _player = ExoPlayer.Builder(application).build()
val player: ExoPlayer get() = _player fun play(url: String) {
val mediaItem = MediaItem.fromUri(url)
_player.apply {
setMediaItem(mediaItem)
prepare()
playWhenReady = true
}
} fun togglePlayPause() {
_player.playWhenReady = !_player.playWhenReady
} override fun onCleared() {
super.onCleared()
_player.release()
}
}@Composable
fun VideoPlayerScreen(url: String) {
val context = LocalContext.current
val viewModel: PlayerViewModel = viewModel() LaunchedEffect(url) { viewModel.play(url) } AndroidView(
factory = {
PlayerView(context).apply {
player = viewModel.player
useController = true
// Esconde controles nativos - vamos customizar
controllerAutoShow = false
}
},
modifier = Modifier.fillMaxSize()
)
}

7. Arquitetura Recomendada para Apps TV

7.1 MVVM + Repository + Clean Architecture

A arquite­tu­ra para apps TV segue o mes­mo padrão do desen­volvi­men­to Android mod­er­no, mas com algu­mas con­sid­er­ações especí­fi­cas. A sep­a­ração de camadas é ain­da mais impor­tante porque apps TV fre­quente­mente pre­cisam inte­grar múlti­plas fontes de dados (APIs de con­teú­do, catál­o­gos locais, cache agres­si­vo).

Use o padrão MVVM com a seguinte estru­tu­ra de pacotes:

com.seuapp.tv/
├── data/
│ ├── remote/ # Retrofit, APIs
│ ├── local/ # Room Database, DataStore
│ ├── repository/ # Implementações do repositório
│ └── model/ # DTOs, entidades de dados
├── domain/
│ ├── model/ # Entidades de domínio
│ ├── repository/ # Interfaces do repositório
│ └── usecase/ # Casos de uso
├── presentation/
│ ├── home/
│ │ ├── HomeScreen.kt
│ │ └── HomeViewModel.kt
│ ├── player/
│ │ ├── PlayerScreen.kt
│ │ └── PlayerViewModel.kt
│ └── components/ # Composables reutilizáveis
└── di/ # Injeção de dependência (Hilt)

7.2 Gerenciamento de estado

Apps de TV têm cic­los de vida pecu­liares. O usuário pode pausar um vídeo, ir para out­ra ativi­dade, dormir com a TV lig­a­da, e voltar horas depois. Seu app pre­cisa per­si­s­tir e restau­rar esta­do de for­ma robus­ta.

// Use SavedStateHandle para persistir estado crítico
class HomeViewModel(
private val getContentUseCase: GetContentUseCase,
private val savedStateHandle: SavedStateHandle
) : ViewModel() { // Restaura a posição de scroll entre recriações
private val _selectedCategory = savedStateHandle.getStateFlow(
"selectedCategory", 0
) val uiState: StateFlow<HomeUiState> = getContentUseCase()
.map { content -> HomeUiState.Success(content) }
.catch { HomeUiState.Error(it.message ?: "Erro desconhecido") }
.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5_000),
initialValue = HomeUiState.Loading
)
}

8. Testando seu App de TV

8.1 Testes de UI com Compose Testing

Tes­tar apps TV tem uma cama­da adi­cional de com­plex­i­dade: você pre­cisa tes­tar não ape­nas a ren­der­iza­ção, mas tam­bém o com­por­ta­men­to de foco. O Com­pose Test­ing Frame­work supor­ta sim­u­lação de even­tos de D‑pad:

@Test
fun contentCard_receivesFocus_showsOverlay() {
composeTestRule.setContent {
ContentCard(
item = fakeContentItem,
onClick = {}
)
} // Simula o card recebendo foco
composeTestRule
.onNodeWithContentDescription(fakeContentItem.title)
.requestFocus() // Verifica que o overlay com título está visível
composeTestRule
.onNodeWithText(fakeContentItem.title)
.assertIsDisplayed()
}@Test
fun navigation_dpadRight_movesFocusToNextCard() {
composeTestRule.setContent { ContentRow(items = fakeItems) } composeTestRule
.onAllNodesWithTag("content_card")[0]
.requestFocus() // Simula pressionar D-pad direita
composeTestRule.onRoot().performKeyPress(
KeyEvent(KEYCODE_DPAD_RIGHT, ACTION_DOWN)
) // Verifica que o segundo card tem foco
composeTestRule
.onAllNodesWithTag("content_card")[1]
.assertIsFocused()
}

8.2 Checklist de testes manuais

Antes de pub­licar, exe­cute este check­list em pelo menos um dis­pos­i­ti­vo físi­co e um emu­lador:

  1. Nave­g­ação com­ple­ta com D‑pad sem usar toque em nen­hum momen­to
  2. Foco sem­pre visív­el em todos os ele­men­tos inter­a­tivos
  3. Com­por­ta­men­to cor­re­to ao pres­sion­ar o botão Voltar (Back)
  4. App fun­ciona com con­t­role remo­to de ter­ceiros (pode ter lay­outs de teclas difer­entes)
  5. Repro­dução de vídeo ini­cia, pausa, retro­cede e avança cor­re­ta­mente
  6. App retor­na ao esta­do cor­re­to após entrar em stand­by e voltar
  7. Per­for­mance: sem jank (quedas de frame) ao rolar lis­tas lon­gas
  8. Tex­to legív­el a 3 met­ros de dis­tân­cia
  9. Con­traste ade­qua­do para uso em ambi­entes com ilu­mi­nação vari­a­da

9. Performance e Otimizações

9.1 Carregamento de imagens

Apps de TV exibem muitas ima­gens simul­tane­a­mente — thumb­nails de con­teú­do em múlti­plas lin­has. O car­rega­men­to ine­fi­ciente de ima­gens é a causa mais comum de jank em apps de TV. Use Coil ou Glide com con­fig­u­ração ade­qua­da:

// No Application class: configure o ImageLoader globalmente
class MyApplication : Application(), ImageLoaderFactory {
override fun newImageLoader(): ImageLoader {
return ImageLoader.Builder(this)
.memoryCache {
MemoryCache.Builder(this)
.maxSizePercent(0.30) // 30% da RAM para cache
.build()
}
.diskCache {
DiskCache.Builder()
.directory(cacheDir.resolve("image_cache"))
.maxSizeBytes(512L * 1024 * 1024) // 512 MB
.build()
}
.crossfade(true)
.build()
}
}

9.2 Composição eficiente

No Com­pose for TV, evite recom­posições desnecessárias usan­do as mel­hores práti­cas do Com­pose:

  1. Use remember e derivedStateOf para cál­cu­los deriva­dos
  2. Pre­fi­ra lamb­das estáveis em call­backs (evite cri­ar lamb­das inline em Com­pos­ables que recom­põem fre­quente­mente)
  3. Use key() em lis­tas para que o Com­pose reuti­lize Com­pos­ables cor­re­ta­mente
  4. Per­fil­ie com Android Stu­dio Pro­fil­er — a aba Lay­out Inspec­tor mostra recom­posições em tem­po real

10. Publicando na Google Play Store for TV

10.1 Requisitos obrigatórios da Play Store

O proces­so de pub­li­cação para TV tem req­ui­si­tos adi­cionais além dos apps mobile. O time de review da Google ver­i­fi­ca especi­fi­ca­mente:

  1. O man­i­fest declara android.software.leanback com required="true" (ou false se for app híbri­do TV/mobile)
  2. O man­i­fest declara android.hardware.touchscreen com required="false"
  3. O app tem um ban­ner de 320x180dp declar­a­do no man­i­fest
  4. A Activ­i­ty prin­ci­pal tem a cat­e­go­ria LEANBACK_LAUNCHER no intent-fil­ter
  5. O app fun­ciona inteira­mente sem toque — nen­hu­ma fun­cional­i­dade essen­cial depende de touch­screen
  6. O app não usa per­mis­sões que não fazem sen­ti­do em TV (câmera, por exem­p­lo) como obri­gatórias

10.2 Assets necessários para publicação

Asset
Dimen­são
Obser­vação

Ícone do app
512x512 px
PNG, sem transparên­cia

Ban­ner de TV
1280x720 px
PNG, aparece na Play Store

Fea­ture Graph­ic
1024x500 px
PNG ou JPG

Screen­shots de TV
1920x1080 px
Mín­i­mo 2, máx­i­mo 8

Ban­ner do launch­er
320x180 dp
PNG no pro­je­to

Vídeo pro­mo­cional
YouTube, 30–120s
Opcional mas recomen­da­do

10.3 O processo de publicação passo a passo

  1. Gere o APK ou AAB assi­na­do (Build > Gen­er­ate Signed Bundle/APK).
  2. No Google Play Con­sole, crie um novo app ou adi­cione suporte a TV em um app exis­tente.
  3. Em ‘Con­fig­u­ração > Pre­sença na Play Store’, adi­cione os assets de TV na seção ‘TV’.
  4. Na aba ‘Lança­men­to’, suba o AAB para a tril­ha de Testes Inter­nos primeiro.
  5. Instale via Play Store no dis­pos­i­ti­vo de TV e valide a exper­iên­cia com­ple­ta.
  6. Pro­mo­va para Pro­dução quan­do sat­is­feito com os testes.

⚠️ Atenção: Review de TV é mais rig­oroso
A Google cos­tu­ma rejeitar apps de TV que não pas­sam na ver­i­fi­cação de nave­g­ação por D‑pad. O review pode levar de 3 a 7 dias. Cer­ti­fique-se de que 100% do fluxo do usuário fun­ciona sem toque antes de sub­me­ter, pois cada rejeição adi­ciona dias ao ciclo de pub­li­cação.


11. Monetização em Apps Android TV

11.1 Modelos de monetização que funcionam em TV

O com­por­ta­men­to do usuário de TV é difer­ente do mobile. Não existe o hábito de “checar o app por 2 min­u­tos” — as sessões são lon­gas, imer­si­vas, e muitas vezes soci­ais (assi­s­tir com a família). Isso muda com­ple­ta­mente o que fun­ciona em ter­mos de mon­e­ti­za­ção.

Assi­natu­ra (Sub­scrip­tion) — O mod­e­lo dom­i­nante
Apps de stream­ing, entreten­i­men­to, fit­ness, e edu­cação têm enorme suces­so com assi­natu­ra men­sal ou anu­al. O usuário de TV está acos­tu­ma­do a pagar por con­teú­do (Net­flix, Prime, Dis­ney+), então a bar­reira psi­cológ­i­ca é menor do que no mobile. Imple­mente com Google Play Billing Library 6+.

Com­pra úni­ca (One-time pur­chase)
Para apps util­itários, jogos pre­mi­um, ou con­teú­do especí­fi­co. Fun­ciona bem quan­do o val­or perce­bido é claro e ime­di­a­to.

Freemi­um com des­blo­queio
Ofer­eça o app gra­tu­ito com con­teú­do lim­i­ta­do, e cobre para des­blo­quear a bib­liote­ca com­ple­ta. Fun­ciona espe­cial­mente bem para apps de con­teú­do.

O que NÃO fun­ciona em TV
Anún­cios ban­ner: Ban­ners são inva­sivos em tela grande e destroem a exper­iên­cia. Se você vai usar pub­li­ci­dade, use vídeos de 15–30 segun­dos entre episó­dios, no mod­e­lo de app de TV gra­tu­ito com ads. Mes­mo assim, a con­ver­são para assi­natu­ra sem anún­cios cos­tu­ma ser o obje­ti­vo.


12. Casos de Uso e Inspiração

12.1 Tipos de apps que prosperam no Android TV

Não são ape­nas apps de stream­ing que fazem sen­ti­do na TV. O ecos­sis­tema está maduro para uma var­iedade maior de cat­e­go­rias:

  1. Stream­ing de vídeo e áudio: o caso óbvio, mas ain­da há espaço para nicho
  2. Fit­ness e yoga: seguir uma aula na TV grande é muito supe­ri­or ao celu­lar
  3. Med­i­tação e bem-estar: ambi­ente imer­si­vo e tela grande cri­am exper­iên­cia pre­mi­um
  4. Jogos casuais: espe­cial­mente com con­t­role Blue­tooth, o mer­ca­do de jogos TV cresce
  5. Culinária e receitas: TV na coz­in­ha é um hábito cres­cente
  6. Edu­cação infan­til: pais querem apps educa­tivos de qual­i­dade na TV
  7. Gale­ria de fotos e vídeos famil­iares: Google Fotos tem um óti­mo app de TV — você pode faz­er mel­hor para nichos especí­fi­cos
  8. Dash­boards e infor­mações: mon­i­tora­men­to de câmeras, home automa­tion, mer­ca­do finan­ceiro

Conclusão: O Momento é Agora

O desen­volvi­men­to para Android TV pas­sou de uma espe­cial­i­dade obscu­ra para uma opor­tu­nidade real e acessív­el. Com o Jet­pack Com­pose for TV, você usa as mes­mas habil­i­dades do desen­volvi­men­to Android mod­er­no com adap­tações pon­tu­ais para o con­tex­to de TV. O mer­ca­do está crescen­do, a con­cor­rên­cia em muitas cat­e­go­rias ain­da é baixa, e a bar­reira téc­ni­ca nun­ca foi tão peque­na.

O cam­in­ho que per­cor­re­mos neste guia — do ambi­ente de desen­volvi­men­to até a pub­li­cação na Play Store — é todo o con­hec­i­men­to de base que você pre­cisa. A par­tir daqui, o difer­en­cial é o pro­du­to: enten­der pro­fun­da­mente o usuário de TV, cri­ar uma exper­iên­cia que respeite o con­tex­to da sala de estar, e iter­ar rap­i­da­mente com base em feed­back real.

Três pon­tos para levar con­si­go: primeiro, nun­ca assuma que algo fun­ciona sem tes­tar com um con­t­role remo­to real — o emu­lador de D‑pad não cap­tura todos os edge cas­es. Segun­do, invista tem­po no esta­do de foco dos seus com­po­nentes — é o que sep­a­ra um app amador de um app profis­sion­al na TV. Ter­ceiro, pense nas sessões lon­gas: seu app pre­cisa ser estáv­el por horas, geren­ciar memória efi­cien­te­mente, e lidar gra­ciosa­mente com o stand­by da TV.

O Android TV está esperan­do pelo seu próx­i­mo great app. Ago­ra você tem o mapa.

Posts Similares

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *