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

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. 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
  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, à 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

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 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:

  1. Adicione as dependências do Glance ao seu app.
  2. Crie uma implementação do GlanceAppWidget.
  3. Crie um GlanceAppWidgetReceiver.
  4. Configure seu widget com um arquivo XML de informações do widget do app.
  5. 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:

  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 receptor 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 à tela inicial.

Adicionar informações do provedor do app-widget

  1. Clique com o botão direito do mouse em **res/xml** > New > XML resource file.
  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 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

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 ele estiver em execução e tiver novas informações.

minResizeHeight,minResizeWidth

Define o tamanho mínimo para redimensionar o widget.

minHeight,minWidth

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

initialLayout

Fornece um layout inicial que é mostrado enquanto o Glance 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 aceita. Essas são dicas para o host do widget e não mudam de fato o comportamento dele.

Neste codelab, suas flags 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 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:

  1. Crie um elemento receiver para que o 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. 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:

  1. 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")
       }
   }
}
  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.

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:

  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 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 pacote model encontrado em app/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.

  1. No método provideGlance, fora do seu combinável, acesse uma referência ao WidgetModelRepository e o ID atual do widget. Adicione as linhas abaixo 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 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
  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 esta 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 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)
            }
        }
    }
  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 do 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 código abaixo à 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 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.

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

  1. 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 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. Na SociaLiteAppWidgetConfigActivity, substitua TODO na propriedade ContactRow 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.

  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 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 receptor e injete a instância WidgetModelRepository.
  2. Chame WidgetModelRepository.cleanupWidgetModels na substituição do método de 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. 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.

  1. Revise sua classe WidgetModelRepository no pacote do modelo model.

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

  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 combinável de Content do SociaLiteAppWidget por uma chamada para o combinável FavoriteContact.

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

  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 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
  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 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:

  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