Criar um widget com o Resumo

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 do Resumo e adicionar ao SociaLite e à tela inicial. Depois, vai adicionar um estado zero ao widget usando componentes e um tema do Resumo. O codelab vai mostrar como oferecer suporte para uma 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 Resumo não usa 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 oferecer suporte a widgets.
  • Como usar componentes do Resumo para criar um layout responsivo.
  • Como usar GlanceTheme para oferecer suporte a 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.

Uma tela inicial do Android com o widget Socialite

2. Começar a configuração

Acessar o código inicial

  1. 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.
  2. Faça o download do código inicial (link em inglês) no GitHub.

Ou então, 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
  1. 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:

fb043d54dd01b3e5.png

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, essa é a 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 Resumo?

O Resumo do Jetpack é 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 Resumo 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 receiver precisa ser exportado, tratar o intent da ação android.appwidget.action.APPWIDGET_UPDATE e fornecer um arquivo de configurações do widget do app por um elemento de metadados chamado android.appwidget.provider.

Adicionar um widget ao SociaLite

Uma tela inicial do Android com o widget 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 Resumo 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:

  1. Adicione as dependências do Resumo ao seu app.
  2. Crie uma implementação de GlanceAppWidget.
  3. Crie um GlanceAppWidgetReceiver.
  4. Configure seu widget com um arquivo XML de informações do widget do app.
  5. Adicione o receiver e as informações do widget do app ao arquivo AndroidManifest.xml.

Adicionar Resumo ao seu projeto

O código inicial adicionou as versões do Resumo e as coordenadas da biblioteca ao catálogo de versões do SociaLite: libs.versions.toml.

libs.versions.toml

[versions]
//..

glance = "1.1.0-beta02"

[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 Resumo 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 Resumo.

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 Resumo fornece uma classe abstrata de receiver, 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 Resumo em exibições remotas.

O código inicial inclui duas classes: SocialiteAppWidget, que estende GlanceAppWidget, e SocialiteAppWidgetReceiver, que estende GlanceAppWidgetReceiver.

Para começar, siga estas etapas:

  1. Navegue para o pacote widget em app/src/main/java/com/google/android/samples/socialite/.
  2. Abra a classe SociaLiteAppWidget. Essa classe substitui o método provideGlance.
  3. Substitua o TODO por uma chamada para provideContent e transmita a função combinável do widget como um parâmetro. Por enquanto, o widget mostra apenas a mensagem Hello 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")
           }
       }
   }
}
  1. Abra a classe SociaLiteAppWidgetReceiver no pacote widget. Por enquanto, o receiver fornece uma instância do seu SociaLiteWidget, mas você vai adicionar mais funcionalidades em uma próxima seção.
  2. Substitua o TODO pelo construtor SociaLiteAppWidget():
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 na tela inicial.

Adicionar informações do provedor do app-widget

  1. Clique com o botão direito do mouse em res/xml > New > arquivo de recurso do XML.
  2. Digite socialite_widget_info como o nome do arquivo e appwidget-provider como o elemento raiz, depois clique em OK. Esse arquivo inclui os metadados do seu appwidget que é usado por um AppWidgetHost para mostrar o widget.
  3. Adicione o seguinte 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 a seguir fornece uma visão geral dos atributos nesse código e descreve cada um deles:

Nome do atributo

Descrição

resizeMode

O widget pode ser redimensionado vertical e horizontalmente.

targetCellWidth, targetCellHeight

Especifique o tamanho padrão do widget quando adicionado à tela inicial.

updatePeriodMillis

Controla quando o host pode decidir atualizar o widget. O app pode atualizar o widget sempre que estiver em execução e tiver novas informações.

minResizeHeight,minResizeWidth

Define o tamanho para o qual você pode redimensionar o widget.

minHeight,minWidth

Especifique o tamanho mínimo padrão do widget quando adicionado à tela inicial.

initialLayout

Fornece um layout inicial que é mostrado enquanto o Resumo renderiza os elementos combináveis.

