1. Antes de começar
O SociaLite ensina a usar várias APIs da plataforma Android para implementar recursos comuns de apps de redes sociais, aproveitando uma variedade de APIs do Jetpack para acessar funcionalidades complexas que funcionam de forma confiável em mais dispositivos e exigem menos código.
Este codelab explica o processo para tornar o app SociaLite compatível com a exibição de ponta a ponta no Android 15, e de forma que ele também seja compatível com versões anteriores. Após esse processo, o SociaLite terá a seguinte aparência, dependendo do dispositivo e modo de navegação:
SociaLite na navegação com três botões. | SociaLite na navegação por gestos. |
SociaLite em um dispositivo de tela grande. |
Pré-requisitos
- Conhecimentos básicos sobre Kotlin.
- Ter concluído o codelab Configurar o Android Studio ou ter familiaridade com o uso do Android Studio e apps de teste em um emulador ou dispositivo físico com o Android 15.
O que você vai aprender
- Como processar mudanças de ponta a ponta no Android 15.
- Como mostrar seu app de ponta a ponta de maneira compatível com versões anteriores.
O que é necessário
- Ter a versão mais recente do Android Studio.
- Ter um dispositivo de teste ou emulador com o Android 15 Beta 1 ou versão mais recente.
- Ter um SDK Android 15 Beta 1 ou mais recente.
2. Acessar o código inicial
- Faça o download do código inicial (link em inglês) no GitHub.
Você também pode clonar o repositório e conferir a ramificação codelab_improve_android_experience_2024
.
$ git clone git@github.com:android/socialite.git
$ cd socialite
$ git checkout codelab_improve_android_experience_2024
- Abra o SociaLite no Android Studio e execute o app no seu dispositivo ou emulador com o Android 15. Uma tela parecida com uma destas opções será mostrada:
Navegação com três botões. | Navegação por gestos. |
Tela grande. |
- Na página Chats, selecione uma das conversas, como a que tem uma imagem de cachorro.
Mensagem na conversa sobre cachorros na navegação com três botões. | Mensagem na conversa sobre cachorros na navegação por gestos. |
3. Mostrar seu app de ponta a ponta no Android 15
O que é ponta a ponta?
Os apps podem mostrar informações por trás das barras de sistema, permitindo uma experiência do usuário aprimorada e uso completo do espaço da tela. Isso é chamado de exibição de ponta a ponta.
Como processar mudanças de ponta a ponta no Android 15
Antes do Android 15, por padrão, a interface do seu app era restrita de modo a evitar as áreas das barras de sistema, como as barras de status e navegação. Os apps permitiam escolher a exibição de ponta a ponta. Dependendo do app, implementar esse tipo de exibição podia ser simples ou complexo.
Do Android 15 em diante, seu app será mostrado de ponta a ponta por padrão. Confira alguns dos novos padrões:
- A barra de navegação com três botões é translúcida.
- A barra de navegação por gestos é transparente.
- A barra de status é transparente.
- A menos que o conteúdo aplique encartes ou padding, ele será mostrado por trás das barras de sistema, como as de navegação, status e legenda.
Isso garante que a exibição de ponta a ponta não seja ignorada como uma maneira de aumentar a qualidade do app e reduz o trabalho necessário para mostrar o app dessa forma. No entanto, essa mudança pode impactar seu app negativamente. Você verá dois exemplos de impactos negativos no SociaLite após o upgrade do SDK de destino para o Android 15.
Mudar o valor do SDK de destino para o Android 15
- No arquivo build.gradle do app SociaLite, mude o destino e compile as versões do SDK para o Android 15 ou
VanillaIceCream
.
Se você estiver lendo este codelab antes do lançamento estável do Android 15, o código terá esta aparência:
android {
namespace = "com.google.android.samples.socialite"
compileSdkPreview = "VanillaIceCream"
defaultConfig {
applicationId = "com.google.android.samples.socialite"
minSdk = 21
targetSdkPreview = "VanillaIceCream"
...
}
...
}
Se for após o lançamento estável do Android 15, o código terá esta aparência:
android {
namespace = "com.google.android.samples.socialite"
compileSdk = 35
defaultConfig {
applicationId = "com.google.android.samples.socialite"
minSdk = 21
targetSdk = 35
...
}
...
}
- Recrie o SociaLite e observe estes problemas:
- A proteção em segundo plano da navegação com três botões não corresponde à barra de navegação. Na navegação por gestos, a tela Chats é mostrada de ponta a ponta sem nenhum tipo de intervenção da sua parte. No entanto, há uma proteção em segundo plano da navegação com três botões, que precisa ser removida.
Tela Chats na navegação com três botões. | Tela Chats na navegação por gestos. |
- Interface ocultada. Os elementos da interface na parte de baixo de uma conversa são ocultados pelas barras de navegação. Isso fica mais aparente na navegação com três botões.
Mensagem na conversa sobre cachorros na navegação com três botões. | Mensagem na conversa sobre cachorros na navegação por gestos. |
Corrigir o SociaLite
Para remover a proteção em segundo plano padrão da navegação com três botões, siga estas etapas:
- No arquivo
MainActivity.kt
, configure a propriedade window.isNavigationBarContrastEnforced como falsa para remover a proteção em segundo plano padrão.
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
installSplashScreen()
super.onCreate(savedInstanceState)
setContent {
// Add this block:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
window.isNavigationBarContrastEnforced = false
}
}
}
...
}
A window.isNavigationBarContrastEnforced
garante que a barra de navegação tenha contraste suficiente quando um plano de fundo totalmente transparente é solicitado. Ao definir esse atributo como falso, você configura o plano de fundo da navegação com três botões como transparente. A window.isNavigationBarContrastEnforced
só afeta a navegação com três botões e não tem impacto na navegação por gestos.
- Execute o app novamente e confira uma das conversas no seu dispositivo Android 15. As telas Timeline, Chats e Settings agora são mostradas de ponta a ponta. A
NavigationBar
do app (com os botões Timeline, Chats e Settings) é mostrada por trás da barra de navegação com três botões transparente do sistema.
Tela Chats com remoção das faixas de cor. | Não há mudanças na navegação por gestos. |
No entanto, observe que a InputBar
da conversa ainda está ocultada pelas barras de sistema. Você precisa processar os encartes adequadamente para corrigir esse problema.
Conversa sobre cachorros na navegação com três botões. O campo de entrada na parte de baixo foi ocultado pela barra de navegação do sistema. | Conversa sobre cachorros na navegação por gestos. O campo de entrada na parte de baixo foi ocultado pela barra de navegação do sistema. |
No SociaLite, a InputBar
fica esmaecida. Na prática, você poderá encontrar elementos esmaecidos nos quatro cantos da tela ao mudar para o modo paisagem ou usar um dispositivo de tela grande. É preciso programar o processamento de encartes em todos esses casos de uso. Para o SociaLite, padding é aplicado para destacar o conteúdo tocável da InputBar
.
Para aplicar encartes e corrigir interfaces ocultadas, siga estas etapas:
- Navegue até o arquivo
ui/chat/ChatScreen.kt
e encontre o elemento combinávelChatContent
por volta da linha 178, que contém a interface da tela de conversa. OChatContent
usa oScaffold
para criar a interface com facilidade. Por padrão, oScaffold
fornece informações sobre a interface do sistema, como a profundidade das barras de sistema, na forma de encartes que você pode consumir com valores do padding doScaffold
(o parâmetroinnerPadding
). Adicione padding usando oinnerPadding
doScaffold
comoInputBar
. - Encontre a
InputBar
dentro doChatContent
por volta da linha 214. Esse é um elemento combinável que cria a interface para usuários escreverem mensagens. A prévia é semelhante a esta:
A InputBar
usa um contentPadding
para aplicar como padding ao elemento combinável Row que contém o restante da interface. Esse padding será aplicado a todos os lados do elemento combinável Row. Você pode conferir esse detalhe por volta da linha 432. Para referência, confira o elemento combinável InputBar
(não adicione este código):
// Don't add this code because it's only for reference.
@Composable
private fun InputBar(
contentPadding: PaddingValues,
...,
) {
Surface(...) {
Row(
modifier = Modifier
.padding(contentPadding)
...
) {
IconButton(...) { ... } // take picture
IconButton(...) { ... } // attach picture
TextField(...) // write message
FilledIconButton(...){ ... } // send message
}
}
}
}
- Volte à
InputBar
dentro deChatContent
e mude ocontentPadding
para consumir os encartes da barra de sistema. Faça isso por volta da linha 220.
InputBar(
...
contentPadding = innerPadding, //Add this line.
// contentPadding = PaddingValues(0.dp), // Remove this line.
...
)
- Execute o app novamente no seu dispositivo Android 15.
Conversa sobre cachorros na navegação com três botões com encartes aplicados incorretamente. | Conversa sobre cachorros na navegação por gestos com encartes aplicados incorretamente. |
O padding da parte de baixo foi aplicado para que os botões não ficassem mais esmaecidos pelas barras de sistema, mas o padding da parte de cima também foi aplicado. Ele abrange a profundidade da TopAppBar
e a barra de sistema. O Scaffold transmite os valores do padding ao conteúdo para evitar a barra de apps da parte de cima, bem como as barras de sistema.
- Para corrigir o padding da parte de cima, crie uma cópia dos
PaddingValues
doinnerPadding
, defina o padding da parte de cima como0.dp
e transmita sua cópia modificada paracontentPadding
.
InputBar(
...
contentPadding = innerPadding.copy(layoutDirection, top = 0.dp), //Add this line.
// contentPadding = innerPadding, // Remove this line.
...
)
- Execute o app novamente no seu dispositivo Android 15.
Conversa sobre cachorros na navegação com três botões com encartes aplicados corretamente. | Conversa sobre cachorros na navegação por gestos com encartes aplicados corretamente. |
Parabéns! Você tornou o SociaLite compatível com as mudanças da plataforma de ponta a ponta do Android 15. A seguir, você vai aprender a mostrar o SociaLite de ponta a ponta de maneira compatível com versões anteriores.
4. Mostrar o SociaLite de ponta a ponta de maneira compatível com versões anteriores
O SociaLite agora é mostrado de ponta a ponta no Android 15, mas ainda não tem essa funcionalidade em dispositivos Android mais antigos. Para mostrar o SociaLite de ponta a ponta em dispositivos Android mais antigos, chame enableEdgeToEdge
antes de definir o conteúdo no arquivo MainActivity.kt
.
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
installSplashScreen()
enableEdgeToEdge() // Add this line.
window.isNavigationBarContrastEnforced = false
super.onCreate(savedInstanceState)
setContent {... }
}
}
A importação do enableEdgeToEdge
é import androidx.activity.enableEdgeToEdge
. A dependência é AndroidX Activity 1.8.0 ou uma versão mais recente.
Para conferir uma descrição aprofundada do processo para mostrar seu app de ponta a ponta de maneira compatível com versões anteriores e processar encartes, confira estes guias:
Isso conclui a parte do Programa de treinamentos sobre a exibição de ponta a ponta. A próxima seção é opcional e discute outras considerações sobre a exibição de ponta a ponta que podem ser relevantes para o seu app.
5. Opcional: outras considerações sobre a exibição de ponta a ponta
Como processar encartes em várias arquiteturas
Componentes
Talvez você tenha notado que vários componentes do SociaLite não mudaram após a troca do valor do SDK de destino. O SociaLite é arquitetado com práticas recomendadas, então é fácil processar essa mudança de plataforma. As práticas recomendadas incluem:
- Usar componentes do Material Design 3 (
androidx.compose.material3
), comoTopAppBar
,BottomAppBar
eNavigationBar
, porque eles aplicam encartes automaticamente. - Se o app estiver usando componentes do Material 2 (
androidx.compose.material
) no Compose, eles não vão processar encartes automaticamente por conta própria. No entanto, você pode conseguir acesso aos encartes para fazer a aplicação manual. Noandroidx.compose.material 1.6.0
e versões mais recentes, use o parâmetrowindowInsets
para aplicar os encartes manualmente paraBottomAppBar
,TopAppBar
,BottomNavigation
eNavigationRail
. Da mesma forma, use o parâmetrocontentWindowInsets
paraScaffold
. Você também pode aplicar os encartes manualmente como padding. - Caso o app use componentes do Views e do Material (
com.google.android.material
), a maioria dos componentes do Material baseados no Views (comoBottomNavigationView
,BottomAppBar
,NavigationRailView
eNavigationView
) processa encartes, então não é necessário fazer mais nada. No entanto, você precisará adicionarandroid:fitsSystemWindows="true"
se estiver usando oAppBarLayout
. - Se o app estiver usando o Views e
BottomSheet
,SideSheet
ou contêineres personalizados, você precisa aplicar padding usando oViewCompat.setOnApplyWindowInsetsListener
. ParaRecyclerView
, aplique padding usando esse listener e também adicioneclipToPadding="false"
. - Usar
Scaffold
(ouNavigationSuiteScaffold
ouListDetailPaneScaffold
), nãoSurface
, em uma interface complexa. OScaffold
permite posicionarTopAppBar
,BottomAppBar
,NavigationBar
eNavigationRail
com facilidade.
Como rolar conteúdo
Seu app pode conter listas, e o último item da lista pode ser ocultado pelas barras de navegação do sistema com a mudança do Android 15.
Mostra que o último item da lista está ocultado pela navegação com três botões.
Como rolar conteúdo com o Compose
No Compose, use o contentPadding da LazyColumn
para adicionar um espaço ao último item, a menos que você esteja usando o TextField
:
Scaffold { innerPadding ->
LazyColumn(
contentPadding = innerPadding
) {
// Content that does not contain TextField
}
}
Mostra que o último item da lista não está ocultado pela navegação com três botões.
No caso do TextField
, use um Spacer
para mostrar o último TextField
em uma LazyColumn
. Para saber mais, consulte Consumo de encartes.
LazyColumn(
Modifier.imePadding()
) {
// Content with TextField
item {
Spacer(
Modifier.windowInsetsBottomHeight(
WindowInsets.systemBars
)
)
}
}
Como rolar conteúdo com o Views
Para RecyclerView
ou NestedScrollView
, adicione android:clipToPadding="false"
.
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
app:layoutManager="LinearLayoutManager" />
Forneça paddings dos encartes de janela em todos os lados, usando setOnApplyWindowInsetsListener
:
ViewCompat.setOnApplyWindowInsetsListener(binding.recycler) { v, insets ->
val i = insets.getInsets(
WindowInsetsCompat.Type.systemBars() + WindowInsetsCompat.Type.displayCutout()
)
v.updatePadding(
left = i.left,
right = i.right,
bottom = i.bottom + bottomPadding,
)
WindowInsetsCompat.CONSUMED
}
Como usar o LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
Antes de ser destinado ao SDK 35, o SocialLite tinha esta aparência no modo paisagem, com uma grande caixa branca na borda esquerda para compensar o corte da câmera. Na navegação com três botões, os botões ficam do lado direito.
Depois de ser destinado ao SDK 35, o SociaLite terá esta aparência, com a borda esquerda livre, sem precisar compensar o corte da câmera. Para conseguir esse efeito, o Android define LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS automaticamente.
Dependendo do app, convém processar encartes nesta parte.
Para fazer isso no SociaLite, siga estas etapas:
- No arquivo
ui/ContactRow.kt
, encontre o elemento combinável Row. - Modifique o padding para considerar o corte da tela.
@Composable
fun ChatRow(
chat: ChatDetail,
onClick: (() -> Unit)?,
modifier: Modifier = Modifier,
) {
// Add layoutDirection, displayCutout, startPadding, and endPadding.
val layoutDirection = LocalLayoutDirection.current
val displayCutout = WindowInsets.displayCutout.asPaddingValues()
val startPadding = displayCutout.calculateStartPadding(layoutDirection)
val endPadding = displayCutout.calculateEndPadding(layoutDirection)
Row(
modifier = modifier
...
// .padding(16.dp) // Remove this line.
// Add this block:
.padding(
PaddingValues(
top = 16.dp,
bottom = 16.dp,
// Ensure content is not occluded by display cutouts
// when rotating the device.
start = startPadding.coerceAtLeast(16.dp),
end = endPadding.coerceAtLeast(16.dp)
)
),
...
) { ... }
Após o processamento de cortes de tela, o SociaLite terá esta aparência:
Teste várias configurações de corte da tela na seção Opções do desenvolvedor em Corte da tela.
Caso uma janela não flutuante (por exemplo, uma atividade) do seu app esteja usando LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT
, LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER
ou LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
, o Android vai interpretar esses modos de corte como LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
do Android 15 Beta 2 em diante. No Android 15 Beta 1, isso causaria uma falha no app.
Barras de legenda também são barras de sistema
Uma barra de legenda também é uma barra de sistema, já que descreve a decoração da janela da interface do sistema de uma janela com formato livre, como a barra de título da parte de cima. Você pode conferir a barra de legenda em um emulador de computador no Android Studio. Na captura de tela abaixo, a barra de legenda está na parte de cima do app.
No Compose, se você estiver usando PaddingValues
, safeContent
, safeDrawing
ou as WindowInsets.systemBars
integradas do Scaffold, seu app será mostrado como esperado. No entanto, se você estiver processando encartes com a statusBar
, o conteúdo do app talvez não seja mostrado como esperado, já que a barra de status não considera a barra de legenda.
No Views, se você estiver processando encartes manualmente usando WindowInsetsCompat.systemBars
, seu app será mostrado como esperado. Se você estiver processando encartes manualmente usando WindowInsetsCompat.statusBars
, talvez o app não seja mostrado como esperado, já que barras de status não são barras de legenda.
Apps no modo imersivo
Telas no modo imersivo não são afetadas pelas restrições de ponta a ponta do Android 15, já que apps imersivos já são mostrados dessa forma.
Como proteger as barras de sistema
Talvez você queira que o app tenha uma barra transparente na navegação por gestos, mas translúcida ou opaca na navegação com três botões.
No Android 15, uma navegação com três botões translúcida é o padrão, já que a plataforma define a propriedade window.isNavigationBarContrastEnforced
como true
. A navegação por gestos permanece transparente.
A navegação com três botões é translúcida por padrão. |
No geral, uma navegação com três botões translúcida será suficiente. No entanto, em alguns casos, seu app pode exigir uma navegação com três botões opaca. Primeiro, defina a propriedade window.isNavigationBarContrastEnforced
como false
. Em seguida, use WindowInsetsCompat.tappableElement
para o Views ou WindowInsets.tappableElement
para o Compose. Se o conteúdo for 0, isso significa que o usuário está utilizando a navegação por gestos. Do contrário, o usuário está utilizando a navegação com três botões. Se esse for o caso, mostre uma visualização ou caixa por trás da barra de navegação. Confira um possível exemplo de composição:
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
window.isNavigationBarContrastEnforced = false
MyTheme {
Surface(...) {
MyContent(...)
ProtectNavigationBar()
}
}
}
}
}
// Use only if required.
@Composable
fun ProtectNavigationBar(modifier: Modifier = Modifier) {
val density = LocalDensity.current
val tappableElement = WindowInsets.tappableElement
val bottomPixels = tappableElement.getBottom(density)
val usingTappableBars = remember(bottomPixels) {
bottomPixels != 0
}
val barHeight = remember(bottomPixels) {
tappableElement.asPaddingValues(density).calculateBottomPadding()
}
Column(
modifier = modifier.fillMaxSize(),
verticalArrangement = Arrangement.Bottom
) {
if (usingTappableBars) {
Box(
modifier = Modifier
.background(MaterialTheme.colorScheme.background)
.fillMaxWidth()
.height(barHeight)
)
}
}
}
Navegação com três botões opaca. |
6. Analisar o código da solução
O método onCreate
do arquivo MainActivity.kt
tem esta aparência:
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
installSplashScreen()
enableEdgeToEdge()
window.isNavigationBarContrastEnforced = false
super.onCreate(savedInstanceState)
setContent {
Main(
shortcutParams = extractShortcutParams(intent),
)
}
}
}
O elemento combinável ChatContent
dentro do arquivo ChatScreen.kt
processa encartes desta forma:
private fun ChatContent(...) {
...
Scaffold(...) { innerPadding ->
Column {
...
InputBar(
input = input,
onInputChanged = onInputChanged,
onSendClick = onSendClick,
onCameraClick = onCameraClick,
onPhotoPickerClick = onPhotoPickerClick,
contentPadding = innerPadding.copy(
layoutDirection, top = 0.dp
),
sendEnabled = sendEnabled,
modifier = Modifier
.fillMaxWidth()
.windowInsetsPadding(
WindowInsets.ime.exclude(WindowInsets.navigationBars)
),
)
}
}
}
O código de solução está disponível na ramificação principal. Se você já tiver baixado o SociaLite:
git checkout main
Caso contrário, baixe o código novamente de forma direta ou pelo git para visualizar a ramificação principal:
git clone git@github.com:android/socialite.git