1. Antes de começar
Este codelab mostra o processo de criação de um widget de app para o SociaLite. Primeiro, você vai criar um widget simples usando o Glance e adicionar ao SociaLite e à tela inicial. Depois, vai adicionar um estado zero ao widget usando componentes e um tema do Glance. O codelab vai mostrar como oferecer suporte para interação do usuário e selecionar seu contato favorito no widget. Por fim, você vai aprender a atualizar o widget no seu app.
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 teste de apps em um emulador do Android 15 ou em um dispositivo físico com Android 15.
- Conhecimentos básicos sobre o Hilt.
- Conhecimentos básicos sobre o Compose. O Glance não usa elementos combináveis do Jetpack Compose, mas sim a estrutura e o estilo de programação dele.
O que você vai aprender neste codelab
- Como configurar seu app para ser compatível com widgets.
- Como usar componentes do Glance para criar um layout responsivo.
- Como usar um
GlanceTheme
para compatibilidade das cores dinâmicas nas telas iniciais dos seus usuários. - Como processar interações do usuário no seu widget.
- Como atualizar seu widget no app.
O que é necessário
- Ter a versão mais recente do Android Studio.
- Um dispositivo de teste ou emulador com o Android 12 ou versão mais recente.
- SDK do Android 12 ou versão mais recente.
2. Começar a configuração
Acessar o código inicial
- Se você tiver concluído os codelabs "Processar restrições de ponta a ponta no Android 15" ou "Adicionar animações de volta preditiva", siga para a seção Adicionar um Widget porque você já tem o código inicial.
- Baixe o código inicial (link em inglês) no GitHub.
Ou você pode clonar o repositório e verificar 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 Android 15. Uma tela parecida com esta vai aparecer:
SociaLite com navegação por gestos
3. Adicionar um widget
O que são widgets?
Um widget é uma parte do seu app que pode ser incorporada em outros apps Android. Geralmente, à tela inicial do usuário.
Adicionar widgets ao app pode dar aos usuários a capacidade de iniciar rapidamente tarefas comuns, visualizar informações e personalizar o dispositivo com seu conteúdo.
O que é o Glance?
O Jetpack Glance é uma biblioteca para criar widgets usando uma API do tipo Compose em Kotlin. Ele tem várias das mesmas vantagens do Compose, como recomposição, código de UI declarativo escrito em Kotlin e componentes opinativos. O Glance elimina grande parte da necessidade de visualizações remotas de XML nos seus widgets.
Criar um widget
Os widgets no Android são declarados no AndroidManifest
como um elemento <receiver>
. Esse receptor precisa ser exportado, processe a intent da ação android.appwidget.action.APPWIDGET_UPDATE
e forneça um arquivo de configurações do widget do app por um elemento de metadados chamado android.appwidget.provider
.
Adicionar um widget ao SociaLite
Você precisa adicionar um widget ao Socialite que permita ao usuário ver o contato favorito e se há mensagens não lidas dessa pessoa. Se for o caso, tocar no widget deve levar o usuário ao chat do contato favorito. Além disso, use os componentes e temas do Glance para garantir que o widget tenha a melhor aparência possível, usando um design responsivo e cores dinâmicas.
Para começar, adicione um widget estático "Hello World" ao Socialite. Depois disso, amplie os recursos do widget.
Para isso, faça o seguinte:
- Adicione as dependências do Glance ao seu app.
- Crie uma implementação do
GlanceAppWidget
. - Crie um
GlanceAppWidgetReceiver
. - Configure seu widget com um arquivo XML de informações do widget do app.
- Adicione o receptor e as informações do widget do app ao arquivo
AndroidManifest.xml
.
Adicionar o Glance ao seu projeto
O código inicial adicionou as versões do Glance e as coordenadas da biblioteca ao catálogo de versões do SociaLite: libs.versions.toml
.
libs.versions.toml
[versions]
//..
glance = "1.1.1"
[libraries]
glance-appwidget = { group = "androidx.glance", name = "glance-appwidget", version.ref = "glance" }
glance-material = { group = "androidx.glance", name = "glance-material3", version.ref = "glance" }
Além disso, as dependências do Glance estão incluídas no arquivo app/build.gradle.kts
do SociaLite.
build.gradle.kts
dependencies {
...
implementation(libs.glance.appwidget)
implementation(libs.glance.material)
...
}
- Se você modificou esses arquivos, sincronize o projeto para baixar as bibliotecas do Glance.
Criar o GlanceAppWidget
e o GlanceAppWidgetReceiver
O Android usa um broadcast receiver para alertar o SociaLite de que um widget foi adicionado, precisa ser atualizado ou foi removido. O Glance fornece uma classe abstrata de receptor, GlanceAppWidgetReceiver, que estende AppWidgetProvider.
As implementações do GlanceAppWidgetReceiver
também são responsáveis por fornecer instâncias do GlanceAppWidget
. Essa classe renderiza os combináveis do Glance em visualizações remotas.
O código inicial inclui duas classes: SocialiteAppWidget
, que estende GlanceAppWidget
, e SocialiteAppWidgetReceiver
, que estende GlanceAppWidgetReceiver
.
Para começar, siga estas etapas:
- Navegue para o pacote
widget
emapp/src/main/java/com/google/android/samples/socialite/
. - Abra a classe
SociaLiteAppWidget
. Essa classe substitui o métodoprovideGlance
. - Substitua o
TODO
por uma chamada paraprovideContent
e transmita a função combinável do widget como um parâmetro. Por enquanto, o widget mostra apenas a mensagemHello World
, mas você vai adicionar mais funcionalidades mais tarde neste codelab.
package com.google.android.samples.socialite.widget
import android.content.Context
import androidx.glance.GlanceId
import androidx.glance.GlanceTheme
import androidx.glance.appwidget.GlanceAppWidget
import androidx.glance.appwidget.provideContent
import androidx.glance.text.Text
class SociaLiteAppWidget : GlanceAppWidget() {
override suspend fun provideGlance(context: Context, id: GlanceId) {
provideContent {
GlanceTheme {
Text("Hello World")
}
}
}
}
- Abra a classe
SociaLiteAppWidgetReceiver
no pacotewidget
. Por enquanto, o receptor fornece uma instância do seuSociaLiteWidget
, mas você vai adicionar mais funcionalidades em uma próxima seção. - Substitua o
TODO
pelo construtorSociaLiteAppWidget()
:
package com.google.android.samples.socialite.widget
import androidx.glance.appwidget.GlanceAppWidget
import androidx.glance.appwidget.GlanceAppWidgetReceiver
class SociaLiteAppWidgetReceiver : GlanceAppWidgetReceiver() {
override val glanceAppWidget: GlanceAppWidget = SociaLiteAppWidget()
}
Está tudo pronto para configurar o Android para mostrar seu widget e permitir que os usuários o adicionem à tela inicial.
Adicionar informações do provedor do app-widget
- Clique com o botão direito do mouse em **
res/xml
** > New > XML resource file. - Digite
socialite_widget_info
como o nome do arquivo eappwidget-provider
como o elemento raiz, depois clique em OK. Esse arquivo inclui os metadados do seuappwidget
que é usado por umAppWidgetHost
para mostrar o widget. - Adicione este código ao arquivo
socialite_widget_info.xml
:
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:resizeMode="horizontal|vertical"
android:updatePeriodMillis="3600000"
android:minHeight="128dp"
android:minWidth="128dp"
android:minResizeHeight="128dp"
android:minResizeWidth="128dp"
android:configure="com.google.android.samples.socialite.widget.SociaLiteAppWidgetConfigActivity"
android:widgetFeatures="configuration_optional|reconfigurable"
android:previewImage="@drawable/widget_preview"
android:maxResizeHeight="512dp"
android:maxResizeWidth="512dp"
android:targetCellWidth="2"
android:targetCellHeight="2"
android:initialLayout="@layout/glance_default_loading_layout">
</appwidget-provider>
A tabela abaixo fornece uma visão geral dos atributos nesse código e descreve cada um deles:
Nome do atributo | Descrição |
| O widget pode ser redimensionado vertical e horizontalmente. |
| Especifique o tamanho padrão do widget quando adicionado à tela inicial. |
| Controla quando o host pode decidir atualizar o widget. O app pode atualizar o widget sempre que ele estiver em execução e tiver novas informações. |
| Define o tamanho mínimo para redimensionar o widget. |
| Especifica o tamanho mínimo padrão do widget quando adicionado à tela inicial. |
| Fornece um layout inicial que é mostrado enquanto o Glance renderiza os elementos combináveis. |
| Fornece uma imagem estática do widget a ser mostrada no seletor de widgets. |
| Indique vários recursos que o widget aceita. Essas são dicas para o host do widget e não mudam de fato o comportamento dele. |
| O nome da classe da atividade de configuração. Essa é uma atividade que configura o widget mais tarde. |
Para saber quais são todos os atributos disponíveis, incluindo os recursos da API 31 ou mais recente, consulte AppWidgetProviderInfo
.
Atualizar AndroidManifest
e testar
Por fim, está tudo pronto para atualizar o arquivo AndroidManifest.xml
e testar seu widget. Defina um elemento receiver
como filho do elemento application
no arquivo. Esse receptor processa a intent APPWIDGET_UPDATE
e fornece seus metadados appwidget
para a tela de início do Android.
Para começar, siga estas etapas:
- Crie um elemento
receiver
para que oSociaLiteAppWidgetReceiver
seja exportado. Copie e cole o seguinte no seu arquivoAndroidManifest.xml
depois do elementoapplication
:
<receiver
android:name=".widget.SociaLiteAppWidgetReceiver"
android:exported="true"
android:label="Favorite Contact">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/socialite_widget_info" />
</receiver>
- Compile e execute seu app.
- Quando o app estiver em execução, adicione o widget à tela inicial. Por exemplo, em um Pixel, toque e mantenha pressionado o plano de fundo, selecione Widgets > SociaLite. Você pode adicionar o widget à sua tela inicial.
O widget diz "Hello World" e tem um plano de fundo transparente. Esse widget não é muito bonito nem o mais funcional até o momento. Na próxima seção, você vai adicionar um layout mais complicado e melhorar a aparência dele com um toque de cor do Material Design.
4. Melhorar o projeto
Agora você tem um widget estático, sem muitos recursos. Um bom widget faz o seguinte:
- Mantém a funcionalidade simples com conteúdo atualizado e curto.
- Minimiza falhas incômodas com um layout redimensionável.
- Aplica a cor do plano de fundo do host do widget do app.
Para uma discussão mais aprofundada sobre o que faz com que um widget seja bom, consulte Widgets.
Adicionar um Scaffold
Atualize seu widget para mostrar o componente Scaffold
do Glance.
O Scaffold
é fornecido pela biblioteca do Glance. Ele é uma API de slot simples para mostrar a interface de um widget com uma TitleBar
. Ele também define a cor do plano de fundo como GlanceTheme.colors.widgetBackground
e aplica padding e será seu componente de nível superior.
Para começar, siga estas etapas:
- Substitua sua implementação de
SociaLiteAppWidget
por este código:
package com.google.android.samples.socialite.widget
import android.content.Context
import androidx.compose.runtime.Composable
import androidx.glance.GlanceId
import androidx.glance.GlanceModifier
import androidx.glance.GlanceTheme
import androidx.glance.ImageProvider
import androidx.glance.appwidget.GlanceAppWidget
import androidx.glance.appwidget.components.Scaffold
import androidx.glance.appwidget.components.TitleBar
import androidx.glance.appwidget.provideContent
import androidx.glance.layout.fillMaxSize
import androidx.glance.text.Text
import com.google.android.samples.socialite.R
class SociaLiteAppWidget : GlanceAppWidget() {
override suspend fun provideGlance(context: Context, id: GlanceId) {
provideContent {
GlanceTheme() {
Content()
}
}
}
@Composable
private fun Content() {
Scaffold(titleBar = {TitleBar(startIcon = ImageProvider(R.drawable.ic_launcher_monochrome), title = "SociaLite")},
modifier = GlanceModifier.fillMaxSize()) {
Text("Hello World")
}
}
}
- Para acessar suas atualizações, execute o app novamente e adicione uma nova cópia do widget à sua tela inicial.
Os widgets são visualizações remotas que são mostradas por um host externo. Depois, você vai adicionar a capacidade de atualizar automaticamente o widget dentro do app. Até lá, é preciso adicionar o widget do seletor para ver as mudanças de código.
Agora está muito melhor, mas quando você compara seu widget com outros, as cores parecem estar erradas. Nas telas iniciais, o esperado é que os widgets definam as cores com base nas configurações do tema do usuário. Com tokens de cores dinâmicas, você pode fazer com que o tema do widget se adapte ao plano de fundo e ao tema do seu dispositivo.
Adicionar cores dinâmicas
Adicione os tokens de cor widgetBackground
ao plano de fundo do scaffold e onSurface
ao texto da TitleBar
e outros componentes de texto. Para atualizar o estilo do texto, é preciso importar a classe TextStyle
do Glance. Para atualizar o plano de fundo do scaffold, defina a propriedade backgroundColor
do Scaffold
como GlanceTheme.colors.widgetBackground
.
Para começar, siga estas etapas:
- Inclua a nova importação no arquivo
SociaLiteAppWidget.kt
.
//Add to the imports section of your Kotlin code.
import androidx.glance.text.TextStyle
- Atualize o combinável
Content
para adicionarwidgetBackground
.
Scaffold(
titleBar = {
TitleBar(
textColor = GlanceTheme.colors.onSurface,
startIcon = ImageProvider(R.drawable.ic_launcher_monochrome),
title = "SociaLite",
)
},
backgroundColor = GlanceTheme.colors.widgetBackground,
modifier = GlanceModifier.fillMaxSize(),
) {
Text(text = "Hello World", style = TextStyle(color = GlanceTheme.colors.onSurface))
}
- Para acessar suas atualizações, execute o app novamente e adicione uma nova cópia do widget à sua tela inicial.
Ela combina com os temas de outros widgets na sua tela inicial e atualiza automaticamente as cores se você mudar o plano de fundo ou definir o modo escuro. Para um plano de fundo bem colorido, o widget se adapta à seção do plano de fundo em que se encontra.
|
|
Adicionar um estado zero
Agora você precisa pensar sobre o estado e configurar seu widget. Quando um widget é adicionado à tela inicial e requer configuração, geralmente é melhor mostrar um estado zero. O estado zero solicita que o usuário configure o widget. Você adiciona uma atividade de configuração ao widget e o vincula ao estado zero.
Este codelab oferece aulas sobre como armazenar, acessar e modificar o estado de configuração de um widget. Você vai adicionar código para atualizar a interface do widget e mostrar esse estado, além de criar uma ação lambda para processar as ações de toque do usuário.
Revisar o modelo do widget
Dedique um momento para revisar as classes no pacote com.google.android.samples.socialite.widget.model
.
Isso inclui as classes WidgetModel
, WidgetModelDao
e WidgetModelRepository
. Elas já estão presentes no código inicial do codelab e processam a persistência do estado dos widgets no banco de dados Room
. Além disso, essas classes usam o Hilt para gerenciar os ciclos de vida.
A classe WidgetModel
contém um widgetId
que é atribuído pelo Android, o contactId
do contato do SociaLite que ela mostra, um displayName
e uma photo
a serem mostrados e um booleano se o contato tiver mensagens não lidas. Eles são consumidos pelos combináveis SociaLiteAppWidget
e mostrados no widget.
O WidgetModelDao
é um objeto de acesso a dados que resume o acesso ao banco de dados do SociaLite. O WidgetModelRepository
fornece funções de conveniência para criar, ler, atualizar e excluir instâncias WidgetModel
. Essas classes são criadas pelo Hilt e injetadas no app com injeção de dependência.
- Abra o arquivo
WidgetModel.kt
no pacotemodel
encontrado emapp/src/main/java/com/google/android/samples/socialite/widget/model/
.
Esse arquivo é uma classe data
com uma anotação Entity
. A cada instância do widget é atribuído um ID dedicado pelo Android, que é usado pelo SociaLite como uma chave primária para os dados do modelo. Cada instância do modelo monitora as informações básicas do contato associado e se há mensagens não lidas.
@Entity(
foreignKeys = [
ForeignKey(
entity = Contact::class,
parentColumns = ["id"],
childColumns = ["contactId"],
onDelete = ForeignKey.CASCADE,
),
],
indices = [
Index("widgetId"),
Index("contactId"),
],
)
data class WidgetModel(
@PrimaryKey val widgetId: Int,
val contactId: Long,
val displayName: String,
val photo: String,
val unreadMessages: Boolean = false,
) : WidgetState
O estado zero
Seu combinável Content
precisa carregar o modelo do widget a partir do WidgetModelRepository
e mostrar o estado zero se não houver nenhum modelo disponível. Caso contrário, ele vai mostrar o conteúdo normal do widget. Por enquanto, esta será sua mensagem "Hello World", mas na próxima parte você vai criar uma interface melhor.
Substitua seu combinável Content
por uma expressão when
que mostre o combinável ZeroState
ou um marcador de posição Text
.
- No método
provideGlance
, fora do seu combinável, acesse uma referência aoWidgetModelRepository
e o ID atual do widget. Adicione as linhas abaixo antes deprovideContent
no métodoSociaLiteAppWidget
provideGlance
.
override suspend fun provideGlance(context: Context, id: GlanceId) {
val widgetId = GlanceAppWidgetManager(context).getAppWidgetId(id)
val repository = WidgetModelRepository.get(context)
Talvez também seja necessário adicionar estas importações:
import com.google.android.samples.socialite.widget.model.WidgetModel
import com.google.android.samples.socialite.widget.model.WidgetModelRepository
import com.google.android.samples.socialite.widget.model.WidgetState.Loading
import androidx.glance.appwidget.GlanceAppWidgetManager
- Na função combinável
Content
, adicione o repositório e o ID do widget como parâmetros e use-os para carregar seu modelo. Atualize a assinatura da função do combinávelContent
e adicione esta linha:
private fun Content(repository: WidgetModelRepository, widgetId: Int) {
val model = repository.loadModel(widgetId).collectAsState(Loading).value
- Se o Android Studio não adicionar a importação abaixo de maneira automática, faça isso manualmente:
import androidx.compose.runtime.collectAsState
Também será necessário atualizar provideGlance
para transmitir o ID e o repositório do widget para o Content
.
Substitua provideGlance
pelo seguinte:
override suspend fun provideGlance(context: Context, id: GlanceId) {
val widgetId = GlanceAppWidgetManager(context).getAppWidgetId(id)
val repository = WidgetModelRepository.get(context)
provideContent {
GlanceTheme {
Content(repository, widgetId)
}
}
}
- Na função combinável
Content
, determine qual estado será mostrado caso exista um modelo. Transfira oScaffold
e o conteúdo do widget para o combinávelZeroState
substituindo o componenteScaffold
e o conteúdo dele por este:
when (model) {
is WidgetModel -> {Text("Hello World")}
else -> ZeroState(widgetId)
}
O combinável ZeroState
já está incluído no código inicial do pacote com.google.android.samples.socialite.widget.ui
.
- Se o Android Studio não importar automaticamente o pacote
com.google.android.samples.socialite.widget.ui
, adicione o código abaixo à seção de importação doSociaLiteAppWidget
.
import com.google.android.samples.socialite.widget.ui.ZeroState
- Para acessar suas atualizações, execute o app novamente e adicione uma nova cópia do widget à sua tela inicial. O widget vai mostrar o componente ZeroState e um botão. O botão vai abrir a atividade de configuração quando você clicar nele e, na próxima seção, você vai atualizar o estado do widget dessa atividade.
A atividade da configuração
Revise a função combinável ZeroState. Essa função está no pacote com.google.android.samples.socialite.widget.ui
do arquivo ZeroState.kt
.
@Composable fun ZeroState(widgetId: Int) { val widgetIdKey = ActionParameters.Key<Int>(AppWidgetManager.EXTRA_APPWIDGET_ID) Scaffold( titleBar = { TitleBar( modifier = GlanceModifier.clickable(actionStartActivity(MainActivity::class.java)), textColor = GlanceTheme.colors.onSurface, startIcon = ImageProvider(R.drawable.ic_launcher_monochrome), title = "SociaLite", ) }, backgroundColor = GlanceTheme.colors.widgetBackground, modifier = GlanceModifier.fillMaxSize(), ) { Box(modifier = GlanceModifier.fillMaxSize(), contentAlignment = Alignment.Center) { Button( text = "Select Favorite Contact", onClick = actionStartActivity<SociaLiteAppWidgetConfigActivity>( parameters = actionParametersOf(widgetIdKey to widgetId), ), ) } } }
O combinável Scaffold
é transferido para o combinável ZeroState
. A TitleBar
tem o modificador clickable
, que abre a atividade principal do SociaLite. O ZeroState
usa o combinável Button
do Glance para mostrar uma call-to-action para o usuário. Quando clicada, abre a atividade SociaLiteAppWidgetConfigActivity
e inclui o ID do widget como uma intent extra. As duas ações usam a função de conveniência actionStartActivity
do Glance. Para mais informações sobre ações, consulte Processar interação do usuário.
- Revise como a
SociaLiteAppWidgetConfigActivity
é usada para atualizar a configuração do seu widget. Essa classe também é a atividade de configuração do seu widget. As atividades de configuração leem o número inteiro da intent extra com a chaveAppWidgetManager.
*EXTRA_APPWIDGET_ID.
*Para mais informações sobre as atividades de configuração, consulte Permitir que os usuários configurem widgets de apps. - Na
SociaLiteAppWidgetConfigActivity
, substituaTODO
na propriedadeContactRow
onClick
por este código:
{
coroutineScope.launch {
widgetModelRepository.createOrUpdate(
WidgetModel(
appWidgetId,
contact.id,
contact.name,
contact.iconUri.toString(),
false,
),
)
SociaLiteAppWidget().updateAll(this@SociaLiteAppWidgetConfigActivity)
val resultValue = Intent().putExtra(
AppWidgetManager.EXTRA_APPWIDGET_ID,
appWidgetId,
)
setResult(RESULT_OK, resultValue)
finish()
}
}
Adicione o código abaixo se o Android Studio não fizer isso automaticamente:
import com.google.android.samples.socialite.widget.model.WidgetModel
import androidx.glance.appwidget.updateAll
import kotlinx.coroutines.launch
Esse bloco de código atualiza o estado do seu widget. Primeiro, ele usa o repositório para salvar e atualizar o WidgetModel
com as informações do contato selecionado. Depois, chama a função de suspensão updateAll
. Essa função atualiza todos os widgets na tela inicial e pode ser chamada de qualquer lugar do seu app. Por fim, esse bloco define o resultado da atividade de configuração para indicar que o widget foi atualizado.
- Execute e substitua o widget na tela inicial. O novo estado zero vai aparecer.
- Clique em Selecionar o contato favorito. Essa ação direciona você para a atividade de configuração.
- Selecione um contato. Essa ação atualiza seu widget. Porém, o widget ainda não mostra seu contato favorito porque você vai adicionar esse recurso na próxima seção.
Gerenciar os dados do widget
- Abra a ferramenta App Inspection e conecte um processo, se necessário. Selecione a guia Database inspector para ver o conteúdo do banco de dados do app.
- Selecione um contato favorito no widget e veja que ele atualiza como "Hello World." Voltando à ferramenta App Inspection, a guia Widget model vai mostrar uma entrada para seu widget. Talvez seja necessário atualizar a tabela ou pressionar Live updates para ver as mudanças.
- Adicione outro widget e selecione outro contato. Talvez seja necessário pressionar Refresh table ou Live updates para mostrar o novo modelo.
- Remova o widget e observe que o modelo é deixado para trás no banco de dados depois que um widget é removido.
Você pode atualizar o SociaLiteAppWidgetReceiver
para limpar o banco de dados quando um widget é removido substituindo onDeleted
.
Para limpar os modelos de widgets órfãos, chame WidgetModelRepository.cleanupWidgetModels
. A classe do repositório é gerenciada pelo Hilt e é preciso usar a injeção de dependência para acessar a instância dela.
- Em
SociaLiteAppWidgetReceiver
, adicione a anotação HiltAndroidEntryPoint
à sua declaração de classe do receptor e injete a instânciaWidgetModelRepository
. - Chame
WidgetModelRepository.cleanupWidgetModels
na substituição do método deonDeleted
.
O código ficará assim:
package com.google.android.samples.socialite.widget
import android.content.Context
import androidx.glance.appwidget.GlanceAppWidget
import androidx.glance.appwidget.GlanceAppWidgetReceiver
import com.google.android.samples.socialite.widget.model.WidgetModelRepository
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject
@AndroidEntryPoint
class SociaLiteAppWidgetReceiver : GlanceAppWidgetReceiver() {
override val glanceAppWidget: GlanceAppWidget = SociaLiteAppWidget()
@Inject
lateinit var repository: WidgetModelRepository
override fun onDeleted(context: Context, appWidgetIds: IntArray) {
super.onDeleted(context, appWidgetIds)
repository.cleanupWidgetModels(context)
}
}
- Execute o app de novo. Observe que a linha de modelo é removida no App Inspector quando o widget é removido da tela inicial.
5. Adicionar a UI do contato e atualizar quando novas mensagens chegarem
Você chegou à parte final do codelab. Nesta seção, você implementa a interface final do contato para o widget e a atualiza quando há uma mensagem não lida para o contato.
- Revise sua classe
WidgetModelRepository
no pacote do modelomodel
.
Esse é o método de conveniência updateUnreadMessagesForContact
. Ele atualiza widgets associados com o ID de um contato.
//Don't add this code.
fun updateUnreadMessagesForContact(contactId: Long, unread: Boolean) {
coroutineScope.launch {
widgetModelDao.modelsForContact(contactId).filterNotNull().forEach { model ->
widgetModelDao.update(
WidgetModel(model.widgetId, model.contactId, model.displayName, model.photo, unread)
)
SociaLiteAppWidget().updateAll(appContext)
}
}
}
Esse método tem dois parâmetros: contactId
, o ID do contato que está sendo atualizado, e unread
, um booleano do estado da mensagem não lida. Esse método usa WidgetModelDao
para encontrar todos os modelos de widget que mostram esse contato e atualizar o modelo com o novo estado. Então, a função chama o método SociaLiteAppWidget().updateAll
fornecido pelo Glance para atualizar todos os widgets na tela inicial do usuário.
Agora que você entende como um widget e seu estado são atualizados, pode criar sua UI de contato, enviar uma mensagem e vê-la ser atualizada. Para isso, atualize o SociaLiteAppWidget
com um FavoriteContact
combinável no layout do widget. Nesse layout, confira também se ele vai mostrar No new messages
ou New Messages!
.
- Revise o arquivo
FavoriteContact.kt
no pacotecom.google.android.samples.socialite.widget.ui
.
//Don't add this code.
@Composable
fun FavoriteContact(model: WidgetModel, onClick: Action) {
Column(
modifier = GlanceModifier.fillMaxSize().clickable(onClick)
.background(GlanceTheme.colors.widgetBackground).appWidgetBackground()
.padding(bottom = 8.dp),
verticalAlignment = Alignment.Vertical.Bottom,
horizontalAlignment = Alignment.Horizontal.CenterHorizontally,
) {
Image(
modifier = GlanceModifier.fillMaxWidth().wrapContentHeight().defaultWeight()
.cornerRadius(16.dp),
provider = ImageProvider(model.photo.toUri()),
contentScale = ContentScale.Crop,
contentDescription = model.displayName,
)
Column(
modifier = GlanceModifier.fillMaxWidth().wrapContentHeight().padding(top = 4.dp),
verticalAlignment = Alignment.Vertical.Bottom,
horizontalAlignment = Alignment.Horizontal.CenterHorizontally,
) {
Text(
text = model.displayName,
style = TextStyle(
fontWeight = FontWeight.Bold,
fontSize = 24.sp,
color = (GlanceTheme.colors.onSurface),
),
)
Text(
text = if (model.unreadMessages) "New Message!" else "No messages",
style = TextStyle(
fontWeight = FontWeight.Bold,
fontSize = 16.sp,
color = (GlanceTheme.colors.onSurface),
),
)
}
}
}
- Substitua
Text("Hello World")
no combinável deContent
doSociaLiteAppWidget
por uma chamada para o combinávelFavoriteContact
.
Esse combinável terá o WidgetModel e uma ação criada pela função actionStartActivity
do Glance.
- Adicione a chamada para o bloco
when
antes doZeroState
quando o modelo não forWidgetModel
.
when (model) {
is WidgetModel -> FavoriteContact(model = model, onClick = actionStartActivity(
Intent(LocalContext.current.applicationContext, MainActivity::class.java)
.setAction(Intent.ACTION_VIEW)
.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
.setData("https://socialite.google.com/chat/${model.contactId}".toUri()))
)
else -> ZeroState(widgetId)
}
- Se o Android Studio não adicionar automaticamente as importações abaixo, faça isso agora:
import com.google.android.samples.socialite.widget.ui.FavoriteContact
import androidx.glance.appwidget.action.actionStartActivity
import android.content.Intent
import com.google.android.samples.socialite.MainActivity
import androidx.core.net.toUri
- Execute o app.
- Selecione um contato favorito, envie uma mensagem e saia imediatamente do app antes da resposta. Quando ela chegar, o estado do widget vai mudar.
- Clique no widget para abrir o chat e ver que o estado foi atualizado novamente quando você sair para a tela principal.
6. Parabéns
Você concluiu o codelab e aprendeu a criar um widget usando o Glance! Você vai se sentir à vontade para criar um widget bonito que fica bonito em muitas telas iniciais, processa a entrada do usuário e mostra atualizações.
Para acessar o código da solução na ramificação main
, siga estas etapas:
- Se você já baixou o SocialLite, execute este comando:
git checkout main
- Caso contrário, baixe o código de novo para visualizar a ramificação
main
:
git clone git@github.com:android/socialite.git