previewImage

Fornece uma imagem estática do widget a ser mostrada no seletor de widgets.

widgetFeatures

Indique vários recursos que o widget suporta. Essas são dicas para o host do widget e não mudam de fato o comportamento dele.

Neste codelab, seus sinalizadores informam ao host que o widget não requer configuração antes de ser adicionado à tela inicial e que pode ser configurado depois de ser adicionado.

configure

O nome da classe da atividade de configuração. Essa é uma atividade que configura o widget posteriormente.

Para saber quais são todos os atributos disponíveis, incluindo os recursos na 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 receiver processa a intent APPWIDGET_UPDATE e fornece seus metadados appwidget para a tela de início do Android.

Para começar, siga estas etapas:

  1. Crie um elemento receiver para que SociaLiteAppWidgetReceiver seja exportado. Copie e cole o seguinte no seu arquivo AndroidManifest.xml depois do elemento application:
<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>
  1. Compile e execute seu app.
  2. 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.

Uma tela inicial do Android com o widget de trabalho em andamento. O widget é transparente e mostra a mensagem

O widget diz "Hello World" e tem um plano de fundo transparente. É evidente que esse não é o widget mais 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 Resumo.

Scaffold é fornecido pela biblioteca do Resumo. É uma API de slot simples para mostrar a interface de um widget com uma TitleBar. Define a cor do plano de fundo como GlanceTheme.colors.widgetBackground e aplica padding. Ele será seu componente de nível superior.

Para começar, siga estas etapas:

  1. Substitua sua implementação de SociaLiteAppWidget pelo seguinte 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")
       }
   }
}
  1. Para acessar suas atualizações, execute o app novamente e adicione uma nova cópia do widget à sua tela inicial.

Uma tela inicial do Android com o widget de trabalho em andamento. O widget tem um plano de fundo e título opacos

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.

Isso é muito melhor, mas quando você compara seu widget com outros, as cores parecem estar erradas. Nas telas iniciais, espera-se que os widgets definam suas 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 componentes. Para atualizar o estilo do texto, é preciso importar a classe TextStyle do Resumo. Para atualizar o plano de fundo do scaffold, defina a propriedade backgroundColor de Scaffold como GlanceTheme.colors.widgetBackground.

Para começar, siga estas etapas:

  1. Inclua a nova importação no arquivo SociaLiteAppWidget.kt.
//Add to the imports section of your Kotlin code.
import androidx.glance.text.TextStyle
  1. Atualize o combinável Content para adicionar widgetBackground.
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))
}
  1. 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.

Uma tela inicial do Android com o widget Hello World em um tema claroTela inicial em um tema claro

Uma tela inicial do Android com o widget Hello World em um tema escuroTela inicial em um tema escuro

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 uma mensagem 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 exibir 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 subjacente. 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 exibe, um displayName e photo a serem mostrados e um booleano se o contato tiver mensagens não lidas. Eles são consumidos pelos combináveis SociaLiteAppWidget e exibidos no widget.

WidgetModelDao é um objeto de acesso a dados que resume o acesso ao banco de dados do SociaLite. 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 pacote model encontrado em app/src/main/java/com/google/android/samples/socialite/widget/model/.

É 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 exibir 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 exibe o combinável ZeroState ou um marcador de posição Text.

  1. No método provideGlance, fora do seu combinável, acesse uma referência ao WidgetModelRepository e o ID atual do widget. Adicione as seguintes linhas antes de provideContent no método SociaLiteAppWidget 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 as seguintes 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
  1. 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ável Content e adicione a seguinte linha:
private fun Content(repository: WidgetModelRepository, widgetId: Int) {
   val model = repository.loadModel(widgetId).collectAsState(Loading).value
  1. Se o Android Studio não adicionar a seguinte importação 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 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)
            }
        }
    }
  1. Na função combinável Content, determine qual estado será mostrado caso exista um modelo. Transfira o Scaffold e o conteúdo do widget para o combinável ZeroState substituindo o componente Scaffold 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 no pacote com.google.android.samples.socialite.widget.ui.

  1. Se o Android Studio não importar automaticamente o pacote com.google.android.samples.socialite.widget.ui, adicione o seguinte código à seção de importação do SociaLiteAppWidget.
