1. Antes de comenzar
En este codelab, aprenderás el proceso de crear un widget de app para SociaLite. Primero, crearás un widget simple de Glance y lo agregarás a SociaLite y a la pantalla principal. Luego, agregarás al widget un estado cero usando componentes y un tema de Glance. A continuación, en el codelab se explicará cómo brindar compatibilidad con una interacción del usuario para seleccionar el contacto favorito desde el widget. Por último, aprenderás a actualizar el widget desde tu app.
Requisitos previos
- Conocimientos básicos de Kotlin
- Haber completado el codelab sobre configuración de Android Studio o saber cómo usar Android Studio y probar apps en un emulador de Android 15 o un dispositivo físico que ejecute Android 15
- Conocimientos básicos de Hilt
- Conocimientos básicos de Compose (Glance no usa funciones de componibilidad de Jetpack Compose, pero sí reutiliza el framework y el estilo de programación)
Qué aprenderás en este codelab
- Cómo configurar tu app para que sea compatible con los widgets
- Cómo usar componentes de Glance para crear un diseño responsivo
- Cómo usar
GlanceTheme
para admitir colores dinámicos en las pantallas principales de los usuarios - Cómo controlar las interacciones del usuario en tu widget
- Cómo actualizar tu widget desde la app
Requisitos
- La versión más reciente de Android Studio
- Un dispositivo de prueba o un emulador que ejecuten Android 12 o una versión posterior
- El SDK de Android 12 o una versión posterior
2. Prepárate
Obtén el código de partida
- Si ya completaste los codelabs "Cómo cumplir con los requisitos de borde a borde de Android 15" o "Cómo agregar animaciones del gesto atrás predictivo", avanza a la sección Agrega un widget porque ya tienes el código de partida.
- Descarga el código de partida de GitHub.
También puedes clonar el repositorio y consultar la rama codelab_improve_android_experience_2024
.
git clone git@github.com:android/socialite.git
cd socialite
git checkout codelab_improve_android_experience_2024
- Abre SociaLite en Android Studio y ejecuta la app en tu dispositivo o emulador con Android 15. Verás una pantalla como la siguiente:
SociaLite con navegación por gestos
3. Agrega un widget
¿Qué son los widgets?
Un widget es una parte de tu app que se puede incorporar en otras apps para Android. Con mayor frecuencia, se trata de la pantalla principal del usuario.
Al agregar widgets a tu app, los usuarios pueden iniciar tareas comunes rápidamente, ver información de un vistazo y personalizar su dispositivo con tu contenido.
¿Qué es Glance?
Jetpack Glance es una biblioteca para escribir widgets usando una API similar a la de Compose en Kotlin. Tiene varias ventajas de Compose, como la recomposición, el código declarativo de la IU escrito en Kotlin y los componentes definidos. Glance elimina gran parte de la necesidad de usar vistas remotas XML en tus widgets.
Crea un widget
Los widgets en Android se declaran en el AndroidManifest
como un elemento <receiver>
. Este receptor debe exportarse, controlar el intent de acción android.appwidget.action.APPWIDGET_UPDATE
y proporcionar un archivo de configuración del widget de la app a través de un elemento de metadatos llamado android.appwidget.provider
.
Agrega un widget a SociaLite
Te recomendamos que agregues un widget a SociaLite que les permita a los usuarios ver su contacto favorito y si tienen mensajes no leídos de esa persona. Si es así, al presionar el widget, el usuario debería poder chatear con su contacto favorito. Además, puedes utilizar componentes y temas de Glance para asegurarte de que tu widget se vea de manera óptima con un diseño responsivo y color dinámico.
Para comenzar, agregarás un widget estático "Hello World" a SociaLite. Luego, ampliarás las capacidades del widget.
Para ello, haz lo siguiente:
- Agrega las dependencias de Glance a tu app.
- Crea una implementación de
GlanceAppWidget
. - Crea un
GlanceAppWidgetReceiver
. - Configura el widget con un archivo en formato XML de información sobre el widget de la app.
- Agrega el receptor y la información sobre el widget de la app al archivo
AndroidManifest.xml
.
Agrega Glance a tu proyecto
El código de partida agregó las versiones de Glance y las coordenadas de la biblioteca al catálogo de versiones de 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" }
Además, se incluyeron las dependencias de Glance en el archivo app/build.gradle.kts
de SociaLite.
build.gradle.kts
dependencies {
...
implementation(libs.glance.appwidget)
implementation(libs.glance.material)
...
}
- Si modificaste estos archivos, sincroniza el proyecto para descargar las bibliotecas de Glance.
Crea tu GlanceAppWidget
y GlanceAppWidgetReceiver
Android usa un receptor de transmisiones para alertar a SociaLite que un widget se agregó, se debe actualizar o se quitó. Glance proporciona una clase de receptor abstracta, GlanceAppWidgetReceiver, que extiende AppWidgetProvider.
Las implementaciones de GlanceAppWidgetReceiver
también son responsables de proporcionar instancias de GlanceAppWidget
. Esta clase renderiza las funciones de componibilidad de Glance en vistas remotas.
El código de partida incluye dos clases: SocialiteAppWidget
, que extiende GlanceAppWidget
, y SocialiteAppWidgetReceiver
, que extiende GlanceAppWidgetReceiver
.
Para comenzar, sigue estos pasos:
- Navega al paquete
widget
enapp/src/main/java/com/google/android/samples/socialite/
. - Abre la clase
SociaLiteAppWidget
. Esta clase anula el métodoprovideGlance
. - Reemplaza
TODO
por una llamada aprovideContent
y pasa la función de componibilidad de tu widget como un parámetro. Por ahora, el widget solo muestra el mensajeHello World
, pero agregarás otras funciones más adelante en este 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")
}
}
}
}
- Abre la clase
SociaLiteAppWidgetReceiver
en el paquetewidget
. Por ahora, el receptor proporciona una instancia de tuSociaLiteWidget
, pero agregarás otras funciones en una sección más adelante. - Reemplaza
TODO
por el constructorSociaLiteAppWidget()
:
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()
}
Ya está todo listo para configurar Android para que muestre tu widget y les permita a los usuarios agregarlo a la pantalla principal.
Agrega información de proveedor de app-widget
- Haz clic con el botón derecho en **
res/xml
** > New > XML resource file. - Ingresa
socialite_widget_info
como el nombre del archivo yappwidget-provider
como el elemento raíz. Luego, haz clic en OK. Este archivo incluye los metadatos de tuappwidget
que utiliza unAppWidgetHost
para mostrar el widget al principio. - Agrega el siguiente código al archivo
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>
En la siguiente tabla, se proporciona una descripción general de los atributos en este código y se detalla cada uno de ellos:
Nombre del atributo | Descripción |
| El widget puede cambiar de tamaño vertical y horizontalmente. |
| Especifica el tamaño predeterminado del widget cuando se agregue a la pantalla principal. |
| Controla cuándo el host del widget podría decidir actualizarlo. Tu app puede actualizar el widget cuando se esté ejecutando y tenga información nueva para mostrar. |
| Establece el tamaño mínimo del widget. |
| Especifica el tamaño mínimo predeterminado del widget cuando se agrega a la pantalla principal. |
| Proporciona un diseño inicial que se muestra mientras Glance renderiza las funciones de componibilidad. |
| Proporciona una imagen estática del widget que se mostrará en el selector. |
| Indica las diferentes funciones que admite el widget. Son sugerencias para el host y no cambian el comportamiento del widget. |
| El nombre de la clase de actividad de configuración. Se trata de una actividad que configura el widget más adelante. |
Para ver todos los atributos disponibles, incluidas las funciones de la API 31 o posterior, consulta AppWidgetProviderInfo
.
Actualiza AndroidManifest
y realiza pruebas
Ya tienes todo listo para actualizar el archivo AndroidManifest.xml
y probar tu widget. Define un receiver
como elemento secundario del elemento application
en el archivo. Este receptor controla el intent APPWIDGET_UPDATE
y le brinda metadatos de tu appwidget
al Launcher de Android.
Para comenzar, sigue estos pasos:
- Crea un elemento
receiver
paraSociaLiteAppWidgetReceiver
para que se exporte. Copia y pega lo siguiente en el archivoAndroidManifest.xml
después del 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>
- Compila y ejecuta tu app.
- Con la app en ejecución, agrega el widget a tu pantalla principal. Por ejemplo, en un dispositivo Pixel, mantén presionado el fondo y selecciona Widgets > SociaLite. Deberías poder agregar tu widget a la pantalla principal.
El widget dice "Hello World" y tiene un fondo transparente. Claro que aún no tiene el mejor aspecto ni es el más funcional. En la siguiente sección, agregarás un diseño más complicado y embellecerás el widget con un toque de color de Material Design.
4. Mejora el diseño
Ahora tienes un widget estático al que le faltan muchas de las funciones que conforman un widget útil. Un widget útil realiza lo siguiente:
- Mantiene el contenido breve y actual, y la funcionalidad simple.
- Minimiza los espacios raros con un diseño que se puede cambiar de tamaño.
- Aplica color a partir del fondo del host del widget de la app.
Consulta un análisis más detallado de qué factores conforman un buen widget en Widgets.
Agrega andamiaje
Ahora actualizarás el widget para que muestre el componente Scaffold
de Glance.
La biblioteca de Glance proporciona Scaffold
. Es una API de ranuras simple para mostrar la IU de un widget con una TitleBar
. Establece el color de fondo en GlanceTheme.colors.widgetBackground
y aplica padding. Será tu componente de nivel superior.
Para comenzar, sigue estos pasos:
- Reemplaza tu implementación de
SociaLiteAppWidget
por el siguiente 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 ver tus actualizaciones, vuelve a ejecutar la app y agrega una nueva copia del widget a la pantalla principal.
Recuerda que los widgets son vistas remotas que muestra un host externo. Más adelante, agregarás la habilidad de actualizar automáticamente el widget dentro de la app. Hasta entonces, deberás agregar el widget desde el selector para ver los cambios del código.
Como ves, ha mejorado mucho. Sin embargo, cuando lo comparas con otros widgets, los colores no parecen adecuados. En las pantallas principales, se espera que los widgets establezcan sus colores en función del tema elegido por el usuario. Los tokens de color dinámico permiten que el tema de tu widget se adapte al fondo de pantalla y al tema de tu dispositivo.
Agrega color dinámico
Ahora agregarás el token de color widgetBackground
al fondo del andamiaje y los tokens de color onSurface
al texto de TitleBar
y los componentes Text. Para actualizar el estilo del texto, debes importar la clase TextStyle
de Glance. Para actualizar el fondo del andamiaje, configura la propiedad backgroundColor
de Scaffold
en GlanceTheme.colors.widgetBackground
.
Para comenzar, sigue estos pasos:
- Incluye la nueva importación en el archivo
SociaLiteAppWidget.kt
.
//Add to the imports section of your Kotlin code.
import androidx.glance.text.TextStyle
- Actualiza tu función de componibilidad
Content
para agregarwidgetBackground
.
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 ver tus actualizaciones, vuelve a ejecutar la app y agrega una nueva copia del widget a la pantalla principal.
Coincide con los temas de otros widgets en la pantalla principal y actualiza automáticamente los colores si cambias el fondo o estableces el modo oscuro. Si quieres un fondo muy colorido, el widget se adaptará a la sección del fondo donde se ubica.
|
|
Agrega un estado cero
Ahora debes dedicarte al estado y la configuración de tu widget. Cuando se agrega un widget a la pantalla principal y requiere configuración, lo mejor suele ser mostrar un estado cero. El estado cero le indica al usuario que configure el widget. Agregas una actividad de configuración a tu widget y creas un vínculo a ella desde el estado cero.
Este codelab proporciona clases para almacenar el estado de configuración de un widget, acceder a él y modificarlo. Agregarás código para actualizar la IU de tu widget de modo que muestre este estado, y crearás una acción de lambda para controlar las acciones de presión de un usuario.
Revisa el modelo del widget
Dedica un momento a revisar las clases del paquete com.google.android.samples.socialite.widget.model
.
Esto incluye la clase WidgetModel
y las clases WidgetModelDao
y WidgetModelRepository
. Estas clases ya están presentes en el código de partida del codelab y se encargan de conservar el estado de los widgets en la base de datos subyacente de Room
. Además, estas clases emplean Hilt para administrar sus ciclos de vida.
La clase WidgetModel
contiene un widgetId
que asigna Android, el contactId
del contacto de SociaLite que muestra, un displayName
y una photo
que se mostrarán, y un booleano si el contacto tiene mensajes no leídos. Las funciones de componibilidad de SociaLiteAppWidget
los consumen y se muestran en el widget.
WidgetModelDao
es un objeto de acceso a datos que abstrae el acceso a la base de datos de SociaLite. WidgetModelRepository
proporciona funciones útiles para crear, leer, actualizar y borrar instancias de WidgetModel
. Hilt crea estas clases, que se insertan en la app con la inserción de dependencias.
- Abre el archivo
WidgetModel.kt
en el paquetemodel
que se encuentra enapp/src/main/java/com/google/android/samples/socialite/widget/model/
.
Es una clase data
con una anotación Entity
. Android asigna un ID dedicado a cada instancia del widget, y SociaLite utiliza este ID como clave primaria para los datos del modelo. Cada instancia del modelo registra la información básica del contacto asociado y si hay mensajes no leídos de ese contacto.
@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
El estado cero
Te recomendamos que, si no hay ningún modelo disponible, tu función de componibilidad Content
cargue el modelo del widget desde el WidgetModelRepository
y muestre el estado cero; de lo contrario, te recomendamos mostrar el contenido normal de tu widget. Por ahora, este será tu mensaje "Hello World", pero en la próxima sección, crearás una interfaz mejor.
Reemplazarás tu función de componibilidad Content
por una expresión when
que muestre la función de componibilidad ZeroState
o un marcador de posición Text
.
- En el método
provideGlance
, fuera de la función de componibilidad, obtén una referencia alWidgetModelRepository
y el ID de widget actual. Agrega las siguientes líneas antes deprovideContent
en el métodoSociaLiteAppWidget
provideGlance
.
override suspend fun provideGlance(context: Context, id: GlanceId) {
val widgetId = GlanceAppWidgetManager(context).getAppWidgetId(id)
val repository = WidgetModelRepository.get(context)
Es posible que también debas agregar estas importaciones:
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
- En la función de componibilidad
Content
, agrega el ID de widget y repositorio como parámetros, y utilízalos para cargar tu modelo. Actualiza la firma de la función de componibilidadContent
y agrega la siguiente línea:
private fun Content(repository: WidgetModelRepository, widgetId: Int) {
val model = repository.loadModel(widgetId).collectAsState(Loading).value
- Si Android Studio no agrega la siguiente importación automáticamente, agrégala de forma manual:
import androidx.compose.runtime.collectAsState
También deberás actualizar provideGlance
para pasar el ID de widget y repositorio a Content
.
Reemplaza provideGlance
por lo siguiente:
override suspend fun provideGlance(context: Context, id: GlanceId) {
val widgetId = GlanceAppWidgetManager(context).getAppWidgetId(id)
val repository = WidgetModelRepository.get(context)
provideContent {
GlanceTheme {
Content(repository, widgetId)
}
}
}
- En tu función de componibilidad
Content
, determina qué estado mostrar en función de si existe un modelo. Mueve elScaffold
y el contenido del widget a la función de componibilidadZeroState
reemplazando el componenteScaffold
y su contenido por lo siguiente en un bloque:
when (model) {
is WidgetModel -> {Text("Hello World")}
else -> ZeroState(widgetId)
}
La función de componibilidad ZeroState
ya está incluida en el código de partida en el paquete com.google.android.samples.socialite.widget.ui
.
- Si Android Studio no importó automáticamente el paquete
com.google.android.samples.socialite.widget.ui
, agrega el siguiente código a la sección de importaciones deSociaLiteAppWidget
.
import com.google.android.samples.socialite.widget.ui.ZeroState
- Para ver tus actualizaciones, vuelve a ejecutar la app y agrega una nueva copia del widget a la pantalla principal. Verás que el widget muestra el componente ZeroState y un botón. El botón abrirá la actividad de configuración cuando hagas clic en él, y, en la siguiente sección, actualizarás el estado de tu widget a partir de esta actividad.
La actividad de configuración
Revisa la función de componibilidad ZeroState. Esta función se encuentra en el paquete com.google.android.samples.socialite.widget.ui
en el archivo 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), ), ) } } }
La función de componibilidad Scaffold
se mueve a la función de componibilidad ZeroState
. TitleBar
tiene el modificador clickable
, que abre la actividad principal de SociaLite. Tu ZeroState
usa la función de componibilidad Button
de Glance para mostrar un llamado a la acción al usuario y, cuando hace clic en él, abre la actividad SociaLiteAppWidgetConfigActivity
e incluye el ID de widget como extra de intent. Ambas acciones usan la función útil actionStartActivity
de Glance. Para obtener más información sobre las acciones, consulta Controla la interacción del usuario.
- Revisa cómo se usa
SociaLiteAppWidgetConfigActivity
para actualizar la configuración de tu widget. Esta clase también es la actividad de configuración para tu widget. Las actividades de configuración leen el extra de número entero de intent con la claveAppWidgetManager.
*EXTRA_APPWIDGET_ID.
* Para obtener más información sobre las actividades de configuración, consulta Permite que los usuarios configuren widgets de apps. - En
SociaLiteAppWidgetConfigActivity
, reemplazaTODO
en la propiedadContactRow
onClick
por el siguiente 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()
}
}
Agrega las siguientes inclusiones si Android Studio no lo hace automáticamente
import com.google.android.samples.socialite.widget.model.WidgetModel
import androidx.glance.appwidget.updateAll
import kotlinx.coroutines.launch
Este bloque de código actualiza el estado de tu widget. Primero, usa el repositorio para guardar o actualizar el WidgetModel
con la información del contacto seleccionado. Luego, llama a la función de suspensión updateAll
. Esta función actualiza todos los widgets en la pantalla principal, y se la puede llamar desde cualquier parte de tu app. Por último, el bloque establece el resultado de la actividad de configuración para indicar que actualizó el widget correctamente.
- Ejecuta y reemplaza tu widget en la pantalla principal. Deberías ver el nuevo estado cero.
- Haz clic en Seleccionar contacto favorito. Esto te lleva a la actividad de configuración.
- Selecciona un contacto. Esto actualiza tu widget. Sin embargo, el widget aún no muestra tu contacto favorito porque agregarás esa capacidad en la siguiente sección.
Administra los datos del widget
- Abre la herramienta App inspection, conéctate a un proceso si es necesario y selecciona la pestaña Inspector de bases de datos para observar el contenido de la base de datos de la app.
- Selecciona un contacto favorito en el widget y comprueba si se actualiza a "Hello World". De regreso en la herramienta de inspección de apps, deberías ver una pestaña Widget model con una entrada para tu widget. Tal vez debas actualizar la tabla o presionar Live updates para ver los cambios.
- Agrega otro widget y selecciona otro contacto. Tal vez debas presionar Refresh table o Live updates para ver el modelo nuevo.
- Quita el widget y comprueba que el modelo queda en la base de datos después de quitarlo.
Para actualizar SociaLiteAppWidgetReceiver
y limpiar tu base de datos cuando se quita un widget, anula onDeleted
.
Para limpiar modelos de widget huérfanos, puedes llamar a WidgetModelRepository.cleanupWidgetModels
. Hilt administra la clase de repositorio, y debes usar la inserción de dependencias para acceder a su instancia.
- En
SociaLiteAppWidgetReceiver
, agrega la anotaciónAndroidEntryPoint
de Hilt a tu declaración de clase de receptor e inserta la instancia deWidgetModelRepository
. - Llama a
WidgetModelRepository.cleanupWidgetModels
en la anulación de método paraonDeleted
.
Tu código debería verse de la siguiente manera:
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)
}
}
- Vuelve a ejecutar la app. Deberías ver que la fila del modelo se quita del inspector de apps cuando se quita el widget de la pantalla principal.
5. Agrega la IU de contactos y actualízala cuando lleguen nuevos mensajes
Estás en la recta final del codelab. En esta sección, implementarás la IU de contactos final para el widget y la actualizarás cuando se reciba un mensaje no leído del contacto.
- Revisa la clase
WidgetModelRepository
en el paquetemodel
.
Aquí es donde tienes el método útil updateUnreadMessagesForContact
; actualiza los widgets asociados con el ID de un contacto.
//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)
}
}
}
Este método tiene dos parámetros: contactId
, el ID del contacto que se actualizará, y unread
, un booleano del estado del mensaje no leído. Este método usa WidgetModelDao
para encontrar todos los modelos de widget que muestran este contacto y actualizan el modelo con el nuevo estado de leído. Luego, la función llama al método SociaLiteAppWidget().updateAll
que proporcionó Glance para actualizar todos los widgets en la pantalla principal del usuario.
Ahora que sabes cómo se actualizan los widgets y su estado, puedes crear tu IU de contacto, enviar un mensaje y ver cómo se actualiza. Para ello, actualiza SociaLiteAppWidget
con una función de componibilidad FavoriteContact
en el diseño de tu widget. En este diseño, también comprueba si deberías mostrar No new messages
o New Messages!
.
- Revisa el archivo
FavoriteContact.kt
en el paquetecom.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),
),
)
}
}
}
- Reemplaza
Text("Hello World")
en la función de componibilidadContent
deSociaLiteAppWidget
por una llamada tu función de componibilidadFavoriteContact
.
Esta función de componibilidad tomará el WidgetModel y una Action creada por la función actionStartActivity
de Glance.
- Agrega la llamada a tu bloque
when
antes deZeroState
cuando el modelo no seaWidgetModel
.
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)
}
- Si Android Studio no agrega automáticamente las siguientes importaciones, hazlo ahora:
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
- Ejecuta tu app.
- Selecciona un contacto favorito, envía un mensaje y cierra la app de inmediato antes de que el contacto responda. El estado del widget debería cambiar cuando llegue la respuesta.
- Haz clic en el widget para abrir el chat y comprueba que el estado se haya vuelto a actualizar cuando regreses a la pantalla principal.
6. Felicitaciones
Completaste correctamente el codelab y aprendiste a escribir un widget con Glance. Ya tienes todas las herramientas para crear un widget atractivo que se vea bien en muchas pantallas principales, controle las entradas del usuario y se actualice solo.
Para obtener el código de la solución en la rama main
, sigue estos pasos:
- Si ya descargaste SociaLite, ejecuta este comando:
git checkout main
- De lo contrario, vuelve a descargar el código para ver la rama
main
:
git clone git@github.com:android/socialite.git