import com.google.android.samples.socialite.widget.ui.ZeroState
  1. Para acessar suas atualizações, execute o app novamente e adicione uma nova cópia do widget à sua tela inicial. O widget vai exibir 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.

Uma tela inicial do Android com o estado zero do widget do SociaLite. O estado zero tem um único botão.

A atividade de configuração do widget do app SociaLite mostrando 4 contatos para escolher.

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 no 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 Resumo para exibir 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 Resumo. Para mais informações sobre ações, consulte Processar interação do usuário.

  1. Revise como 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 chave AppWidgetManager.*EXTRA_APPWIDGET_ID.*Para mais informações sobre as atividades de configuração, consulte Permitir que os usuários configurem widgets de apps.
  2. Em SociaLiteAppWidgetConfigActivity, substitua TODO na propriedade ContactRow onClick pelo seguinte 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 seguinte se o Android Studio não o fizer 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. A seguir, 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 sinalizar que o widget foi atualizado.

  1. Execute e substitua o widget na tela inicial. O novo estado zero vai aparecer.

Uma tela inicial do Android com o estado zero do widget do SociaLite. O estado zero tem um único botão.

  1. Clique em Selecionar o contato favorito. Essa ação direciona você para a atividade de configuração.

Uma captura de tela da atividade de configuração mostrando os quatro contatos para escolher Uma tela inicial do Android com o widget Hello World em um tema claro

  1. 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

  1. Abra a ferramenta App inspection e conecte a um processo, se necessário. Selecione a guia Database inspector para ver o conteúdo do banco de dados do app.
  2. 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.

dd030cce6a75be25.png

  1. Adicione outro widget e selecione outro contato. Talvez seja necessário pressionar Refresh table ou Live updates para mostrar o novo modelo.
  2. 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.

  1. Em SociaLiteAppWidgetReceiver, adicione a anotação Hilt AndroidEntryPoint à sua declaração de classe do receiver e injete a instância WidgetModelRepository.
  2. Chame WidgetModelRepository.cleanupWidgetModels na substituição do método por onDeleted.

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)
   }

}
  1. Execute o app de novo. Veja 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.

  1. Revise sua classe WidgetModelRepository no pacote do modelo.

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 Resumo 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, verifique também se vai mostrar No new messages ou New Messages!.

  1. Revise o arquivo FavoriteContact.kt no pacote com.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),
               ),
           )
       }
   }
}
  1. Substitua Text("Hello World") no Content combinável do SociaLiteAppWidget por uma chamada para o FavoriteContact combinável.

Esse combinável terá o WidgetModel e uma ação criada pela função actionStartActivity do Resumo.

  1. Adicione a chamada para o bloco when antes do ZeroState quando o modelo não for WidgetModel.
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)
}
  1. Se o Android Studio não adicionar automaticamente as seguintes importações, 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
  1. Execute o app.
  2. Selecione um contato favorito, envie uma mensagem e saia imediatamente do app antes da resposta. Quando ela chegar, o estado do widget vai mudar.
  3. Clique no widget para abrir o chat e ver que o estado foi atualizado novamente quando você sair para a tela principal.

Uma tela inicial do Android com o widget do SociaLite concluído em um tema claro.

6. Parabéns

Você concluiu o codelab e aprendeu a criar um widget usando o Resumo! Você vai se sentir à vontade para criar um widget bonito que fique bem em muitas telas iniciais, processe a entrada do usuário e se atualize.

Para acessar o código da solução na ramificação main, siga estas etapas:

  1. Se você já baixou o SocialLite, execute este comando:
git checkout main
  1. Caso contrário, baixe o código de novo para visualizar a ramificação main:
git clone git@github.com:android/socialite.git

Saiba mais